blitz-cli 0.7.0__tar.gz → 0.8.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 (55) hide show
  1. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/PKG-INFO +22 -22
  2. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/README.md +21 -19
  3. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_ontology.py +25 -8
  4. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/cli.py +3 -140
  5. blitz_cli-0.8.0/blitz_cli/templates/locker_room_README.md.tmpl +38 -0
  6. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/templates/locker_room_compose.yml.tmpl +21 -18
  7. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/pyproject.toml +7 -10
  8. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_cli.py +9 -23
  9. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_scaffold.py +5 -4
  10. blitz_cli-0.7.0/blitz_cli/_locker_room_mcp.py +0 -117
  11. blitz_cli-0.7.0/blitz_cli/templates/locker_room_README.md.tmpl +0 -39
  12. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/.github/workflows/homebrew.yml +0 -0
  13. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/.github/workflows/publish.yml +0 -0
  14. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/.gitignore +0 -0
  15. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/LICENSE +0 -0
  16. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/__init__.py +0 -0
  17. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_activity.py +0 -0
  18. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_code_graph.py +0 -0
  19. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_corpus.py +0 -0
  20. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_dataset.py +0 -0
  21. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_git.py +0 -0
  22. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_make.py +0 -0
  23. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_playbooks.py +0 -0
  24. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_redact.py +0 -0
  25. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_scaffold.py +0 -0
  26. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_tool_schemas.py +0 -0
  27. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/_train.py +0 -0
  28. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/__init__.py +0 -0
  29. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/__init__.py +0 -0
  30. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/analyze.md +0 -0
  31. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/clarify.md +0 -0
  32. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/implement.md +0 -0
  33. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/plan.md +0 -0
  34. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/playbook.md +0 -0
  35. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/specify.md +0 -0
  36. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/commands/tasks.md +0 -0
  37. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/memory/playbook.md +0 -0
  38. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/scripts/bash/check-prerequisites.sh +0 -0
  39. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/scripts/bash/common.sh +0 -0
  40. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/scripts/bash/create-new-feature.sh +0 -0
  41. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/scripts/bash/setup-plan.sh +0 -0
  42. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/scripts/bash/setup-tasks.sh +0 -0
  43. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/templates/plan-template.md +0 -0
  44. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/templates/spec-template.md +0 -0
  45. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/scaffold/.playbooks/templates/tasks-template.md +0 -0
  46. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/workspace/__init__.py +0 -0
  47. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/playbooks/workspace/huddle.md +0 -0
  48. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/templates/__init__.py +0 -0
  49. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/blitz_cli/templates/locker_room_Makefile.tmpl +0 -0
  50. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_activity.py +0 -0
  51. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_corpus.py +0 -0
  52. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_make.py +0 -0
  53. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_playbooks.py +0 -0
  54. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_redact.py +0 -0
  55. {blitz_cli-0.7.0 → blitz_cli-0.8.0}/tests/test_tool_schemas.py +0 -0
@@ -1,13 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blitz-cli
3
- Version: 0.7.0
3
+ Version: 0.8.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
7
7
  Requires-Python: >=3.9
8
8
  Requires-Dist: rich>=13
9
- Provides-Extra: locker-room
10
- Requires-Dist: mcp>=1.0; extra == 'locker-room'
11
9
  Provides-Extra: train
12
10
  Requires-Dist: accelerate==1.2.1; extra == 'train'
13
11
  Requires-Dist: datasets==3.2.0; extra == 'train'
@@ -55,14 +53,17 @@ for coordinating a feature across repos. Use `--ignore PATH` to skip a repo and
55
53
 
56
54
  ## Locker-room: ontology graph, watcher, trainer, MCP
57
55
 
58
- `blitz locker-room init` scaffolds a `docker-compose.yml` that pulls prebuilt
59
- images (from `sparepartslabs/blitz-locker-room`, pinned to this CLI version) for
60
- three roles sharing `./data`:
56
+ The locker-room is a **context node**: a set of containers that ingest, build,
57
+ and serve an ontology of your codebase to agents (on your machine, a VPN, or a
58
+ team host). `blitz locker-room init` scaffolds a `docker-compose.yml` that pulls
59
+ the prebuilt node images (from `sparepartslabs/blitz-locker-room`) for four roles
60
+ sharing `./data`:
61
61
 
62
62
  - **watch**: ingests your coding agent's session transcripts into a corpus.
63
63
  - **graph**: rebuilds an ontology of your playbook-enabled repos, huddles,
64
64
  features, and their code lineage into `data/ontology.db` on a loop.
65
- - **mcp**: serves that ontology to agents over SSE.
65
+ - **mcp**: serves that ontology to agents over MCP (SSE) and a browser graph view.
66
+ - **train**: on-demand QLoRA trainer (needs a GPU).
66
67
 
67
68
  ```bash
68
69
  blitz locker-room init # write ./locker-room/docker-compose.yml
@@ -71,24 +72,23 @@ blitz locker-room up # start watch + graph + mcp
71
72
  blitz locker-room logs
72
73
  ```
73
74
 
74
- The ontology is also usable directly on the host, no containers required:
75
+ The CLI is just these static commands (`init`, and the `up`/`down`/`logs`/`pull`/
76
+ `train` lifecycle passthroughs). The runtime (ingest, graph build, serving) lives
77
+ in the node images, not the CLI.
75
78
 
76
- ```bash
77
- blitz locker-room ontologize # scan a workspace into ./data/ontology.db
78
- blitz locker-room list # list playbook-enabled repos + their active feature
79
- blitz locker-room mcp # stdio MCP server for a local agent
80
- ```
79
+ ### The graph agents can query
81
80
 
82
- ### The graph an agent can query
81
+ The graph engine (a blitz-cli library the node runs) reuses playbook discovery and
82
+ static code parsing (Python symbols + imports + calls; File nodes for other
83
+ languages) to build `Workspace`, `Repo`, `AgentTool`, `Playbook`, `Feature` (with
84
+ lifecycle stage), `Huddle`, `File`, and `Symbol` nodes. Each feature's git branch
85
+ diff links it to the code it touches (`Feature -TOUCHES-> File/Symbol`), so an
86
+ agent can walk a spec out to the full lineage of code it implements, and back.
83
87
 
84
- `ontologize` reuses playbook discovery and static code parsing (Python symbols +
85
- imports + calls; File nodes for other languages) to build a graph of `Workspace`,
86
- `Repo`, `AgentTool`, `Playbook`, `Feature` (with lifecycle stage), `Huddle`,
87
- `File`, and `Symbol` nodes. Each feature's git branch diff links it to the code
88
- it touches (`Feature -TOUCHES-> File/Symbol`), so an agent can walk a spec out to
89
- the full lineage of code it implements, and back. The MCP tools expose this:
90
- `list_repos`, `get_repo`, `list_huddles`, `get_huddle`, `get_playbooks`,
91
- `get_lineage`, `impact_of`, `get_file`, `get_symbol`, and a generic `neighbors`.
88
+ The `mcp` role serves this: point an agent's MCP config at
89
+ `http://<node>:8848/sse` (tools `list_repos`, `get_repo`, `list_huddles`,
90
+ `get_huddle`, `get_playbooks`, `get_lineage`, `impact_of`, `get_file`,
91
+ `get_symbol`, `neighbors`), or open `http://<node>:8848/graph` in a browser.
92
92
 
93
93
  ### Training (optional)
94
94
 
@@ -34,14 +34,17 @@ for coordinating a feature across repos. Use `--ignore PATH` to skip a repo and
34
34
 
35
35
  ## Locker-room: ontology graph, watcher, trainer, MCP
36
36
 
37
- `blitz locker-room init` scaffolds a `docker-compose.yml` that pulls prebuilt
38
- images (from `sparepartslabs/blitz-locker-room`, pinned to this CLI version) for
39
- three roles sharing `./data`:
37
+ The locker-room is a **context node**: a set of containers that ingest, build,
38
+ and serve an ontology of your codebase to agents (on your machine, a VPN, or a
39
+ team host). `blitz locker-room init` scaffolds a `docker-compose.yml` that pulls
40
+ the prebuilt node images (from `sparepartslabs/blitz-locker-room`) for four roles
41
+ sharing `./data`:
40
42
 
41
43
  - **watch**: ingests your coding agent's session transcripts into a corpus.
42
44
  - **graph**: rebuilds an ontology of your playbook-enabled repos, huddles,
43
45
  features, and their code lineage into `data/ontology.db` on a loop.
44
- - **mcp**: serves that ontology to agents over SSE.
46
+ - **mcp**: serves that ontology to agents over MCP (SSE) and a browser graph view.
47
+ - **train**: on-demand QLoRA trainer (needs a GPU).
45
48
 
46
49
  ```bash
47
50
  blitz locker-room init # write ./locker-room/docker-compose.yml
@@ -50,24 +53,23 @@ blitz locker-room up # start watch + graph + mcp
50
53
  blitz locker-room logs
51
54
  ```
52
55
 
53
- The ontology is also usable directly on the host, no containers required:
56
+ The CLI is just these static commands (`init`, and the `up`/`down`/`logs`/`pull`/
57
+ `train` lifecycle passthroughs). The runtime (ingest, graph build, serving) lives
58
+ in the node images, not the CLI.
54
59
 
55
- ```bash
56
- blitz locker-room ontologize # scan a workspace into ./data/ontology.db
57
- blitz locker-room list # list playbook-enabled repos + their active feature
58
- blitz locker-room mcp # stdio MCP server for a local agent
59
- ```
60
+ ### The graph agents can query
60
61
 
61
- ### The graph an agent can query
62
+ The graph engine (a blitz-cli library the node runs) reuses playbook discovery and
63
+ static code parsing (Python symbols + imports + calls; File nodes for other
64
+ languages) to build `Workspace`, `Repo`, `AgentTool`, `Playbook`, `Feature` (with
65
+ lifecycle stage), `Huddle`, `File`, and `Symbol` nodes. Each feature's git branch
66
+ diff links it to the code it touches (`Feature -TOUCHES-> File/Symbol`), so an
67
+ agent can walk a spec out to the full lineage of code it implements, and back.
62
68
 
63
- `ontologize` reuses playbook discovery and static code parsing (Python symbols +
64
- imports + calls; File nodes for other languages) to build a graph of `Workspace`,
65
- `Repo`, `AgentTool`, `Playbook`, `Feature` (with lifecycle stage), `Huddle`,
66
- `File`, and `Symbol` nodes. Each feature's git branch diff links it to the code
67
- it touches (`Feature -TOUCHES-> File/Symbol`), so an agent can walk a spec out to
68
- the full lineage of code it implements, and back. The MCP tools expose this:
69
- `list_repos`, `get_repo`, `list_huddles`, `get_huddle`, `get_playbooks`,
70
- `get_lineage`, `impact_of`, `get_file`, `get_symbol`, and a generic `neighbors`.
69
+ The `mcp` role serves this: point an agent's MCP config at
70
+ `http://<node>:8848/sse` (tools `list_repos`, `get_repo`, `list_huddles`,
71
+ `get_huddle`, `get_playbooks`, `get_lineage`, `impact_of`, `get_file`,
72
+ `get_symbol`, `neighbors`), or open `http://<node>:8848/graph` in a browser.
71
73
 
72
74
  ### Training (optional)
73
75
 
@@ -1,11 +1,12 @@
1
- """Local ontology graph of playbook-enabled workspaces/repos/huddles/features.
2
-
3
- `blitz locker-room ontologize` scans a workspace and upserts a small graph into a
4
- SQLite file (``./data/ontology.db`` by default), which the stdio MCP server
5
- (:mod:`blitz_cli._locker_room_mcp`) and ``blitz locker-room list`` read back. The
6
- graph is deliberately tiny (dozens of repos, a handful of huddles) and stored as
7
- generic ``nodes``/``edges`` tables so the query surface stays engine-agnostic a
8
- Neo4j/Memgraph backend could drop in behind the same query helpers later.
1
+ """Ontology graph engine for playbook-enabled workspaces/repos/huddles/features.
2
+
3
+ This is a library: ``ontologize()`` scans a workspace and upserts a small graph
4
+ into a SQLite file, and ``OntologyStore`` reads it back. The locker-room context
5
+ node (the blitz-locker-room images) runs both -- its ontologize loop builds the
6
+ graph and its server serves it over MCP/HTTP. The graph is deliberately tiny
7
+ (dozens of repos, a handful of huddles) and stored as generic ``nodes``/``edges``
8
+ tables so the query surface stays engine-agnostic; a Neo4j/Memgraph backend could
9
+ drop in behind the same query helpers later.
9
10
  """
10
11
 
11
12
  from __future__ import annotations
@@ -178,6 +179,22 @@ class OntologyStore:
178
179
  node["coordinates"] = self.neighbors(node["id"], rel="COORDINATES")
179
180
  return node
180
181
 
182
+ def graph_data(self, include_code: bool = False) -> dict:
183
+ """Nodes + edges for visualization. Structural labels only by default
184
+ (Workspace/Repo/Playbook/AgentTool/Feature/Stage/Huddle); pass
185
+ ``include_code`` to add File/Symbol (much larger)."""
186
+ nodes = []
187
+ for r in self.conn.execute("SELECT id, label, props FROM nodes").fetchall():
188
+ if not include_code and r["label"] in ("File", "Symbol"):
189
+ continue
190
+ nodes.append({"id": r["id"], "label": r["label"], **json.loads(r["props"])})
191
+ ids = {n["id"] for n in nodes}
192
+ edges = []
193
+ for e in self.conn.execute("SELECT src, dst, rel FROM edges").fetchall():
194
+ if e["src"] in ids and e["dst"] in ids:
195
+ edges.append({"source": e["src"], "target": e["dst"], "rel": e["rel"]})
196
+ return {"nodes": nodes, "edges": edges}
197
+
181
198
  def get_playbooks(self, repo: str | None = None) -> list[dict]:
182
199
  if repo:
183
200
  node = self.get_node(repo) or self.get_node(f"repo:{Path(repo).resolve()}")
@@ -3,10 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
- import json
7
6
  import os
8
7
  import sys
9
- import time
10
8
  from pathlib import Path
11
9
  from typing import Optional
12
10
 
@@ -51,81 +49,14 @@ def _cmd_locker_room_make(args: argparse.Namespace) -> int:
51
49
  return _make.run_make(Path(args.run_dir), args.target)
52
50
 
53
51
 
54
- def _cmd_locker_room_ontologize(args: argparse.Namespace) -> int:
55
- from blitz_cli import _ontology
56
52
 
57
- ignore = [Path(p) for p in getattr(args, "ignore", [])]
58
-
59
- def once() -> str:
60
- counts = _ontology.ontologize(
61
- args.workspace, args.db, ignore=ignore,
62
- code=args.code, max_file_bytes=args.max_file_bytes,
63
- )
64
- return ", ".join(f"{k}={v}" for k, v in sorted(counts.items())) or "empty"
65
-
66
- if not args.loop:
67
- print(f"Ontologized {Path(args.workspace).resolve()} -> {args.db} ({once()})")
68
- return 0
69
-
70
- # Daemon mode (the graph container): rebuild on an interval forever.
71
- print(f"ontologize: {Path(args.workspace).resolve()} -> {args.db} every {args.interval}s")
72
- while True:
73
- try:
74
- print(f"ontologize: rebuilt ({once()})", flush=True)
75
- except Exception as exc: # never let one bad scan kill the loop
76
- print(f"ontologize: error {exc!r}", flush=True)
77
- time.sleep(args.interval)
78
-
79
-
80
- def _cmd_locker_room_watch(args: argparse.Namespace) -> int:
81
- """Activity-ingest daemon (the watch container): tail coding-agent session
82
- transcripts into a local corpus on a loop (currently Claude Code's format)."""
83
- from blitz_cli import _corpus
84
53
 
85
- store = _corpus.TurnStore(args.db)
86
- print(f"watch: {args.projects_dir} -> {args.db} every {args.interval}s")
87
- while True:
88
- try:
89
- n = _corpus.ingest_once(args.projects_dir, store)
90
- if n:
91
- print(f"watch: upserted {n} turns", flush=True)
92
- except Exception as exc:
93
- print(f"watch: error {exc!r}", flush=True)
94
- time.sleep(args.interval)
95
54
 
96
55
 
97
- def _cmd_locker_room_list(args: argparse.Namespace) -> int:
98
- from blitz_cli import _ontology
99
56
 
100
- if not Path(args.db).exists():
101
- _eprint(f"error: no ontology graph at {args.db}. Run `blitz locker-room ontologize` first.")
102
- return 2
103
- repos = _ontology.OntologyStore(args.db, read_only=True).list_repos()
104
- if args.json:
105
- print(json.dumps(repos, indent=2))
106
- return 0
107
- if not repos:
108
- print("No playbook-enabled repos in the graph.")
109
- return 0
110
- for r in repos:
111
- feat = f"{r['active_feature']} ({r['active_stage']})" if r.get("active_feature") else "-"
112
- agents = ",".join(r.get("agents", [])) or "-"
113
- print(f"{r['name']:<24} agents={agents:<28} active={feat} {r['path']}")
114
- return 0
115
57
 
116
58
 
117
- def _cmd_locker_room_train_run(args: argparse.Namespace) -> int:
118
- from blitz_cli import _train
119
59
 
120
- return _train.run()
121
-
122
-
123
- def _cmd_locker_room_mcp(args: argparse.Namespace) -> int:
124
- from blitz_cli import _locker_room_mcp
125
-
126
- return _locker_room_mcp.serve(
127
- args.db, transport=args.transport, host=args.host, port=args.port
128
- )
129
60
 
130
61
 
131
62
  def _cmd_activity(args: argparse.Namespace) -> int:
@@ -412,15 +343,15 @@ def build_parser() -> argparse.ArgumentParser:
412
343
 
413
344
  lr = sub.add_parser(
414
345
  "locker-room",
415
- help="Create and manage the locker-room (watcher, trainer, ontology graph) "
416
- "and its MCP tool.",
346
+ help="Scaffold and manage a locker-room context node (watcher, ontology "
347
+ "graph, MCP/graph server, trainer) as containers.",
417
348
  )
418
349
  lr_sub = lr.add_subparsers(dest="locker_room_cmd", required=True)
419
350
 
420
351
  lr_init = lr_sub.add_parser(
421
352
  "init",
422
353
  help="Scaffold a locker-room project here: a docker-compose that pulls the "
423
- "watcher/trainer/graph images plus an ontology MCP tool.",
354
+ "prebuilt node images (watch / graph / mcp-web / trainer).",
424
355
  )
425
356
  lr_init.add_argument(
426
357
  "-o", "--out", default="./locker-room", help="Output dir (default ./locker-room)"
@@ -444,74 +375,6 @@ def build_parser() -> argparse.ArgumentParser:
444
375
  )
445
376
  leaf.set_defaults(func=_cmd_locker_room_make, target=target)
446
377
 
447
- lr_ont = lr_sub.add_parser(
448
- "ontologize", help="Scan a workspace of playbook-enabled repos into the ontology graph."
449
- )
450
- lr_ont.add_argument("--workspace", default=".", help="Workspace root to scan (default: cwd)")
451
- lr_ont.add_argument("--db", default="./data/ontology.db", help="Ontology SQLite path")
452
- lr_ont.add_argument(
453
- "--ignore", action="append", metavar="PATH", default=[],
454
- help="Path to skip during discovery (repeatable)",
455
- )
456
- lr_ont.add_argument(
457
- "--no-code", dest="code", action="store_false", default=True,
458
- help="Skip the code graph (File/Symbol/imports/calls); structural ontology only",
459
- )
460
- lr_ont.add_argument(
461
- "--max-file-bytes", type=int, default=1_000_000,
462
- help="Skip source files larger than this when building the code graph (default 1MB)",
463
- )
464
- lr_ont.add_argument(
465
- "--loop", action="store_true",
466
- help="Rebuild forever on --interval (the graph container runs this)",
467
- )
468
- lr_ont.add_argument(
469
- "--interval", type=int, default=300, help="Seconds between rebuilds in --loop mode",
470
- )
471
- lr_ont.set_defaults(func=_cmd_locker_room_ontologize)
472
-
473
- lr_watch = lr_sub.add_parser(
474
- "watch", help="Ingest coding-agent transcripts into a corpus on a loop (the watch container)."
475
- )
476
- lr_watch.add_argument(
477
- "--projects-dir", default=os.environ.get("CLAUDE_PROJECTS_DIR", str(Path.home() / ".claude" / "projects")),
478
- help="Agent transcript dir to ingest (default: ~/.claude/projects; env CLAUDE_PROJECTS_DIR)",
479
- )
480
- lr_watch.add_argument("--db", default="./data/activity.db", help="Corpus SQLite path")
481
- lr_watch.add_argument(
482
- "--interval", type=int, default=int(os.environ.get("WATCH_INTERVAL_SECONDS", "45")),
483
- help="Seconds between ingests (env WATCH_INTERVAL_SECONDS)",
484
- )
485
- lr_watch.set_defaults(func=_cmd_locker_room_watch)
486
-
487
- lr_train_run = lr_sub.add_parser(
488
- "train-run",
489
- help="Run the QLoRA training job in-process (the trainer container's entrypoint; "
490
- "needs blitz-cli[train] + a GPU).",
491
- )
492
- lr_train_run.set_defaults(func=_cmd_locker_room_train_run)
493
-
494
- lr_list = lr_sub.add_parser("list", help="List playbook-enabled repos from the ontology graph.")
495
- lr_list.add_argument("--db", default="./data/ontology.db", help="Ontology SQLite path")
496
- lr_list.add_argument("--json", action="store_true", help="Emit JSON")
497
- lr_list.set_defaults(func=_cmd_locker_room_list)
498
-
499
- lr_mcp = lr_sub.add_parser(
500
- "mcp", help="Serve the ontology graph to agents over MCP (needs blitz-cli[locker-room])."
501
- )
502
- lr_mcp.add_argument("--db", default="./data/ontology.db", help="Ontology SQLite path")
503
- lr_mcp.add_argument(
504
- "--transport", choices=["stdio", "sse", "http"], default="stdio",
505
- help="stdio (agent spawns it, default) or sse/http (bind a port so a "
506
- "container can host it and agents connect over the network)",
507
- )
508
- lr_mcp.add_argument(
509
- "--host", default="127.0.0.1",
510
- help="Bind host for sse/http (use 0.0.0.0 inside a container)",
511
- )
512
- lr_mcp.add_argument("--port", type=int, default=8848, help="Bind port for sse/http")
513
- lr_mcp.set_defaults(func=_cmd_locker_room_mcp)
514
-
515
378
  return parser
516
379
 
517
380
 
@@ -0,0 +1,38 @@
1
+ # locker-room
2
+
3
+ A locker-room **context node**: a containerized watcher + ontology graph + MCP/web
4
+ server (+ on-demand trainer), scaffolded by `blitz locker-room init`. This dir
5
+ holds only a `docker-compose.yml` that pulls **prebuilt node images** (`:latest`,
6
+ from `sparepartslabs/blitz-locker-room`). Nothing is built locally. Refresh with
7
+ `blitz locker-room pull`.
8
+
9
+ ## Roles (all share `./data`)
10
+
11
+ - **watch** — ingests coding-agent session transcripts into `data/activity.db`.
12
+ - **graph** — rebuilds the ontology of your playbook-enabled repos/huddles/features
13
+ and their code lineage into `data/ontology.db` on a loop. Scans `WORKSPACE_DIR`.
14
+ - **mcp** — serves the ontology to agents over MCP (SSE) and a browser graph view.
15
+ - **train** — on-demand QLoRA trainer (profile `train`, needs a GPU).
16
+
17
+ ## Run
18
+
19
+ ```
20
+ blitz locker-room pull # pull the node images
21
+ blitz locker-room up # start watch + graph + mcp
22
+ blitz locker-room logs
23
+ blitz locker-room train # on-demand QLoRA (GPU); outputs -> ./train-out
24
+ blitz locker-room down
25
+ ```
26
+
27
+ Override host paths via env, e.g. `WORKSPACE_DIR=/path/to/code blitz locker-room up`.
28
+ Defaults: `CLAUDE_PROJECTS_DIR=$projects_dir`, `WORKSPACE_DIR=$workspace_dir`.
29
+
30
+ ## Use the node
31
+
32
+ - **MCP for agents:** the `mcp` service serves SSE at
33
+ `http://localhost:$${MCP_PORT:-8848}/sse` — add that URL to your agent's MCP config
34
+ (on a VPN / team host, point agents at the node's address instead of localhost).
35
+ - **Browser graph view:** open `http://localhost:$${MCP_PORT:-8848}/graph`.
36
+
37
+ The ontology is built and served entirely by the node; there are no host-side
38
+ `ontologize`/`list`/`mcp` commands — query it through the MCP tools or `/graph`.
@@ -1,9 +1,9 @@
1
- # Generated by `blitz locker-room init`. The watch/graph/mcp roles all run the
2
- # SAME core image (blitz-cli); only the command differs. The trainer is the one
3
- # genuinely-separate image (CUDA/torch base). `:latest` tracks the top of the
4
- # supported blitz-cli range; `blitz locker-room pull` refreshes it. Manage with
5
- # `blitz locker-room <up|down|logs|pull|train>` or `docker compose` directly.
6
- # Override the ${VAR:-default} paths via the shell env.
1
+ # Generated by `blitz locker-room init`. This runs a locker-room context node:
2
+ # the watch/graph/mcp roles all run the SAME core image (a baked entrypoint per
3
+ # role); the trainer is the one genuinely-separate image (CUDA/torch base).
4
+ # `:latest` tracks the top of the supported blitz-cli range; `blitz locker-room
5
+ # pull` refreshes it. Manage with `blitz locker-room <up|down|logs|pull|train>` or
6
+ # `docker compose` directly. Override the ${VAR:-default} paths via the shell env.
7
7
  name: locker-room
8
8
 
9
9
  x-core: &core
@@ -11,39 +11,42 @@ x-core: &core
11
11
  restart: unless-stopped
12
12
 
13
13
  services:
14
- # Ingest Claude Code transcripts (ALL projects) -> data/activity.db
14
+ # Ingest coding-agent transcripts -> data/activity.db
15
15
  watch:
16
16
  <<: *core
17
- command: ["blitz", "locker-room", "watch",
18
- "--projects-dir", "/claude-projects",
19
- "--db", "/workspace/data/activity.db"]
17
+ command: ["python", "/app/watch.py"]
20
18
  volumes:
21
19
  - "$${CLAUDE_PROJECTS_DIR:-$projects_dir}:/claude-projects:ro"
22
20
  - ./data:/workspace/data
23
21
  environment:
22
+ PROJECTS_DIR: /claude-projects
23
+ ACTIVITY_DB_PATH: /workspace/data/activity.db
24
24
  WATCH_INTERVAL_SECONDS: "$${WATCH_INTERVAL_SECONDS:-$interval}"
25
25
 
26
26
  # Rebuild the ontology (repos/huddles/features + code lineage) on a loop.
27
27
  graph:
28
28
  <<: *core
29
- command: ["blitz", "locker-room", "ontologize", "--loop",
30
- "--workspace", "/repos",
31
- "--db", "/workspace/data/ontology.db",
32
- "--interval", "$${ONTOLOGIZE_INTERVAL_SECONDS:-300}"]
29
+ command: ["python", "/app/ontologize.py"]
33
30
  volumes:
34
31
  - "$${WORKSPACE_DIR:-$workspace_dir}:/repos:ro"
35
32
  - ./data:/workspace/data
33
+ environment:
34
+ SCAN_DIR: /repos
35
+ ONTOLOGY_DB_PATH: /workspace/data/ontology.db
36
+ ONTOLOGIZE_INTERVAL_SECONDS: "$${ONTOLOGIZE_INTERVAL_SECONDS:-300}"
36
37
 
37
- # Serve the ontology to agents over SSE.
38
+ # Serve the ontology to agents over MCP (SSE) + a /graph web view.
38
39
  mcp:
39
40
  <<: *core
40
- command: ["blitz", "locker-room", "mcp",
41
- "--transport", "sse", "--host", "0.0.0.0", "--port", "8848",
42
- "--db", "/workspace/data/ontology.db"]
41
+ command: ["python", "/app/server.py"]
43
42
  ports:
44
43
  - "$${MCP_PORT:-8848}:8848"
45
44
  volumes:
46
45
  - ./data:/workspace/data:ro
46
+ environment:
47
+ ONTOLOGY_DB_PATH: /workspace/data/ontology.db
48
+ MCP_HOST: 0.0.0.0
49
+ MCP_PORT: 8848
47
50
 
48
51
  # On-demand (profile "train"): `docker compose run --rm train` / `make train`.
49
52
  train:
@@ -1,24 +1,21 @@
1
1
  [project]
2
2
  name = "blitz-cli"
3
- version = "0.7.0"
3
+ version = "0.8.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" }
7
7
  requires-python = ">=3.9"
8
8
  # `rich` (the `blitz activity` session viewer) is the only runtime dependency for
9
- # the core CLI; everything else uses the stdlib (urllib, sqlite3, string.Template,
10
- # importlib.resources). The MCP server and the trainer bring their own heavy deps
11
- # via the optional extras below, so a bare install stays light.
9
+ # the core CLI; everything else uses the stdlib (sqlite3, string.Template,
10
+ # importlib.resources). The ontology engine here is a plain-SQLite library the
11
+ # locker-room node imports; the node (blitz-locker-room images) brings the server
12
+ # deps (mcp). The trainer's heavy ML deps are the optional [train] extra below.
12
13
  dependencies = ["rich>=13"]
13
14
 
14
- # The locker-room ontology MCP server (`blitz locker-room mcp`) needs the `mcp`
15
- # package. It's optional so the core CLI stays dep-light — the ontology graph
16
- # itself is plain SQLite (stdlib). Install with: pip install 'blitz-cli[locker-room]'.
17
- [project.optional-dependencies]
18
- locker-room = ["mcp>=1.0"]
19
- # The locker-room trainer (`blitz locker-room train-run`, run inside the trainer
15
+ # The locker-room trainer library (blitz_cli._train.run, run inside the trainer
20
16
  # image). torch is intentionally omitted -- the CUDA base image ships the matching
21
17
  # build, and reinstalling it via pip risks a CPU-only/wrong-arch wheel.
18
+ [project.optional-dependencies]
22
19
  train = [
23
20
  "transformers==4.48.0",
24
21
  "trl==0.13.0",
@@ -1,7 +1,8 @@
1
1
  """Argparse-shape tests: these don't exercise command behavior (that's
2
2
  covered per-module elsewhere), just that `build_parser()` wires tokens to the
3
- right handler — the `locker-room init` scaffolder and the `locker-room` verb
4
- group (make-passthroughs vs native ontologize/watch/list/mcp)."""
3
+ right handler — the `locker-room init` scaffolder and the `locker-room`
4
+ container-lifecycle make-passthroughs. (The runtime commands moved to the
5
+ locker-room node images; the CLI is static commands only.)"""
5
6
 
6
7
  from blitz_cli import cli
7
8
 
@@ -42,25 +43,10 @@ def test_locker_room_make_verbs_resolve_to_the_make_passthrough():
42
43
  assert logs.run_dir == "./x"
43
44
 
44
45
 
45
- def test_locker_room_native_verbs_resolve_to_their_handlers():
46
+ def test_runtime_commands_are_not_in_the_cli():
47
+ # The node runs watch/ontologize/mcp; the CLI must not expose them.
46
48
  parser = cli.build_parser()
47
-
48
- ont = parser.parse_args(["locker-room", "ontologize", "--workspace", "."])
49
- assert ont.func is cli._cmd_locker_room_ontologize
50
- assert ont.code is True # code graph on by default
51
- assert ont.loop is False
52
-
53
- watch = parser.parse_args(["locker-room", "watch"])
54
- assert watch.func is cli._cmd_locker_room_watch
55
-
56
- lst = parser.parse_args(["locker-room", "list", "--json"])
57
- assert lst.func is cli._cmd_locker_room_list
58
- assert lst.json is True
59
-
60
- mcp = parser.parse_args(["locker-room", "mcp"])
61
- assert mcp.func is cli._cmd_locker_room_mcp
62
- assert mcp.transport == "stdio" # stdio by default; sse/http for the container
63
-
64
- mcp_sse = parser.parse_args(["locker-room", "mcp", "--transport", "sse", "--port", "9000"])
65
- assert mcp_sse.transport == "sse"
66
- assert mcp_sse.port == 9000
49
+ import pytest
50
+ for verb in ("ontologize", "watch", "mcp", "train-run", "list"):
51
+ with pytest.raises(SystemExit):
52
+ parser.parse_args(["locker-room", verb])
@@ -110,13 +110,14 @@ def test_render_locker_room_compose_pulls_latest_and_shares_core(tmp_path):
110
110
  compose = (out / "docker-compose.yml").read_text()
111
111
 
112
112
  # watch/graph/mcp all run the one core image (shared via a YAML anchor) at
113
- # :latest (top of the supported blitz-cli range); only the command differs
113
+ # :latest (top of the supported blitz-cli range); each runs a baked node
114
+ # entrypoint (the runtime lives in the images, not the CLI)
114
115
  assert "ghcr.io/sparepartslabs/blitz-locker-room/core:latest" in compose
115
116
  assert compose.count("<<: *core") == 3
116
117
  assert "ghcr.io/sparepartslabs/blitz-locker-room/train:latest" in compose
117
- assert '"blitz", "locker-room", "watch"' in compose
118
- assert '"blitz", "locker-room", "ontologize", "--loop"' in compose
119
- assert '"blitz", "locker-room", "mcp"' in compose
118
+ assert '"python", "/app/watch.py"' in compose
119
+ assert '"python", "/app/ontologize.py"' in compose
120
+ assert '"python", "/app/server.py"' in compose
120
121
  # trainer is opt-in via a compose profile
121
122
  assert 'profiles: ["train"]' in compose
122
123
  # substitutions resolved; no raw template vars leak through
@@ -1,117 +0,0 @@
1
- """stdio MCP server exposing the locker-room ontology graph to agentic coders.
2
-
3
- Run via ``blitz locker-room mcp --db ./data/ontology.db``. Agents add it to their
4
- MCP config (e.g. ``{"command": "blitz", "args": ["locker-room", "mcp"]}``) and get
5
- cheap read access to the ontology of playbook-enabled repos, huddles, and
6
- playbooks. The ``mcp`` package is an optional dependency (``pip install
7
- blitz-cli[locker-room]``); it is imported lazily so the core CLI stays dep-light.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from pathlib import Path
13
-
14
- _MISSING_MCP = (
15
- "The MCP server needs the optional 'mcp' dependency.\n"
16
- "Install it with: pip install 'blitz-cli[locker-room]'"
17
- )
18
-
19
-
20
- # CLI transport name -> FastMCP transport. "http" is the modern streamable-HTTP.
21
- _TRANSPORTS = {"stdio": "stdio", "sse": "sse", "http": "streamable-http"}
22
-
23
-
24
- def serve(db_path: str, transport: str = "stdio", host: str = "127.0.0.1", port: int = 8848) -> int:
25
- """Serve the ontology graph at ``db_path`` over MCP.
26
-
27
- ``stdio`` (default) is for an agent that spawns the process locally; ``sse``/
28
- ``http`` bind ``host``:``port`` so a container can host the server and agents
29
- connect over the network (e.g. the scaffolded `make mcp`)."""
30
- import sys
31
-
32
- try:
33
- from mcp.server.fastmcp import FastMCP
34
- except ImportError:
35
- print(_MISSING_MCP, file=sys.stderr)
36
- return 2
37
-
38
- from blitz_cli._ontology import OntologyStore
39
-
40
- db = Path(db_path)
41
- if not db.exists():
42
- print(
43
- f"error: no ontology graph at {db}. Run `blitz locker-room ontologize` first.",
44
- file=sys.stderr,
45
- )
46
- return 2
47
-
48
- mcp = FastMCP("blitz-locker-room", host=host, port=port)
49
-
50
- def _store() -> OntologyStore:
51
- # Open read-only per call — cheap, and safe against the periodic rebuild.
52
- return OntologyStore(db, read_only=True)
53
-
54
- @mcp.tool()
55
- def list_workspaces() -> list[dict]:
56
- """List the playbook-enabled workspaces in the ontology."""
57
- return _store().list_workspaces()
58
-
59
- @mcp.tool()
60
- def list_repos() -> list[dict]:
61
- """List playbook-enabled repos with their active feature and stage."""
62
- return _store().list_repos()
63
-
64
- @mcp.tool()
65
- def get_repo(repo: str) -> dict | None:
66
- """Get one repo (by node id or filesystem path): agents, playbooks, features."""
67
- return _store().get_repo(repo)
68
-
69
- @mcp.tool()
70
- def list_huddles() -> list[dict]:
71
- """List cross-repo huddles and which repos each coordinates."""
72
- return _store().list_huddles()
73
-
74
- @mcp.tool()
75
- def get_huddle(huddle: str) -> dict | None:
76
- """Get one huddle (by node id): title, status, coordinated repos + roles."""
77
- return _store().get_huddle(huddle)
78
-
79
- @mcp.tool()
80
- def get_playbooks(repo: str | None = None) -> list[dict]:
81
- """List playbooks (slash-commands); pass a repo to scope to that repo."""
82
- return _store().get_playbooks(repo)
83
-
84
- @mcp.tool()
85
- def neighbors(node_id: str, rel: str | None = None, incoming: bool = False) -> list[dict]:
86
- """Generic graph traversal: nodes adjacent to ``node_id`` (optionally by relation)."""
87
- return _store().neighbors(node_id, rel=rel, incoming=incoming)
88
-
89
- @mcp.tool()
90
- def get_lineage(feature: str, depth: int = 3) -> dict:
91
- """Code lineage of a spec/feature (by id or slug): the files/symbols it
92
- touches plus the transitive CALLS/IMPORTS closure of that code."""
93
- return _store().lineage(feature, max_depth=depth)
94
-
95
- @mcp.tool()
96
- def impact_of(node: str, depth: int = 3) -> dict:
97
- """Reverse lineage of a file or symbol (by id or path): what transitively
98
- depends on it, and which features touch it."""
99
- return _store().impact(node, max_depth=depth)
100
-
101
- @mcp.tool()
102
- def get_file(path: str) -> dict | None:
103
- """Get a File node by id or filesystem path."""
104
- return _store().get_file(path)
105
-
106
- @mcp.tool()
107
- def get_symbol(symbol_id: str) -> dict | None:
108
- """Get a Symbol node (function/class/method) by id."""
109
- return _store().get_symbol(symbol_id)
110
-
111
- if transport != "stdio":
112
- print(
113
- f"blitz locker-room mcp: serving {transport} on {host}:{port} (db {db})",
114
- file=sys.stderr,
115
- )
116
- mcp.run(transport=_TRANSPORTS[transport])
117
- return 0
@@ -1,39 +0,0 @@
1
- # locker-room
2
-
3
- Composable, containerized watcher + trainer + ontology-graph/MCP stack, scaffolded
4
- by `blitz locker-room init`. This dir holds only a `docker-compose.yml` that pulls
5
- **prebuilt images** (`:latest`, from `sparepartslabs/blitz-locker-room`, which
6
- ship the newest blitz-cli in their supported range). Nothing is built locally.
7
- Refresh with `blitz locker-room pull`.
8
-
9
- ## Pieces (all share `./data`)
10
-
11
- - **watch** — ingests Claude Code session transcripts (ALL projects) into
12
- `data/activity.db`.
13
- - **graph** — rebuilds the ontology of your playbook-enabled repos/huddles/features
14
- and their code lineage into `data/ontology.db` on a loop. Scans `WORKSPACE_DIR`.
15
- - **mcp** — serves the ontology to agents over SSE at `http://localhost:$${MCP_PORT:-8848}/sse`.
16
- - **train** — on-demand QLoRA trainer (profile `train`, needs a GPU).
17
-
18
- ## Run
19
-
20
- ```
21
- blitz locker-room pull # pull the pinned images
22
- blitz locker-room up # start watch + graph + mcp
23
- blitz locker-room logs
24
- blitz locker-room train # on-demand QLoRA (GPU); outputs -> ./train-out
25
- blitz locker-room down
26
- ```
27
-
28
- Override host paths via env, e.g. `WORKSPACE_DIR=/path/to/code blitz locker-room up`.
29
- Defaults: `CLAUDE_PROJECTS_DIR=$projects_dir`, `WORKSPACE_DIR=$workspace_dir`.
30
-
31
- ## Point your agent at the ontology MCP
32
-
33
- - **Networked (container):** the `mcp` service serves SSE at
34
- `http://localhost:$${MCP_PORT:-8848}/sse` — add that URL to your agent's MCP config.
35
- - **Local (no container):** `blitz locker-room mcp` speaks stdio; add
36
- `{"command": "blitz", "args": ["locker-room", "mcp"]}`.
37
-
38
- You can also drive the graph host-side without containers:
39
- `blitz locker-room ontologize` then `blitz locker-room list`.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes