blitz-cli 0.10.0__tar.gz → 0.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/PKG-INFO +9 -9
  2. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/README.md +8 -8
  3. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/__init__.py +1 -1
  4. blitz_cli-0.10.0/blitz_cli/_implement.py → blitz_cli-0.11.0/blitz_cli/_execute.py +5 -5
  5. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_ontology.py +4 -4
  6. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/cli.py +26 -20
  7. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/implement.md +3 -3
  8. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/tasks.md +2 -2
  9. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/templates/workspace_playbook.md.tmpl +1 -1
  10. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/pyproject.toml +1 -1
  11. blitz_cli-0.11.0/specs/001-cli-model-namespace/checklists/requirements.md +34 -0
  12. blitz_cli-0.11.0/specs/001-cli-model-namespace/contracts/cli-commands.md +47 -0
  13. blitz_cli-0.11.0/specs/001-cli-model-namespace/plan.md +73 -0
  14. blitz_cli-0.11.0/specs/001-cli-model-namespace/quickstart.md +37 -0
  15. blitz_cli-0.11.0/specs/001-cli-model-namespace/research.md +34 -0
  16. blitz_cli-0.11.0/specs/001-cli-model-namespace/spec.md +69 -0
  17. blitz_cli-0.11.0/specs/001-cli-model-namespace/tasks.md +83 -0
  18. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_cli.py +19 -0
  19. blitz_cli-0.10.0/tests/test_implement.py → blitz_cli-0.11.0/tests/test_execute.py +27 -27
  20. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_ontology.py +2 -2
  21. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/.github/workflows/homebrew.yml +0 -0
  22. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/.github/workflows/publish.yml +0 -0
  23. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/.gitignore +0 -0
  24. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/.mcp.json +0 -0
  25. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/LICENSE +0 -0
  26. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_activity.py +0 -0
  27. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_code_graph.py +0 -0
  28. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_corpus.py +0 -0
  29. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_dataset.py +0 -0
  30. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_git.py +0 -0
  31. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_make.py +0 -0
  32. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_playbooks.py +0 -0
  33. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_redact.py +0 -0
  34. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_scaffold.py +0 -0
  35. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_tool_schemas.py +0 -0
  36. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/_train.py +0 -0
  37. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/__init__.py +0 -0
  38. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/__init__.py +0 -0
  39. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/analyze.md +0 -0
  40. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/clarify.md +0 -0
  41. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/plan.md +0 -0
  42. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/playbook.md +0 -0
  43. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/commands/specify.md +0 -0
  44. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/memory/playbook.md +0 -0
  45. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/scripts/bash/check-prerequisites.sh +0 -0
  46. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/scripts/bash/common.sh +0 -0
  47. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/scripts/bash/create-new-feature.sh +0 -0
  48. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/scripts/bash/setup-plan.sh +0 -0
  49. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/scripts/bash/setup-tasks.sh +0 -0
  50. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/templates/plan-template.md +0 -0
  51. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/templates/spec-template.md +0 -0
  52. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/scaffold/.blitz/templates/tasks-template.md +0 -0
  53. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/workspace/__init__.py +0 -0
  54. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/playbooks/workspace/huddle.md +0 -0
  55. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/templates/__init__.py +0 -0
  56. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/templates/locker_room_Makefile.tmpl +0 -0
  57. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/templates/locker_room_README.md.tmpl +0 -0
  58. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/blitz_cli/templates/locker_room_compose.yml.tmpl +0 -0
  59. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_activity.py +0 -0
  60. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_corpus.py +0 -0
  61. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_make.py +0 -0
  62. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_playbooks.py +0 -0
  63. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_redact.py +0 -0
  64. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_scaffold.py +0 -0
  65. {blitz_cli-0.10.0 → blitz_cli-0.11.0}/tests/test_tool_schemas.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blitz-cli
3
- Version: 0.10.0
3
+ Version: 0.11.0
4
4
  Summary: Developer CLI for Blitz locker-room: spec-driven playbooks, a codebase/huddle ontology graph with an MCP tool, and the composable watcher/trainer/graph containers.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -31,7 +31,7 @@ The core CLI is stdlib plus `rich`. Two optional extras carry heavier deps only
31
31
  when you need them: `blitz-cli[locker-room]` (the MCP server) and
32
32
  `blitz-cli[train]` (the QLoRA trainer).
33
33
 
34
- Top-level commands: `init`, `implement`, `locker-room`.
34
+ Top-level commands: `init`, `model`, `locker-room`.
35
35
 
36
36
  ## Playbooks: spec-driven development commands
37
37
 
@@ -51,9 +51,9 @@ and `analyze`. Run at a folder of repos, `init` installs into each repo it finds
51
51
  for coordinating a feature across repos. Use `--ignore PATH` to skip a repo and
52
52
  `--root` to force a single install.
53
53
 
54
- ## Implement: execute a tasks.md with a local model
54
+ ## Model execute: run a tasks.md with a local model
55
55
 
56
- `blitz implement` drives a local model (served by Ollama) through a
56
+ `blitz model execute` drives a local model (served by Ollama) through a
57
57
  playbook-generated `tasks.md`, one task at a time. The model only proposes
58
58
  actions (full-file writes and shell commands); the driver applies them, runs
59
59
  each phase's machine-runnable verification, retries with the failure fed back
@@ -61,17 +61,17 @@ each phase's machine-runnable verification, retries with the failure fed back
61
61
  `- [x]` in the tasks.md. A task that keeps failing stops the run so the state
62
62
  stays inspectable. Per-attempt model replies, snapshots, and exact token
63
63
  accounting (`usage.json`, per task and total) land under
64
- `.blitz/implement/runs/<timestamp>/`.
64
+ `.blitz/execute/runs/<timestamp>/`.
65
65
 
66
66
  ```bash
67
- blitz implement # auto-detect the open specs/*/tasks.md
68
- blitz implement specs/001-my-feature/tasks.md # or name it
69
- blitz implement --model qwen2.5-14b-28k --start-at T005
67
+ blitz model execute # auto-detect the open specs/*/tasks.md
68
+ blitz model execute specs/001-my-feature/tasks.md # or name it
69
+ blitz model execute --model qwen2.5-14b-28k --start-at T005
70
70
  ```
71
71
 
72
72
  Tasks whose text says "optional manual" are skipped and left unchecked. The
73
73
  Ollama endpoint comes from `--ollama-url` or `OLLAMA_HOST`; the model from
74
- `--model` or `BLITZ_IMPLEMENT_MODEL`.
74
+ `--model` or `BLITZ_EXECUTE_MODEL`.
75
75
 
76
76
  ## Locker-room: ontology graph, watcher, trainer, MCP
77
77
 
@@ -12,7 +12,7 @@ The core CLI is stdlib plus `rich`. Two optional extras carry heavier deps only
12
12
  when you need them: `blitz-cli[locker-room]` (the MCP server) and
13
13
  `blitz-cli[train]` (the QLoRA trainer).
14
14
 
15
- Top-level commands: `init`, `implement`, `locker-room`.
15
+ Top-level commands: `init`, `model`, `locker-room`.
16
16
 
17
17
  ## Playbooks: spec-driven development commands
18
18
 
@@ -32,9 +32,9 @@ and `analyze`. Run at a folder of repos, `init` installs into each repo it finds
32
32
  for coordinating a feature across repos. Use `--ignore PATH` to skip a repo and
33
33
  `--root` to force a single install.
34
34
 
35
- ## Implement: execute a tasks.md with a local model
35
+ ## Model execute: run a tasks.md with a local model
36
36
 
37
- `blitz implement` drives a local model (served by Ollama) through a
37
+ `blitz model execute` drives a local model (served by Ollama) through a
38
38
  playbook-generated `tasks.md`, one task at a time. The model only proposes
39
39
  actions (full-file writes and shell commands); the driver applies them, runs
40
40
  each phase's machine-runnable verification, retries with the failure fed back
@@ -42,17 +42,17 @@ each phase's machine-runnable verification, retries with the failure fed back
42
42
  `- [x]` in the tasks.md. A task that keeps failing stops the run so the state
43
43
  stays inspectable. Per-attempt model replies, snapshots, and exact token
44
44
  accounting (`usage.json`, per task and total) land under
45
- `.blitz/implement/runs/<timestamp>/`.
45
+ `.blitz/execute/runs/<timestamp>/`.
46
46
 
47
47
  ```bash
48
- blitz implement # auto-detect the open specs/*/tasks.md
49
- blitz implement specs/001-my-feature/tasks.md # or name it
50
- blitz implement --model qwen2.5-14b-28k --start-at T005
48
+ blitz model execute # auto-detect the open specs/*/tasks.md
49
+ blitz model execute specs/001-my-feature/tasks.md # or name it
50
+ blitz model execute --model qwen2.5-14b-28k --start-at T005
51
51
  ```
52
52
 
53
53
  Tasks whose text says "optional manual" are skipped and left unchecked. The
54
54
  Ollama endpoint comes from `--ollama-url` or `OLLAMA_HOST`; the model from
55
- `--model` or `BLITZ_IMPLEMENT_MODEL`.
55
+ `--model` or `BLITZ_EXECUTE_MODEL`.
56
56
 
57
57
  ## Locker-room: ontology graph, watcher, trainer, MCP
58
58
 
@@ -30,4 +30,4 @@ Commands:
30
30
 
31
31
  __all__ = ["__version__"]
32
32
 
33
- __version__ = "0.10.0"
33
+ __version__ = "0.11.0"
@@ -1,6 +1,6 @@
1
1
  """Drive a local model (served by Ollama) through a playbook-generated
2
2
  tasks.md, one task at a time — the local-model execution backend for the
3
- `implement` playbook.
3
+ `blitz model execute` command.
4
4
 
5
5
  The model never touches the repo directly: it only proposes actions
6
6
  (full-file ===WRITE=== blocks and ===RUN=== commands) and this driver applies
@@ -237,12 +237,12 @@ def local_model_config(repo: Path) -> dict:
237
237
 
238
238
 
239
239
  def usage_by_spec(repo: Path) -> dict:
240
- """Aggregate every implement run's usage.json under
241
- .blitz/implement/runs/, keyed by spec: total input/output tokens, model
240
+ """Aggregate every execute run's usage.json under
241
+ .blitz/execute/runs/, keyed by spec: total input/output tokens, model
242
242
  calls, run count, and the models used. This is what the locker-room
243
243
  ontologizer attaches to Feature nodes so the UI can show per-spec cost."""
244
244
  totals: dict = {}
245
- for f in sorted(repo.glob(".blitz/implement/runs/*/usage.json")):
245
+ for f in sorted(repo.glob(".blitz/execute/runs/*/usage.json")):
246
246
  try:
247
247
  record = json.loads(f.read_text())
248
248
  except (OSError, ValueError):
@@ -289,7 +289,7 @@ def run(
289
289
  """Execute every unchecked task in tasks_file. Returns 0 on success, 1 when a
290
290
  task fails after all retries (the run stops there so state stays inspectable)."""
291
291
  call_model = call_model or (lambda messages: call_ollama(ollama_url, model, messages))
292
- run_root = run_root or (repo / ".blitz" / "implement" / "runs")
292
+ run_root = run_root or (repo / ".blitz" / "execute" / "runs")
293
293
  rundir = run_root / datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
294
294
  snapdir = rundir / "snapshots"
295
295
  snapdir.mkdir(parents=True, exist_ok=True)
@@ -16,7 +16,7 @@ import re
16
16
  import sqlite3
17
17
  from pathlib import Path
18
18
 
19
- from blitz_cli import _activity, _implement, _playbooks
19
+ from blitz_cli import _activity, _execute, _playbooks
20
20
 
21
21
  # --- graph store -------------------------------------------------------------
22
22
 
@@ -349,10 +349,10 @@ def _read_active_feature(repo: Path) -> str | None:
349
349
 
350
350
  def _tasks_complete(tasks_md: Path) -> bool:
351
351
  """True when every required task in a tasks.md is checked off. Tasks whose
352
- text says "optional manual" don't count against completion (the implement
352
+ text says "optional manual" don't count against completion (the execute
353
353
  driver skips them by design)."""
354
354
  try:
355
- tasks, _ = _implement.parse_tasks(tasks_md.read_text(encoding="utf-8"))
355
+ tasks, _ = _execute.parse_tasks(tasks_md.read_text(encoding="utf-8"))
356
356
  except OSError:
357
357
  return False
358
358
  required = [t for t in tasks if "optional manual" not in t["body"].lower()]
@@ -527,7 +527,7 @@ def ontologize(workspace_root, db_path, ignore=None, code=True,
527
527
  _code_graph.scan_repo(store, repo, rid, max_file_bytes=max_file_bytes)
528
528
 
529
529
  active = _read_active_feature(repo)
530
- usage = _implement.usage_by_spec(repo)
530
+ usage = _execute.usage_by_spec(repo)
531
531
  specs = repo / "specs"
532
532
  if specs.is_dir():
533
533
  for feat in sorted(specs.iterdir()):
@@ -59,8 +59,8 @@ def _cmd_locker_room_make(args: argparse.Namespace) -> int:
59
59
 
60
60
 
61
61
 
62
- def _cmd_implement(args: argparse.Namespace) -> int:
63
- from blitz_cli import _implement
62
+ def _cmd_execute(args: argparse.Namespace) -> int:
63
+ from blitz_cli import _execute
64
64
 
65
65
  repo = Path(args.repo).resolve()
66
66
  if args.tasks:
@@ -68,7 +68,7 @@ def _cmd_implement(args: argparse.Namespace) -> int:
68
68
  if not tasks_file.is_absolute():
69
69
  tasks_file = repo / tasks_file
70
70
  else:
71
- tasks_file = _implement.find_tasks_file(repo)
71
+ tasks_file = _execute.find_tasks_file(repo)
72
72
  if tasks_file is None:
73
73
  _eprint(
74
74
  f"error: could not auto-detect a unique specs/*/tasks.md with "
@@ -80,16 +80,16 @@ def _cmd_implement(args: argparse.Namespace) -> int:
80
80
  _eprint(f"error: no tasks file at {tasks_file}.")
81
81
  return 2
82
82
 
83
- # Model resolution: --model flag > BLITZ_IMPLEMENT_MODEL env >
83
+ # Model resolution: --model flag > BLITZ_EXECUTE_MODEL env >
84
84
  # .blitz/config.json local_model.model (repo, then workspace) > default.
85
85
  model = (
86
86
  args.model
87
- or os.environ.get("BLITZ_IMPLEMENT_MODEL")
88
- or _implement.local_model_config(repo).get("model")
89
- or _implement.DEFAULT_MODEL
87
+ or os.environ.get("BLITZ_EXECUTE_MODEL")
88
+ or _execute.local_model_config(repo).get("model")
89
+ or _execute.DEFAULT_MODEL
90
90
  )
91
91
 
92
- return _implement.run(
92
+ return _execute.run(
93
93
  repo=repo,
94
94
  tasks_file=tasks_file,
95
95
  model=model,
@@ -290,48 +290,54 @@ def build_parser() -> argparse.ArgumentParser:
290
290
  sub = parser.add_subparsers(dest="cmd", required=True)
291
291
 
292
292
 
293
- impl = sub.add_parser(
294
- "implement",
293
+ model = sub.add_parser(
294
+ "model",
295
+ help="Model lifecycle commands (e.g. execute a tasks.md with a local model).",
296
+ )
297
+ model_sub = model.add_subparsers(dest="model_cmd", required=True)
298
+
299
+ execute = model_sub.add_parser(
300
+ "execute",
295
301
  help="Execute a playbook-generated tasks.md with a local model served by "
296
302
  "Ollama: one task per call, driver-side verification, bounded retries, "
297
- "snapshots under .blitz/implement/runs/.",
303
+ "snapshots under .blitz/execute/runs/.",
298
304
  )
299
- impl.add_argument(
305
+ execute.add_argument(
300
306
  "tasks", nargs="?", default=None,
301
307
  help="Path to the tasks.md (relative to --repo). Default: the unique "
302
308
  "specs/*/tasks.md that still has unchecked tasks.",
303
309
  )
304
- impl.add_argument(
310
+ execute.add_argument(
305
311
  "--repo", default=".", help="Repo root the tasks operate on (default: current dir)."
306
312
  )
307
- impl.add_argument(
313
+ execute.add_argument(
308
314
  "--model",
309
315
  default=None,
310
316
  help="Ollama model to drive. Default resolution: this flag, then "
311
- "BLITZ_IMPLEMENT_MODEL, then local_model.model in .blitz/config.json "
317
+ "BLITZ_EXECUTE_MODEL, then local_model.model in .blitz/config.json "
312
318
  "(repo, then workspace), then qwen2.5-14b-28k.",
313
319
  )
314
- impl.add_argument(
320
+ execute.add_argument(
315
321
  "--local-only", action="store_true",
316
322
  help="Execute only tasks marked [L] (local-model eligible), stopping at "
317
323
  "the first unchecked task without the marker so an orchestrating agent "
318
324
  "can take over. Used to split a tasks.md between the local model and a "
319
325
  "frontier agent.",
320
326
  )
321
- impl.add_argument(
327
+ execute.add_argument(
322
328
  "--ollama-url",
323
329
  default=os.environ.get("OLLAMA_HOST", "http://localhost:11434"),
324
330
  help="Ollama base URL (default http://localhost:11434; env OLLAMA_HOST).",
325
331
  )
326
- impl.add_argument(
332
+ execute.add_argument(
327
333
  "--max-retries", type=int, default=2,
328
334
  help="Retries per task with the failure fed back before stopping the run (default 2).",
329
335
  )
330
- impl.add_argument(
336
+ execute.add_argument(
331
337
  "--start-at", default=None, metavar="TASK_ID",
332
338
  help="Skip ahead to this task id (e.g. T005) before executing.",
333
339
  )
334
- impl.set_defaults(func=_cmd_implement)
340
+ execute.set_defaults(func=_cmd_execute)
335
341
 
336
342
  sp_init = sub.add_parser(
337
343
  "init",
@@ -151,15 +151,15 @@ You **MUST** consider the user input before proceeding (if not empty).
151
151
  that is the point of the marker: the user is paying for frontier tokens only where needed.
152
152
  - Work through the task list in ID order with this loop:
153
153
  1. Find the next unchecked, non-"optional manual" task.
154
- 2. If it is marked `[L]`: run `blitz implement <path-to-tasks.md> --local-only --start-at <TaskID>`
154
+ 2. If it is marked `[L]`: run `blitz model execute <path-to-tasks.md> --local-only --start-at <TaskID>`
155
155
  and wait. The command executes the contiguous run of `[L]` tasks, checks them off in
156
156
  tasks.md, and exits 0 when it reaches a task without the marker (or finishes).
157
157
  Exit 1 means a local task failed after retries — take that task over yourself
158
- (its per-attempt replies are in `.blitz/implement/runs/<stamp>/`), mark it off,
158
+ (its per-attempt replies are in `.blitz/execute/runs/<stamp>/`), mark it off,
159
159
  then resume the loop.
160
160
  3. If it is NOT marked `[L]`: execute it yourself as described below, mark it off,
161
161
  and resume the loop.
162
- 4. After every `blitz implement` invocation, RE-READ tasks.md — the file changed on disk.
162
+ 4. After every `blitz model execute` invocation, RE-READ tasks.md — the file changed on disk.
163
163
  - Never execute an `[L]` task yourself while the CLI is available unless it has already
164
164
  failed a local run.
165
165
 
@@ -206,7 +206,7 @@ files that are exactly specified and unforgiving of drift.
206
206
  ### Executor Annotation ([L] marker)
207
207
 
208
208
  Task lists can be executed by a mix of a frontier agent and the user's own local model (via
209
- `blitz implement --local-only`). Whether a local model is available — and what it can handle —
209
+ `blitz model execute --local-only`). Whether a local model is available — and what it can handle —
210
210
  is configured, not guessed:
211
211
 
212
212
  1. Read `.blitz/config.json` in the repo root. If it has no `local_model` key, read
@@ -240,7 +240,7 @@ is configured, not guessed:
240
240
 
241
241
  4. Place `[L]` after `[P]` and before the `[Story]` label:
242
242
  `- [ ] T003 [P] [L] [US1] CREATE ...`
243
- 5. The marker is load-bearing and machine-parsed: `blitz implement --local-only` executes
243
+ 5. The marker is load-bearing and machine-parsed: `blitz model execute --local-only` executes
244
244
  consecutive `[L]` tasks and stops at the first task without the marker, handing control back
245
245
  to the orchestrating agent. Keep runs of local-eligible tasks contiguous where dependencies
246
246
  allow — interleaving one frontier task between local ones forces a round-trip per task.
@@ -21,7 +21,7 @@ and what code a feature touches via lineage queries.
21
21
  ## Executor routing
22
22
 
23
23
  `.blitz/config.json` may define a `local_model`. Tasks marked `[L]` in a
24
- tasks.md are executable by that model via `blitz implement --local-only`;
24
+ tasks.md are executable by that model via `blitz model execute --local-only`;
25
25
  frontier agents handle the rest. See the /implement command for the handoff
26
26
  loop.
27
27
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "blitz-cli"
3
- version = "0.10.0"
3
+ version = "0.11.0"
4
4
  description = "Developer CLI for Blitz locker-room: spec-driven playbooks, a codebase/huddle ontology graph with an MCP tool, and the composable watcher/trainer/graph containers."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -0,0 +1,34 @@
1
+ # Specification Quality Checklist: CLI model namespace (retire "implement")
2
+
3
+ **Purpose**: Validate specification completeness and quality before proceeding to planning
4
+ **Created**: 2026-07-14
5
+ **Feature**: [spec.md](../spec.md)
6
+
7
+ ## Content Quality
8
+
9
+ - [x] No implementation details (languages, frameworks, APIs)
10
+ - [x] Focused on user value and business needs
11
+ - [x] Written for non-technical stakeholders
12
+ - [x] All mandatory sections completed
13
+
14
+ ## Requirement Completeness
15
+
16
+ - [x] No [NEEDS CLARIFICATION] markers remain
17
+ - [x] Requirements are testable and unambiguous
18
+ - [x] Success criteria are measurable
19
+ - [x] Success criteria are technology-agnostic (no implementation details)
20
+ - [x] All acceptance scenarios are defined
21
+ - [x] Edge cases are identified
22
+ - [x] Scope is clearly bounded
23
+ - [x] Dependencies and assumptions identified
24
+
25
+ ## Feature Readiness
26
+
27
+ - [x] All functional requirements have clear acceptance criteria
28
+ - [x] User scenarios cover primary flows
29
+ - [x] Feature meets measurable outcomes defined in Success Criteria
30
+ - [x] No implementation details leak into specification
31
+
32
+ ## Notes
33
+
34
+ - Hard-rename decision resolved with the user; no open clarifications. Spec ready for `/plan`.
@@ -0,0 +1,47 @@
1
+ # CLI Command Surface Contract
2
+
3
+ ## Before
4
+
5
+ ```
6
+ blitz implement [tasks] [--repo DIR] [--model M] [--local-only]
7
+ [--ollama-url URL] [--max-retries N] [--start-at TASK_ID]
8
+ ```
9
+
10
+ ## After
11
+
12
+ ```
13
+ blitz model execute [tasks] [--repo DIR] [--model M] [--local-only]
14
+ [--ollama-url URL] [--max-retries N] [--start-at TASK_ID]
15
+ ```
16
+
17
+ - `blitz model` (no subcommand) → prints `model` group help, exits non-zero (subparser `required=True`).
18
+ - `blitz model execute …` → identical arguments, defaults, behavior, and exit codes as the former `blitz implement`. Handler: `_cmd_execute`.
19
+ - `blitz --help` → lists `model`; does NOT list `implement`.
20
+ - `blitz model --help` → lists `execute` with the help text formerly on `implement`.
21
+ - `blitz implement …` → **removed**. Argparse default "invalid choice: 'implement'" error, non-zero exit. No stub, no forwarding.
22
+
23
+ ## Arguments carried onto `execute` (unchanged)
24
+
25
+ | Arg | Type | Default |
26
+ |-----|------|---------|
27
+ | `tasks` | positional, optional | None (auto-detect open `specs/*/tasks.md`) |
28
+ | `--repo` | str | `.` |
29
+ | `--model` | str | None (resolves via flag → `BLITZ_EXECUTE_MODEL` → config → `qwen2.5-14b-28k`) |
30
+ | `--local-only` | flag | False |
31
+ | `--ollama-url` | str | `$OLLAMA_HOST` or `http://localhost:11434` |
32
+ | `--max-retries` | int | 2 |
33
+ | `--start-at` | str (TASK_ID) | None |
34
+
35
+ ## Renamed identifiers (breaking, no migration)
36
+
37
+ | Old | New |
38
+ |-----|-----|
39
+ | env `BLITZ_IMPLEMENT_MODEL` | env `BLITZ_EXECUTE_MODEL` |
40
+ | path `.blitz/implement/runs/` | path `.blitz/execute/runs/` |
41
+ | module `blitz_cli/_implement.py` | module `blitz_cli/_execute.py` |
42
+ | handler `_cmd_implement` | handler `_cmd_execute` |
43
+ | test `tests/test_implement.py` | test `tests/test_execute.py` |
44
+
45
+ ## Not changed
46
+
47
+ The `/implement` playbook SDD slash-command, the `"implemented"` lifecycle stage, and generic "implementation" prose are out of scope.
@@ -0,0 +1,73 @@
1
+ # Implementation Plan: CLI model namespace (retire "implement")
2
+
3
+ **Branch**: `001-cli-model-namespace` | **Date**: 2026-07-14 | **Spec**: [spec.md](./spec.md)
4
+
5
+ **Input**: Feature specification from `specs/001-cli-model-namespace/spec.md`
6
+
7
+ ## Summary
8
+
9
+ Move the top-level `blitz implement` subcommand under a new `model` command group and rename it to `execute` (`blitz model execute`), and purge the "implement" nomenclature that names this command. The engine behavior is reused unchanged, but the module, handler, env var, and run-snapshot path are all renamed to "execute". The old `blitz implement` is removed outright (no error stub) — argparse reports an invalid choice. This is a breaking change with no migration of existing `.blitz/implement/runs/` snapshots or `BLITZ_IMPLEMENT_MODEL` settings.
10
+
11
+ Out of scope (distinct concept, NOT renamed): the `/implement` playbook SDD slash-command and its `__SPECKIT_COMMAND_IMPLEMENT__` token; the `"implemented"` feature lifecycle stage in `_ontology.py`; generic "implementation" prose.
12
+
13
+ ## Technical Context
14
+
15
+ **Language/Version**: Python 3 (stdlib + `rich`); `argparse` CLI.
16
+ **Primary Dependencies**: stdlib `argparse`; no new deps.
17
+ **Storage**: On-disk run snapshots move `.blitz/implement/runs/` → `.blitz/execute/runs/` (breaking, no migration).
18
+ **Testing**: `pytest`; `tests/test_implement.py` → `tests/test_execute.py`; plus `tests/test_cli.py`, `tests/test_ontology.py`.
19
+ **Target Platform**: Local developer machines (pip-installed CLI).
20
+ **Project Type**: Single-project CLI (`blitz_cli/`).
21
+ **Performance Goals**: N/A (rename/refactor).
22
+ **Constraints**: Backward-incompatible by design; no compatibility shims.
23
+ **Scale/Scope**: One subparser relocation+rename, module/handler/env/path rename across ~4 source files + tests + docs.
24
+
25
+ ## Playbook Check
26
+
27
+ `.blitz/memory/playbook.md` is the unfilled template — no concrete gates. PASS.
28
+
29
+ ## Project Structure
30
+
31
+ ### Documentation (this feature)
32
+
33
+ ```text
34
+ specs/001-cli-model-namespace/
35
+ ├── plan.md · research.md · quickstart.md
36
+ ├── contracts/cli-commands.md
37
+ ├── checklists/requirements.md
38
+ └── tasks.md
39
+ ```
40
+
41
+ (No data-model.md — no entities.)
42
+
43
+ ### Source Code — files touched
44
+
45
+ ```text
46
+ blitz_cli/
47
+ ├── cli.py # EDIT: build_parser() — replace top-level `implement` parser with
48
+ │ # `model` group + `execute` child; rename _cmd_implement → _cmd_execute;
49
+ │ # import _execute; read BLITZ_EXECUTE_MODEL; update help text
50
+ ├── _implement.py # RENAME → _execute.py; update `.blitz/implement/runs` → `.blitz/execute/runs`
51
+ │ # (lines ~240,241,245,292) and the module docstring
52
+ ├── _ontology.py # EDIT: import _execute (was _implement); `.blitz/implement/runs` → `.blitz/execute/runs`
53
+ │ # at line ~68-equivalent usage; KEEP "implemented" stage + prose
54
+ README.md # EDIT: command list, examples, BLITZ_IMPLEMENT_MODEL → BLITZ_EXECUTE_MODEL,
55
+ │ # `.blitz/implement/runs` → `.blitz/execute/runs`
56
+ blitz_cli/playbooks/commands/implement.md # EDIT: `blitz implement … --local-only` → `blitz model execute …`
57
+ blitz_cli/playbooks/commands/tasks.md # EDIT: same CLI-ref update
58
+ blitz_cli/templates/workspace_playbook.md.tmpl # EDIT: same CLI-ref update
59
+ tests/test_implement.py # RENAME → tests/test_execute.py; `_implement` → `_execute`; path updates
60
+ tests/test_cli.py # EDIT/ADD: assert `model execute` wired; `implement` absent
61
+ tests/test_ontology.py # EDIT: `.blitz/implement/runs` → `.blitz/execute/runs` (line ~68); test name optional
62
+ ```
63
+
64
+ **Structure Decision**: Single-project CLI; `model` group follows the `locker-room` nested-subparser pattern. `execute` carries the identical argument set and `func=_cmd_execute`.
65
+
66
+ ## Complexity Tracking
67
+
68
+ No playbook violations.
69
+
70
+ ## Phases
71
+
72
+ - **Phase 0 (research.md)**: decisions recorded — hard rename (no stub), nested `model` group, full nomenclature purge (breaking), out-of-scope list, playbook-ref updates.
73
+ - **Phase 1 (contracts/, quickstart.md)**: command surface + renamed-identifier table; runnable validation incl. a grep for stray nomenclature. No data-model.md.
@@ -0,0 +1,37 @@
1
+ # Quickstart / Validation: model namespace
2
+
3
+ Prerequisites: editable install of blitz-cli (`pip install -e .`) or run via `python -m blitz_cli`.
4
+
5
+ ## Validate the new surface
6
+
7
+ ```bash
8
+ blitz --help # expect: `model` listed; `implement` NOT listed
9
+ blitz model --help # expect: `execute` subcommand listed
10
+ blitz model execute --help # expect: same flags as the old `blitz implement --help`
11
+ ```
12
+
13
+ ## Validate the old command is gone
14
+
15
+ ```bash
16
+ blitz implement ; echo "exit=$?" # expect: argparse "invalid choice: 'implement'"; exit != 0
17
+ ```
18
+
19
+ ## Validate behavior parity + renamed path/env
20
+
21
+ Run `blitz model execute` against a spec's tasks.md as before; confirm identical driver behavior and that snapshots are written under `.blitz/execute/runs/` (not `.blitz/implement/runs/`). Confirm `BLITZ_EXECUTE_MODEL` selects the model.
22
+
23
+ ## No stray nomenclature
24
+
25
+ ```bash
26
+ grep -rnE "blitz implement|BLITZ_IMPLEMENT_MODEL|\.blitz/implement/runs|_cmd_implement|_implement" \
27
+ blitz_cli README.md tests
28
+ # expect: no matches (out-of-scope /implement slash-command & "implemented" stage excluded from this pattern set)
29
+ ```
30
+
31
+ ## Test suite
32
+
33
+ ```bash
34
+ pytest -q # expect: all pass, including renamed tests/test_execute.py
35
+ ```
36
+
37
+ Contract reference: [contracts/cli-commands.md](./contracts/cli-commands.md).
@@ -0,0 +1,34 @@
1
+ # Research: CLI model namespace (retire "implement")
2
+
3
+ ## Decision 1 — Hard rename, no error stub
4
+
5
+ - **Decision**: Delete the `implement` parser entirely. `blitz implement` produces argparse's default "invalid choice" error and non-zero exit.
6
+ - **Rationale**: User explicitly does not want an error stub forwarding to `model execute`. Simplest surface; no dangling "implement" reference.
7
+ - **Alternatives considered**: Error stub naming the replacement — rejected by user.
8
+
9
+ ## Decision 2 — `model` group mirrors the `locker-room` pattern
10
+
11
+ - **Decision**: `model = sub.add_parser("model", ...)`; `model_sub = model.add_subparsers(dest="model_cmd", required=True)`; register `execute` under it.
12
+ - **Rationale**: `build_parser()` already uses this nested pattern for `locker-room`; stay consistent and extensible.
13
+
14
+ ## Decision 3 — Full nomenclature purge (breaking, no migration)
15
+
16
+ - **Decision**: Rename everything that names this command:
17
+ - env var `BLITZ_IMPLEMENT_MODEL` → `BLITZ_EXECUTE_MODEL`
18
+ - run-snapshot path `.blitz/implement/runs/` → `.blitz/execute/runs/`
19
+ - module `blitz_cli/_implement.py` → `blitz_cli/_execute.py`
20
+ - handler `_cmd_implement` → `_cmd_execute`
21
+ - test file `tests/test_implement.py` → `tests/test_execute.py`
22
+ - all imports/usages in `cli.py`, `_ontology.py`, tests
23
+ - **Rationale**: User wants the "implement" nomenclature gone. These are internal but user-facing (env var, on-disk path).
24
+ - **Consequence**: Existing `.blitz/implement/runs/` snapshots and `BLITZ_IMPLEMENT_MODEL` settings stop working; no migration is provided (accepted by user).
25
+
26
+ ## Decision 4 — What is explicitly NOT renamed
27
+
28
+ - The `/implement` playbook SDD slash-command and its `__SPECKIT_COMMAND_IMPLEMENT__` token — a separate lifecycle verb shared across the playbook ecosystem.
29
+ - The `"implemented"` feature lifecycle stage in `_ontology.py` (`_infer_stage`) and its test.
30
+ - Generic English "implementation"/"implementer" prose (docstrings, plan-template title, SYSTEM_PROMPT).
31
+
32
+ ## Decision 5 — Rendered playbook CLI references
33
+
34
+ - **Decision**: Update `blitz implement … --local-only` invocations inside `playbooks/commands/implement.md`, `playbooks/commands/tasks.md`, and `templates/workspace_playbook.md.tmpl` to `blitz model execute …`. Do not rename the `/implement` slash-command itself.
@@ -0,0 +1,69 @@
1
+ # Feature Specification: CLI model namespace (retire "implement")
2
+
3
+ **Feature Branch**: `001-cli-model-namespace`
4
+
5
+ **Created**: 2026-07-14
6
+
7
+ **Status**: Draft
8
+
9
+ **Input**: User description: "Update blitz-cli to put the implement command under a 'model' namespace and rename implement to execute (`blitz model execute`). Hard rename with no error stub, and remove the 'implement' nomenclature everywhere it names this command — including the env var, the run-snapshot path, and internal module/handler names."
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Run the execution flow via the new namespaced command (Priority: P1)
14
+
15
+ A developer who previously ran `blitz implement` now runs `blitz model execute` and gets the identical behavior, flags, and output. The command lives under a new `model` command group so related model-lifecycle commands can be added there later.
16
+
17
+ **Why this priority**: This is the core of the feature.
18
+
19
+ **Independent Test**: Run `blitz model execute` with the arguments previously passed to `blitz implement` and confirm identical behavior. `blitz model --help` lists `execute`; `blitz --help` lists `model` and not `implement`.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** a repo where `blitz implement <args>` previously worked, **When** the user runs `blitz model execute <args>`, **Then** the command performs the same work and produces equivalent output/exit code.
24
+ 2. **Given** the CLI is installed, **When** the user runs `blitz model --help`, **Then** `execute` appears with the help text formerly shown for `implement`.
25
+ 3. **Given** the CLI is installed, **When** the user runs `blitz --help`, **Then** `model` appears and `implement` does not.
26
+ 4. **Given** the CLI is installed, **When** the user runs `blitz implement`, **Then** the standard argparse "invalid choice" error is shown and exit is non-zero (no special stub, no forwarding).
27
+
28
+ ### Edge Cases
29
+
30
+ - `blitz model` with no subcommand: prints the `model` group help and exits non-zero (subparser required).
31
+ - All flags/positional arguments previously accepted by `implement` are accepted unchanged by `execute`.
32
+ - The model-selection env var is now `BLITZ_EXECUTE_MODEL`; the old `BLITZ_IMPLEMENT_MODEL` is no longer read (breaking change, accepted).
33
+ - Run snapshots are now written under `.blitz/execute/runs/`; the old `.blitz/implement/runs/` path is no longer read or written (breaking change, accepted — no migration).
34
+
35
+ ## Requirements *(mandatory)*
36
+
37
+ ### Functional Requirements
38
+
39
+ - **FR-001**: The CLI MUST expose a `model` command group at the top level.
40
+ - **FR-002**: The CLI MUST provide `model execute` performing exactly the behavior of the former top-level `implement` command, with identical flags, positional arguments, defaults, and exit codes.
41
+ - **FR-003**: The CLI MUST NOT expose a top-level `implement` command in any form (no working command and no special error stub); invoking `blitz implement` yields the default argparse invalid-choice failure.
42
+ - **FR-004**: Help output MUST reflect the new structure: `model` at top level, `execute` under `model`, no `implement` anywhere.
43
+ - **FR-005**: README and any in-repo documentation or rendered playbook command text referencing the `blitz implement` CLI invocation MUST be updated to `blitz model execute`.
44
+ - **FR-006**: The "implement" nomenclature naming THIS command MUST be removed from the code: the env var `BLITZ_IMPLEMENT_MODEL` becomes `BLITZ_EXECUTE_MODEL`; the run-snapshot path `.blitz/implement/runs/` becomes `.blitz/execute/runs/`; the module `_implement.py` becomes `_execute.py`; the handler `_cmd_implement` becomes `_cmd_execute`; all imports/references update accordingly.
45
+ - **FR-007**: The existing test suite MUST be updated to the new command, env var, path, and module names (including renaming `tests/test_implement.py` to `tests/test_execute.py`), and a test MUST assert `blitz model execute` is wired and no top-level `implement` command exists.
46
+
47
+ **Out of scope (NOT renamed)**: the separate `/implement` playbook SDD lifecycle slash-command and its `__SPECKIT_COMMAND_IMPLEMENT__` token; the "implemented" feature lifecycle stage in `_ontology.py`; generic English "implementation" prose. These are distinct from the `blitz implement` CLI command.
48
+
49
+ ### Key Entities
50
+
51
+ - **`model` command group**: A namespace for model-lifecycle commands; currently holds `execute`.
52
+ - **`execute` subcommand**: The relocated, renamed former `implement` command; unchanged behavior.
53
+
54
+ ## Success Criteria *(mandatory)*
55
+
56
+ ### Measurable Outcomes
57
+
58
+ - **SC-001**: 100% of the behavior/flags previously under `blitz implement` are available under `blitz model execute`.
59
+ - **SC-002**: `blitz implement` no longer runs anything; it exits non-zero with the standard invalid-choice error.
60
+ - **SC-003**: No occurrence of the strings `blitz implement`, `BLITZ_IMPLEMENT_MODEL`, `.blitz/implement/runs`, `_implement`, or `_cmd_implement` remains in `blitz_cli/`, `README.md`, `tests/`, or rendered playbook CLI references (verifiable by search). Matches limited to the out-of-scope items above are acceptable.
61
+ - **SC-004**: The full test suite passes after the rename.
62
+
63
+ ## Assumptions
64
+
65
+ - Hard rename confirmed: no alias, no error stub. `blitz implement` simply ceases to exist as a choice.
66
+ - Full nomenclature purge confirmed for env var, run-snapshot path, and internal module/handler names — breaking, with no migration of existing `.blitz/implement/runs/` snapshots or `BLITZ_IMPLEMENT_MODEL` settings.
67
+ - The `_execute` engine behavior (task driving, retries, snapshots) is otherwise reused unchanged; only names and the snapshot directory change.
68
+ - `homebrew-tap` and other repos are unaffected (they package the binary, not subcommands).
69
+ - The `/implement` playbook SDD command and the `"implemented"` lifecycle stage are explicitly out of scope.
@@ -0,0 +1,83 @@
1
+ ---
2
+ description: "Task list for CLI model namespace (retire 'implement')"
3
+ ---
4
+
5
+ # Tasks: CLI model namespace (retire "implement")
6
+
7
+ **Input**: Design documents from `specs/001-cli-model-namespace/`
8
+
9
+ **Prerequisites**: plan.md, spec.md, research.md, contracts/cli-commands.md, quickstart.md
10
+
11
+ **Tests**: Included — FR-007 requires migrating/renaming the suite.
12
+
13
+ **Organization**: Single user story (US1) — the new `model execute` surface plus the full nomenclature purge. No error-stub story.
14
+
15
+ ## Format: `[ID] [P?] [L?] [US1] Description`
16
+
17
+ - **[P]**: parallelizable (different file)
18
+ - **[L]**: local-model eligible (shell command verified by exit code)
19
+
20
+ ---
21
+
22
+ ## Phase 1: Setup
23
+
24
+ - [X] T001 [L] Baseline green: `cd /home/ike/companies/sparepartslabs/blitz-dev-tools/blitz-cli && pip install -e . >/dev/null 2>&1 && pytest -q`. Expected: passes before changes.
25
+
26
+ ---
27
+
28
+ ## Phase 2: User Story 1 — `blitz model execute` + retire "implement" (P1) 🎯 MVP
29
+
30
+ **Goal**: `blitz model execute` replaces `blitz implement` with identical behavior; the "implement" nomenclature naming this command is gone from code, env var, run path, and tests. `blitz implement` is an invalid choice.
31
+
32
+ **Independent Test**: quickstart.md — help output correct, `blitz implement` errors, snapshots land under `.blitz/execute/runs/`, grep finds no stray nomenclature, `pytest` passes.
33
+
34
+ ### Engine module rename
35
+
36
+ - [X] T002 [L] [US1] Rename the engine module: `cd /home/ike/companies/sparepartslabs/blitz-dev-tools/blitz-cli && git mv blitz_cli/_implement.py blitz_cli/_execute.py` (use `mv` if not tracked). Expected: exit 0.
37
+ - [X] T003 [US1] In `blitz_cli/_execute.py`, update the run-snapshot path from `.blitz/implement/runs` to `.blitz/execute/runs` at every occurrence (currently the docstring ~L240-241, the glob `.blitz/implement/runs/*/usage.json` ~L245, and `repo / ".blitz" / "implement" / "runs"` ~L292), and update the module docstring's `implement` playbook reference to `execute`. Do NOT change the `SYSTEM_PROMPT` "code implementer" prose.
38
+
39
+ ### CLI wiring
40
+
41
+ - [X] T004 [US1] In `blitz_cli/cli.py`, rename handler `def _cmd_implement(args)` → `def _cmd_execute(args)`; change its `from blitz_cli import _implement` to `from blitz_cli import _execute` and update all `_implement.` references in the function body to `_execute.` (find_tasks_file, local_model_config, DEFAULT_MODEL, run); change the env read `os.environ.get("BLITZ_IMPLEMENT_MODEL")` → `os.environ.get("BLITZ_EXECUTE_MODEL")` and the adjacent comment.
42
+ - [X] T005 [US1] In `blitz_cli/cli.py` `build_parser()`, replace the whole top-level `impl = sub.add_parser("implement", ...)` block (through `impl.set_defaults(func=_cmd_implement)`, ~L293-334) with: `model = sub.add_parser("model", help="Model lifecycle commands.")`; `model_sub = model.add_subparsers(dest="model_cmd", required=True)`; `execute = model_sub.add_parser("execute", help=...)` re-attaching the identical arguments (`tasks`, `--repo`, `--model`, `--local-only`, `--ollama-url`, `--max-retries`, `--start-at`) verbatim; update the `--model` help text `BLITZ_IMPLEMENT_MODEL` → `BLITZ_EXECUTE_MODEL` and `snapshots under .blitz/implement/runs/` → `.blitz/execute/runs/`; end with `execute.set_defaults(func=_cmd_execute)`. Follow the `locker-room` nested pattern. No `implement` parser remains.
43
+
44
+ ### Ontology reference
45
+
46
+ - [X] T006 [US1] In `blitz_cli/_ontology.py`, change `from blitz_cli import _activity, _implement, _playbooks` → `... _execute ...` and update every `_implement.` usage (~L355 parse_tasks, ~L530 usage_by_spec) to `_execute.`. KEEP the `"implemented"` return in `_infer_stage` (~L366) and the "the implement" comment's meaning — those are the lifecycle stage, not the CLI command. If `_ontology.py` builds any `.blitz/implement/runs` path, change it to `.blitz/execute/runs`.
47
+
48
+ ### Docs & rendered playbook refs
49
+
50
+ - [X] T007 [P] [US1] Update `README.md`: "Top-level commands" line (~L15) list `model` not `implement`; the `## Implement:` section heading and body (~L35-50) → `blitz model execute` usage; `BLITZ_IMPLEMENT_MODEL` (~L55) → `BLITZ_EXECUTE_MODEL`; `.blitz/implement/runs/` (~L45) → `.blitz/execute/runs/`.
51
+ - [X] T008 [P] [US1] Update rendered playbook CLI refs to `blitz model execute … --local-only`: `blitz_cli/playbooks/commands/implement.md` (~L154, L158, L162), `blitz_cli/playbooks/commands/tasks.md` (~L209, L243), `blitz_cli/templates/workspace_playbook.md.tmpl` (~L24-25). Do NOT rename the `/implement` slash-command, its filename, or `agent: implement` frontmatter.
52
+
53
+ ### Tests
54
+
55
+ - [X] T009 [US1] Rename `tests/test_implement.py` → `tests/test_execute.py` (`git mv`), change `from blitz_cli import _implement` → `_execute` and every `_implement.` → `_execute.`, and update the `.blitz/implement/runs` path (~L233) → `.blitz/execute/runs`.
56
+ - [X] T010 [US1] In `tests/test_ontology.py`, update the run dir `.blitz/implement/runs` (~L68) → `.blitz/execute/runs`. Keep the `"implemented"` stage assertions (~L49) unchanged; the test name `test_feature_node_carries_implement_usage` may be left or renamed to `..._execute_usage` (cosmetic).
57
+ - [X] T011 [P] [US1] In `tests/test_cli.py`, add tests: (a) `build_parser().parse_args(["model","execute"])` resolves `func == _cmd_execute`; (b) parsing `["implement"]` raises `SystemExit` (invalid choice); (c) `model` is present and `implement` absent in the top-level choices.
58
+
59
+ ---
60
+
61
+ ## Phase 3: Verification
62
+
63
+ - [X] T012 [L] Run the suite: `cd /home/ike/companies/sparepartslabs/blitz-dev-tools/blitz-cli && pytest -q`. Expected: all pass.
64
+ - [X] T013 [L] No stray nomenclature: `cd /home/ike/companies/sparepartslabs/blitz-dev-tools/blitz-cli && ! grep -rnE "blitz implement|BLITZ_IMPLEMENT_MODEL|\.blitz/implement/runs|_cmd_implement|_implement" blitz_cli README.md tests`. Expected: exit 0 (no matches).
65
+ - [X] T014 [L] Surface check per quickstart.md: `blitz --help`, `blitz model --help`, `blitz model execute --help`, `blitz implement; echo exit=$?`. Confirm `model`/`execute` present, `implement` invalid.
66
+
67
+ ---
68
+
69
+ ## Dependencies
70
+
71
+ - T001 → all.
72
+ - T002 (module move) → T003, T004, T006, T009 (they reference `_execute`).
73
+ - T004 → T005 (handler must exist before `set_defaults(func=_cmd_execute)`), same file `cli.py`, sequential.
74
+ - T007, T008, T011 [P] — independent files.
75
+ - T012-T014 last (after all edits).
76
+
77
+ ## Parallel opportunities
78
+
79
+ - T007, T008 (docs/templates) and T011 (test_cli.py) run in parallel once code edits land.
80
+
81
+ ## MVP scope
82
+
83
+ All of US1 is the deliverable; there is no separate secondary story.
@@ -43,6 +43,25 @@ def test_locker_room_make_verbs_resolve_to_the_make_passthrough():
43
43
  assert logs.run_dir == "./x"
44
44
 
45
45
 
46
+ def test_model_execute_resolves():
47
+ args = cli.build_parser().parse_args(["model", "execute", "specs/001/tasks.md"])
48
+ assert args.func is cli._cmd_execute
49
+ assert args.tasks == "specs/001/tasks.md"
50
+ assert args.max_retries == 2
51
+
52
+
53
+ def test_old_implement_command_is_gone():
54
+ import pytest
55
+ with pytest.raises(SystemExit):
56
+ cli.build_parser().parse_args(["implement"])
57
+
58
+
59
+ def test_model_group_requires_subcommand():
60
+ import pytest
61
+ with pytest.raises(SystemExit):
62
+ cli.build_parser().parse_args(["model"])
63
+
64
+
46
65
  def test_runtime_commands_are_not_in_the_cli():
47
66
  # The node runs watch/ontologize/mcp; the CLI must not expose them.
48
67
  parser = cli.build_parser()
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- from blitz_cli import _implement
3
+ from blitz_cli import _execute
4
4
 
5
5
  TASKS_MD = """\
6
6
  # Tasks: Example
@@ -33,7 +33,7 @@ def _write_tasks(tmp_path):
33
33
 
34
34
 
35
35
  def test_parse_tasks_ids_done_phases_and_verify():
36
- tasks, verify = _implement.parse_tasks(TASKS_MD)
36
+ tasks, verify = _execute.parse_tasks(TASKS_MD)
37
37
  assert [t["id"] for t in tasks] == ["T001", "T002", "T003", "T004"]
38
38
  assert [t["done"] for t in tasks] == [False, True, False, False]
39
39
  assert [t["phase"] for t in tasks] == [1, 1, 2, 2]
@@ -48,7 +48,7 @@ def test_parse_actions_orders_writes_and_runs():
48
48
  "===RUN===\n```\nmkdir -p tests\n```\n"
49
49
  "===WRITE src/mod.py===\n```python\nX = 1\n```\n"
50
50
  )
51
- actions = _implement.parse_actions(reply)
51
+ actions = _execute.parse_actions(reply)
52
52
  assert actions == [("run", "mkdir -p tests", None), ("write", "src/mod.py", "X = 1")]
53
53
 
54
54
 
@@ -59,14 +59,14 @@ def test_apply_actions_writes_snapshots_and_rejects_escape(tmp_path):
59
59
  snap.mkdir()
60
60
  (repo / "a.txt").write_text("old\n")
61
61
 
62
- ok, err = _implement.apply_actions(
62
+ ok, err = _execute.apply_actions(
63
63
  repo, [("write", "a.txt", "new")], snap, "T001", 0, print_fn=lambda *_: None
64
64
  )
65
65
  assert ok, err
66
66
  assert (repo / "a.txt").read_text() == "new\n"
67
67
  assert (snap / "T001.a0.a.txt").read_text() == "old\n"
68
68
 
69
- ok, err = _implement.apply_actions(
69
+ ok, err = _execute.apply_actions(
70
70
  repo, [("write", "../evil.txt", "x")], snap, "T001", 0, print_fn=lambda *_: None
71
71
  )
72
72
  assert not ok and "escapes the repo" in err
@@ -74,14 +74,14 @@ def test_apply_actions_writes_snapshots_and_rejects_escape(tmp_path):
74
74
 
75
75
 
76
76
  def test_apply_actions_reports_failed_command(tmp_path):
77
- ok, err = _implement.apply_actions(
77
+ ok, err = _execute.apply_actions(
78
78
  tmp_path, [("run", "exit 3", None)], tmp_path, "T001", 0, print_fn=lambda *_: None
79
79
  )
80
80
  assert not ok and "exit 3" in err
81
81
 
82
82
 
83
83
  def test_apply_actions_empty_reply_is_an_error(tmp_path):
84
- ok, err = _implement.apply_actions(
84
+ ok, err = _execute.apply_actions(
85
85
  tmp_path, [], tmp_path, "T001", 0, print_fn=lambda *_: None
86
86
  )
87
87
  assert not ok and "No action blocks" in err
@@ -89,7 +89,7 @@ def test_apply_actions_empty_reply_is_an_error(tmp_path):
89
89
 
90
90
  def test_mark_done_checks_exactly_one_box(tmp_path):
91
91
  f = _write_tasks(tmp_path)
92
- _implement.mark_done(f, "T001")
92
+ _execute.mark_done(f, "T001")
93
93
  text = f.read_text()
94
94
  assert "- [x] T001 " in text
95
95
  assert "- [ ] T003 " in text
@@ -98,7 +98,7 @@ def test_mark_done_checks_exactly_one_box(tmp_path):
98
98
  def test_gather_context_inlines_referenced_files(tmp_path):
99
99
  (tmp_path / "src").mkdir()
100
100
  (tmp_path / "src" / "mod.py").write_text("X = 1\n")
101
- ctx = _implement.gather_context(tmp_path, "EDIT `src/mod.py` and `src/missing.py`.")
101
+ ctx = _execute.gather_context(tmp_path, "EDIT `src/mod.py` and `src/missing.py`.")
102
102
  assert "current contents of src/mod.py" in ctx
103
103
  assert "1\tX = 1" in ctx
104
104
  assert "missing.py" not in ctx
@@ -111,10 +111,10 @@ def test_find_tasks_file_unique_incomplete(tmp_path):
111
111
  open_.mkdir(parents=True)
112
112
  done.joinpath("tasks.md").write_text("- [x] T001 done\n")
113
113
  open_.joinpath("tasks.md").write_text("- [ ] T001 todo\n")
114
- assert _implement.find_tasks_file(tmp_path) == open_ / "tasks.md"
114
+ assert _execute.find_tasks_file(tmp_path) == open_ / "tasks.md"
115
115
  # Two incomplete lists -> ambiguous -> None.
116
116
  done.joinpath("tasks.md").write_text("- [ ] T001 todo\n")
117
- assert _implement.find_tasks_file(tmp_path) is None
117
+ assert _execute.find_tasks_file(tmp_path) is None
118
118
 
119
119
 
120
120
  def test_run_end_to_end_with_fake_model(tmp_path):
@@ -131,7 +131,7 @@ def test_run_end_to_end_with_fake_model(tmp_path):
131
131
  return replies[tid]
132
132
 
133
133
  logs = []
134
- rc = _implement.run(
134
+ rc = _execute.run(
135
135
  repo=repo, tasks_file=tasks_file, call_model=fake_model,
136
136
  run_root=tmp_path / "runs", print_fn=logs.append,
137
137
  )
@@ -148,7 +148,7 @@ def test_run_end_to_end_with_fake_model(tmp_path):
148
148
 
149
149
 
150
150
  def test_parse_tasks_local_marker():
151
- tasks, _ = _implement.parse_tasks(
151
+ tasks, _ = _execute.parse_tasks(
152
152
  "## Phase 1: x\n\n"
153
153
  "- [ ] T001 [P] [L] CREATE a file.\n"
154
154
  "- [ ] T002 [L] [US1] Run `true`.\n"
@@ -169,14 +169,14 @@ def test_local_model_config_walks_up_to_workspace(tmp_path):
169
169
  json.dumps({"local_model": {"model": "ws-model"}})
170
170
  )
171
171
  # No repo-level config -> workspace config wins.
172
- assert _implement.local_model_config(repo) == {"model": "ws-model"}
172
+ assert _execute.local_model_config(repo) == {"model": "ws-model"}
173
173
  # Repo-level config takes precedence.
174
174
  (repo / ".blitz" / "config.json").write_text(
175
175
  json.dumps({"local_model": {"model": "repo-model"}})
176
176
  )
177
- assert _implement.local_model_config(repo) == {"model": "repo-model"}
177
+ assert _execute.local_model_config(repo) == {"model": "repo-model"}
178
178
  # Nothing configured anywhere -> {}.
179
- assert _implement.local_model_config(tmp_path) == {}
179
+ assert _execute.local_model_config(tmp_path) == {}
180
180
 
181
181
 
182
182
  def test_run_local_only_stops_at_first_frontier_task(tmp_path):
@@ -196,7 +196,7 @@ def test_run_local_only_stops_at_first_frontier_task(tmp_path):
196
196
  return "===RUN===\n```\ntrue\n```"
197
197
 
198
198
  logs = []
199
- rc = _implement.run(
199
+ rc = _execute.run(
200
200
  repo=repo, tasks_file=tasks_file, call_model=fake_model,
201
201
  local_only=True, run_root=tmp_path / "runs", print_fn=logs.append,
202
202
  )
@@ -212,7 +212,7 @@ def test_run_local_only_warns_when_no_markers(tmp_path):
212
212
  tasks_file = tmp_path / "tasks.md"
213
213
  tasks_file.write_text("- [ ] T001 Do a thing.\n")
214
214
  logs = []
215
- rc = _implement.run(
215
+ rc = _execute.run(
216
216
  repo=tmp_path, tasks_file=tasks_file, call_model=lambda m: "",
217
217
  local_only=True, run_root=tmp_path / "runs", print_fn=logs.append,
218
218
  )
@@ -222,15 +222,15 @@ def test_run_local_only_warns_when_no_markers(tmp_path):
222
222
 
223
223
  def test_spec_name_from_specs_layout(tmp_path):
224
224
  tasks = tmp_path / "specs" / "001-my-feature" / "tasks.md"
225
- assert _implement.spec_name(tmp_path, tasks) == "001-my-feature"
225
+ assert _execute.spec_name(tmp_path, tasks) == "001-my-feature"
226
226
  other = tmp_path / "docs" / "tasks.md"
227
- assert _implement.spec_name(tmp_path, other) == "docs/tasks.md"
227
+ assert _execute.spec_name(tmp_path, other) == "docs/tasks.md"
228
228
 
229
229
 
230
230
  def test_usage_by_spec_aggregates_runs(tmp_path):
231
231
  import json
232
232
 
233
- runs = tmp_path / ".blitz" / "implement" / "runs"
233
+ runs = tmp_path / ".blitz" / "execute" / "runs"
234
234
  for stamp, out in (("20260714-1", 100), ("20260714-2", 50)):
235
235
  d = runs / stamp
236
236
  d.mkdir(parents=True)
@@ -241,7 +241,7 @@ def test_usage_by_spec_aggregates_runs(tmp_path):
241
241
  (runs / "broken").mkdir()
242
242
  (runs / "broken" / "usage.json").write_text("not json")
243
243
 
244
- agg = _implement.usage_by_spec(tmp_path)
244
+ agg = _execute.usage_by_spec(tmp_path)
245
245
  assert agg == {"001-a": {
246
246
  "input": 2000, "output": 150, "calls": 6, "runs": 2,
247
247
  "models": ["qwen2.5-14b-28k"],
@@ -264,7 +264,7 @@ def test_run_records_token_usage(tmp_path):
264
264
  return replies[tid], {"input": 100, "output": 10}
265
265
 
266
266
  logs = []
267
- rc = _implement.run(
267
+ rc = _execute.run(
268
268
  repo=repo, tasks_file=tasks_file, call_model=fake_model,
269
269
  run_root=tmp_path / "runs", print_fn=logs.append,
270
270
  )
@@ -272,7 +272,7 @@ def test_run_records_token_usage(tmp_path):
272
272
  rundir = next((tmp_path / "runs").iterdir())
273
273
  usage = json.loads((rundir / "usage.json").read_text())
274
274
  assert usage["spec"] == str(tasks_file.relative_to(repo)) # not under specs/
275
- assert usage["model"] == _implement.DEFAULT_MODEL
275
+ assert usage["model"] == _execute.DEFAULT_MODEL
276
276
  assert usage["tasks"]["T001"] == {"input": 100, "output": 10, "calls": 1}
277
277
  assert usage["total"] == {"input": 200, "output": 20, "calls": 2}
278
278
  assert any("T001: DONE (100 in / 10 out tokens)" in ln for ln in logs)
@@ -286,7 +286,7 @@ def test_run_flushes_usage_on_failure(tmp_path):
286
286
  tasks_file = repo / "tasks.md"
287
287
  tasks_file.write_text("## Phase 1: x\n\n- [ ] T001 Run `false`.\n")
288
288
 
289
- rc = _implement.run(
289
+ rc = _execute.run(
290
290
  repo=repo, tasks_file=tasks_file,
291
291
  call_model=lambda m: ("===RUN===\n```\nexit 7\n```", {"input": 50, "output": 5}),
292
292
  max_retries=1, run_root=tmp_path / "runs", print_fn=lambda *_: None,
@@ -308,7 +308,7 @@ def test_run_retries_with_error_feedback_then_stops(tmp_path):
308
308
  calls.append(messages)
309
309
  return "===RUN===\n```\nexit 7\n```"
310
310
 
311
- rc = _implement.run(
311
+ rc = _execute.run(
312
312
  repo=repo, tasks_file=tasks_file, call_model=failing_model,
313
313
  max_retries=1, run_root=tmp_path / "runs", print_fn=lambda *_: None,
314
314
  )
@@ -327,7 +327,7 @@ def test_run_phase_verify_failure_fails_last_task(tmp_path):
327
327
  "**Phase 1 verification**: `test -f produced.txt` succeeds.\n"
328
328
  )
329
329
 
330
- rc = _implement.run(
330
+ rc = _execute.run(
331
331
  repo=repo, tasks_file=tasks_file,
332
332
  call_model=lambda m: "===RUN===\n```\ntrue\n```",
333
333
  max_retries=0, run_root=tmp_path / "runs", print_fn=lambda *_: None,
@@ -62,10 +62,10 @@ def test_playbook_nodes_carry_command_docs(workspace):
62
62
  assert docs["playbook"].strip()
63
63
 
64
64
 
65
- def test_feature_node_carries_implement_usage(tmp_path):
65
+ def test_feature_node_carries_execute_usage(tmp_path):
66
66
  ws = tmp_path / "ws"
67
67
  repo = _make_repo(ws, "api", specs={"001-endpoint": {"spec.md": "# s", "tasks.md": "# t"}})
68
- rundir = repo / ".blitz" / "implement" / "runs" / "20260714-120000"
68
+ rundir = repo / ".blitz" / "execute" / "runs" / "20260714-120000"
69
69
  rundir.mkdir(parents=True)
70
70
  (rundir / "usage.json").write_text(json.dumps({
71
71
  "spec": "001-endpoint", "model": "qwen2.5-14b-28k",
File without changes
File without changes
File without changes
File without changes