dspyteach is a Python CLI that analyzes one file or many files and generates either:
- a teaching brief (
--mode teach, default), or - a refactor prompt template (
--mode refactor)
It supports:
- single files and recursive directory scans
- repeated include globs (
-g/--glob) - directory exclusions (
-ed/--exclude-dirs) - local providers such as Ollama and LM Studio
- OpenAI-compatible hosted endpoints
- mirrored output directory layouts when
--output-diris set
The package centers on:
dspy_file/analyze_file_cli.py– CLI entrypoint and provider setupdspy_file/file_analyzer.py– teaching pipelinedspy_file/refactor_analyzer.py– refactor template pipelinedspy_file/file_helpers.py– file discovery, ignore rules, renderingdspy_file/prompts/– bundled refactor prompt templates
- Python
>=3.10,<3.12(frompyproject.toml) - a supported model backend:
- Ollama
- LM Studio
- OpenAI-compatible API
uvrecommended for local development
uv venv -p 3.11
source .venv/bin/activate
uv syncpip install dspyteachSmoke check:
dspyteach --helpDefault provider is Ollama.
dspyteach path/to/file.mdThe current default Ollama base URL is:
http://localhost:11434
LM Studio uses the OpenAI-compatible server at:
http://localhost:1234/v1
Example:
dspyteach path/to/project \
--provider lmstudio \
--model qwen_qwen3-4b-instruct-2507 \
--api-base http://localhost:1234/v1More details:
dspyteach path/to/project \
--provider openai \
--model gpt-5 \
--api-base https://your-endpoint.example/v1 \
--api-key YOUR_KEYThe CLI loads .env automatically via python-dotenv.
Common settings:
DSPYTEACH_PROVIDER=ollama
DSPYTEACH_MODEL=hf.co/Mungert/osmosis-mcp-4b-GGUF:Q5_K_M
#DSPYTEACH_API_BASE=http://localhost:1234/v1
#DSPYTEACH_API_KEY=lm-studio
#OPENAI_API_KEY=
#DSPYTEACH_LOG_PATH=.dspyteach/logs/custom.log
#DSPYTEACH_MAX_TOKENS=4000
#DSPYTEACH_LMSTUDIO_TIMEOUT_SECONDS=60Notes:
DSPYTEACH_API_KEYfalls back toOPENAI_API_KEYwhenDSPYTEACH_PROVIDER=openaiDSPYTEACH_LOG_PATHcontrols the runtime log file destinationDSPYTEACH_MAX_TOKENScontrols the root LM token budget used during DSPy configurationDSPYTEACH_LMSTUDIO_TIMEOUT_SECONDScontrols the LM Studio HTTP request timeout; set it to0,off,none, ordisabledto remove the timeout entirely
See:
dspyteach docs/example.mddspyteach ./repo -g '**/*.py' -g '**/*.md'dspyteach ./repo \
-g '**/*.md' \
-o ./outWhen --output-dir is set, the CLI mirrors the original relative directory layout inside the output directory and keeps the original filename.
dspyteach ./repo \
-g '**/*README*' \
-g '**/package.json' \
-g '**/pyproject.toml' \
-ed '.git,node_modules,.venv,dist,build' \
-o ./outdspyteach ./repo -g '**/*.md' --confirm-eachdspyteach docs/example.md --rawInclude globs are relative to the path you pass to dspyteach.
Good:
dspyteach ../../../ai-apps \
-g '**/README.md' \
-g '**/package.json' \
-g '**/pyproject.toml' \
-o ../../../ai-apps/.readMes/.outNot recommended:
# full absolute paths inside --glob are not needed
-g '~/projects/temp/ai-apps/**/README.md'Repeat -g once per pattern.
Default mode. Generates a teaching-oriented markdown brief.
dspyteach path/to/file.md --mode teachGenerates a refactor-oriented prompt template.
dspyteach path/to/file.md --mode refactorRefactor mode supports -p/--prompt:
dspyteach ./repo --mode refactor --prompt refactor_prompt_templateIf multiple bundled templates are available and you do not pass --prompt, the CLI will prompt you to choose one.
Current behavior:
- if
--output-diris omitted, the CLI writes output underdspy_file/data/ - if
--output-diris provided, the CLI writes into that directory and mirrors the source tree - teaching mode appends
.teaching.md; refactor mode appends.refactor.md --in-placerequests source replacement, but real overwrites require per-file confirmation- if
--in-placeis combined with--output-dir, the CLI writes the original filename into that directory instead of overwriting the source
This keeps the original source files unchanged unless you explicitly approve each overwrite.
Example:
dspyteach ../../../ai-apps \
-m teach \
-g '**/*README*' \
-o ../../../ai-apps/.readMes/.outRuntime logging is configured once at the CLI boundary.
Default log path pattern:
.dspyteach/logs/run-YYYYMMDD-HHMMSS-<pid>.log
Each CLI run now gets its own log file by default.
Override it with:
DSPYTEACH_LOG_PATH=/absolute/path/to/dspyteach.logThe logger uses a rotating file handler.
Batch runs now persist resumable state under:
.dspyteach/runs/<run_id>/
A saved run includes:
manifest.jsonwith run-level settings and status- per-file checkpoint JSON files under
files/ - stage data for teaching-mode resume, so cancelled files can continue from the last completed stage
By default, a fresh run gets a generated run id. You can also name one explicitly:
dspyteach ./repo -g '**/*.py' --run-id docs-pass-1Resume a saved run:
dspyteach ./repo -g '**/*.py' --resume docs-pass-1Resume validation is intentionally strict. The CLI expects the resumed run to match the original path, mode, provider/model settings, and scan filters.
List saved runs:
dspyteach --list-runsShow one run and its per-file checkpoint stages:
dspyteach --show-run docs-pass-1Machine-readable output is also available:
dspyteach --list-runs --json
dspyteach --show-run docs-pass-1 --jsonDelete a single saved run:
dspyteach --delete-run docs-pass-1Preview which runs would be pruned without deleting them:
dspyteach --prune-runs --prune-status failed --dry-runDelete completed runs older than 14 days:
dspyteach --prune-runs --prune-status completed --prune-older-than-days 14Preview prune results as JSON:
dspyteach --prune-runs \
--prune-status failed,completed_with_errors \
--prune-older-than-days 7 \
--dry-run \
--jsonPruning requires at least one filter:
--prune-status--prune-older-than-days
This is intentional so pruning cannot accidentally behave like a delete-all command.
Unless --keep-provider-alive is set, the CLI attempts to free local resources after the run:
- Ollama – stops the active model
- LM Studio – unloads matching loaded instances
dspyteach ./repo --provider lmstudio --keep-provider-alive- If Ollama cannot be reached, verify it is running on
http://localhost:11434 - If LM Studio cannot be reached, verify the local server is running on
http://localhost:1234/v1 - If you are scanning large trees, prefer
--output-dirplus--exclude-dirs - If one file fails during a batch, the CLI logs the exception and continues with the next file
- For debugging LM Studio integration, capture console output and inspect the runtime log file
Example verbose capture:
{ dspyteach ./repo -g '**/*.md'; } |& tee dspyteach.$(date +%Y%m%d-%H%M%S).logMaintainer release steps live in:
Tag workflow helper:
./scripts/tag-release.shPushing a tag matching v* triggers:
.github/workflows/release.yml
Targeted test runs used frequently in this repo:
uv run pytest -qFocused examples:
uv run pytest -q tests/test_cli_connectivity.py
uv run pytest -q tests/test_file_helpers.py
uv run pytest -q tests/test_lmstudio_structured.pySample generated outputs live under: