cctx-cli 1.10.0__tar.gz → 1.12.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 (187) hide show
  1. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/CHANGELOG.md +79 -0
  2. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/PKG-INFO +1 -1
  3. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/__init__.py +1 -1
  4. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/cli.py +123 -3
  5. cctx_cli-1.12.0/cctx/hook_installer.py +101 -0
  6. cctx_cli-1.12.0/cctx/parsers/otel.py +346 -0
  7. cctx_cli-1.12.0/docs/quickstart-openai-agents.md +106 -0
  8. cctx_cli-1.12.0/docs/superpowers/plans/2026-06-19-otel-parser.md +1215 -0
  9. cctx_cli-1.12.0/docs/superpowers/specs/2026-06-19-otel-parser-design.md +115 -0
  10. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/pyproject.toml +1 -1
  11. cctx_cli-1.12.0/tests/fixtures/otel_fanout.jsonl +1 -0
  12. cctx_cli-1.12.0/tests/fixtures/otel_handoff.jsonl +1 -0
  13. cctx_cli-1.12.0/tests/test_init.py +323 -0
  14. cctx_cli-1.12.0/tests/test_otel_parser.py +350 -0
  15. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/.github/workflows/ci.yml +0 -0
  16. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/.github/workflows/publish.yml +0 -0
  17. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/.github/workflows/release.yml +0 -0
  18. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/.gitignore +0 -0
  19. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/CLAUDE.md +0 -0
  20. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/DESIGN.md +0 -0
  21. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/PRODUCT.md +0 -0
  22. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/README.md +0 -0
  23. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/action.yml +0 -0
  24. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/agents.py +0 -0
  25. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/__init__.py +0 -0
  26. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/aggregate.py +0 -0
  27. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/inflection.py +0 -0
  28. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/__init__.py +0 -0
  29. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/dead_end.py +0 -0
  30. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/fan_out.py +0 -0
  31. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/project_specific.py +0 -0
  32. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/retry_loop.py +0 -0
  33. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/scope_creep.py +0 -0
  34. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/stale_context.py +0 -0
  35. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/diagnostician/patterns/tool_thrash.py +0 -0
  36. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/discovery.py +0 -0
  37. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/exporters/__init__.py +0 -0
  38. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/exporters/csv.py +0 -0
  39. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/exporters/json.py +0 -0
  40. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/exporters/jsonl.py +0 -0
  41. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/harvest.py +0 -0
  42. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/models.py +0 -0
  43. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/parsers/__init__.py +0 -0
  44. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/parsers/claude_code.py +0 -0
  45. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/pricing.py +0 -0
  46. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/recommender/__init__.py +0 -0
  47. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/recommender/claude_md.py +0 -0
  48. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/recommender/evidence.py +0 -0
  49. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/renderers/__init__.py +0 -0
  50. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/renderers/github.py +0 -0
  51. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/renderers/report.py +0 -0
  52. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/renderers/templates/autopsy.html.j2 +0 -0
  53. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/renderers/terminal.py +0 -0
  54. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/renderers/trace_tui.py +0 -0
  55. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/tokenizer.py +0 -0
  56. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx/watcher.py +0 -0
  57. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/cctx-project-brief.md +0 -0
  58. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/demo.gif +0 -0
  59. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/demo.tape +0 -0
  60. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/health-reviews/2026-05-15-deep-review-summary.md +0 -0
  61. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/health-reviews/2026-05-15-health-review.md +0 -0
  62. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/product-reviews/2026-05-15-product-review.md +0 -0
  63. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/product-reviews/2026-06-09-product-review.md +0 -0
  64. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/plans/2026-05-12-claude-code-parser.md +0 -0
  65. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/plans/2026-05-14-autopsy-v0.md +0 -0
  66. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/plans/2026-05-16-readme-pypi-release.md +0 -0
  67. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/plans/2026-05-17-harvest-check-depth.md +0 -0
  68. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/plans/2026-05-17-project-pattern-detection.md +0 -0
  69. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/plans/2026-05-19-claude-agents-live-integration.md +0 -0
  70. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-12-claude-code-parser-design.md +0 -0
  71. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-14-autopsy-design.md +0 -0
  72. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-14-harvest-design.md +0 -0
  73. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-14-trace-tui-design.md +0 -0
  74. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-16-readme-pypi-release-design.md +0 -0
  75. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-17-harvest-check-depth-design.md +0 -0
  76. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-17-project-pattern-detection-design.md +0 -0
  77. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-05-19-claude-agents-live-integration-design.md +0 -0
  78. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/docs/superpowers/specs/2026-06-09-cross-agent-emit-design.md +0 -0
  79. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/__init__.py +0 -0
  80. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/conftest.py +0 -0
  81. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/__init__.py +0 -0
  82. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/conftest.py +0 -0
  83. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_dead_end.py +0 -0
  84. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_inflection.py +0 -0
  85. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_orchestrator.py +0 -0
  86. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_project_specific.py +0 -0
  87. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_retry_loop.py +0 -0
  88. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_scope_creep.py +0 -0
  89. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_stale_context.py +0 -0
  90. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/diagnostician/test_tool_thrash.py +0 -0
  91. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/exporters/__init__.py +0 -0
  92. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/exporters/test_csv.py +0 -0
  93. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/exporters/test_jsonl.py +0 -0
  94. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/README.md +0 -0
  95. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/short-clean/short-clean.jsonl +0 -0
  96. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a0b4c2cf1dde0ca56.meta.json +0 -0
  97. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a116ae34b1b09c332.meta.json +0 -0
  98. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a1c4c417b35658c9e.meta.json +0 -0
  99. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a1e41a901de38f1b5.meta.json +0 -0
  100. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a338f8d0c74612a24.meta.json +0 -0
  101. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a34f6f3c0e7094186.meta.json +0 -0
  102. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a5a5a0cff4d13308b.meta.json +0 -0
  103. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a6b0a3da6a0484db5.meta.json +0 -0
  104. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a7f73f1790b02cde5.meta.json +0 -0
  105. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a7f7c17c38a9d8788.meta.json +0 -0
  106. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a853259e2cd7bbe8a.meta.json +0 -0
  107. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-a8d9aedb0d0c6e12d.meta.json +0 -0
  108. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aa778bc1d59e4a441.meta.json +0 -0
  109. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aba869dedee4a12ba.meta.json +0 -0
  110. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-ada2746d9774b94db.meta.json +0 -0
  111. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aea0132068c64d2dd.meta.json +0 -0
  112. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-aea215eff50874d5f.meta.json +0 -0
  113. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments/subagents/agent-afee21f2b3852a4a0.meta.json +0 -0
  114. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-attachments/with-attachments.jsonl +0 -0
  115. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a051d9c9a6b2f5cc3.jsonl +0 -0
  116. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a051d9c9a6b2f5cc3.meta.json +0 -0
  117. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a171f16f4e65cfe75.jsonl +0 -0
  118. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a171f16f4e65cfe75.meta.json +0 -0
  119. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a1b77fea2c0a2269b.jsonl +0 -0
  120. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a1b77fea2c0a2269b.meta.json +0 -0
  121. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a20da4c01a54acca8.jsonl +0 -0
  122. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a20da4c01a54acca8.meta.json +0 -0
  123. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a3c82739b1383fb14.jsonl +0 -0
  124. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a3c82739b1383fb14.meta.json +0 -0
  125. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a49e8539611c5fe12.jsonl +0 -0
  126. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a49e8539611c5fe12.meta.json +0 -0
  127. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a7bb58f3fff2b3e8d.jsonl +0 -0
  128. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a7bb58f3fff2b3e8d.meta.json +0 -0
  129. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a92b48c0331195aac.jsonl +0 -0
  130. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-a92b48c0331195aac.meta.json +0 -0
  131. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ab96c4264099694a9.jsonl +0 -0
  132. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ab96c4264099694a9.meta.json +0 -0
  133. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-acb2895c5e34ffec0.jsonl +0 -0
  134. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-acb2895c5e34ffec0.meta.json +0 -0
  135. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-adb2302769938fb3f.jsonl +0 -0
  136. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-adb2302769938fb3f.meta.json +0 -0
  137. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ae585eca15cb93b9c.jsonl +0 -0
  138. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-ae585eca15cb93b9c.meta.json +0 -0
  139. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-aec9c917feb903d67.jsonl +0 -0
  140. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction/subagents/agent-aec9c917feb903d67.meta.json +0 -0
  141. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-compaction/with-compaction.jsonl +0 -0
  142. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-a1a3a21aeb76bb0a9.jsonl +0 -0
  143. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-a1a3a21aeb76bb0a9.meta.json +0 -0
  144. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-aaa1d6ecc05a78442.jsonl +0 -0
  145. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-aaa1d6ecc05a78442.meta.json +0 -0
  146. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-af3c545ccd30036d2.jsonl +0 -0
  147. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/subagents/agent-af3c545ccd30036d2.meta.json +0 -0
  148. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/tool-results/btwp2bzro.txt +0 -0
  149. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents/tool-results/byqjbgy4b.txt +0 -0
  150. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-subagents/with-subagents.jsonl +0 -0
  151. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-tool-results/with-tool-results/tool-results/bosbkda0h.txt +0 -0
  152. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/claude_code/with-tool-results/with-tool-results.jsonl +0 -0
  153. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/scrub.py +0 -0
  154. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/synthetic/bookkeeping_only.jsonl +0 -0
  155. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/synthetic/malformed_middle.jsonl +0 -0
  156. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/synthetic/truncated_final_line.jsonl +0 -0
  157. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/synthetic/unknown_attachment_shape.jsonl +0 -0
  158. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/fixtures/synthetic/unknown_type.jsonl +0 -0
  159. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/parsers/__init__.py +0 -0
  160. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/parsers/test_claude_code.py +0 -0
  161. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/parsers/test_claude_code_integration.py +0 -0
  162. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/recommender/__init__.py +0 -0
  163. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/recommender/test_claude_md.py +0 -0
  164. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/recommender/test_evidence.py +0 -0
  165. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/renderers/__init__.py +0 -0
  166. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/renderers/test_report.py +0 -0
  167. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/renderers/test_terminal_renderer_full.py +0 -0
  168. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_agents.py +0 -0
  169. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_aggregate.py +0 -0
  170. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_cli.py +0 -0
  171. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_cli_export.py +0 -0
  172. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_diagnostician_subagents.py +0 -0
  173. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_discovery.py +0 -0
  174. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_efficacy.py +0 -0
  175. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_fanout_classifier.py +0 -0
  176. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_github_summary.py +0 -0
  177. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_harvest.py +0 -0
  178. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_harvest_check.py +0 -0
  179. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_harvest_emit.py +0 -0
  180. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_models.py +0 -0
  181. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_models_project_pattern.py +0 -0
  182. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_recommender.py +0 -0
  183. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_smoke.py +0 -0
  184. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_terminal_renderer.py +0 -0
  185. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_tokenizer.py +0 -0
  186. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_trace_tui.py +0 -0
  187. {cctx_cli-1.10.0 → cctx_cli-1.12.0}/tests/test_watcher.py +0 -0
@@ -2,6 +2,85 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v1.12.0 (2026-06-20)
6
+
7
+ ### Bug Fixes
8
+
9
+ - Otel parser — per-trace warnings, multiple-root warning, full span list for subagent turns
10
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
11
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
12
+
13
+ - Ruff F401 + I001 in test_otel_parser.py ([#116](https://github.com/jacquardlabs/cctx/pull/116),
14
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
15
+
16
+ ### Documentation
17
+
18
+ - OTEL parser design spec — OpenAI Agents SDK support via parsers/otel.py
19
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
20
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
21
+
22
+ - OTEL parser implementation plan ([#116](https://github.com/jacquardlabs/cctx/pull/116),
23
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
24
+
25
+ - Quickstart guide for OpenAI Agents SDK + cctx OTEL integration
26
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
27
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
28
+
29
+ ### Features
30
+
31
+ - _detect_source() — auto-detect Claude Code vs OTEL trace format
32
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
33
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
34
+
35
+ - OpenAI Agents SDK support via OTEL parser ([#116](https://github.com/jacquardlabs/cctx/pull/116),
36
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
37
+
38
+ - OTEL parser skeleton — span loading and session trace construction
39
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
40
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
41
+
42
+ - Wire OTEL auto-detection into autopsy — cctx autopsy <otel.jsonl> just works
43
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
44
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
45
+
46
+ ### Testing
47
+
48
+ - Add OTLP JSONL fixtures for otel parser (handoff + fanout)
49
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
50
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
51
+
52
+ - OTEL parser error handling — malformed JSON, unknown spans, empty file
53
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
54
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
55
+
56
+ - Verify child AgentSpan → subagents mapping (handoff + fan-out)
57
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
58
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
59
+
60
+ - Verify FunctionSpan → ToolUse + ToolResult mapping
61
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
62
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
63
+
64
+ - Verify GenerationSpan → Turn mapping for OTEL parser
65
+ ([#116](https://github.com/jacquardlabs/cctx/pull/116),
66
+ [`a6b56b6`](https://github.com/jacquardlabs/cctx/commit/a6b56b61ccff5493dd26f375a698eb59aff62272))
67
+
68
+
69
+ ## v1.11.0 (2026-06-11)
70
+
71
+ ### Bug Fixes
72
+
73
+ - Ruff E501 + I001 in test_init.py (line length + import order)
74
+ ([#113](https://github.com/jacquardlabs/cctx/pull/113),
75
+ [`a04426e`](https://github.com/jacquardlabs/cctx/commit/a04426ef62cfeaa4ba7ea392bdf0dd49975cffbb))
76
+
77
+ ### Features
78
+
79
+ - Cctx init — SessionEnd hook installer + autopsy --quiet (closes #92)
80
+ ([#113](https://github.com/jacquardlabs/cctx/pull/113),
81
+ [`a04426e`](https://github.com/jacquardlabs/cctx/commit/a04426ef62cfeaa4ba7ea392bdf0dd49975cffbb))
82
+
83
+
5
84
  ## v1.10.0 (2026-06-11)
6
85
 
7
86
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cctx-cli
3
- Version: 1.10.0
3
+ Version: 1.12.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
@@ -1,3 +1,3 @@
1
1
  """cctx: profile, debug, and optimize Claude Code and Agent SDK sessions."""
2
2
 
3
- __version__ = "1.10.0"
3
+ __version__ = "1.12.0"
@@ -9,9 +9,11 @@ Commands:
9
9
  cctx harvest <session> Apply autopsy patches to CLAUDE.md
10
10
  cctx harvest <project> --since Cross-session harvest
11
11
  cctx watch [project] Live waste signals during an active session
12
+ cctx init Install opt-in SessionEnd hook for auto-diagnostics
12
13
  """
13
14
  from __future__ import annotations
14
15
 
16
+ import json as _json
15
17
  from datetime import datetime, timedelta, timezone
16
18
  from pathlib import Path
17
19
  from typing import IO
@@ -178,6 +180,48 @@ def _render_check_findings(findings: list, target_dir: Path) -> None:
178
180
  con.print(f" {badge:<6} {f.heading} {label}: {f.detail}")
179
181
 
180
182
 
183
+ _CLAUDE_CODE_LINE_TYPES = frozenset({
184
+ "user", "assistant", "system", "attachment",
185
+ "last-prompt", "permission-mode", "ai-title", "custom-title",
186
+ "queue-operation", "file-history-snapshot", "pr-link",
187
+ })
188
+
189
+
190
+ def _detect_source(path: Path) -> str:
191
+ """Sniff first non-empty lines to detect trace format.
192
+
193
+ Returns "claude_code" or "otel".
194
+ Raises click.UsageError if the format cannot be determined.
195
+ """
196
+ try:
197
+ with path.open(encoding="utf-8", errors="replace") as f:
198
+ for _ in range(5):
199
+ line = f.readline()
200
+ if not line:
201
+ break
202
+ line = line.strip()
203
+ if not line:
204
+ continue
205
+ try:
206
+ obj = _json.loads(line)
207
+ except _json.JSONDecodeError:
208
+ continue
209
+ if "resourceSpans" in obj:
210
+ return "otel"
211
+ if "traceId" in obj and "spanId" in obj:
212
+ return "otel"
213
+ line_type = obj.get("type")
214
+ if isinstance(line_type, str) and line_type in _CLAUDE_CODE_LINE_TYPES:
215
+ return "claude_code"
216
+ except OSError as exc:
217
+ raise click.UsageError(f"Cannot read file: {path}: {exc}") from exc
218
+
219
+ raise click.UsageError(
220
+ f"Cannot determine trace format for {path}.\n"
221
+ "Expected a Claude Code JSONL session file or an OTLP JSON trace export."
222
+ )
223
+
224
+
181
225
  @click.group()
182
226
  def cli() -> None:
183
227
  """cctx — find out why your Claude Code session went sideways."""
@@ -292,7 +336,15 @@ def ls(project: Path | None) -> None:
292
336
  "json_out",
293
337
  is_flag=True,
294
338
  default=False,
295
- help="Output diagnosis as JSON to stdout (single-session only).",
339
+ help="Output diagnosis (or aggregate) as JSON to stdout.",
340
+ )
341
+ @click.option(
342
+ "--quiet",
343
+ "quiet",
344
+ is_flag=True,
345
+ default=False,
346
+ help="Print one verdict line only when findings exist; nothing if clean. "
347
+ "Designed for SessionEnd hook use (cctx init).",
296
348
  )
297
349
  def autopsy(
298
350
  target: Path | None,
@@ -305,6 +357,7 @@ def autopsy(
305
357
  top_n: int | None,
306
358
  turn_num: int | None,
307
359
  json_out: bool,
360
+ quiet: bool,
308
361
  ) -> None:
309
362
  """Diagnose a session or project directory.
310
363
 
@@ -405,10 +458,22 @@ def autopsy(
405
458
  "TARGET is a directory. Use --since N for cross-session mode, "
406
459
  "or pass a .jsonl file directly."
407
460
  )
408
- trace = tokenize_session(parse_session(target))
461
+ source = _detect_source(target)
462
+ if source == "otel":
463
+ from cctx.parsers.otel import parse_otel_file as _parse_otel_file
464
+ otel_traces = _parse_otel_file(target)
465
+ if not otel_traces:
466
+ raise click.UsageError(f"No traces found in {target}")
467
+ trace = tokenize_session(otel_traces[0])
468
+ else:
469
+ trace = tokenize_session(parse_session(target))
409
470
  diagnosis = diagnostician.run(trace)
410
471
  diagnosis = claude_md.generate(diagnosis)
411
- if json_out:
472
+ if quiet:
473
+ if diagnosis.findings:
474
+ kinds = list(dict.fromkeys(f.kind.value for f in diagnosis.findings))
475
+ click.echo(f"{len(diagnosis.findings)} finding(s): {', '.join(kinds)}")
476
+ elif json_out:
412
477
  import json as _json
413
478
 
414
479
  from cctx.exporters.jsonl import export_diagnosis as _export_diag
@@ -729,3 +794,58 @@ def watch(target: Path | None) -> None:
729
794
  """
730
795
  from cctx.watcher import watch as _watch
731
796
  _watch(target)
797
+
798
+
799
+ @cli.command("init")
800
+ @click.option(
801
+ "--global",
802
+ "global_",
803
+ is_flag=True,
804
+ default=False,
805
+ help="Install to ~/.claude/settings.json (user scope) instead of .claude/settings.json.",
806
+ )
807
+ @click.option(
808
+ "--remove",
809
+ "remove_",
810
+ is_flag=True,
811
+ default=False,
812
+ help="Remove the SessionEnd hook instead of installing it.",
813
+ )
814
+ @click.option(
815
+ "--force",
816
+ is_flag=True,
817
+ default=False,
818
+ help="Reinstall even if hook is already present.",
819
+ )
820
+ def init_cmd(global_: bool, remove_: bool, force: bool) -> None:
821
+ """Install an opt-in SessionEnd hook for automatic post-session diagnostics.
822
+
823
+ Writes a hook to .claude/settings.json (project) or ~/.claude/settings.json
824
+ (--global) that runs 'cctx autopsy --latest --quiet' when a Claude Code
825
+ session ends. Output appears only when findings exist.
826
+
827
+ Idempotent — running twice does not duplicate the hook.
828
+ """
829
+ from cctx import hook_installer
830
+
831
+ if force and remove_:
832
+ raise click.UsageError("--force and --remove are mutually exclusive.")
833
+
834
+ scope = "~/.claude/settings.json" if global_ else ".claude/settings.json"
835
+
836
+ if remove_:
837
+ path = hook_installer.remove(global_=global_)
838
+ if path is None:
839
+ click.echo(f"No cctx hook found in {scope} — nothing to remove.")
840
+ else:
841
+ click.echo(f"✓ SessionEnd hook removed from {scope}")
842
+ return
843
+
844
+ result = hook_installer.install(global_=global_, force=force)
845
+ if result == "already_installed":
846
+ click.echo(f"! SessionEnd hook already installed in {scope}")
847
+ click.echo(" Use 'cctx init --force' to reinstall.")
848
+ else:
849
+ click.echo(f"✓ SessionEnd hook installed to {scope}")
850
+ remove_flag = "--global --remove" if global_ else "--remove"
851
+ click.echo(f" Run 'cctx init {remove_flag}' to uninstall.")
@@ -0,0 +1,101 @@
1
+ """Settings-merge hook installer — install/remove the cctx SessionEnd hook.
2
+
3
+ Reads, merges, and writes ~/.claude/settings.json or .claude/settings.json
4
+ without touching any other keys. Idempotent: fingerprinted by the hook's
5
+ description field so a double-install is a no-op.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ HOOK_DESCRIPTION = "cctx SessionEnd hook (diagnostics on session exit)"
14
+ HOOK_COMMAND = "cctx autopsy --latest --quiet"
15
+
16
+
17
+ def settings_path(global_: bool) -> Path:
18
+ if global_:
19
+ return Path.home() / ".claude" / "settings.json"
20
+ return Path(".claude") / "settings.json"
21
+
22
+
23
+ def _load(path: Path) -> dict[str, Any]:
24
+ if not path.exists():
25
+ return {}
26
+ try:
27
+ return json.loads(path.read_text(encoding="utf-8"))
28
+ except json.JSONDecodeError as exc:
29
+ raise ValueError(f"Invalid JSON in {path}: {exc}") from exc
30
+
31
+
32
+ def _save(path: Path, settings: dict[str, Any]) -> None:
33
+ path.parent.mkdir(parents=True, exist_ok=True)
34
+ tmp = path.with_suffix(".json.tmp")
35
+ tmp.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
36
+ tmp.replace(path)
37
+
38
+
39
+ def _find_hook(session_end: list[dict[str, Any]]) -> int | None:
40
+ """Return the index of the cctx hook group in the SessionEnd array, or None."""
41
+ for i, group in enumerate(session_end):
42
+ for h in group.get("hooks", []):
43
+ desc = h.get("description", "")
44
+ if isinstance(desc, str) and "cctx SessionEnd" in desc:
45
+ return i
46
+ return None
47
+
48
+
49
+ def _hook_entry() -> dict[str, Any]:
50
+ return {
51
+ "hooks": [
52
+ {
53
+ "type": "command",
54
+ "command": HOOK_COMMAND,
55
+ "async": True,
56
+ "description": HOOK_DESCRIPTION,
57
+ }
58
+ ]
59
+ }
60
+
61
+
62
+ def is_installed(global_: bool = False) -> bool:
63
+ path = settings_path(global_)
64
+ settings = _load(path)
65
+ session_end = settings.get("hooks", {}).get("SessionEnd", [])
66
+ return _find_hook(session_end) is not None
67
+
68
+
69
+ def install(global_: bool = False, force: bool = False) -> str:
70
+ """Install the hook. Returns "already_installed" or the path written."""
71
+ path = settings_path(global_)
72
+ settings = _load(path)
73
+ hooks = settings.setdefault("hooks", {})
74
+ session_end = hooks.setdefault("SessionEnd", [])
75
+ idx = _find_hook(session_end)
76
+ if idx is not None and not force:
77
+ return "already_installed"
78
+ if idx is not None:
79
+ session_end[idx] = _hook_entry()
80
+ else:
81
+ session_end.append(_hook_entry())
82
+ _save(path, settings)
83
+ return str(path)
84
+
85
+
86
+ def remove(global_: bool = False) -> str | None:
87
+ """Remove the hook. Returns the path written, or None if not found."""
88
+ path = settings_path(global_)
89
+ settings = _load(path)
90
+ hooks = settings.get("hooks", {})
91
+ session_end = hooks.get("SessionEnd", [])
92
+ idx = _find_hook(session_end)
93
+ if idx is None:
94
+ return None
95
+ session_end.pop(idx)
96
+ if not session_end:
97
+ del hooks["SessionEnd"]
98
+ if not hooks:
99
+ del settings["hooks"]
100
+ _save(path, settings)
101
+ return str(path)