kata-cli 0.9.2__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 (157) hide show
  1. {kata_cli-0.9.2 → kata_cli-0.11.0}/.github/workflows/publish.yml +3 -3
  2. {kata_cli-0.9.2 → kata_cli-0.11.0}/.gitignore +5 -0
  3. {kata_cli-0.9.2 → kata_cli-0.11.0}/CHANGELOG.md +56 -0
  4. {kata_cli-0.9.2 → kata_cli-0.11.0}/CLAUDE.md +24 -38
  5. {kata_cli-0.9.2 → kata_cli-0.11.0}/PKG-INFO +28 -10
  6. {kata_cli-0.9.2 → kata_cli-0.11.0}/README.md +27 -9
  7. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/__init__.py +3 -7
  8. kata_cli-0.11.0/antoine/cli/_commands/log.py +129 -0
  9. kata_cli-0.11.0/antoine/kata/__init__.py +5 -0
  10. kata_cli-0.11.0/antoine/kata/log/__init__.py +11 -0
  11. kata_cli-0.11.0/antoine/kata/log/_args.py +63 -0
  12. kata_cli-0.11.0/antoine/kata/log/_gc.py +55 -0
  13. kata_cli-0.11.0/antoine/kata/log/_schema.py +62 -0
  14. kata_cli-0.11.0/antoine/kata/log/_store.py +56 -0
  15. kata_cli-0.11.0/docs/kata/log-schema.md +42 -0
  16. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/skill-sources.md +6 -2
  17. kata_cli-0.11.0/docs/superpowers/plans/2026-05-17-kata-log-subsystem.md +1459 -0
  18. kata_cli-0.11.0/docs/superpowers/specs/2026-05-17-kata-loop-design.md +345 -0
  19. {kata_cli-0.9.2 → kata_cli-0.11.0}/pyproject.toml +1 -1
  20. kata_cli-0.11.0/tests/kata/test_log_args.py +63 -0
  21. kata_cli-0.11.0/tests/kata/test_log_gc.py +72 -0
  22. kata_cli-0.11.0/tests/kata/test_log_schema.py +91 -0
  23. kata_cli-0.11.0/tests/kata/test_log_store.py +90 -0
  24. kata_cli-0.11.0/tests/kata/test_package.py +11 -0
  25. kata_cli-0.11.0/tests/scripts_eval/fixtures/.gitkeep +0 -0
  26. kata_cli-0.11.0/tests/test_cli_log_cmd.py +261 -0
  27. {kata_cli-0.9.2 → kata_cli-0.11.0}/uv.lock +1 -1
  28. kata_cli-0.9.2/.claude/skills/code-lookup/SKILL.md +0 -100
  29. kata_cli-0.9.2/.claude/skills/code-lookup/scripts/classify.sh +0 -4
  30. kata_cli-0.9.2/.claude/skills/code-lookup/scripts/grep.sh +0 -4
  31. kata_cli-0.9.2/.claude/skills/code-lookup/scripts/recent.sh +0 -4
  32. kata_cli-0.9.2/.claude/skills/repo-map/SKILL.md +0 -106
  33. kata_cli-0.9.2/.claude/skills/repo-map/scripts/connections.sh +0 -4
  34. kata_cli-0.9.2/.claude/skills/repo-map/scripts/graph.sh +0 -4
  35. kata_cli-0.9.2/.claude/skills/repo-map/scripts/profile.sh +0 -4
  36. kata_cli-0.9.2/antoine/cli/_commands/classify.py +0 -40
  37. kata_cli-0.9.2/antoine/cli/_commands/grep.py +0 -44
  38. kata_cli-0.9.2/antoine/cli/_commands/recent.py +0 -52
  39. kata_cli-0.9.2/antoine/lookup/__init__.py +0 -25
  40. kata_cli-0.9.2/antoine/lookup/ast_scope.py +0 -74
  41. kata_cli-0.9.2/antoine/lookup/classify.py +0 -301
  42. kata_cli-0.9.2/antoine/lookup/grep_context.py +0 -160
  43. kata_cli-0.9.2/antoine/lookup/recent_outline.py +0 -304
  44. kata_cli-0.9.2/antoine/lookup/render.py +0 -41
  45. kata_cli-0.9.2/antoine/repo/__init__.py +0 -9
  46. kata_cli-0.9.2/antoine/repo/__main__.py +0 -228
  47. kata_cli-0.9.2/antoine/repo/config.py +0 -57
  48. kata_cli-0.9.2/antoine/repo/connections.py +0 -298
  49. kata_cli-0.9.2/antoine/repo/detect.py +0 -86
  50. kata_cli-0.9.2/antoine/repo/errors.py +0 -81
  51. kata_cli-0.9.2/antoine/repo/graph.py +0 -182
  52. kata_cli-0.9.2/antoine/repo/manifest.py +0 -36
  53. kata_cli-0.9.2/antoine/repo/profile.py +0 -700
  54. kata_cli-0.9.2/antoine/repo/render.py +0 -470
  55. kata_cli-0.9.2/tests/test_ast_scope.py +0 -78
  56. kata_cli-0.9.2/tests/test_classify.py +0 -319
  57. kata_cli-0.9.2/tests/test_classify_render.py +0 -80
  58. kata_cli-0.9.2/tests/test_grep_cmd.py +0 -153
  59. kata_cli-0.9.2/tests/test_grep_context.py +0 -172
  60. kata_cli-0.9.2/tests/test_recent_cmd.py +0 -141
  61. kata_cli-0.9.2/tests/test_recent_outline.py +0 -266
  62. kata_cli-0.9.2/tests/test_repo_cli.py +0 -160
  63. kata_cli-0.9.2/tests/test_repo_config.py +0 -76
  64. kata_cli-0.9.2/tests/test_repo_connections.py +0 -113
  65. kata_cli-0.9.2/tests/test_repo_detect.py +0 -76
  66. kata_cli-0.9.2/tests/test_repo_errors.py +0 -52
  67. kata_cli-0.9.2/tests/test_repo_graph.py +0 -118
  68. kata_cli-0.9.2/tests/test_repo_manifest.py +0 -62
  69. kata_cli-0.9.2/tests/test_repo_profile.py +0 -691
  70. kata_cli-0.9.2/tests/test_repo_render.py +0 -465
  71. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/settings.json +0 -0
  72. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/cicd/SKILL.md +0 -0
  73. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  74. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  75. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  76. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  77. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  78. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/communicate/SKILL.md +0 -0
  79. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  80. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  81. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  82. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  83. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  84. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/eval/SKILL.md +0 -0
  85. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/run-tests/SKILL.md +0 -0
  86. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  87. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
  88. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  89. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/version-bump/SKILL.md +0 -0
  90. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
  91. {kata_cli-0.9.2 → kata_cli-0.11.0}/.claude/skills.local.yaml.example +0 -0
  92. {kata_cli-0.9.2 → kata_cli-0.11.0}/.flake8 +0 -0
  93. {kata_cli-0.9.2 → kata_cli-0.11.0}/.github/workflows/security-checks.yml +0 -0
  94. {kata_cli-0.9.2 → kata_cli-0.11.0}/.github/workflows/tests.yml +0 -0
  95. {kata_cli-0.9.2 → kata_cli-0.11.0}/.markdownlint-cli2.yaml +0 -0
  96. {kata_cli-0.9.2 → kata_cli-0.11.0}/.pre-commit-config.yaml +0 -0
  97. {kata_cli-0.9.2 → kata_cli-0.11.0}/LICENSE +0 -0
  98. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/__init__.py +0 -0
  99. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/__main__.py +0 -0
  100. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/_commands/__init__.py +0 -0
  101. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/_commands/explain.py +0 -0
  102. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/_commands/learn.py +0 -0
  103. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/_commands/whoami.py +0 -0
  104. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/_errors.py +0 -0
  105. {kata_cli-0.9.2 → kata_cli-0.11.0}/antoine/cli/_output.py +0 -0
  106. {kata_cli-0.9.2 → kata_cli-0.11.0}/culture.yaml +0 -0
  107. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/eval-rounds/2026-05-15-round-01.md +0 -0
  108. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/eval-rounds/2026-05-15-smoke-02-examples.md +0 -0
  109. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/eval-rounds/2026-05-16-round-02.md +0 -0
  110. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/superpowers/plans/2026-05-15-repo-map.md +0 -0
  111. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/superpowers/plans/2026-05-15-scripts-eval-harness.md +0 -0
  112. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/superpowers/plans/2026-05-16-seer-classify.md +0 -0
  113. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/superpowers/specs/2026-05-15-repo-map-design.md +0 -0
  114. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/superpowers/specs/2026-05-15-scripts-eval-harness-design.md +0 -0
  115. {kata_cli-0.9.2 → kata_cli-0.11.0}/docs/superpowers/specs/2026-05-16-seer-classify-design.md +0 -0
  116. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/__init__.py +0 -0
  117. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/README.md +0 -0
  118. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/RUNBOOK.md +0 -0
  119. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/__init__.py +0 -0
  120. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/_io.py +0 -0
  121. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/backfill.py +0 -0
  122. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/corpus.py +0 -0
  123. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/corpus.yaml +0 -0
  124. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/hooks/__init__.py +0 -0
  125. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/hooks/post_tool.py +0 -0
  126. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/hooks/pre_tool.py +0 -0
  127. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/judge.py +0 -0
  128. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/judge_rubric.md +0 -0
  129. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/manifest.py +0 -0
  130. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/report.py +0 -0
  131. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/results/.gitkeep +0 -0
  132. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/summarize.py +0 -0
  133. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/switch-arm.sh +0 -0
  134. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/trial.py +0 -0
  135. {kata_cli-0.9.2 → kata_cli-0.11.0}/experiments/scripts_eval/validate.py +0 -0
  136. {kata_cli-0.9.2 → kata_cli-0.11.0}/sonar-project.properties +0 -0
  137. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/__init__.py +0 -0
  138. {kata_cli-0.9.2/tests/scripts_eval → kata_cli-0.11.0/tests/kata}/__init__.py +0 -0
  139. /kata_cli-0.9.2/tests/scripts_eval/fixtures/.gitkeep → /kata_cli-0.11.0/tests/scripts_eval/__init__.py +0 -0
  140. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/fixtures/corpus_minimal.yaml +0 -0
  141. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/fixtures/sidechain_min.jsonl +0 -0
  142. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_backfill.py +0 -0
  143. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_corpus.py +0 -0
  144. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_hooks_post_tool.py +0 -0
  145. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_hooks_pre_tool.py +0 -0
  146. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_io.py +0 -0
  147. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_judge.py +0 -0
  148. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_manifest.py +0 -0
  149. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_report.py +0 -0
  150. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_summarize.py +0 -0
  151. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_trial.py +0 -0
  152. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/scripts_eval/test_validate.py +0 -0
  153. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/test_cli_chassis.py +0 -0
  154. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/test_cli_errors.py +0 -0
  155. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/test_cli_output.py +0 -0
  156. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/test_cli_stubs.py +0 -0
  157. {kata_cli-0.9.2 → kata_cli-0.11.0}/tests/test_package.py +0 -0
@@ -57,7 +57,7 @@ jobs:
57
57
  - name: Build and publish each distribution to TestPyPI
58
58
  run: |
59
59
  set -euo pipefail
60
- for pkg in antoine-cli kata-cli code-lens-cli; do
60
+ for pkg in antoine-cli kata-cli; do
61
61
  echo "::group::TestPyPI publish $pkg"
62
62
  # Run the per-package steps in a subshell so set -e failures
63
63
  # don't skip the ::endgroup:: marker — keeps Actions logs
@@ -81,7 +81,7 @@ jobs:
81
81
  - name: Print install commands
82
82
  if: always()
83
83
  run: |
84
- for pkg in antoine-cli kata-cli code-lens-cli; do
84
+ for pkg in antoine-cli kata-cli; do
85
85
  echo "::notice::Test with: uv tool install --index-url https://test.pypi.org/simple/ --index-strategy unsafe-best-match $pkg==${DEV_VERSION}"
86
86
  done
87
87
 
@@ -105,7 +105,7 @@ jobs:
105
105
  - name: Build and publish each distribution
106
106
  run: |
107
107
  set -euo pipefail
108
- for pkg in antoine-cli kata-cli code-lens-cli; do
108
+ for pkg in antoine-cli kata-cli; do
109
109
  echo "::group::Publishing $pkg"
110
110
  # Run the per-package steps in a subshell so set -e failures
111
111
  # don't skip the ::endgroup:: marker — keeps Actions logs
@@ -232,3 +232,8 @@ experiments/scripts_eval/results/*
232
232
 
233
233
  # Claude Code per-machine scheduler lock
234
234
  .claude/scheduled_tasks.lock
235
+
236
+ # kata-cli local store — everything under .antoine/ is local-only,
237
+ # EXCEPT katas.toml which is the committed audit ledger.
238
+ .antoine/*
239
+ !.antoine/katas.toml
@@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.11.0] - 2026-05-17
9
+
10
+ ### Removed
11
+
12
+ - `antoine.lookup` engine + `kata classify` / `kata grep` / `kata recent`
13
+ verbs — migrated to `code-lens-cli` v0.10.0.
14
+ - `antoine.repo` engine + `python -m antoine.repo {profile,connections,graph}`
15
+ surface — migrated to `code-lens-cli` v0.10.0.
16
+ - `.claude/skills/code-lookup/` and `.claude/skills/repo-map/` — re-homed
17
+ under `agentculture/code-lens-cli/.claude/skills/`.
18
+ - 16 tests for the migrated surface (`test_ast_scope`, `test_classify*`,
19
+ `test_grep*`, `test_recent*`, `test_repo_*`).
20
+
21
+ ### Changed
22
+
23
+ - `docs/skill-sources.md`: `code-lookup` and `repo-map` rows now point
24
+ at `code-lens-cli` as the upstream supplier; added "graduation"
25
+ precedent bullet to the vendoring policy.
26
+ - `CLAUDE.md` "Dispatching subagents" table now names `code-lens`
27
+ verbs (soft dep — install with `uv tool install code-lens-cli`);
28
+ preserved as **evidence** of the first catalog this loop produced
29
+ rather than as a prescription antoine pushes downstream.
30
+ - `README.md`: new "Results of this loop" section pointing at
31
+ `code-lens-cli`; corrected the alt-publish bullet to drop
32
+ `code-lens-cli` (which left the dual-publish loop in 0.10.0).
33
+
34
+ ### Migration
35
+
36
+ - `kata classify .` → `code-lens classify .`
37
+ - `kata grep <pattern> .` → `code-lens grep <pattern> .`
38
+ - `kata recent .` → `code-lens recent .`
39
+ - `python -m antoine.repo profile .` → `code-lens profile .`
40
+
41
+ antoine continues to ship the capture/reduce/assess loop
42
+ (`kata log {tail, gc, grep}` plus the forthcoming cells 2–8); the
43
+ inspection verbs were extracted because they are the loop's *result*,
44
+ not its mechanism. See <https://github.com/agentculture/code-lens-cli/issues/2>
45
+ for handover history.
46
+
47
+ ## [0.10.0] - 2026-05-17
48
+
49
+ ### Added
50
+
51
+ - `kata log {tail, gc, grep}` verbs for inspecting the local capture log
52
+ under `.antoine/log/`.
53
+ - `antoine.kata.log` engine subpackage: `LogEntry` schema, daily-sharded
54
+ `LogStore`, `ArgsSidecar`, TTL `gc` with privacy invariant (args files
55
+ deleted first).
56
+ - `.gitignore` rule: everything under `.antoine/` is local-only except
57
+ `katas.toml` (the future committed ledger; introduced in cell 4).
58
+ - `docs/kata/log-schema.md` documenting the JSONL row format.
59
+
60
+ This is **Cell 1** of the kata-cli capture/reduce/assess loop. See
61
+ `docs/superpowers/specs/2026-05-17-kata-loop-design.md` for the full
62
+ 8-cell scope.
63
+
8
64
  ## [0.9.2] - 2026-05-17
9
65
 
10
66
  ### Changed
@@ -112,45 +112,31 @@ The harness gates the planned redesign of the `learn` / `explain` /
112
112
  brainstorm** — fed by the eval results, not by the harness's
113
113
  existence.
114
114
 
115
- ## Dispatching subagents — inject code-lookup / repo-map directives
116
-
117
- This table applies in two places for **the conversational agent that loads
118
- this CLAUDE.md at session start** (i.e. you, the parent reading this now
119
- not subagents you dispatch):
120
-
121
- 1. **When you delegate** a task to a subagent (Explore, general-purpose,
122
- Plan, etc.) whose task shape matches a row below: include the matching
123
- directive **verbatim** in the subagent's prompt.
124
- 2. **When you answer first-person** a task shape that matches a row below
125
- without delegating: invoke the named verb yourself rather than
126
- ad-hoc combinations of `ls` / `cat` / `grep` / `git` calls. The table
127
- is one application of a broader rule: prefer a small purpose-built
128
- script that returns structured high-value data in one call.
129
-
130
- | Task shape | Directive (verbatim if delegating; otherwise the verb to invoke yourself) |
115
+ ## Dispatching subagents — code-lens verbs (first catalog this loop produced)
116
+
117
+ The four task shapes below have 1-call verbs in
118
+ [`code-lens-cli`](https://github.com/agentculture/code-lens-cli)
119
+ the first published results catalog of running kata's
120
+ capture/reduce/assess loop on a real codebase (this one). Install with
121
+ `uv tool install code-lens-cli`. If installed, prefer the verb; if not,
122
+ the loop will eventually re-surface the N-call pattern and the next
123
+ catalog release will offer a replacement.
124
+
125
+ This is the **evidence** view of code-lens-cli: it is what running the
126
+ loop produces, not a dependency antoine prescribes downstream. The
127
+ verbs lived under `antoine/lookup/` + `antoine/repo/` through 0.10.0;
128
+ 0.11.0 migrated them out (see [code-lens-cli#2](https://github.com/agentculture/code-lens-cli/issues/2)).
129
+
130
+ | Task shape | Verb |
131
131
  |---|---|
132
- | "what changed in the last N commits / which functions or classes changed across commits" | `Use the code-lookup skill's recent verb (.claude/skills/code-lookup/scripts/recent.sh) for the commit + symbol-diff data. Do not call git log / git diff / git show directly for this question.` |
133
- | "where is `<pattern>` referenced / find usages with enclosing scope" | `Use the code-lookup skill's grep verb (.claude/skills/code-lookup/scripts/grep.sh <pattern>) for match + scope. Do not use raw rg / grep for this question.` |
134
- | "what kind of project is this — CLI? library? PyPI-published? dockerized?" | `Use the code-lookup skill's classify verb (.claude/skills/code-lookup/scripts/classify.sh).` |
135
- | "profile this repo / build-test story / repo overview / what fields does pyproject expose" | `Use the repo-map skill's profile verb (.claude/skills/repo-map/scripts/profile.sh).` |
136
-
137
- **Why this lives in CLAUDE.md and not in the skill descriptions:** round-2
138
- of the PR #18 organic-adoption smokes showed that **subagents construct
139
- their plan from the prompt body before consulting the skills catalog** —
140
- so a description-shape change on the skill itself does not move adoption
141
- (0 of 2 models picked up `antoine recent` for a question perfectly tuned for
142
- it). Round-3 confirmed the parent-agent path: a fresh session loading the
143
- table delegated *and the subagent invoked the verb directly via the
144
- injected directive*.
145
-
146
- **Scope of this rule — empirical:** the table directly governs the parent
147
- agent's behavior at delegation time and first-person execution time. It
148
- does **not** reliably propagate to subagents through CLAUDE.md alone —
149
- round-4 of the smokes (PR #18 commit `171980f`) showed a fresh subagent
150
- receiving the broadened table as ambient context still defaulted to `git
151
- log` via Bash (7 calls, no skill use) for a perfectly-shaped question.
152
- The lever for subagent adoption stays the prompt-body directive injection
153
- in row 1 above — the table does not change that.
132
+ | "what changed in the last N commits / which functions or classes changed" | `code-lens recent .` |
133
+ | "where is `<pattern>` referenced / find usages with enclosing scope" | `code-lens grep <pattern> .` |
134
+ | "what kind of project is this — CLI? library? PyPI-published? dockerized?" | `code-lens classify .` |
135
+ | "profile this repo / build-test story / repo overview" | `code-lens profile .` |
136
+
137
+ Empirical adoption notes for this table (PR #18 round-2/3/4 smokes —
138
+ why CLAUDE.md is the lever, not skill descriptions) moved with the
139
+ verbs to code-lens-cli's CLAUDE.md.
154
140
 
155
141
  ## Workspace Context
156
142
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kata-cli
3
- Version: 0.9.2
3
+ Version: 0.11.0
4
4
  Summary: antoine — codebase lookup and indexing for agent skills (greenfield AgentCulture sibling).
5
5
  Project-URL: Homepage, https://github.com/agentculture/antoine
6
6
  Project-URL: Issues, https://github.com/agentculture/antoine/issues
@@ -41,16 +41,19 @@ actually worth the bet, and the recorded results from past rounds.
41
41
  placeholder stubs. See [`CLAUDE.md`](./CLAUDE.md) for build / test /
42
42
  architecture details.
43
43
 
44
- - **`kata-cli` (and `code-lens-cli`) — alt-published PyPI distributions**
45
- carrying the same wheel content as `antoine-cli`. Installing any of the
46
- three exposes the same pair of console scripts — `antoine` and `kata`
47
- (see [`pyproject.toml`](./pyproject.toml)). The dispatching directives
48
- baked into [`CLAUDE.md`](./CLAUDE.md) refer to **verbs** invoked via the
49
- `kata` / `antoine` commands; `kata-cli` is the distribution label users
50
- `pip install`, not a command they run. The dual-publish loop is defined
51
- in [`.github/workflows/`](./.github/workflows/); see
44
+ - **`kata-cli` — alt-published PyPI distribution** carrying the same
45
+ wheel content as `antoine-cli`. Installing either exposes the same
46
+ pair of console scripts — `antoine` and `kata`
47
+ (see [`pyproject.toml`](./pyproject.toml)). `kata-cli` is the
48
+ distribution label users `pip install`, not a command they run. The
49
+ dual-publish loop is defined in
50
+ [`.github/workflows/`](./.github/workflows/); see
52
51
  [`CHANGELOG.md`](./CHANGELOG.md) entries for v0.7.0 / v0.7.1 for the
53
- history of how the three distribution names were wired up.
52
+ history of how the distribution names were wired up. (A third name,
53
+ `code-lens-cli`, was published from this repo through v0.9.2; from
54
+ v0.10.0 onward it lives in [its own
55
+ repo](https://github.com/agentculture/code-lens-cli) — see "Results
56
+ of this loop" below.)
54
57
 
55
58
  - **`experiments/scripts_eval/`** — the A/B-test harness for the
56
59
  `repo-map` skill (env-var-gated hooks, three-layer scoring, 5-repo
@@ -69,3 +72,18 @@ actually worth the bet, and the recorded results from past rounds.
69
72
  *before* consulting the skills catalog, which is why the dispatching
70
73
  table lives in the parent agent's instructions rather than in skill
71
74
  descriptions.
75
+
76
+ ## Results of this loop
77
+
78
+ antoine ships the *tool*. The first published *catalog of results* from
79
+ running its capture/reduce/assess loop is
80
+ [`code-lens-cli`](https://github.com/agentculture/code-lens-cli) —
81
+ four 1-call verbs (`classify` / `recent` / `grep` / `profile`) that
82
+ antoine maintainers identified as recurring N-call patterns and
83
+ packaged into a sibling distribution. Install with
84
+ `uv tool install code-lens-cli`. Most agents will install both.
85
+
86
+ The migration history (antoine 0.10.0 → code-lens-cli 0.10.0,
87
+ 2026-05-17) is the first concrete proof that the loop produces shippable
88
+ artifacts. Future cells (2–8) of the loop are designed to make catalogs
89
+ like this one routine rather than artisanal.
@@ -23,16 +23,19 @@ actually worth the bet, and the recorded results from past rounds.
23
23
  placeholder stubs. See [`CLAUDE.md`](./CLAUDE.md) for build / test /
24
24
  architecture details.
25
25
 
26
- - **`kata-cli` (and `code-lens-cli`) — alt-published PyPI distributions**
27
- carrying the same wheel content as `antoine-cli`. Installing any of the
28
- three exposes the same pair of console scripts — `antoine` and `kata`
29
- (see [`pyproject.toml`](./pyproject.toml)). The dispatching directives
30
- baked into [`CLAUDE.md`](./CLAUDE.md) refer to **verbs** invoked via the
31
- `kata` / `antoine` commands; `kata-cli` is the distribution label users
32
- `pip install`, not a command they run. The dual-publish loop is defined
33
- in [`.github/workflows/`](./.github/workflows/); see
26
+ - **`kata-cli` — alt-published PyPI distribution** carrying the same
27
+ wheel content as `antoine-cli`. Installing either exposes the same
28
+ pair of console scripts — `antoine` and `kata`
29
+ (see [`pyproject.toml`](./pyproject.toml)). `kata-cli` is the
30
+ distribution label users `pip install`, not a command they run. The
31
+ dual-publish loop is defined in
32
+ [`.github/workflows/`](./.github/workflows/); see
34
33
  [`CHANGELOG.md`](./CHANGELOG.md) entries for v0.7.0 / v0.7.1 for the
35
- history of how the three distribution names were wired up.
34
+ history of how the distribution names were wired up. (A third name,
35
+ `code-lens-cli`, was published from this repo through v0.9.2; from
36
+ v0.10.0 onward it lives in [its own
37
+ repo](https://github.com/agentculture/code-lens-cli) — see "Results
38
+ of this loop" below.)
36
39
 
37
40
  - **`experiments/scripts_eval/`** — the A/B-test harness for the
38
41
  `repo-map` skill (env-var-gated hooks, three-layer scoring, 5-repo
@@ -51,3 +54,18 @@ actually worth the bet, and the recorded results from past rounds.
51
54
  *before* consulting the skills catalog, which is why the dispatching
52
55
  table lives in the parent agent's instructions rather than in skill
53
56
  descriptions.
57
+
58
+ ## Results of this loop
59
+
60
+ antoine ships the *tool*. The first published *catalog of results* from
61
+ running its capture/reduce/assess loop is
62
+ [`code-lens-cli`](https://github.com/agentculture/code-lens-cli) —
63
+ four 1-call verbs (`classify` / `recent` / `grep` / `profile`) that
64
+ antoine maintainers identified as recurring N-call patterns and
65
+ packaged into a sibling distribution. Install with
66
+ `uv tool install code-lens-cli`. Most agents will install both.
67
+
68
+ The migration history (antoine 0.10.0 → code-lens-cli 0.10.0,
69
+ 2026-05-17) is the first concrete proof that the loop produces shippable
70
+ artifacts. Future cells (2–8) of the loop are designed to make catalogs
71
+ like this one routine rather than artisanal.
@@ -12,7 +12,7 @@ the raw argv (:func:`main` sets ``_AntoineArgumentParser._json_hint`` before
12
12
  ``parse_args``).
13
13
  """
14
14
 
15
- # pylint: disable=duplicate-code # _dispatch mirrors antoine.repo.__main__ by design
15
+ # pylint: disable=duplicate-code # _dispatch keeps a deliberate pattern shared with sibling CLIs
16
16
 
17
17
  from __future__ import annotations
18
18
 
@@ -57,11 +57,9 @@ def _build_parser() -> argparse.ArgumentParser:
57
57
  sub = parser.add_subparsers(dest="command", parser_class=_AntoineArgumentParser)
58
58
 
59
59
  # pylint: disable=import-outside-toplevel
60
- from antoine.cli._commands import classify as _classify_cmd
61
60
  from antoine.cli._commands import explain as _explain_cmd
62
- from antoine.cli._commands import grep as _grep_cmd
63
61
  from antoine.cli._commands import learn as _learn_cmd
64
- from antoine.cli._commands import recent as _recent_cmd
62
+ from antoine.cli._commands import log as _log_cmd
65
63
  from antoine.cli._commands import whoami as _whoami_cmd
66
64
 
67
65
  # pylint: enable=import-outside-toplevel
@@ -69,9 +67,7 @@ def _build_parser() -> argparse.ArgumentParser:
69
67
  _learn_cmd.register(sub)
70
68
  _explain_cmd.register(sub)
71
69
  _whoami_cmd.register(sub)
72
- _classify_cmd.register(sub)
73
- _grep_cmd.register(sub)
74
- _recent_cmd.register(sub)
70
+ _log_cmd.register(sub)
75
71
 
76
72
  return parser
77
73
 
@@ -0,0 +1,129 @@
1
+ """`kata log` verb: parent + tail/gc/grep subcommands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from collections import deque
7
+
8
+ from antoine.cli._errors import EXIT_ENV_ERROR, EXIT_USER_ERROR, AntoineError
9
+ from antoine.cli._output import emit_result
10
+ from antoine.kata.log._gc import gc as _gc_run
11
+ from antoine.kata.log._store import LogStore
12
+
13
+
14
+ def _no_subcommand(args: argparse.Namespace) -> int:
15
+ args._parent_parser.print_help()
16
+ return 0
17
+
18
+
19
+ def _handle_grep(args: argparse.Namespace) -> int:
20
+ store = LogStore()
21
+ if not store.root.exists() or not any(store.root.glob("*.jsonl")):
22
+ raise AntoineError(
23
+ code=EXIT_ENV_ERROR,
24
+ message="No capture data found in .antoine/log/. antoine has nothing to grep.",
25
+ remediation=(
26
+ "Run `kata learn` to see how to instrument your agent, "
27
+ "then start a session before grepping the log."
28
+ ),
29
+ )
30
+
31
+ needle = args.pattern
32
+ for entry in store.read_all():
33
+ haystack = " ".join(
34
+ v for v in (entry.tool, entry.bash_argv0 or "", entry.agent, entry.session) if v
35
+ )
36
+ if needle in haystack:
37
+ emit_result(entry.to_json_line().rstrip("\n"), json_mode=False)
38
+ return 0
39
+
40
+
41
+ def _handle_tail(args: argparse.Namespace) -> int:
42
+ store = LogStore()
43
+ if not store.root.exists() or not any(store.root.glob("*.jsonl")):
44
+ raise AntoineError(
45
+ code=EXIT_ENV_ERROR,
46
+ message=(
47
+ "No capture data found in .antoine/log/. "
48
+ "antoine has no observed tool calls to display."
49
+ ),
50
+ remediation=(
51
+ "Run `kata learn` to see how to instrument your agent, "
52
+ "then start a session and try `kata log tail` again."
53
+ ),
54
+ )
55
+
56
+ n = max(1, int(args.n))
57
+ last = deque(store.read_all(), maxlen=n)
58
+ for entry in last:
59
+ emit_result(entry.to_json_line().rstrip("\n"), json_mode=False)
60
+ return 0
61
+
62
+
63
+ def _handle_gc(args: argparse.Namespace) -> int:
64
+ if args.ttl_days < 0:
65
+ raise AntoineError(
66
+ code=EXIT_USER_ERROR,
67
+ message=(
68
+ f"--ttl-days must be >= 0 (got {args.ttl_days}). "
69
+ "A negative TTL would classify every log file as stale and "
70
+ "delete the entire log."
71
+ ),
72
+ remediation="Re-run with a non-negative value, e.g. `kata log gc --ttl-days 7`.",
73
+ )
74
+
75
+ store = LogStore()
76
+ if not store.root.exists():
77
+ message = "deleted 0 shape files, 0 args files (no log present)"
78
+ else:
79
+ try:
80
+ result = _gc_run(root=store.root, ttl_days=args.ttl_days)
81
+ except PermissionError as exc:
82
+ raise AntoineError(
83
+ code=EXIT_ENV_ERROR,
84
+ message=(
85
+ f"GC could not delete files past TTL: {exc}. "
86
+ "The privacy invariant requires expired data to be removed."
87
+ ),
88
+ remediation=(
89
+ "Check filesystem permissions on .antoine/log/ "
90
+ "(needs delete access for the user running antoine), then retry."
91
+ ),
92
+ ) from exc
93
+ message = (
94
+ f"deleted {len(result.deleted_shape)} shape files, "
95
+ f"{len(result.deleted_args)} args files"
96
+ )
97
+
98
+ emit_result(message, json_mode=False)
99
+ return 0
100
+
101
+
102
+ def register(sub: argparse._SubParsersAction) -> None:
103
+ parser = sub.add_parser(
104
+ "log",
105
+ help="raw access to the local capture log",
106
+ description="Inspect, prune, or search the local kata capture log under ./.antoine/log/.",
107
+ )
108
+ parser.set_defaults(func=_no_subcommand, _parent_parser=parser)
109
+ # Nested parsers MUST share the structured-error wrapper used by the top
110
+ # level — otherwise `kata log tail --bogus` exits 2 via default argparse,
111
+ # bypassing the "antoine never crashes" contract. (Qodo #25 bug 1.)
112
+ subsub = parser.add_subparsers(dest="log_command", parser_class=type(parser))
113
+
114
+ tail = subsub.add_parser("tail", help="print the last N captured entries")
115
+ tail.add_argument("-n", default=10, type=int, help="number of entries (default: 10)")
116
+ tail.set_defaults(func=_handle_tail)
117
+
118
+ gc_p = subsub.add_parser("gc", help="delete capture entries past TTL")
119
+ gc_p.add_argument(
120
+ "--ttl-days",
121
+ type=int,
122
+ default=7,
123
+ help="retention window in days (default: 7)",
124
+ )
125
+ gc_p.set_defaults(func=_handle_gc)
126
+
127
+ grep_p = subsub.add_parser("grep", help="filter log entries by substring")
128
+ grep_p.add_argument("pattern", help="substring matched against tool/bash_argv0/agent/session")
129
+ grep_p.set_defaults(func=_handle_grep)
@@ -0,0 +1,5 @@
1
+ """antoine.kata — the capture/reduce/assess loop engine.
2
+
3
+ See `docs/superpowers/specs/2026-05-17-kata-loop-design.md` for the design.
4
+ This package is purely the engine; CLI verbs live in `antoine.cli._commands`.
5
+ """
@@ -0,0 +1,11 @@
1
+ """Log subsystem: JSONL schema, on-disk store, TTL gc.
2
+
3
+ Layout (under the current working directory):
4
+
5
+ .antoine/log/<YYYY-MM-DD>.jsonl shape index, one line per tool call
6
+ .antoine/log/args/<session>.jsonl raw args sidecar, keyed by row index
7
+
8
+ The shape index is what `suggest`/`assess` read. The args sidecar holds
9
+ raw, privacy-sensitive data and is the first thing the 7-day TTL pass
10
+ deletes.
11
+ """
@@ -0,0 +1,63 @@
1
+ """ArgsSidecar — raw args kept separately from the shape index.
2
+
3
+ This is the privacy-sensitive half of the log. TTL gc deletes this
4
+ directory first; the shape index can be retained longer if the user wants
5
+ aggregate stats without raw content.
6
+
7
+ Files live at <root>/args/<session>.jsonl, one JSON object per line.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import re
14
+ from collections.abc import Iterator
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ # Whitelist: alphanumeric, dot, underscore, hyphen. No path separators,
19
+ # no `..`, no whitespace. Keeps args/<session>.jsonl strictly flat so
20
+ # `kata log gc`'s non-recursive `*.jsonl` scan can't miss a file (the
21
+ # privacy invariant requires expired args to be deleted on schedule).
22
+ # (Qodo #25 bug 2.)
23
+ _SAFE_SESSION_RE = re.compile(r"^[A-Za-z0-9._-]+$")
24
+
25
+
26
+ def _validate_session(session: str) -> str:
27
+ # `.` and `..` pass the char-class regex but are path-traversal refs.
28
+ if session in {".", ".."} or not _SAFE_SESSION_RE.fullmatch(session):
29
+ raise ValueError(
30
+ f"unsafe session id {session!r}: must match [A-Za-z0-9._-]+ "
31
+ "and cannot be '.' or '..' (the args sidecar stays flat under "
32
+ "args/<session>.jsonl)"
33
+ )
34
+ return session
35
+
36
+
37
+ class ArgsSidecar:
38
+ """Per-session JSONL store for raw tool args."""
39
+
40
+ def __init__(self, root: Path) -> None:
41
+ self.root = root
42
+ self.args_dir = root / "args"
43
+
44
+ def _file_for(self, session: str) -> Path:
45
+ safe = _validate_session(session)
46
+ return self.args_dir / f"{safe}.jsonl"
47
+
48
+ def append(self, session: str, args: dict[str, Any]) -> None:
49
+ self.args_dir.mkdir(parents=True, exist_ok=True)
50
+ path = self._file_for(session)
51
+ with path.open("a", encoding="utf-8") as fp:
52
+ fp.write(json.dumps(args, ensure_ascii=False, separators=(",", ":")) + "\n")
53
+
54
+ def read(self, session: str) -> Iterator[dict[str, Any]]:
55
+ path = self._file_for(session)
56
+ if not path.exists():
57
+ return
58
+ with path.open("r", encoding="utf-8") as fp:
59
+ for raw in fp:
60
+ stripped = raw.strip()
61
+ if not stripped:
62
+ continue
63
+ yield json.loads(stripped)
@@ -0,0 +1,55 @@
1
+ """TTL gc — privacy invariant: if files past TTL exist, they MUST be deleted.
2
+
3
+ The order is intentional: raw args first (privacy-sensitive), then the
4
+ shape index. If any unlink raises ``PermissionError``, propagate it
5
+ unchanged so the caller (the ``kata log gc`` verb handler) can translate
6
+ it into an ``AntoineError`` with an actionable Fix line.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import time
12
+ from dataclasses import dataclass, field
13
+ from pathlib import Path
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class GCResult:
18
+ deleted_shape: list[Path] = field(default_factory=list)
19
+ deleted_args: list[Path] = field(default_factory=list)
20
+
21
+
22
+ def _stale_files(directory: Path, ttl_seconds: float, now: float) -> list[Path]:
23
+ if not directory.exists():
24
+ return []
25
+ out: list[Path] = []
26
+ for p in sorted(directory.glob("*.jsonl")):
27
+ if (now - p.stat().st_mtime) > ttl_seconds:
28
+ out.append(p)
29
+ return out
30
+
31
+
32
+ def gc(*, root: Path, ttl_days: int) -> GCResult:
33
+ """Delete files older than ``ttl_days`` from ``root`` and ``root/args``.
34
+
35
+ Raises ``ValueError`` on negative ``ttl_days`` — a negative TTL would
36
+ classify every file as stale and wipe the whole log from a single typo,
37
+ which we treat as a programming error, not silently allowed input.
38
+ Raises ``PermissionError`` if any unlink fails — privacy invariant.
39
+ """
40
+ if ttl_days < 0:
41
+ raise ValueError(f"ttl_days must be >= 0 (got {ttl_days})")
42
+ now = time.time()
43
+ ttl_seconds = ttl_days * 86400.0
44
+
45
+ args_dir = root / "args"
46
+ stale_args = _stale_files(args_dir, ttl_seconds, now)
47
+ stale_shape = _stale_files(root, ttl_seconds, now)
48
+
49
+ # Args first — they hold the raw, privacy-sensitive data.
50
+ for path in stale_args:
51
+ path.unlink()
52
+ for path in stale_shape:
53
+ path.unlink()
54
+
55
+ return GCResult(deleted_shape=stale_shape, deleted_args=stale_args)
@@ -0,0 +1,62 @@
1
+ """LogEntry — the JSONL row written by an adapter per captured tool call.
2
+
3
+ The schema is documented in:
4
+ docs/kata/log-schema.md (added in a later task in this plan)
5
+
6
+ This module is pure data + serialization; it owns no IO.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from dataclasses import asdict, dataclass, fields
13
+ from typing import Any
14
+
15
+ _REQUIRED = ("ts", "session", "agent", "tool", "args_digest")
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class LogEntry:
20
+ """One captured tool call.
21
+
22
+ Required fields (always present): ts, session, agent, tool, args_digest.
23
+ Optional fields (None if the adapter could not provide them):
24
+ bash_argv0, tokens_in, tokens_out, duration_ms.
25
+ """
26
+
27
+ ts: str
28
+ session: str
29
+ agent: str
30
+ tool: str
31
+ args_digest: str
32
+ bash_argv0: str | None = None
33
+ tokens_in: int | None = None
34
+ tokens_out: int | None = None
35
+ duration_ms: int | None = None
36
+
37
+ def __post_init__(self) -> None:
38
+ if not self.args_digest.startswith("sha256:"):
39
+ raise ValueError(
40
+ "args_digest must start with 'sha256:' " f"(got: {self.args_digest!r})"
41
+ )
42
+
43
+ def to_json_line(self) -> str:
44
+ """Return a single newline-terminated JSON object."""
45
+ payload = asdict(self)
46
+ return json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + "\n"
47
+
48
+ @classmethod
49
+ def from_json_line(cls, line: str) -> "LogEntry":
50
+ """Parse one JSONL row. Raises ValueError on missing required fields.
51
+
52
+ Unknown fields in ``payload`` are silently dropped — adapters may emit
53
+ future-evolved schemas, and tolerating that keeps log ingestion working
54
+ instead of TypeError-ing through ``cls(**payload)``. Forward-compat is
55
+ part of the "antoine never crashes" contract.
56
+ """
57
+ payload: dict[str, Any] = json.loads(line)
58
+ for field_name in _REQUIRED:
59
+ if field_name not in payload:
60
+ raise ValueError(f"missing required field: {field_name!r}")
61
+ known = {f.name for f in fields(cls)}
62
+ return cls(**{k: v for k, v in payload.items() if k in known})