blitz-cli 0.8.0__tar.gz → 0.10.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.
- blitz_cli-0.10.0/.github/workflows/homebrew.yml +104 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/.gitignore +1 -1
- blitz_cli-0.10.0/.mcp.json +8 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/PKG-INFO +35 -13
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/README.md +34 -12
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/__init__.py +2 -2
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_activity.py +79 -2
- blitz_cli-0.10.0/blitz_cli/_implement.py +412 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_ontology.py +149 -18
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_playbooks.py +33 -9
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_redact.py +1 -1
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/cli.py +129 -53
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/analyze.md +13 -13
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/clarify.md +4 -4
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/implement.md +28 -8
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/plan.md +6 -6
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/playbook.md +23 -23
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/specify.md +6 -6
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/tasks.md +79 -8
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/scripts/bash/common.sh +31 -31
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/scripts/bash/create-new-feature.sh +2 -2
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/scripts/bash/setup-tasks.sh +1 -1
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/templates/plan-template.md +3 -3
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/workspace/huddle.md +8 -8
- blitz_cli-0.10.0/blitz_cli/templates/workspace_playbook.md.tmpl +32 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/pyproject.toml +1 -1
- blitz_cli-0.10.0/tests/test_implement.py +336 -0
- blitz_cli-0.10.0/tests/test_ontology.py +241 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_playbooks.py +29 -8
- blitz_cli-0.8.0/.github/workflows/homebrew.yml +0 -54
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/.github/workflows/publish.yml +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/LICENSE +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_code_graph.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_corpus.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_dataset.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_git.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_make.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_scaffold.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_tool_schemas.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/_train.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/__init__.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/commands/__init__.py +0 -0
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/memory/playbook.md +0 -0
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/scripts/bash/check-prerequisites.sh +0 -0
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/scripts/bash/setup-plan.sh +0 -0
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/templates/spec-template.md +0 -0
- {blitz_cli-0.8.0/blitz_cli/playbooks/scaffold/.playbooks → blitz_cli-0.10.0/blitz_cli/playbooks/scaffold/.blitz}/templates/tasks-template.md +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/playbooks/workspace/__init__.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/templates/__init__.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/templates/locker_room_Makefile.tmpl +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/templates/locker_room_README.md.tmpl +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/blitz_cli/templates/locker_room_compose.yml.tmpl +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_activity.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_cli.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_corpus.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_make.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_redact.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_scaffold.py +0 -0
- {blitz_cli-0.8.0 → blitz_cli-0.10.0}/tests/test_tool_schemas.py +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
name: Update Homebrew tap
|
|
2
|
+
|
|
3
|
+
# On every release tag, point the sparepartslabs/homebrew-tap formula at the new
|
|
4
|
+
# PyPI sdist. Runs alongside publish.yml (same v* trigger) and waits for the
|
|
5
|
+
# sdist to appear on PyPI before reading its canonical URL + sha256.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- "v*"
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
bump:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Resolve version
|
|
16
|
+
id: v
|
|
17
|
+
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
|
18
|
+
|
|
19
|
+
- name: Fetch sdist url + sha256 from PyPI (wait for publish)
|
|
20
|
+
id: pypi
|
|
21
|
+
run: |
|
|
22
|
+
VERSION="${{ steps.v.outputs.version }}"
|
|
23
|
+
for i in $(seq 1 30); do
|
|
24
|
+
JSON=$(curl -fsSL "https://pypi.org/pypi/blitz-cli/${VERSION}/json" || true)
|
|
25
|
+
URL=$(printf '%s' "$JSON" | jq -r '.urls[]? | select(.packagetype=="sdist") | .url')
|
|
26
|
+
SHA=$(printf '%s' "$JSON" | jq -r '.urls[]? | select(.packagetype=="sdist") | .digests.sha256')
|
|
27
|
+
if [ -n "$URL" ] && [ "$URL" != "null" ]; then break; fi
|
|
28
|
+
echo "sdist for ${VERSION} not on PyPI yet, retrying ($i)..."
|
|
29
|
+
sleep 10
|
|
30
|
+
done
|
|
31
|
+
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
|
|
32
|
+
echo "::error::blitz-cli ${VERSION} sdist not found on PyPI"; exit 1
|
|
33
|
+
fi
|
|
34
|
+
echo "url=$URL" >> "$GITHUB_OUTPUT"
|
|
35
|
+
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
|
36
|
+
|
|
37
|
+
- name: Checkout tap
|
|
38
|
+
uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
repository: sparepartslabs/homebrew-tap
|
|
41
|
+
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
42
|
+
|
|
43
|
+
- name: Update formula (url, sha256, and vendored resources)
|
|
44
|
+
env:
|
|
45
|
+
VERSION: ${{ steps.v.outputs.version }}
|
|
46
|
+
URL: ${{ steps.pypi.outputs.url }}
|
|
47
|
+
SHA: ${{ steps.pypi.outputs.sha }}
|
|
48
|
+
run: |
|
|
49
|
+
F=Formula/blitz-cli.rb
|
|
50
|
+
sed -i -E "s#^ url \".*\"# url \"${URL}\"#" "$F"
|
|
51
|
+
sed -i -E "s#^ sha256 \".*\"# sha256 \"${SHA}\"#" "$F"
|
|
52
|
+
|
|
53
|
+
# Refresh the vendored `resource` blocks for blitz-cli's runtime deps
|
|
54
|
+
# (rich + its transitive deps). `brew update-python-resources` can't be
|
|
55
|
+
# used on release day (it skips PyPI uploads < 1 day old), so resolve
|
|
56
|
+
# the deps in a throwaway venv and read each sdist url+sha256 from PyPI.
|
|
57
|
+
python3 - "$F" "$VERSION" <<'PY'
|
|
58
|
+
import json, os, re, subprocess, sys, tempfile, urllib.request, venv
|
|
59
|
+
|
|
60
|
+
formula, version = sys.argv[1], sys.argv[2]
|
|
61
|
+
tmp = tempfile.mkdtemp()
|
|
62
|
+
venv.create(f"{tmp}/v", with_pip=True)
|
|
63
|
+
pip = f"{tmp}/v/bin/pip"
|
|
64
|
+
subprocess.check_call([pip, "install", "-q", f"blitz-cli=={version}"])
|
|
65
|
+
freeze = subprocess.check_output([pip, "list", "--format=freeze"]).decode()
|
|
66
|
+
|
|
67
|
+
skip = {"blitz-cli", "pip", "setuptools", "wheel"}
|
|
68
|
+
deps = []
|
|
69
|
+
for line in freeze.splitlines():
|
|
70
|
+
if "==" in line:
|
|
71
|
+
name, ver = line.split("==", 1)
|
|
72
|
+
if name.lower() not in skip:
|
|
73
|
+
deps.append((name, ver))
|
|
74
|
+
|
|
75
|
+
blocks = []
|
|
76
|
+
for name, ver in sorted(deps, key=lambda x: x[0].lower()):
|
|
77
|
+
d = json.load(urllib.request.urlopen(f"https://pypi.org/pypi/{name}/{ver}/json"))
|
|
78
|
+
sd = next((u for u in d["urls"] if u["packagetype"] == "sdist"), None)
|
|
79
|
+
if not sd:
|
|
80
|
+
sys.exit(f"no sdist for {name} {ver}")
|
|
81
|
+
blocks.append(
|
|
82
|
+
f' resource "{d["info"]["name"]}" do\n'
|
|
83
|
+
f' url "{sd["url"]}"\n'
|
|
84
|
+
f' sha256 "{sd["digests"]["sha256"]}"\n'
|
|
85
|
+
f' end'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
text = open(formula).read()
|
|
89
|
+
# drop existing resource blocks, then insert fresh ones after depends_on
|
|
90
|
+
text = re.sub(r'\n resource "[^"]+" do\n(?:.*\n)*? end\n', '\n', text)
|
|
91
|
+
inject = "\n" + "\n\n".join(blocks) + "\n"
|
|
92
|
+
text = re.sub(r'( depends_on "python@[^"]+"\n)', r'\1' + inject, text, count=1)
|
|
93
|
+
text = re.sub(r'\n{3,}', '\n\n', text) # collapse blank runs (idempotent)
|
|
94
|
+
open(formula, "w").write(text)
|
|
95
|
+
print(f"refreshed {len(blocks)} resources: {', '.join(n for n, _ in deps)}")
|
|
96
|
+
PY
|
|
97
|
+
|
|
98
|
+
git config user.name "github-actions[bot]"
|
|
99
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
100
|
+
if git diff --quiet; then
|
|
101
|
+
echo "formula already up to date"; exit 0
|
|
102
|
+
fi
|
|
103
|
+
git commit -am "blitz-cli ${VERSION}"
|
|
104
|
+
git push
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blitz-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.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,18 +31,18 @@ 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: `
|
|
34
|
+
Top-level commands: `init`, `implement`, `locker-room`.
|
|
35
35
|
|
|
36
36
|
## Playbooks: spec-driven development commands
|
|
37
37
|
|
|
38
|
-
`blitz
|
|
38
|
+
`blitz init` installs a set of spec-driven-development slash-commands
|
|
39
39
|
into a repo, rendered for whichever agent you use (Claude Code, Cursor, GitHub
|
|
40
|
-
Copilot, Gemini CLI, OpenCode), plus a shared `.
|
|
40
|
+
Copilot, Gemini CLI, OpenCode), plus a shared `.blitz/` working area.
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
blitz
|
|
44
|
-
blitz
|
|
45
|
-
blitz
|
|
43
|
+
blitz init # auto-detect the agent(s) in this repo
|
|
44
|
+
blitz init --agent claude # or target one explicitly
|
|
45
|
+
blitz init --all # install for every supported agent
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The commands are `specify`, `plan`, `tasks`, `implement`, `playbook`, `clarify`,
|
|
@@ -51,6 +51,28 @@ 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
|
|
55
|
+
|
|
56
|
+
`blitz implement` drives a local model (served by Ollama) through a
|
|
57
|
+
playbook-generated `tasks.md`, one task at a time. The model only proposes
|
|
58
|
+
actions (full-file writes and shell commands); the driver applies them, runs
|
|
59
|
+
each phase's machine-runnable verification, retries with the failure fed back
|
|
60
|
+
(default 2), snapshots every file before overwriting it, and checks off
|
|
61
|
+
`- [x]` in the tasks.md. A task that keeps failing stops the run so the state
|
|
62
|
+
stays inspectable. Per-attempt model replies, snapshots, and exact token
|
|
63
|
+
accounting (`usage.json`, per task and total) land under
|
|
64
|
+
`.blitz/implement/runs/<timestamp>/`.
|
|
65
|
+
|
|
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
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Tasks whose text says "optional manual" are skipped and left unchecked. The
|
|
73
|
+
Ollama endpoint comes from `--ollama-url` or `OLLAMA_HOST`; the model from
|
|
74
|
+
`--model` or `BLITZ_IMPLEMENT_MODEL`.
|
|
75
|
+
|
|
54
76
|
## Locker-room: ontology graph, watcher, trainer, MCP
|
|
55
77
|
|
|
56
78
|
The locker-room is a **context node**: a set of containers that ingest, build,
|
|
@@ -98,17 +120,17 @@ the base model with `BASE_MODEL_HF` or `blitz locker-room init --base-model`.
|
|
|
98
120
|
|
|
99
121
|
## Activity: replay a coding-agent session
|
|
100
122
|
|
|
101
|
-
`blitz activity` reads your coding agent's own local session transcripts
|
|
123
|
+
`blitz locker-room activity` reads your coding agent's own local session transcripts
|
|
102
124
|
(currently Claude Code's, at `~/.claude/projects`) for the current repo and
|
|
103
125
|
replays each prompt alongside the actions taken to resolve it (tool calls,
|
|
104
126
|
sub-agents) and the final response:
|
|
105
127
|
|
|
106
128
|
```bash
|
|
107
|
-
blitz activity --list # sessions for this repo, most recent first
|
|
108
|
-
blitz activity # the most recent session's timeline
|
|
109
|
-
blitz activity <session-id-prefix> # a specific session
|
|
110
|
-
blitz activity --full # untruncated prompts/responses/tool I/O
|
|
111
|
-
blitz activity --html report.html # also write a self-contained HTML report
|
|
129
|
+
blitz locker-room activity --list # sessions for this repo, most recent first
|
|
130
|
+
blitz locker-room activity # the most recent session's timeline
|
|
131
|
+
blitz locker-room activity <session-id-prefix> # a specific session
|
|
132
|
+
blitz locker-room activity --full # untruncated prompts/responses/tool I/O
|
|
133
|
+
blitz locker-room activity --html report.html # also write a self-contained HTML report
|
|
112
134
|
```
|
|
113
135
|
|
|
114
136
|
The terminal view is compact by default; `--full` shows untruncated text. The
|
|
@@ -12,18 +12,18 @@ 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: `
|
|
15
|
+
Top-level commands: `init`, `implement`, `locker-room`.
|
|
16
16
|
|
|
17
17
|
## Playbooks: spec-driven development commands
|
|
18
18
|
|
|
19
|
-
`blitz
|
|
19
|
+
`blitz init` installs a set of spec-driven-development slash-commands
|
|
20
20
|
into a repo, rendered for whichever agent you use (Claude Code, Cursor, GitHub
|
|
21
|
-
Copilot, Gemini CLI, OpenCode), plus a shared `.
|
|
21
|
+
Copilot, Gemini CLI, OpenCode), plus a shared `.blitz/` working area.
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
blitz
|
|
25
|
-
blitz
|
|
26
|
-
blitz
|
|
24
|
+
blitz init # auto-detect the agent(s) in this repo
|
|
25
|
+
blitz init --agent claude # or target one explicitly
|
|
26
|
+
blitz init --all # install for every supported agent
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
The commands are `specify`, `plan`, `tasks`, `implement`, `playbook`, `clarify`,
|
|
@@ -32,6 +32,28 @@ 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
|
|
36
|
+
|
|
37
|
+
`blitz implement` drives a local model (served by Ollama) through a
|
|
38
|
+
playbook-generated `tasks.md`, one task at a time. The model only proposes
|
|
39
|
+
actions (full-file writes and shell commands); the driver applies them, runs
|
|
40
|
+
each phase's machine-runnable verification, retries with the failure fed back
|
|
41
|
+
(default 2), snapshots every file before overwriting it, and checks off
|
|
42
|
+
`- [x]` in the tasks.md. A task that keeps failing stops the run so the state
|
|
43
|
+
stays inspectable. Per-attempt model replies, snapshots, and exact token
|
|
44
|
+
accounting (`usage.json`, per task and total) land under
|
|
45
|
+
`.blitz/implement/runs/<timestamp>/`.
|
|
46
|
+
|
|
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
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Tasks whose text says "optional manual" are skipped and left unchecked. The
|
|
54
|
+
Ollama endpoint comes from `--ollama-url` or `OLLAMA_HOST`; the model from
|
|
55
|
+
`--model` or `BLITZ_IMPLEMENT_MODEL`.
|
|
56
|
+
|
|
35
57
|
## Locker-room: ontology graph, watcher, trainer, MCP
|
|
36
58
|
|
|
37
59
|
The locker-room is a **context node**: a set of containers that ingest, build,
|
|
@@ -79,17 +101,17 @@ the base model with `BASE_MODEL_HF` or `blitz locker-room init --base-model`.
|
|
|
79
101
|
|
|
80
102
|
## Activity: replay a coding-agent session
|
|
81
103
|
|
|
82
|
-
`blitz activity` reads your coding agent's own local session transcripts
|
|
104
|
+
`blitz locker-room activity` reads your coding agent's own local session transcripts
|
|
83
105
|
(currently Claude Code's, at `~/.claude/projects`) for the current repo and
|
|
84
106
|
replays each prompt alongside the actions taken to resolve it (tool calls,
|
|
85
107
|
sub-agents) and the final response:
|
|
86
108
|
|
|
87
109
|
```bash
|
|
88
|
-
blitz activity --list # sessions for this repo, most recent first
|
|
89
|
-
blitz activity # the most recent session's timeline
|
|
90
|
-
blitz activity <session-id-prefix> # a specific session
|
|
91
|
-
blitz activity --full # untruncated prompts/responses/tool I/O
|
|
92
|
-
blitz activity --html report.html # also write a self-contained HTML report
|
|
110
|
+
blitz locker-room activity --list # sessions for this repo, most recent first
|
|
111
|
+
blitz locker-room activity # the most recent session's timeline
|
|
112
|
+
blitz locker-room activity <session-id-prefix> # a specific session
|
|
113
|
+
blitz locker-room activity --full # untruncated prompts/responses/tool I/O
|
|
114
|
+
blitz locker-room activity --html report.html # also write a self-contained HTML report
|
|
93
115
|
```
|
|
94
116
|
|
|
95
117
|
The terminal view is compact by default; `--full` shows untruncated text. The
|
|
@@ -13,7 +13,7 @@ Commands:
|
|
|
13
13
|
- ``blitz traces list`` / ``blitz traces show <trace_id>`` — inspect captured
|
|
14
14
|
traces from the local SQLite store.
|
|
15
15
|
|
|
16
|
-
- ``blitz activity [session] [--list] [--html report.html]`` — replay a
|
|
16
|
+
- ``blitz locker-room activity [session] [--list] [--html report.html]`` — replay a
|
|
17
17
|
Claude Code session for this repo: each prompt you sent, the actions taken
|
|
18
18
|
to resolve it (tool calls, sub-agents), and the final response. Reads
|
|
19
19
|
``~/.claude/projects`` directly; unrelated to Blitz's own captured traces.
|
|
@@ -30,4 +30,4 @@ Commands:
|
|
|
30
30
|
|
|
31
31
|
__all__ = ["__version__"]
|
|
32
32
|
|
|
33
|
-
__version__ = "0.
|
|
33
|
+
__version__ = "0.10.0"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""`blitz activity` — replay a coding-agent session: what you asked, and how it
|
|
1
|
+
"""`blitz locker-room activity` — replay a coding-agent session: what you asked, and how it
|
|
2
2
|
was resolved.
|
|
3
3
|
|
|
4
4
|
The currently-supported transcript format is Claude Code's, at
|
|
@@ -82,6 +82,83 @@ def default_sessions_dir() -> Path:
|
|
|
82
82
|
return Path.home() / ".claude" / "projects"
|
|
83
83
|
|
|
84
84
|
|
|
85
|
+
def _zero_usage(**extra) -> dict:
|
|
86
|
+
return {"input": 0, "output": 0, "cache_read": 0, "cache_creation": 0, **extra}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _add_usage(agg: dict, usage: dict) -> None:
|
|
90
|
+
agg["input"] += usage.get("input_tokens") or 0
|
|
91
|
+
agg["output"] += usage.get("output_tokens") or 0
|
|
92
|
+
agg["cache_read"] += usage.get("cache_read_input_tokens") or 0
|
|
93
|
+
agg["cache_creation"] += usage.get("cache_creation_input_tokens") or 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def frontier_usage_by_slug(sessions_root: Path, slugs) -> dict:
|
|
97
|
+
"""Attribute frontier-agent (Claude Code) token spend to feature specs by
|
|
98
|
+
scanning session transcripts under ``sessions_root`` for ``specs/<slug>``
|
|
99
|
+
mentions, at two granularities:
|
|
100
|
+
|
|
101
|
+
- ``session``: the whole session's usage goes to the single most-mentioned
|
|
102
|
+
slug (sessions mentioning no spec are ignored). An upper bound — long
|
|
103
|
+
sessions that merely discuss a spec inflate it.
|
|
104
|
+
- ``turn``: each assistant message is attributed to the slug most recently
|
|
105
|
+
mentioned at that point in the transcript, so usage before the spec
|
|
106
|
+
comes up — or after the session moves on to another spec — is excluded.
|
|
107
|
+
The closer proxy for implementation cost.
|
|
108
|
+
|
|
109
|
+
Usage is deduplicated by API message id (a transcript writes several
|
|
110
|
+
entries per message, each repeating the same usage block). Returns
|
|
111
|
+
``{slug: {"session": {input, output, cache_read, cache_creation, sessions},
|
|
112
|
+
"turn": {input, output, cache_read, cache_creation, messages}}}``."""
|
|
113
|
+
needles = {slug: f"specs/{slug}" for slug in slugs}
|
|
114
|
+
totals: dict = {}
|
|
115
|
+
|
|
116
|
+
def slot(slug: str) -> dict:
|
|
117
|
+
return totals.setdefault(slug, {
|
|
118
|
+
"session": _zero_usage(sessions=0),
|
|
119
|
+
"turn": _zero_usage(messages=0),
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
for jsonl in sorted(sessions_root.glob("*/*.jsonl")):
|
|
123
|
+
try:
|
|
124
|
+
text = jsonl.read_text(encoding="utf-8", errors="replace")
|
|
125
|
+
except OSError:
|
|
126
|
+
continue
|
|
127
|
+
counts = {slug: text.count(n) for slug, n in needles.items()}
|
|
128
|
+
best = max(counts, key=counts.get) if counts else None
|
|
129
|
+
if not best or counts[best] == 0:
|
|
130
|
+
continue
|
|
131
|
+
current: str | None = None # sticky most-recently-mentioned slug
|
|
132
|
+
by_message: dict = {} # message id -> (usage, slug at that point)
|
|
133
|
+
for i, line in enumerate(text.splitlines()):
|
|
134
|
+
mentioned = [(line.find(n), slug) for slug, n in needles.items() if n in line]
|
|
135
|
+
if mentioned:
|
|
136
|
+
current = max(mentioned)[1] # last mention in the line wins
|
|
137
|
+
if '"usage"' not in line:
|
|
138
|
+
continue
|
|
139
|
+
try:
|
|
140
|
+
rec = json.loads(line)
|
|
141
|
+
except ValueError:
|
|
142
|
+
continue
|
|
143
|
+
if rec.get("type") != "assistant":
|
|
144
|
+
continue
|
|
145
|
+
message = rec.get("message", {})
|
|
146
|
+
usage = message.get("usage")
|
|
147
|
+
if usage:
|
|
148
|
+
by_message[message.get("id") or f"line:{i}"] = (usage, current)
|
|
149
|
+
if not by_message:
|
|
150
|
+
continue
|
|
151
|
+
session_agg = slot(best)["session"]
|
|
152
|
+
for usage, slug in by_message.values():
|
|
153
|
+
_add_usage(session_agg, usage)
|
|
154
|
+
if slug is not None:
|
|
155
|
+
turn_agg = slot(slug)["turn"]
|
|
156
|
+
_add_usage(turn_agg, usage)
|
|
157
|
+
turn_agg["messages"] += 1
|
|
158
|
+
session_agg["sessions"] += 1
|
|
159
|
+
return totals
|
|
160
|
+
|
|
161
|
+
|
|
85
162
|
def default_file_history_dir() -> Path:
|
|
86
163
|
"""Root of Claude Code's own pre-edit file backups, one subdir per
|
|
87
164
|
session id — a separate store from ~/.claude/projects, linked to it via
|
|
@@ -657,7 +734,7 @@ def render_html(session: Session, limit: int) -> str:
|
|
|
657
734
|
'<html lang="en">',
|
|
658
735
|
"<head>",
|
|
659
736
|
'<meta charset="utf-8">',
|
|
660
|
-
f"<title>blitz activity — {html.escape(session.title)}</title>",
|
|
737
|
+
f"<title>blitz locker-room activity — {html.escape(session.title)}</title>",
|
|
661
738
|
f"<style>{_HTML_STYLE}</style>",
|
|
662
739
|
"</head>",
|
|
663
740
|
"<body>",
|