cctx-cli 1.5.0__tar.gz → 1.6.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 (175) hide show
  1. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/CHANGELOG.md +74 -0
  2. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/PKG-INFO +1 -1
  3. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/PRODUCT.md +25 -13
  4. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/__init__.py +1 -1
  5. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/cli.py +36 -1
  6. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/harvest.py +74 -4
  7. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/models.py +16 -0
  8. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/recommender/claude_md.py +21 -3
  9. cctx_cli-1.6.0/docs/product-reviews/2026-06-09-product-review.md +186 -0
  10. cctx_cli-1.6.0/docs/superpowers/specs/2026-06-09-cross-agent-emit-design.md +214 -0
  11. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/pyproject.toml +1 -1
  12. cctx_cli-1.6.0/tests/test_harvest_emit.py +204 -0
  13. cctx_cli-1.6.0/tests/test_recommender.py +125 -0
  14. cctx_cli-1.5.0/tests/test_recommender.py +0 -56
  15. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/.github/workflows/ci.yml +0 -0
  16. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/.github/workflows/publish.yml +0 -0
  17. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/.github/workflows/release.yml +0 -0
  18. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/.gitignore +0 -0
  19. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/CLAUDE.md +0 -0
  20. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/DESIGN.md +0 -0
  21. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/README.md +0 -0
  22. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/action.yml +0 -0
  23. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/agents.py +0 -0
  24. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/__init__.py +0 -0
  25. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/aggregate.py +0 -0
  26. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/inflection.py +0 -0
  27. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/__init__.py +0 -0
  28. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/dead_end.py +0 -0
  29. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/project_specific.py +0 -0
  30. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/retry_loop.py +0 -0
  31. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/scope_creep.py +0 -0
  32. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/stale_context.py +0 -0
  33. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/diagnostician/patterns/tool_thrash.py +0 -0
  34. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/discovery.py +0 -0
  35. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/exporters/__init__.py +0 -0
  36. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/exporters/csv.py +0 -0
  37. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/exporters/json.py +0 -0
  38. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/exporters/jsonl.py +0 -0
  39. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/parsers/__init__.py +0 -0
  40. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/parsers/claude_code.py +0 -0
  41. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/pricing.py +0 -0
  42. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/recommender/__init__.py +0 -0
  43. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/recommender/evidence.py +0 -0
  44. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/renderers/__init__.py +0 -0
  45. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/renderers/github.py +0 -0
  46. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/renderers/report.py +0 -0
  47. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/renderers/templates/autopsy.html.j2 +0 -0
  48. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/renderers/terminal.py +0 -0
  49. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/renderers/trace_tui.py +0 -0
  50. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/tokenizer.py +0 -0
  51. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx/watcher.py +0 -0
  52. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/cctx-project-brief.md +0 -0
  53. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/demo.gif +0 -0
  54. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/demo.tape +0 -0
  55. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/health-reviews/2026-05-15-deep-review-summary.md +0 -0
  56. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/health-reviews/2026-05-15-health-review.md +0 -0
  57. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/product-reviews/2026-05-15-product-review.md +0 -0
  58. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/plans/2026-05-12-claude-code-parser.md +0 -0
  59. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/plans/2026-05-14-autopsy-v0.md +0 -0
  60. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/plans/2026-05-16-readme-pypi-release.md +0 -0
  61. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/plans/2026-05-17-harvest-check-depth.md +0 -0
  62. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/plans/2026-05-17-project-pattern-detection.md +0 -0
  63. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/plans/2026-05-19-claude-agents-live-integration.md +0 -0
  64. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-12-claude-code-parser-design.md +0 -0
  65. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-14-autopsy-design.md +0 -0
  66. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-14-harvest-design.md +0 -0
  67. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-14-trace-tui-design.md +0 -0
  68. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-16-readme-pypi-release-design.md +0 -0
  69. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-17-harvest-check-depth-design.md +0 -0
  70. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-17-project-pattern-detection-design.md +0 -0
  71. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/docs/superpowers/specs/2026-05-19-claude-agents-live-integration-design.md +0 -0
  72. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/__init__.py +0 -0
  73. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/conftest.py +0 -0
  74. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/__init__.py +0 -0
  75. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/conftest.py +0 -0
  76. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_dead_end.py +0 -0
  77. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_inflection.py +0 -0
  78. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_orchestrator.py +0 -0
  79. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_project_specific.py +0 -0
  80. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_retry_loop.py +0 -0
  81. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_scope_creep.py +0 -0
  82. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_stale_context.py +0 -0
  83. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/diagnostician/test_tool_thrash.py +0 -0
  84. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/exporters/__init__.py +0 -0
  85. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/exporters/test_csv.py +0 -0
  86. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/exporters/test_jsonl.py +0 -0
  87. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/README.md +0 -0
  88. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/short-clean/short-clean.jsonl +0 -0
  89. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a0b4c2cf1dde0ca56.meta.json +0 -0
  90. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a116ae34b1b09c332.meta.json +0 -0
  91. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a1c4c417b35658c9e.meta.json +0 -0
  92. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a1e41a901de38f1b5.meta.json +0 -0
  93. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a338f8d0c74612a24.meta.json +0 -0
  94. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a34f6f3c0e7094186.meta.json +0 -0
  95. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a5a5a0cff4d13308b.meta.json +0 -0
  96. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a6b0a3da6a0484db5.meta.json +0 -0
  97. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a7f73f1790b02cde5.meta.json +0 -0
  98. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a7f7c17c38a9d8788.meta.json +0 -0
  99. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a853259e2cd7bbe8a.meta.json +0 -0
  100. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a8d9aedb0d0c6e12d.meta.json +0 -0
  101. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aa778bc1d59e4a441.meta.json +0 -0
  102. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aba869dedee4a12ba.meta.json +0 -0
  103. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-ada2746d9774b94db.meta.json +0 -0
  104. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aea0132068c64d2dd.meta.json +0 -0
  105. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aea215eff50874d5f.meta.json +0 -0
  106. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-afee21f2b3852a4a0.meta.json +0 -0
  107. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-attachments/with-attachments.jsonl +0 -0
  108. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a051d9c9a6b2f5cc3.jsonl +0 -0
  109. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a051d9c9a6b2f5cc3.meta.json +0 -0
  110. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a171f16f4e65cfe75.jsonl +0 -0
  111. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a171f16f4e65cfe75.meta.json +0 -0
  112. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a1b77fea2c0a2269b.jsonl +0 -0
  113. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a1b77fea2c0a2269b.meta.json +0 -0
  114. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a20da4c01a54acca8.jsonl +0 -0
  115. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a20da4c01a54acca8.meta.json +0 -0
  116. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a3c82739b1383fb14.jsonl +0 -0
  117. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a3c82739b1383fb14.meta.json +0 -0
  118. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a49e8539611c5fe12.jsonl +0 -0
  119. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a49e8539611c5fe12.meta.json +0 -0
  120. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a7bb58f3fff2b3e8d.jsonl +0 -0
  121. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a7bb58f3fff2b3e8d.meta.json +0 -0
  122. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a92b48c0331195aac.jsonl +0 -0
  123. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a92b48c0331195aac.meta.json +0 -0
  124. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ab96c4264099694a9.jsonl +0 -0
  125. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ab96c4264099694a9.meta.json +0 -0
  126. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-acb2895c5e34ffec0.jsonl +0 -0
  127. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-acb2895c5e34ffec0.meta.json +0 -0
  128. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-adb2302769938fb3f.jsonl +0 -0
  129. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-adb2302769938fb3f.meta.json +0 -0
  130. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ae585eca15cb93b9c.jsonl +0 -0
  131. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ae585eca15cb93b9c.meta.json +0 -0
  132. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-aec9c917feb903d67.jsonl +0 -0
  133. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-aec9c917feb903d67.meta.json +0 -0
  134. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-compaction/with-compaction.jsonl +0 -0
  135. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-a1a3a21aeb76bb0a9.jsonl +0 -0
  136. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-a1a3a21aeb76bb0a9.meta.json +0 -0
  137. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-aaa1d6ecc05a78442.jsonl +0 -0
  138. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-aaa1d6ecc05a78442.meta.json +0 -0
  139. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-af3c545ccd30036d2.jsonl +0 -0
  140. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-af3c545ccd30036d2.meta.json +0 -0
  141. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/tool-results/btwp2bzro.txt +0 -0
  142. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents/tool-results/byqjbgy4b.txt +0 -0
  143. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-subagents/with-subagents.jsonl +0 -0
  144. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-tool-results/with-tool-results/tool-results/bosbkda0h.txt +0 -0
  145. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/claude_code/with-tool-results/with-tool-results.jsonl +0 -0
  146. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/scrub.py +0 -0
  147. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/synthetic/bookkeeping_only.jsonl +0 -0
  148. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/synthetic/malformed_middle.jsonl +0 -0
  149. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/synthetic/truncated_final_line.jsonl +0 -0
  150. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/synthetic/unknown_attachment_shape.jsonl +0 -0
  151. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/fixtures/synthetic/unknown_type.jsonl +0 -0
  152. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/parsers/__init__.py +0 -0
  153. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/parsers/test_claude_code.py +0 -0
  154. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/parsers/test_claude_code_integration.py +0 -0
  155. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/recommender/__init__.py +0 -0
  156. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/recommender/test_claude_md.py +0 -0
  157. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/recommender/test_evidence.py +0 -0
  158. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/renderers/__init__.py +0 -0
  159. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/renderers/test_report.py +0 -0
  160. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/renderers/test_terminal_renderer_full.py +0 -0
  161. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_agents.py +0 -0
  162. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_aggregate.py +0 -0
  163. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_cli.py +0 -0
  164. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_cli_export.py +0 -0
  165. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_discovery.py +0 -0
  166. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_github_summary.py +0 -0
  167. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_harvest.py +0 -0
  168. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_harvest_check.py +0 -0
  169. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_models.py +0 -0
  170. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_models_project_pattern.py +0 -0
  171. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_smoke.py +0 -0
  172. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_terminal_renderer.py +0 -0
  173. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_tokenizer.py +0 -0
  174. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_trace_tui.py +0 -0
  175. {cctx_cli-1.5.0 → cctx_cli-1.6.0}/tests/test_watcher.py +0 -0
@@ -2,6 +2,80 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v1.6.0 (2026-06-10)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Harvest — preview_patches dedup per (target, heading) not heading-only
10
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
11
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
12
+
13
+ - Harvest — shorten local-import comment under 100-char line limit
14
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
15
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
16
+
17
+ ### Documentation
18
+
19
+ - Harvest — correct misleading local-import comment
20
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
21
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
22
+
23
+ - Spec deviation note (sync returns patches) + PRODUCT.md cross-agent emit row
24
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
25
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
26
+
27
+ ### Features
28
+
29
+ - Cctx harvest --emit — cross-agent layer to AGENTS.md (#82)
30
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
31
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
32
+
33
+ - Cli — harvest --emit / --sync cross-agent emit
34
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
35
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
36
+
37
+ - Harvest — EMIT_TARGETS + retarget_patches (fan-out to AGENTS.md)
38
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
39
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
40
+
41
+ - Harvest — sync_managed_sections backfills CLAUDE.md into emit target
42
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
43
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
44
+
45
+ - Models — MANAGED_HEADINGS registry for cctx-owned CLAUDE.md sections
46
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
47
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
48
+
49
+ ### Testing
50
+
51
+ - Emit + sync idempotency through apply_patches
52
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
53
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
54
+
55
+ - End-to-end fan-out to both targets; spec: reconcile sync error contract
56
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
57
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
58
+
59
+ - Lock MANAGED_HEADINGS registry to recommender templates
60
+ ([#108](https://github.com/jacquardlabs/cctx/pull/108),
61
+ [`afa964c`](https://github.com/jacquardlabs/cctx/commit/afa964c68b445030e1fafe9f41c67a0de4afcd2d))
62
+
63
+
64
+ ## v1.5.1 (2026-06-10)
65
+
66
+ ### Bug Fixes
67
+
68
+ - Recommender — add TOOL_THRASH/DEAD_END patch templates
69
+ ([#107](https://github.com/jacquardlabs/cctx/pull/107),
70
+ [`3c79d58`](https://github.com/jacquardlabs/cctx/commit/3c79d58a55af106d3fd81542e70b8f892569185c))
71
+
72
+ ### Documentation
73
+
74
+ - Product review 2026-06-09 + M15 cross-agent emit spec
75
+ ([#107](https://github.com/jacquardlabs/cctx/pull/107),
76
+ [`3c79d58`](https://github.com/jacquardlabs/cctx/commit/3c79d58a55af106d3fd81542e70b8f892569185c))
77
+
78
+
5
79
  ## v1.5.0 (2026-05-20)
6
80
 
7
81
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cctx-cli
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: Diagnose Claude Code sessions — find what went wrong, what it cost, and what to add to CLAUDE.md
5
5
  Author: Jacquard Labs
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@
4
4
 
5
5
  cctx is a local Python CLI that reads Claude Code session logs and tells you what went wrong, why it cost what it did, and what to add to your CLAUDE.md so it doesn't happen again.
6
6
 
7
- It is a forensic tool, not a monitoring tool. You reach for it after a session — when something felt off, when the bill was higher than expected, or on a weekly review pass.
7
+ It is forensic-first. You reach for it after a session — when something felt off, when the bill was higher than expected, or on a weekly review pass. `cctx watch` extends the same waste detection into a live, opt-in companion for an active session; it is a foreground command you run deliberately, not background infrastructure.
8
8
 
9
9
  ## Primary persona
10
10
 
@@ -20,7 +20,7 @@ This persona is already the target; everything shipped to date serves them direc
20
20
  ## What cctx is NOT for
21
21
 
22
22
  - Teams or organizations (no shared reports, no access control, no multi-user state)
23
- - Real-time monitoring (completed sessions only in v0 and v1)
23
+ - Ambient monitoring (no daemons, no persistent watcher state, no alerting — `cctx watch` is a foreground command you start and stop)
24
24
  - General cost dashboards (CodeBurn covers that; cctx is forensic)
25
25
  - Multi-provider support (Claude Code only in v0/v1)
26
26
  - Users who do not read session transcripts or maintain CLAUDE.md
@@ -28,7 +28,7 @@ This persona is already the target; everything shipped to date serves them direc
28
28
  ## Product principles
29
29
 
30
30
  **1. Forensic, not ambient.**
31
- cctx is used after something goes wrong, not as background infrastructure. Every feature should be justified by this use case: "I just ran a session, something went sideways, I want to know what."
31
+ cctx is forensic-first: the core use case is "I just ran a session, something went sideways, I want to know what." Live mode (`watch`) is justified only as the same detection running earlier it surfaces the identical findings, never becomes a daemon, and never holds state between runs.
32
32
 
33
33
  **2. Output must be actionable.**
34
34
  Every report should produce at least one thing the user can do immediately. Findings without patches are incomplete. Patches must be copy-pasteable or auto-applicable.
@@ -37,15 +37,15 @@ Every report should produce at least one thing the user can do immediately. Find
37
37
  Costs are approximated (85–95% of actual). Pattern classifiers fire only on high-confidence signals. Low-confidence signals are shown with explicit confidence labels. "System internals" token budget is never hidden or misattributed.
38
38
 
39
39
  **4. Local and stateless.**
40
- No network calls in the core analysis path (tokenizer may call the API for token counting; that is opt-in). No persistent database. No telemetry. No account. Users own their data.
40
+ No network calls in the core analysis path (tokenizer may call the API for token counting; that is opt-in). Live-session detection may shell out to the local `claude` CLI and degrades gracefully without it. No persistent database. No telemetry. No account. Users own their data.
41
41
 
42
42
  **5. Deterministic.**
43
43
  Pattern classifiers are heuristics, not LLM calls. The same session file produces the same output every time. Testable on fixtures. Predictable cost to run.
44
44
 
45
45
  **6. Small surface, deep on each command.**
46
- Four commands. No command is shallow. Users should be able to learn the product in an afternoon and trust what it tells them.
46
+ Six commands (`ls`, `autopsy`, `harvest`, `watch`, `trace`, `export`). No command is shallow. Users should be able to learn the product in an afternoon and trust what it tells them.
47
47
 
48
- ## Feature map (v0.2.0)
48
+ ## Feature map (v1.4.0)
49
49
 
50
50
  ### Shipped
51
51
 
@@ -68,8 +68,17 @@ Four commands. No command is shallow. Users should be able to learn the product
68
68
  | Cross-session harvest | `cctx harvest <project> --since N` | M5 |
69
69
  | Session discovery | `cctx ls` / `cctx autopsy --latest` | M6+ |
70
70
  | Live waste signals | `cctx watch <project>` | M6+ |
71
-
72
- ### Pattern classifiers (v0.2.0)
71
+ | Verdict headline + `--top N` + `--turn N` | `autopsy` | v1.1.0 (M9) |
72
+ | `--until DATE` on cross-session mode | `autopsy --since ... --until` | v1.2.0 (M12) |
73
+ | Machine-readable diagnosis | `cctx autopsy <session> --json` | v1.2.0 (M12) |
74
+ | JSON export | `cctx export <session> --format json` | v1.2.0 (M12) |
75
+ | Project-specific pattern detection | `autopsy`/`harvest` `--since` | v1.3.0 (M14) |
76
+ | Memory-hygiene depth | `harvest --check` + `--check-severity` | v1.4.0 (M13) |
77
+ | Live session badges | `cctx ls` | unreleased |
78
+ | Live session detection, early idle exit | `cctx watch` | unreleased |
79
+ | Cross-agent emit | `cctx harvest --emit agents [--sync]` | M15; mirror CLAUDE.md sections to AGENTS.md — unreleased |
80
+
81
+ ### Pattern classifiers (v1.4.0)
73
82
 
74
83
  | Pattern | Status |
75
84
  |---|---|
@@ -78,6 +87,7 @@ Four commands. No command is shallow. Users should be able to learn the product
78
87
  | Stale context | Shipped |
79
88
  | Dead-end exploration | Shipped (v0.2.0) |
80
89
  | Tool thrashing | Shipped (v0.2.0) |
90
+ | Project-specific patterns (cross-session) | Shipped (v1.3.0) |
81
91
 
82
92
  ## What we are NOT building
83
93
 
@@ -87,12 +97,14 @@ Four commands. No command is shallow. Users should be able to learn the product
87
97
  - A fork-and-replay debugger
88
98
  - A general eval or testing framework
89
99
 
90
- ## Known problems (as of 2026-05-16)
100
+ ## Known problems (as of 2026-06-09)
101
+
102
+ **Active gaps:**
91
103
 
92
- **Active gaps (non-blocking for v0.2.0 but worth tracking):**
104
+ 1. **`cctx watch` polling is simple.** Early idle exit via `claude agents --json` has landed, but the watcher still polls at 1s without `fsevents`/`inotify` debouncing.
93
105
 
94
- 1. **`cctx watch` polling is simple.** Polls every 1s and re-runs classifiers on any file growth. Does not debounce or use `fsevents`/`inotify`. Fine for v0 but will chatter on active sessions.
106
+ 2. **Subagent traces are parsed but never diagnosed.** The parser models subagent sessions recursively and the tokenizer counts their tokens, but no classifier or cost attribution reads `trace.subagents`. Autopsy is blind to spend inside agent fan-outs. (M16)
95
107
 
96
- 2. **`--format json` on `export` not shipped.** `--html` moved to `autopsy --html`; `json` format on the `export` subcommand is still deferred.
108
+ 3. **Cross-agent layer not started.** Tracked as M15 / #82 the final step of the original growth staircase.
97
109
 
98
- 3. **Cross-agent layer not started.** Emitting findings as `.cursorrules`, `AGENTS.md`, `.windsurfrules`, or GitHub Copilot instructions is a roadmap item with no milestone yet.
110
+ 4. **Harvest has no feedback loop.** Nothing measures whether an applied patch reduced the recurrence of the pattern it targeted, even though patches carry fingerprints and sessions carry dates. (M17)
@@ -1,3 +1,3 @@
1
1
  """cctx: profile, debug, and optimize Claude Code and Agent SDK sessions."""
2
2
 
3
- __version__ = "1.5.0"
3
+ __version__ = "1.6.0"
@@ -23,6 +23,7 @@ from cctx.agents import live_sessions as _live_sessions
23
23
  from cctx.diagnostician import aggregate
24
24
  from cctx.diagnostician.patterns import project_specific
25
25
  from cctx.discovery import complete_project as _complete_project
26
+ from cctx.harvest import EMIT_TARGETS
26
27
  from cctx.models import KIND_LABEL, AggregateReport
27
28
  from cctx.parsers.claude_code import parse_session
28
29
  from cctx.recommender import claude_md
@@ -570,6 +571,22 @@ def trace(target: Path | None, latest: bool) -> None:
570
571
  show_default=True,
571
572
  help="Minimum severity that causes --check to exit 1.",
572
573
  )
574
+ @click.option(
575
+ "--emit",
576
+ "emit_targets",
577
+ multiple=True,
578
+ type=click.Choice(list(EMIT_TARGETS)),
579
+ help="Also write applicable patches to another agent's instruction file "
580
+ "(e.g. AGENTS.md). Repeatable.",
581
+ )
582
+ @click.option(
583
+ "--sync",
584
+ "sync_mode",
585
+ is_flag=True,
586
+ default=False,
587
+ help="With --emit: also mirror already-harvested cctx-managed sections "
588
+ "from CLAUDE.md into the emit target.",
589
+ )
573
590
  def harvest(
574
591
  target: Path,
575
592
  since: str | None,
@@ -578,9 +595,20 @@ def harvest(
578
595
  target_dir: Path | None,
579
596
  check_mode: bool,
580
597
  check_severity: str,
598
+ emit_targets: tuple[str, ...],
599
+ sync_mode: bool,
581
600
  ) -> None:
582
601
  """Apply autopsy patches to CLAUDE.md."""
583
- from cctx.harvest import apply_patches, check_claude_md, preview_patches
602
+ from cctx.harvest import (
603
+ apply_patches,
604
+ check_claude_md,
605
+ preview_patches,
606
+ retarget_patches,
607
+ sync_managed_sections,
608
+ )
609
+
610
+ if sync_mode and not emit_targets:
611
+ raise click.UsageError("--sync requires --emit.")
584
612
 
585
613
  if check_mode:
586
614
  from cctx.harvest import CheckSeverity
@@ -626,6 +654,13 @@ def harvest(
626
654
  diagnosis = claude_md.generate(diagnosis)
627
655
  patches = diagnosis.patches
628
656
 
657
+ base = patches
658
+ for t in emit_targets:
659
+ emitted = retarget_patches(base, t)
660
+ if sync_mode:
661
+ emitted = emitted + sync_managed_sections(resolved_dir, t)
662
+ patches = patches + emitted
663
+
629
664
  if not patches:
630
665
  render_harvest_results([], dry_run=dry_run)
631
666
  return
@@ -13,6 +13,7 @@ Layering rules (MUST respect):
13
13
  """
14
14
  from __future__ import annotations
15
15
 
16
+ import dataclasses
16
17
  import re
17
18
  from collections import defaultdict
18
19
  from dataclasses import dataclass
@@ -20,6 +21,8 @@ from enum import Enum
20
21
  from pathlib import Path
21
22
  from typing import TYPE_CHECKING
22
23
 
24
+ from cctx.models import MANAGED_HEADING_PREFIX, MANAGED_HEADINGS
25
+
23
26
  if TYPE_CHECKING:
24
27
  from cctx.models import Patch
25
28
 
@@ -105,6 +108,70 @@ def _is_supported_target(patch: Patch) -> bool:
105
108
  # Public API
106
109
  # ---------------------------------------------------------------------------
107
110
 
111
+ # Maps an --emit target name to the destination filename. Single place to add
112
+ # future targets (Cursor, Windsurf, Copilot) when demand exists.
113
+ EMIT_TARGETS: dict[str, str] = {
114
+ "agents": "AGENTS.md",
115
+ }
116
+
117
+
118
+ def retarget_patches(patches: list[Patch], emit_target: str) -> list[Patch]:
119
+ """Clone CLAUDE.md-targeted patches to the emit target's file.
120
+
121
+ Only patches whose target_file is exactly "CLAUDE.md" are emitted —
122
+ .claude/rules/ and .claude/skills/ patches are Claude Code-specific and do
123
+ not translate to other agents. Returns clones; inputs are unmodified.
124
+ """
125
+ dest = EMIT_TARGETS[emit_target]
126
+ return [
127
+ dataclasses.replace(p, target_file=dest)
128
+ for p in patches
129
+ if p.target_file == "CLAUDE.md"
130
+ ]
131
+
132
+
133
+ # Reverse map: exact managed heading -> the FindingKind that owns it.
134
+ _HEADING_TO_KIND = {heading: kind for kind, heading in MANAGED_HEADINGS.items()}
135
+
136
+
137
+ def sync_managed_sections(target_dir: Path, emit_target: str) -> list[Patch]:
138
+ """Build synthetic patches mirroring CLAUDE.md's cctx-managed sections.
139
+
140
+ Reads CLAUDE.md in target_dir, keeps sections whose heading is an exact
141
+ MANAGED_HEADINGS value or starts with MANAGED_HEADING_PREFIX, and returns
142
+ one Patch per kept section targeting the emit file. Returns [] if CLAUDE.md
143
+ is absent. The CLI routes these through preview_patches / apply_patches, so
144
+ idempotency and dry-run come for free from the existing machinery.
145
+ """
146
+ from cctx.models import FindingKind, Patch # runtime use (Patch is TYPE_CHECKING-only above)
147
+
148
+ claude_md = target_dir / "CLAUDE.md"
149
+ if not claude_md.exists():
150
+ return []
151
+
152
+ dest = EMIT_TARGETS[emit_target]
153
+ content = claude_md.read_text(encoding="utf-8")
154
+ patches: list[Patch] = []
155
+
156
+ for heading, body in _parse_sections(content):
157
+ is_fixed = heading in _HEADING_TO_KIND
158
+ is_prefixed = heading.startswith(MANAGED_HEADING_PREFIX)
159
+ if not (is_fixed or is_prefixed):
160
+ continue
161
+
162
+ kind = _HEADING_TO_KIND[heading] if is_fixed else FindingKind.PROJECT_PATTERN
163
+ diff_lines = [heading] + body.splitlines()
164
+ unified_diff = "\n".join(f"+{line}" for line in diff_lines)
165
+ patches.append(Patch(
166
+ target_file=dest,
167
+ description=heading,
168
+ unified_diff=unified_diff,
169
+ finding_kind=kind,
170
+ evidence_summary="synced from CLAUDE.md",
171
+ ))
172
+
173
+ return patches
174
+
108
175
 
109
176
  def apply_patch(patch: Patch, target_dir: Path) -> ApplyResult:
110
177
  """Apply one patch. Never raises — errors go into ApplyResult(status=ERROR)."""
@@ -161,8 +228,10 @@ def apply_patch(patch: Patch, target_dir: Path) -> ApplyResult:
161
228
  def preview_patches(patches: list[Patch], target_dir: Path) -> list[ApplyResult]:
162
229
  """Compute what would happen without writing. Returns APPLIED or SKIPPED."""
163
230
  results = []
164
- # Track fingerprints already "seen" within this preview run (idempotency)
165
- seen_fingerprints: set[str] = set()
231
+ # Track (target_path, fingerprint) pairs already "seen" within this preview
232
+ # run (idempotency). Keyed by file so the same heading in two different
233
+ # target files is correctly treated as two independent patches.
234
+ seen_fingerprints: set[tuple[Path, str]] = set()
166
235
 
167
236
  for patch in patches:
168
237
  target_path = target_dir / patch.target_file
@@ -181,7 +250,8 @@ def preview_patches(patches: list[Patch], target_dir: Path) -> list[ApplyResult]
181
250
 
182
251
  content = target_path.read_text(encoding="utf-8") if target_path.exists() else ""
183
252
 
184
- if fp is not None and (_already_present(content, fp) or fp in seen_fingerprints):
253
+ already_seen = fp is not None and (target_path, fp) in seen_fingerprints
254
+ if fp is not None and (_already_present(content, fp) or already_seen):
185
255
  results.append(ApplyResult(
186
256
  patch=patch,
187
257
  status=ApplyStatus.SKIPPED,
@@ -190,7 +260,7 @@ def preview_patches(patches: list[Patch], target_dir: Path) -> list[ApplyResult]
190
260
  ))
191
261
  else:
192
262
  if fp is not None:
193
- seen_fingerprints.add(fp)
263
+ seen_fingerprints.add((target_path, fp))
194
264
  results.append(ApplyResult(
195
265
  patch=patch,
196
266
  status=ApplyStatus.APPLIED,
@@ -184,6 +184,22 @@ KIND_LABEL: dict[FindingKind, str] = {
184
184
  FindingKind.PROJECT_PATTERN: "PROJECT PATTERN",
185
185
  }
186
186
 
187
+ # Maps FindingKind to the exact ## heading emitted by its recommender patch
188
+ # template. Single source of truth for "which CLAUDE.md sections cctx owns."
189
+ # harvest.py imports this (never reaches into recommender/) so emit/sync can
190
+ # identify cctx-managed sections without depending on the patch generator.
191
+ MANAGED_HEADINGS: dict[FindingKind, str] = {
192
+ FindingKind.RETRY_LOOP: "## Retry discipline",
193
+ FindingKind.SCOPE_CREEP: "## Scope discipline",
194
+ FindingKind.STALE_CONTEXT: "## Context hygiene",
195
+ FindingKind.TOOL_THRASH: "## Tool-call discipline",
196
+ FindingKind.DEAD_END: "## Exploration discipline",
197
+ }
198
+
199
+ # Project-specific patterns use a heading that embeds tool+key, so the managed
200
+ # section is identified by prefix rather than exact match.
201
+ MANAGED_HEADING_PREFIX: str = "## Project-specific: "
202
+
187
203
 
188
204
  class Severity(str, Enum):
189
205
  HIGH = "high"
@@ -41,11 +41,29 @@ _STALE_CONTEXT_DIFF = """\
41
41
  +without re-referencing it. Prefer re-running the tool over dragging stale context
42
42
  +forward — the compaction system handles removal."""
43
43
 
44
+ _TOOL_THRASH_DIFF = """\
45
+ +## Tool-call discipline
46
+ +
47
+ +Before reaching for a tool, decide what specific information the call must return
48
+ +and how it changes the next step. Don't fan out near-identical searches or re-read
49
+ +the same file from different angles hoping something turns up. If two or three calls
50
+ +haven't moved you forward, stop and form a hypothesis before the next one."""
51
+
52
+ _DEAD_END_DIFF = """\
53
+ +## Exploration discipline
54
+ +
55
+ +When an approach stops making progress, name the dead end explicitly and back out
56
+ +rather than pushing deeper on a path that isn't working. Re-read the goal, list the
57
+ +approaches already ruled out, and pick a meaningfully different one. Sunk effort on a
58
+ +failing approach is not a reason to continue it."""
59
+
44
60
  _TEMPLATES: dict[FindingKind, tuple[str, str, str]] = {
45
61
  # kind → (description, diff_body, target_file)
46
- FindingKind.RETRY_LOOP: ("Add retry discipline rule", _RETRY_LOOP_DIFF, "CLAUDE.md"),
47
- FindingKind.SCOPE_CREEP: ("Add scope discipline rule", _SCOPE_CREEP_DIFF, "CLAUDE.md"),
48
- FindingKind.STALE_CONTEXT: ("Add context hygiene rule", _STALE_CONTEXT_DIFF, "CLAUDE.md"),
62
+ FindingKind.RETRY_LOOP: ("Add retry discipline rule", _RETRY_LOOP_DIFF, "CLAUDE.md"),
63
+ FindingKind.SCOPE_CREEP: ("Add scope discipline rule", _SCOPE_CREEP_DIFF, "CLAUDE.md"),
64
+ FindingKind.STALE_CONTEXT: ("Add context hygiene rule", _STALE_CONTEXT_DIFF, "CLAUDE.md"),
65
+ FindingKind.TOOL_THRASH: ("Add tool-call discipline rule", _TOOL_THRASH_DIFF, "CLAUDE.md"),
66
+ FindingKind.DEAD_END: ("Add exploration discipline rule", _DEAD_END_DIFF, "CLAUDE.md"),
49
67
  }
50
68
 
51
69
 
@@ -0,0 +1,186 @@
1
+ # cctx Product Health Review — 2026-06-09
2
+
3
+ Second review. Prior review: `2026-05-15-product-review.md`. Source of product truth: `PRODUCT.md` (created from the prior review's draft), with `cctx-project-brief.md` as the original pitch and growth staircase.
4
+
5
+ **Headline: the growth staircase is one step from complete.** Every milestone from the original brief — autopsy, cross-session, harvest, memory hygiene, live mode — has shipped. The issue board holds three open issues, one of which (#80) actually shipped in v1.4.0. The only substantive roadmap item left is the cross-agent layer (#82, M15). The product needs two things now: PRODUCT.md brought up to date (it describes v0.2.0; reality is v1.4.0+), and a deliberate decision about what comes after the staircase.
6
+
7
+ ---
8
+
9
+ ## Part 1 — Is PRODUCT.md still true?
10
+
11
+ ### 1. Persona check
12
+
13
+ Everything shipped since the last review — M9 polish (verdict headline, `--top`, `--turn`), M11 GitHub Action, M12 output completeness (`--until`, `autopsy --json`, `export --format json`), M13 harvest `--check` depth, M14 project-specific patterns, and the unreleased live-session integration — serves the same single developer reviewing their own sessions. No team features, no dashboards, no hypothetical users.
14
+
15
+ **Verdict: no persona drift.** The discipline noted in the prior review has held through eight releases.
16
+
17
+ ### 2. Principles check
18
+
19
+ **P1 — Forensic, not ambient.** *Bent, deliberately.* `cctx watch` (M10) and the in-flight `claude agents --json` live integration (live badges in `cctx ls`, early idle exit in the watcher) are real-time features. The staircase sanctioned this as v2 ("Live mode — optional"), so it is roadmap-aligned, not drift — but PRODUCT.md's principle text and its "NOT for: real-time monitoring" line now contradict the shipped product. The doc must evolve: forensic-first, with live mode as an explicitly opt-in companion.
20
+
21
+ **P2 — Output must be actionable.** Honored. M14 project-specific patterns emit CLAUDE.md patches via `generate_from_patterns()`; harvest `--check` depth findings carry severity and location.
22
+
23
+ **P3 — Honest about uncertainty.** Honored. The prior review's gap (cost confidence not surfaced) was fixed in #69 — terminal and HTML output annotate costs as ~85–95% estimates.
24
+
25
+ **P4 — Local and stateless.** Honored, with one footnote: the live path now shells out to the `claude` CLI (`claude agents --json`). Still local, no network, gracefully degrades when the binary is absent — but it is the first external-binary dependency in any code path and should be named in the principle.
26
+
27
+ **P5 — Deterministic.** Fully honored. All four new analysis surfaces since the last review (tool-thrash, dead-end, project-specific patterns, check-depth detectors) are heuristic — Jaccard similarity, keyword matching, n-gram overlap. No LLM calls anywhere outside the opt-in tokenizer.
28
+
29
+ **P6 — Small surface, deep on each command.** The text says "Four commands." There are six: `ls`, `autopsy`, `export`, `trace`, `harvest`, `watch`. The spirit holds — each verb is deep and the surface is learnable in an afternoon — but the letter is false.
30
+
31
+ ### 3. Feature map accuracy
32
+
33
+ PRODUCT.md is headed "Feature map (v0.2.0)". The released version is **v1.4.0** (2026-05-20), with live-session integration unreleased on main. Missing from the map:
34
+
35
+ | Shipped feature | Where | Version |
36
+ |---|---|---|
37
+ | Verdict headline | `autopsy` terminal output | v1.1.0 (M9) |
38
+ | `--top N` on cross-session autopsy | `autopsy --since` | v1.1.0 (M9) |
39
+ | `--turn N` drill-down | `autopsy` | v1.1.0 (M9) |
40
+ | `--until DATE` | `autopsy --since` | v1.2.0 (M12) |
41
+ | Machine-readable diagnosis | `autopsy --json` | v1.2.0 (M12) |
42
+ | JSON export | `export --format json` | v1.2.0 (M12) |
43
+ | Project-specific pattern detection | `autopsy --since` / `harvest --since` | v1.3.0 (M14) |
44
+ | Contradiction / redundancy / staleness detectors | `harvest --check`, `--check-severity` | v1.4.0 (M13) |
45
+ | Live session badges | `cctx ls` | unreleased |
46
+ | Live session detection + early idle exit | `cctx watch` | unreleased |
47
+
48
+ The classifier table is also incomplete: project-specific patterns (M14) is a sixth detection surface alongside the five listed.
49
+
50
+ ### 4. "Not building" check
51
+
52
+ - "Real-time monitoring (completed sessions only in v0 and v1)" — **crossed**, per the staircase's own v2 plan. Remove from the NOT-for list; replace with what genuinely stays out (background daemons, persistent monitoring state, alerting).
53
+ - No SaaS, no agent behavior, no multi-provider, no fork-and-replay, no eval framework — all still clean. `claude agents --json` is reading local state from a local binary, not provider expansion.
54
+
55
+ ### 5. Known problems freshness
56
+
57
+ 1. **"watch polling is simple"** — partially fixed. Early idle exit via `claude agents --json` landed (unreleased); 1s polling without fsevents/inotify remains. Update, don't remove.
58
+ 2. **"`--format json` on export not shipped"** — **fixed in v1.2.0**. Remove.
59
+ 3. **"Cross-agent layer not started… no milestone yet"** — stale. It now has milestone M15 and issue #82. Update.
60
+
61
+ **New problems to add:**
62
+ 4. **Subagent traces are parsed but never diagnosed.** The parser recursively parses subagent sessions (`models.py:151`, `parsers/claude_code.py:165`), the tokenizer counts their tokens, but no classifier or cost attribution ever reads `trace.subagents`. As agent fan-out becomes the dominant Claude Code workflow, autopsy is blind to where an increasing share of the spend goes.
63
+ 5. **Issue board hygiene.** #80 shipped in v1.4.0 (PR #87) but is still open. #85 has no milestone.
64
+ 6. **Roadmap exhaustion.** After M15 there is no defined direction. Not a bug — but the next planning conversation is overdue.
65
+
66
+ ---
67
+
68
+ ## Part 2 — Product coherence
69
+
70
+ ### One product?
71
+
72
+ Yes — more so than at the last review. The loop now has a clean narrative arc that matches a real day: `cctx ls` (what sessions exist, which are live) → `cctx watch` (catch waste during) → `cctx autopsy` (diagnose after) → `cctx harvest` (persist the lesson) → `harvest --check` (keep the memory honest). `trace` and `export` are the inspection/escape hatches. Every command operates on the same `SessionTrace → Diagnosis → Patch` chain.
73
+
74
+ ### Feature interaction
75
+
76
+ - `ls` ↔ `watch` via live badges: good new connection (unreleased).
77
+ - `autopsy → harvest`: still the spine, now strengthened by M14 (cross-session patterns produce evidence-backed patches).
78
+ - **Gap: `watch` findings are ephemeral.** A waste signal surfaced live evaporates when the session ends; the user must re-run autopsy to act on it. A natural connection — watch ending with "run `cctx autopsy --latest` to harvest these findings" or writing a findings stub — is unbuilt.
79
+ - **Gap: harvest never learns whether its patches worked.** Patches carry fingerprints and sessions carry dates; nothing compares pattern recurrence before/after a patch was applied. This is the strongest unbuilt connection in the product (see proposals).
80
+
81
+ ### Complexity audit
82
+
83
+ Nothing is deadweight. `trace` (Textual TUI) remains the highest-maintenance surface relative to use; acceptable while it stays stable. The two JSON outputs (`autopsy --json` vs `export --format json`) are different shapes for different purposes (diagnosis vs raw turns) — fine, but the README should say which to use when.
84
+
85
+ ### Onboarding
86
+
87
+ Dramatically improved since the last review: `pip install cctx` → `cctx ls` → `cctx autopsy --latest` is a genuine under-60-second path, and the README exists. Remaining friction: a clean session yields "no findings," which still reads anticlimactic on a first run — consider always showing the cost/token decomposition so a clean session still demonstrates value.
88
+
89
+ ---
90
+
91
+ ## Part 3 — Proposed PRODUCT.md updates
92
+
93
+ Presented as a diff; not applied.
94
+
95
+ ```diff
96
+ --- PRODUCT.md
97
+ +++ PRODUCT.md
98
+ @@ -5,7 +5,8 @@
99
+ cctx is a local Python CLI that reads Claude Code session logs and tells you what went wrong, why it cost what it did, and what to add to your CLAUDE.md so it doesn't happen again.
100
+
101
+ -It is a forensic tool, not a monitoring tool. You reach for it after a session — when something felt off, when the bill was higher than expected, or on a weekly review pass.
102
+ +It is forensic-first. You reach for it after a session — when something felt off, when the bill was higher than expected, or on a weekly review pass. `cctx watch` extends the same waste detection into a live, opt-in companion for an active session; it is a foreground command you run deliberately, not background infrastructure.
103
+
104
+ @@ -20,9 +21,9 @@
105
+ ## What cctx is NOT for
106
+
107
+ - Teams or organizations (no shared reports, no access control, no multi-user state)
108
+ -- Real-time monitoring (completed sessions only in v0 and v1)
109
+ +- Ambient monitoring (no daemons, no persistent watcher state, no alerting — `cctx watch` is a foreground command you start and stop)
110
+ - General cost dashboards (CodeBurn covers that; cctx is forensic)
111
+ - Multi-provider support (Claude Code only in v0/v1)
112
+ - Users who do not read session transcripts or maintain CLAUDE.md
113
+
114
+ @@ -30,8 +31,8 @@
115
+ **1. Forensic, not ambient.**
116
+ -cctx is used after something goes wrong, not as background infrastructure. Every feature should be justified by this use case: "I just ran a session, something went sideways, I want to know what."
117
+ +cctx is forensic-first: the core use case is "I just ran a session, something went sideways, I want to know what." Live mode (`watch`) is justified only as the same detection running earlier — it surfaces the identical findings, never becomes a daemon, and never holds state between runs.
118
+
119
+ @@ -39,7 +40,7 @@
120
+ **4. Local and stateless.**
121
+ -No network calls in the core analysis path (tokenizer may call the API for token counting; that is opt-in). No persistent database. No telemetry. No account. Users own their data.
122
+ +No network calls in the core analysis path (tokenizer may call the API for token counting; that is opt-in). Live-session detection may shell out to the local `claude` CLI and degrades gracefully without it. No persistent database. No telemetry. No account. Users own their data.
123
+
124
+ @@ -45,7 +46,7 @@
125
+ **6. Small surface, deep on each command.**
126
+ -Four commands. No command is shallow. Users should be able to learn the product in an afternoon and trust what it tells them.
127
+ +Six commands (`ls`, `autopsy`, `harvest`, `watch`, `trace`, `export`). No command is shallow. Users should be able to learn the product in an afternoon and trust what it tells them.
128
+
129
+ @@ -48,2 +49,2 @@
130
+ -## Feature map (v0.2.0)
131
+ +## Feature map (v1.4.0)
132
+
133
+ @@ (append to Shipped table)
134
+ +| Verdict headline + `--top N` + `--turn N` | `autopsy` | v1.1.0 (M9) |
135
+ +| `--until DATE` on cross-session mode | `autopsy --since ... --until` | v1.2.0 (M12) |
136
+ +| Machine-readable diagnosis | `cctx autopsy <session> --json` | v1.2.0 (M12) |
137
+ +| JSON export | `cctx export <session> --format json` | v1.2.0 (M12) |
138
+ +| Project-specific pattern detection | `autopsy`/`harvest` `--since` | v1.3.0 (M14) |
139
+ +| Memory-hygiene depth | `harvest --check` + `--check-severity` | v1.4.0 (M13) |
140
+ +| Live session badges | `cctx ls` | unreleased |
141
+ +| Live session detection, early idle exit | `cctx watch` | unreleased |
142
+
143
+ @@ (classifier table)
144
+ +| Project-specific patterns (cross-session) | Shipped (v1.3.0) |
145
+
146
+ @@ -90,12 +99,14 @@
147
+ -## Known problems (as of 2026-05-16)
148
+ +## Known problems (as of 2026-06-09)
149
+
150
+ -1. **`cctx watch` polling is simple.** Polls every 1s and re-runs classifiers on any file growth. Does not debounce or use `fsevents`/`inotify`. Fine for v0 but will chatter on active sessions.
151
+ +1. **`cctx watch` polling is simple.** Early idle exit via `claude agents --json` has landed, but the watcher still polls at 1s without `fsevents`/`inotify` debouncing.
152
+
153
+ -2. **`--format json` on `export` not shipped.** `--html` moved to `autopsy --html`; `json` format on the `export` subcommand is still deferred.
154
+ +2. **Subagent traces are parsed but never diagnosed.** The parser models subagent sessions recursively and the tokenizer counts their tokens, but no classifier or cost attribution reads `trace.subagents`. Autopsy is blind to spend inside agent fan-outs.
155
+
156
+ -3. **Cross-agent layer not started.** Emitting findings as `.cursorrules`, `AGENTS.md`, `.windsurfrules`, or GitHub Copilot instructions is a roadmap item with no milestone yet.
157
+ +3. **Cross-agent layer not started.** Tracked as M15 / #82 — the final step of the original growth staircase.
158
+ +
159
+ +4. **Harvest has no feedback loop.** Nothing measures whether an applied patch reduced the recurrence of the pattern it targeted, even though patches carry fingerprints and sessions carry dates.
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Issue board actions
165
+
166
+ 1. **Close #80** — shipped in v1.4.0 via PR #87 (`check_contradictions`/`check_redundancy`/`check_staleness` + `--check-severity` are on main). Close with a comment linking the PR.
167
+ 2. **#85 (fuzzy/semantic normalization, M14 Option B)** — assign a milestone or label it explicitly as icebox. As written it risks drifting toward embedding/LLM territory; if pursued, constrain to deterministic techniques (stemming, edit distance) per P5.
168
+ 3. **#82 (M15 cross-agent layer)** — the committed next milestone. Note: emitting `AGENTS.md`/`.cursorrules` partially relaxes "multi-provider support" in the NOT-for list; the distinction worth preserving is *we write other agents' config formats; we do not parse other agents' logs*.
169
+
170
+ ## Post-staircase feature proposals
171
+
172
+ Ranked by leverage. All are deterministic, local, and persona-aligned.
173
+
174
+ **1. Subagent-aware diagnosis (highest leverage).** The data model is already built — parser recurses into subagent sessions, tokenizer counts them — only the analysis is missing. Ship: (a) per-subagent cost attribution in the autopsy decomposition ("$1.84 of $3.10 went to 7 subagents"), (b) a fan-out waste classifier (overlapping subagent work, failed-and-retried agents, subagent results never referenced by the parent). Claude Code's evolution (Task tool, workflows, parallel agents) makes this the fastest-growing blind spot in the product.
175
+
176
+ **2. Patch efficacy tracking.** `harvest` patches carry fingerprints; sessions carry dates. Compare pattern recurrence before/after a patch's application date: "retry-loop fired in 5 sessions the week before the 2026-05-20 patch; 0 since." This converts cctx from *suggesting* improvements to *proving* them — no other tool in the space closes this loop, and it requires no new data collection.
177
+
178
+ **3. Loaded-but-never-used context waste (MCP servers / skills).** Extend the brief's binary waste decision to context overhead: MCP tool definitions and skills that are loaded into every request but never invoked across N sessions. Finding: "MCP server X adds ~8K tokens/request and was never called in 30 sessions — disable it for this project." Binary signal, high confidence, directly actionable patch (settings change).
179
+
180
+ **4. `cctx init` — SessionEnd hook installer.** The biggest adoption friction is remembering to run cctx. A command that installs an opt-in Claude Code SessionEnd/Stop hook running `cctx autopsy --latest --quiet` and printing a one-line verdict *only when findings exist*. Preserves forensic-first (output appears only when something went sideways) while removing the memory burden.
181
+
182
+ **5. Compaction findings.** Compaction events are already detected (classifiers reset on them) but never reported. Promote to a finding: compaction count, content re-fetched after compaction (re-reading a file that was compacted away is concrete, attributable waste), and a "compact earlier" recommendation when stale context preceded a forced compaction.
183
+
184
+ **6. Cross-project digest.** `cctx autopsy --all --since 7d` — the persona's stated "weekly review pass" currently requires running per-project. Aggregate the aggregates; lowest effort of the six.
185
+
186
+ Suggested sequencing: board hygiene now → M15 (#82) finishes the staircase → M16 subagent-aware diagnosis → M17 patch efficacy. Items 3–6 slot in as polish-scale milestones between or after.