aiwcli 0.10.2 → 0.11.0

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 (249) hide show
  1. package/bin/run.js +1 -1
  2. package/dist/commands/clear.d.ts +11 -6
  3. package/dist/commands/clear.js +229 -381
  4. package/dist/commands/init/index.d.ts +1 -17
  5. package/dist/commands/init/index.js +22 -107
  6. package/dist/lib/gitignore-manager.d.ts +32 -0
  7. package/dist/lib/gitignore-manager.js +141 -2
  8. package/dist/lib/template-installer.d.ts +7 -12
  9. package/dist/lib/template-installer.js +69 -193
  10. package/dist/lib/template-settings-reconstructor.d.ts +35 -0
  11. package/dist/lib/template-settings-reconstructor.js +130 -0
  12. package/dist/templates/CLAUDE.md +8 -8
  13. package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
  14. package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
  15. package/dist/templates/_shared/.claude/settings.json +7 -7
  16. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
  17. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
  18. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
  19. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
  20. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
  21. package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
  22. package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
  23. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
  24. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
  25. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
  26. package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
  27. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -0
  28. package/dist/templates/_shared/lib-ts/base/constants.ts +306 -0
  29. package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -0
  30. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +439 -0
  31. package/dist/templates/_shared/lib-ts/base/inference.ts +252 -0
  32. package/dist/templates/_shared/lib-ts/base/logger.ts +250 -0
  33. package/dist/templates/_shared/lib-ts/base/state-io.ts +116 -0
  34. package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -0
  35. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +162 -0
  36. package/dist/templates/_shared/lib-ts/base/utils.ts +184 -0
  37. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +438 -0
  38. package/dist/templates/_shared/lib-ts/context/context-selector.ts +515 -0
  39. package/dist/templates/_shared/lib-ts/context/context-store.ts +707 -0
  40. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +316 -0
  41. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -0
  42. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +216 -0
  43. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
  44. package/dist/templates/_shared/lib-ts/package.json +21 -0
  45. package/dist/templates/_shared/lib-ts/templates/formatters.ts +104 -0
  46. package/dist/templates/_shared/{lib/templates/plan_context.py → lib-ts/templates/plan-context.ts} +14 -22
  47. package/dist/templates/_shared/lib-ts/tsconfig.json +13 -0
  48. package/dist/templates/_shared/lib-ts/types.ts +164 -0
  49. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  50. package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
  51. package/dist/templates/_shared/scripts/save_handoff.ts +359 -0
  52. package/dist/templates/_shared/scripts/status_line.ts +733 -0
  53. package/dist/templates/cc-native/.claude/settings.json +175 -185
  54. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  55. package/dist/templates/cc-native/_cc-native/agents/ARCH-EVOLUTION.md +63 -0
  56. package/dist/templates/cc-native/_cc-native/agents/ARCH-PATTERNS.md +62 -0
  57. package/dist/templates/cc-native/_cc-native/agents/ARCH-STRUCTURE.md +63 -0
  58. package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-CHAIN-TRACER.md → ASSUMPTION-TRACER.md} +6 -10
  59. package/dist/templates/cc-native/_cc-native/agents/CLARITY-AUDITOR.md +6 -10
  60. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +74 -3
  61. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-FEASIBILITY.md +67 -0
  62. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-GAPS.md +71 -0
  63. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-ORDERING.md +63 -0
  64. package/dist/templates/cc-native/_cc-native/agents/CONSTRAINT-VALIDATOR.md +73 -0
  65. package/dist/templates/cc-native/_cc-native/agents/DESIGN-ADR-VALIDATOR.md +62 -0
  66. package/dist/templates/cc-native/_cc-native/agents/DESIGN-SCALE-MATCHER.md +65 -0
  67. package/dist/templates/cc-native/_cc-native/agents/DEVILS-ADVOCATE.md +6 -9
  68. package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-PHILOSOPHY.md +87 -0
  69. package/dist/templates/cc-native/_cc-native/agents/HANDOFF-READINESS.md +5 -9
  70. package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY-DETECTOR.md → HIDDEN-COMPLEXITY.md} +6 -10
  71. package/dist/templates/cc-native/_cc-native/agents/INCREMENTAL-DELIVERY.md +67 -0
  72. package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +91 -18
  73. package/dist/templates/cc-native/_cc-native/agents/RISK-DEPENDENCY.md +63 -0
  74. package/dist/templates/cc-native/_cc-native/agents/RISK-FMEA.md +67 -0
  75. package/dist/templates/cc-native/_cc-native/agents/RISK-PREMORTEM.md +72 -0
  76. package/dist/templates/cc-native/_cc-native/agents/RISK-REVERSIBILITY.md +75 -0
  77. package/dist/templates/cc-native/_cc-native/agents/SCOPE-BOUNDARY.md +78 -0
  78. package/dist/templates/cc-native/_cc-native/agents/SIMPLICITY-GUARDIAN.md +5 -9
  79. package/dist/templates/cc-native/_cc-native/agents/SKEPTIC.md +16 -12
  80. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-BEHAVIOR-AUDITOR.md +62 -0
  81. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-CHARACTERIZATION.md +72 -0
  82. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-FIRST-VALIDATOR.md +62 -0
  83. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-PYRAMID-ANALYZER.md +62 -0
  84. package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-COSTS.md +68 -0
  85. package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-STAKEHOLDERS.md +66 -0
  86. package/dist/templates/cc-native/_cc-native/agents/VERIFY-COVERAGE.md +75 -0
  87. package/dist/templates/cc-native/_cc-native/agents/VERIFY-STRENGTH.md +70 -0
  88. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  89. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  90. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
  91. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  92. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
  93. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
  94. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  95. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
  96. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  97. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  98. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  99. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
  100. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
  101. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  102. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
  103. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
  104. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  105. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
  106. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  107. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  108. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
  109. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  110. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
  111. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  112. package/dist/templates/cc-native/_cc-native/plan-review.config.json +12 -16
  113. package/oclif.manifest.json +1 -1
  114. package/package.json +1 -1
  115. package/dist/lib/template-merger.d.ts +0 -47
  116. package/dist/lib/template-merger.js +0 -162
  117. package/dist/templates/_shared/hooks/__init__.py +0 -16
  118. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  130. package/dist/templates/_shared/hooks/archive_plan.py +0 -169
  131. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  132. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  133. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  134. package/dist/templates/_shared/hooks/session_end.py +0 -173
  135. package/dist/templates/_shared/hooks/session_start.py +0 -206
  136. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  137. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  138. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  139. package/dist/templates/_shared/lib/__init__.py +0 -1
  140. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  141. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  142. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  145. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  146. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  147. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  148. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  149. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  150. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  151. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  152. package/dist/templates/_shared/lib/base/constants.py +0 -358
  153. package/dist/templates/_shared/lib/base/hook_utils.py +0 -341
  154. package/dist/templates/_shared/lib/base/inference.py +0 -318
  155. package/dist/templates/_shared/lib/base/logger.py +0 -291
  156. package/dist/templates/_shared/lib/base/stop_words.py +0 -213
  157. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  158. package/dist/templates/_shared/lib/base/utils.py +0 -242
  159. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  160. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  161. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  162. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  163. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  164. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  165. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  166. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  167. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  168. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  169. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  170. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  171. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  172. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  173. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  174. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  175. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  176. package/dist/templates/_shared/lib/context/plan_manager.py +0 -204
  177. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  178. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  179. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  180. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  181. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  182. package/dist/templates/_shared/lib/templates/README.md +0 -206
  183. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  184. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  185. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  186. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  187. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  188. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  189. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  190. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  191. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  192. package/dist/templates/_shared/scripts/status_line.py +0 -701
  193. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  194. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  195. package/dist/templates/cc-native/MIGRATION.md +0 -86
  196. package/dist/templates/cc-native/_cc-native/agents/ACCESSIBILITY-TESTER.md +0 -79
  197. package/dist/templates/cc-native/_cc-native/agents/ARCHITECT-REVIEWER.md +0 -48
  198. package/dist/templates/cc-native/_cc-native/agents/CODE-REVIEWER.md +0 -70
  199. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-CHECKER.md +0 -59
  200. package/dist/templates/cc-native/_cc-native/agents/CONTEXT-EXTRACTOR.md +0 -92
  201. package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-REVIEWER.md +0 -51
  202. package/dist/templates/cc-native/_cc-native/agents/FEASIBILITY-ANALYST.md +0 -57
  203. package/dist/templates/cc-native/_cc-native/agents/FRESH-PERSPECTIVE.md +0 -54
  204. package/dist/templates/cc-native/_cc-native/agents/INCENTIVE-MAPPER.md +0 -61
  205. package/dist/templates/cc-native/_cc-native/agents/PENETRATION-TESTER.md +0 -79
  206. package/dist/templates/cc-native/_cc-native/agents/PERFORMANCE-ENGINEER.md +0 -75
  207. package/dist/templates/cc-native/_cc-native/agents/PRECEDENT-FINDER.md +0 -70
  208. package/dist/templates/cc-native/_cc-native/agents/REVERSIBILITY-ANALYST.md +0 -61
  209. package/dist/templates/cc-native/_cc-native/agents/RISK-ASSESSOR.md +0 -58
  210. package/dist/templates/cc-native/_cc-native/agents/SECOND-ORDER-ANALYST.md +0 -61
  211. package/dist/templates/cc-native/_cc-native/agents/STAKEHOLDER-ADVOCATE.md +0 -55
  212. package/dist/templates/cc-native/_cc-native/agents/TRADE-OFF-ILLUMINATOR.md +0 -204
  213. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  214. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  215. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  216. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  217. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  218. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  219. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  220. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -869
  221. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  222. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  223. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  224. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  225. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  226. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  227. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  228. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  229. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  230. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  231. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  232. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  233. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  234. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  235. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  236. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  237. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  238. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  239. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  240. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  241. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  242. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  243. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  244. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  245. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  246. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1027
  247. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  248. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  249. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
@@ -1,358 +0,0 @@
1
- """Constants and path utilities for shared context management.
2
-
3
- Data hierarchy:
4
- events.jsonl (source of truth)
5
- → context.json (L1 cache)
6
- → index.json (L2 cache)
7
-
8
- All data written to _output/contexts/ (method-agnostic).
9
- No method subfolders - method is just metadata on the context.
10
- """
11
- import os
12
- import re
13
- from pathlib import Path
14
-
15
- # Directory names (relative to project root)
16
- OUTPUT_DIR = "_output"
17
- CONTEXTS_DIR = "contexts"
18
- ARCHIVE_DIR = "_archive"
19
- INDEX_FILENAME = "index.json"
20
-
21
- # Context ID validation
22
- MAX_CONTEXT_ID_LENGTH = 64
23
- VALID_CONTEXT_ID_PATTERN = re.compile(r'^[a-z0-9][a-z0-9_-]*[a-z0-9]$|^[a-z0-9]$')
24
-
25
- # File size limits
26
- MAX_EVENT_SIZE = 64 * 1024 # 64KB per event (reasonable limit)
27
- MAX_INDEX_SIZE = 1024 * 1024 # 1MB for index.json
28
-
29
- # Performance constants
30
- MAX_RETRY_ATTEMPTS = 2
31
- RETRY_BACKOFF_MS = [500, 1000]
32
-
33
-
34
- def sanitize_context_id(context_id: str) -> str:
35
- """
36
- Sanitize a string into a valid context ID.
37
-
38
- Performs these transformations:
39
- - Convert to lowercase
40
- - Replace invalid characters with hyphens
41
- - Collapse consecutive hyphens/underscores
42
- - Strip leading/trailing non-alphanumeric
43
- - Truncate to MAX_CONTEXT_ID_LENGTH
44
-
45
- Args:
46
- context_id: Raw input string
47
-
48
- Returns:
49
- Valid context ID string, or "context" if input is empty/invalid
50
- """
51
- if not context_id:
52
- return "context"
53
-
54
- # Normalize to lowercase
55
- result = context_id.lower()
56
-
57
- # Replace any character that's not alphanumeric, hyphen, or underscore
58
- result = re.sub(r'[^a-z0-9_-]', '-', result)
59
-
60
- # Collapse consecutive hyphens/underscores into single hyphen
61
- result = re.sub(r'[-_]+', '-', result)
62
-
63
- # Strip leading/trailing non-alphanumeric
64
- result = result.strip('-_')
65
-
66
- # Truncate to max length
67
- if len(result) > MAX_CONTEXT_ID_LENGTH:
68
- result = result[:MAX_CONTEXT_ID_LENGTH].rstrip('-_')
69
-
70
- # If nothing left, return default
71
- return result if result else "context"
72
-
73
-
74
- def validate_context_id(context_id: str) -> str:
75
- """
76
- Validate and normalize context ID.
77
-
78
- Auto-sanitizes invalid input instead of throwing errors for format issues.
79
- Only throws for security violations (path traversal).
80
-
81
- Valid context IDs:
82
- - 1-64 characters
83
- - lowercase alphanumeric, hyphens, underscores
84
- - must start and end with alphanumeric
85
- - no consecutive hyphens/underscores
86
-
87
- Raises:
88
- ValueError: Only for path traversal attempts
89
- """
90
- if not context_id:
91
- return "context"
92
-
93
- # SECURITY: Check for path traversal BEFORE any normalization
94
- # This prevents encoded or case-variant attacks
95
- if '..' in context_id or '/' in context_id or '\\' in context_id:
96
- raise ValueError(f"Invalid context ID '{context_id}': path traversal not allowed")
97
-
98
- # Also check for URL-encoded variants
99
- if '%2e' in context_id.lower() or '%2f' in context_id.lower() or '%5c' in context_id.lower():
100
- raise ValueError(f"Invalid context ID '{context_id}': encoded path traversal not allowed")
101
-
102
- # Sanitize instead of throwing for format issues
103
- sanitized = sanitize_context_id(context_id)
104
-
105
- return sanitized
106
-
107
-
108
- def get_output_dir(project_root: Path = None) -> Path:
109
- """
110
- Get the output directory path.
111
-
112
- Args:
113
- project_root: Project root directory (default: cwd)
114
-
115
- Returns:
116
- Path to _output/
117
- """
118
- if project_root is None:
119
- project_root = Path(os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()))
120
- return Path(project_root) / OUTPUT_DIR
121
-
122
-
123
- def get_contexts_dir(project_root: Path = None) -> Path:
124
- """
125
- Get the contexts directory path.
126
-
127
- Args:
128
- project_root: Project root directory (default: cwd)
129
-
130
- Returns:
131
- Path to _output/contexts/
132
- """
133
- return get_output_dir(project_root) / CONTEXTS_DIR
134
-
135
-
136
- def get_context_dir(context_id: str, project_root: Path = None) -> Path:
137
- """
138
- Get the directory path for a specific context.
139
-
140
- Args:
141
- context_id: Context identifier
142
- project_root: Project root directory (default: cwd)
143
-
144
- Returns:
145
- Path to _output/contexts/{context_id}/
146
-
147
- Raises:
148
- ValueError: If context_id is invalid or path escapes expected directory
149
- """
150
- validated_id = validate_context_id(context_id)
151
- contexts_dir = get_contexts_dir(project_root)
152
- result_path = contexts_dir / validated_id
153
-
154
- # SECURITY: Verify resolved path stays within contexts directory
155
- # This prevents symlink attacks and any path manipulation we might have missed
156
- try:
157
- resolved = result_path.resolve()
158
- contexts_resolved = contexts_dir.resolve()
159
- # Check that resolved path starts with the contexts directory
160
- # Use os.path for cross-platform compatibility
161
- import os
162
- resolved_str = os.path.normcase(str(resolved))
163
- contexts_str = os.path.normcase(str(contexts_resolved))
164
- if not resolved_str.startswith(contexts_str):
165
- raise ValueError(f"Invalid context ID '{context_id}': path escapes contexts directory")
166
- except (OSError, ValueError) as e:
167
- if isinstance(e, ValueError):
168
- raise
169
- # OSError can occur if path doesn't exist yet, which is fine for creation
170
- pass
171
-
172
- return result_path
173
-
174
-
175
- def get_context_plans_dir(context_id: str, project_root: Path = None) -> Path:
176
- """
177
- Get the plans directory for a specific context.
178
-
179
- Args:
180
- context_id: Context identifier
181
- project_root: Project root directory (default: cwd)
182
-
183
- Returns:
184
- Path to _output/contexts/{context_id}/plans/
185
- """
186
- return get_context_dir(context_id, project_root) / "plans"
187
-
188
-
189
- def get_context_handoffs_dir(context_id: str, project_root: Path = None) -> Path:
190
- """
191
- Get the handoffs directory for a specific context.
192
-
193
- Args:
194
- context_id: Context identifier
195
- project_root: Project root directory (default: cwd)
196
-
197
- Returns:
198
- Path to _output/contexts/{context_id}/handoffs/
199
- """
200
- return get_context_dir(context_id, project_root) / "handoffs"
201
-
202
-
203
- def get_context_reviews_dir(context_id: str, project_root: Path = None) -> Path:
204
- """
205
- Get the reviews directory for a specific context.
206
-
207
- Args:
208
- context_id: Context identifier
209
- project_root: Project root directory (default: cwd)
210
-
211
- Returns:
212
- Path to _output/contexts/{context_id}/reviews/
213
- """
214
- return get_context_dir(context_id, project_root) / "reviews"
215
-
216
-
217
- def get_index_path(project_root: Path = None) -> Path:
218
- """
219
- Get the global index file path.
220
-
221
- Args:
222
- project_root: Project root directory (default: cwd)
223
-
224
- Returns:
225
- Path to _output/index.json
226
- """
227
- return get_output_dir(project_root) / INDEX_FILENAME
228
-
229
-
230
- def get_context_file_path(context_id: str, project_root: Path = None) -> Path:
231
- """
232
- Get the context.json file path for a specific context.
233
-
234
- Args:
235
- context_id: Context identifier
236
- project_root: Project root directory (default: cwd)
237
-
238
- Returns:
239
- Path to _output/contexts/{context_id}/context.json
240
- """
241
- return get_context_dir(context_id, project_root) / "context.json"
242
-
243
-
244
- def get_events_file_path(context_id: str, project_root: Path = None) -> Path:
245
- """
246
- Get the events.jsonl file path for a specific context.
247
-
248
- Args:
249
- context_id: Context identifier
250
- project_root: Project root directory (default: cwd)
251
-
252
- Returns:
253
- Path to _output/contexts/{context_id}/events.jsonl
254
- """
255
- return get_context_dir(context_id, project_root) / "events.jsonl"
256
-
257
-
258
- def get_auto_state_path(context_id: str, project_root: Path = None) -> Path:
259
- """
260
- Get the auto-state.json file path for a specific context.
261
-
262
- Args:
263
- context_id: Context identifier
264
- project_root: Project root directory (default: cwd)
265
-
266
- Returns:
267
- Path to _output/contexts/{context_id}/auto-state.json
268
- """
269
- return get_context_dir(context_id, project_root) / "auto-state.json"
270
-
271
-
272
- def get_archive_dir(project_root: Path = None) -> Path:
273
- """
274
- Get the archive directory path.
275
-
276
- Args:
277
- project_root: Project root directory (default: cwd)
278
-
279
- Returns:
280
- Path to _output/contexts/_archive/
281
- """
282
- return get_contexts_dir(project_root) / ARCHIVE_DIR
283
-
284
-
285
- def get_archive_context_dir(context_id: str, project_root: Path = None) -> Path:
286
- """
287
- Get the archive directory for a specific context.
288
-
289
- Args:
290
- context_id: Context identifier
291
- project_root: Project root directory (default: cwd)
292
-
293
- Returns:
294
- Path to _output/contexts/_archive/{context_id}/
295
-
296
- Raises:
297
- ValueError: If context_id is invalid
298
- """
299
- validated_id = validate_context_id(context_id)
300
- return get_archive_dir(project_root) / validated_id
301
-
302
-
303
- def get_archive_index_path(project_root: Path = None) -> Path:
304
- """
305
- Get the archive index file path.
306
-
307
- Args:
308
- project_root: Project root directory (default: cwd)
309
-
310
- Returns:
311
- Path to _output/contexts/_archive/index.json
312
- """
313
- return get_archive_dir(project_root) / INDEX_FILENAME
314
-
315
-
316
- def get_handoff_folder_path(context_id: str, project_root: Path = None) -> Path:
317
- """Get path for a new handoff folder with datetime naming.
318
-
319
- Returns: _output/contexts/{context_id}/handoffs/{YYYY-MM-DD-HHMM}/
320
- Handles collisions by appending -N suffix if folder exists.
321
-
322
- Args:
323
- context_id: Context identifier
324
- project_root: Project root directory (default: cwd)
325
-
326
- Returns:
327
- Path to new handoff folder (not yet created)
328
- """
329
- from datetime import datetime
330
- handoffs_dir = get_context_handoffs_dir(context_id, project_root)
331
- timestamp = datetime.now().strftime("%Y-%m-%d-%H%M")
332
- folder = handoffs_dir / timestamp
333
-
334
- counter = 1
335
- while folder.exists():
336
- folder = handoffs_dir / f"{timestamp}-{counter}"
337
- counter += 1
338
-
339
- return folder
340
-
341
-
342
- def get_review_folder_path(context_id: str, iteration: int, project_root: Path = None) -> Path:
343
- """Get path for a new review folder with datetime and iteration naming.
344
-
345
- Returns: _output/contexts/{context_id}/reviews/cc-native/{YYYY-MM-DD-HHMM-iteration-N}/
346
-
347
- Args:
348
- context_id: Context identifier
349
- iteration: Iteration number (1-based)
350
- project_root: Project root directory (default: cwd)
351
-
352
- Returns:
353
- Path to new review folder (not yet created)
354
- """
355
- from datetime import datetime
356
- reviews_dir = get_context_reviews_dir(context_id, project_root) / "cc-native"
357
- timestamp = datetime.now().strftime("%Y-%m-%d-%H%M")
358
- return reviews_dir / f"{timestamp}-iteration-{iteration}"
@@ -1,341 +0,0 @@
1
- """Common utilities for hook scripts.
2
-
3
- Provides standardized boilerplate for:
4
- - Path setup for imports
5
- - JSON parsing from stdin
6
- - Hook payload validation
7
- - Error handling decorators
8
- """
9
-
10
- import json
11
- import os
12
- import sys
13
- from datetime import datetime, timezone
14
- from functools import wraps
15
- from pathlib import Path
16
- from typing import Any, Callable, Dict, Optional, TypeVar
17
-
18
- from .logger import log_hook_error, hook_log, log_debug, log_info, log_warn, log_error, log_diagnostic, set_context_path, _get_context_path
19
-
20
-
21
- # Context window baseline: tokens not visible in hook data
22
- # (system prompt, tools, MCP tokens)
23
- # See: https://github.com/anthropics/claude-code/issues/13783
24
- CONTEXT_BASELINE_TOKENS = 22_600
25
- DEFAULT_CONTEXT_WINDOW_SIZE = 200_000
26
-
27
-
28
- def parse_context_window(hook_input: dict) -> tuple:
29
- """Parse context window from hook input.
30
-
31
- Returns (tokens_used, max_tokens) or (None, None).
32
- tokens_used includes baseline offset for system prompt/tools.
33
- """
34
- context_window = hook_input.get("context_window")
35
- if not context_window:
36
- return None, None
37
- current_usage = context_window.get("current_usage")
38
- if not current_usage:
39
- return None, None
40
- cache_read = current_usage.get("cache_read_input_tokens", 0) or 0
41
- input_tokens = current_usage.get("input_tokens", 0) or 0
42
- cache_creation = current_usage.get("cache_creation_input_tokens", 0) or 0
43
- output_tokens = current_usage.get("output_tokens", 0) or 0
44
- content_tokens = cache_read + input_tokens + cache_creation + output_tokens
45
- tokens_used = content_tokens + CONTEXT_BASELINE_TOKENS
46
- max_tokens = context_window.get("context_window_size") or DEFAULT_CONTEXT_WINDOW_SIZE
47
- return tokens_used, max_tokens
48
-
49
-
50
- def get_context_percent_remaining(hook_input: dict) -> tuple:
51
- """Get context percentage remaining with context.json fallback.
52
-
53
- Tries two sources in order:
54
- 1. Hook input context_window data (most accurate, real-time)
55
- 2. context.json remaining_percentage (written by status_line.py)
56
-
57
- Returns:
58
- (percent_remaining, tokens_used, max_tokens) where tokens_used and
59
- max_tokens may be None if data came from context.json fallback.
60
- Returns (None, None, None) if no data available from either source.
61
- """
62
- # Source 1: Hook input (most accurate)
63
- tokens_used, max_tokens = parse_context_window(hook_input)
64
- if tokens_used is not None and max_tokens is not None and max_tokens > 0:
65
- remaining = max_tokens - tokens_used
66
- percent_remaining = max(0, min(100, int((remaining / max_tokens) * 100)))
67
- return percent_remaining, tokens_used, max_tokens
68
-
69
- # Source 2: context.json fallback (written by status_line.py)
70
- try:
71
- from .utils import project_dir
72
- from ..context.context_store import get_context_by_session_id
73
-
74
- session_id = hook_input.get("session_id")
75
- if session_id:
76
- project_root = project_dir(hook_input)
77
- context = get_context_by_session_id(session_id, project_root)
78
- if context and context.last_session:
79
- pct = context.last_session.get("context_remaining_pct")
80
- if pct is not None:
81
- return pct, None, None
82
- except Exception:
83
- pass # Fallback failed — degrade gracefully
84
-
85
- return None, None, None
86
-
87
-
88
- # Type variable for generic decorators
89
- F = TypeVar('F', bound=Callable[..., Any])
90
-
91
- # Event metadata stash — populated by load_hook_input(), read by run_hook()
92
- _last_hook_event: Optional[str] = None
93
- _last_tool_name: Optional[str] = None
94
-
95
-
96
- def load_hook_input() -> Optional[Dict[str, Any]]:
97
- """
98
- Load and parse JSON from stdin.
99
-
100
- Returns:
101
- Parsed JSON dict, or None if stdin is empty or invalid JSON
102
- """
103
- global _last_hook_event, _last_tool_name
104
- try:
105
- input_data = sys.stdin.read().strip()
106
- if not input_data:
107
- return None
108
- result = json.loads(input_data)
109
- if isinstance(result, dict):
110
- _last_hook_event = result.get("hook_event_name")
111
- _last_tool_name = result.get("tool_name")
112
- return result
113
- except json.JSONDecodeError:
114
- return None
115
-
116
-
117
- def validate_hook_event(
118
- payload: Dict[str, Any],
119
- expected_event: str,
120
- expected_tool: Optional[str] = None
121
- ) -> bool:
122
- """
123
- Validate hook event type and optional tool name.
124
-
125
- Args:
126
- payload: Hook payload from stdin
127
- expected_event: Expected hook_event_name (e.g., "PostToolUse", "PreToolUse")
128
- expected_tool: Optional expected tool_name (e.g., "TaskCreate")
129
-
130
- Returns:
131
- True if payload matches expected event/tool, False otherwise
132
- """
133
- if payload.get("hook_event_name") != expected_event:
134
- return False
135
- if expected_tool and payload.get("tool_name") != expected_tool:
136
- return False
137
- return True
138
-
139
-
140
- def get_tool_input(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
141
- """
142
- Extract and validate tool_input from payload.
143
-
144
- Args:
145
- payload: Hook payload from stdin
146
-
147
- Returns:
148
- tool_input dict, or None if missing/invalid
149
- """
150
- tool_input = payload.get("tool_input", {})
151
- return tool_input if isinstance(tool_input, dict) else None
152
-
153
-
154
- def check_skip_persistence(payload: Dict[str, Any], hook_name: str = "hook") -> bool:
155
- """
156
- Check if persistence should be skipped based on metadata flags.
157
-
158
- Args:
159
- payload: Hook payload from stdin
160
- hook_name: Name of hook for logging
161
-
162
- Returns:
163
- True if skip_persistence flag is set, False otherwise
164
- """
165
- tool_input = get_tool_input(payload)
166
- if not tool_input:
167
- return False
168
-
169
- metadata = tool_input.get("metadata", {})
170
- if isinstance(metadata, dict) and metadata.get("skip_persistence"):
171
- log_debug(hook_name, "Skipping persistence (skip_persistence flag set)")
172
- return True
173
- return False
174
-
175
-
176
- def safe_hook_main(hook_name: str) -> Callable[[F], F]:
177
- """
178
- Decorator for hook main functions with standard error handling.
179
-
180
- Catches exceptions, logs them to stderr, and returns 0 (non-blocking).
181
-
182
- Args:
183
- hook_name: Name of hook for error messages
184
-
185
- Returns:
186
- Decorator function
187
-
188
- Example:
189
- @safe_hook_main("my_hook")
190
- def main() -> int:
191
- # ... hook logic ...
192
- return 0
193
- """
194
- def decorator(func: F) -> F:
195
- @wraps(func)
196
- def wrapper(*args, **kwargs):
197
- try:
198
- return func(*args, **kwargs)
199
- except json.JSONDecodeError as e:
200
- import traceback
201
- tb = traceback.format_exc()
202
- log_hook_error(hook_name, e, traceback_str=tb)
203
- log_error(hook_name, f"JSON decode error: {e}")
204
- return 0
205
- except Exception as e:
206
- import traceback
207
- tb = traceback.format_exc()
208
- log_hook_error(hook_name, e, traceback_str=tb)
209
- log_error(hook_name, f"Unexpected error: {e}", traceback_str=tb)
210
- return 0
211
- return wrapper # type: ignore
212
- return decorator
213
-
214
-
215
- def emit_context(additional_context: str, ensure_ascii: bool = False) -> None:
216
- """Emit hookSpecificOutput with additionalContext to stdout.
217
-
218
- Args:
219
- additional_context: Context string to inject into Claude's context
220
- ensure_ascii: If True, escape non-ASCII characters in JSON output
221
- """
222
- out = {
223
- "hookSpecificOutput": {
224
- "additionalContext": additional_context,
225
- }
226
- }
227
- print(json.dumps(out, ensure_ascii=ensure_ascii))
228
-
229
-
230
- def emit_context_and_block(
231
- additional_context: str,
232
- reason: str,
233
- ensure_ascii: bool = True,
234
- ) -> None:
235
- """Emit hookSpecificOutput that denies the tool call with context and reason.
236
-
237
- Args:
238
- additional_context: Context string to inject into Claude's context
239
- reason: Reason shown to Claude for why the tool call was denied
240
- ensure_ascii: If True, escape non-ASCII characters in JSON output
241
- """
242
- out = {
243
- "hookSpecificOutput": {
244
- "additionalContext": additional_context,
245
- "permissionDecision": "deny",
246
- "permissionDecisionReason": reason,
247
- }
248
- }
249
- print(json.dumps(out, ensure_ascii=ensure_ascii))
250
-
251
-
252
- def _detect_template(script_path: str = "") -> str:
253
- """Auto-detect template origin from the hook script path.
254
-
255
- Returns "shared", a template name (e.g., "cc-native"), or "unknown".
256
- """
257
- import re
258
- path = (script_path or (sys.argv[0] if sys.argv else "")).replace("\\", "/")
259
- if "/_shared/hooks/" in path or path.startswith("_shared/hooks/"):
260
- return "shared"
261
- match = re.search(r'_([a-z][a-z0-9-]*)/hooks/', path)
262
- if match:
263
- return match.group(1) # e.g., "cc-native"
264
- return "unknown"
265
-
266
-
267
- def run_hook(main_func: Callable[[], int], hook_name: str = "unknown") -> None:
268
- """
269
- Standard hook entry point wrapper with lifecycle logging.
270
-
271
- Logs HOOK_START before calling main, HOOK_END after completion.
272
- Catches unhandled exceptions and logs them before exiting cleanly.
273
-
274
- Args:
275
- main_func: Hook main function that returns exit code
276
- hook_name: Name of the hook for error logging
277
-
278
- Example:
279
- if __name__ == "__main__":
280
- run_hook(main, "my_hook")
281
- """
282
- import time
283
- start_time = time.monotonic()
284
- template = _detect_template()
285
- event = _last_hook_event or "unknown"
286
- tool = _last_tool_name
287
-
288
- # HOOK_START
289
- start_data: Dict[str, Any] = {"lifecycle": "start", "template": template, "event": event}
290
- if tool:
291
- start_data["tool"] = tool
292
- log_info(hook_name, "HOOK_START", data=start_data)
293
-
294
- exit_code = 0
295
- status = "success"
296
- error_info = None
297
-
298
- try:
299
- result = main_func()
300
- exit_code = result if isinstance(result, int) else 0
301
- status = "blocked" if exit_code != 0 else "success"
302
- except SystemExit as e:
303
- exit_code = e.code if isinstance(e.code, int) else (1 if e.code else 0)
304
- status = "blocked" if exit_code != 0 else "success"
305
- except Exception as e:
306
- import traceback
307
- exit_code = 0 # Non-blocking
308
- status = "error"
309
- error_info = (e, traceback.format_exc())
310
-
311
- # Retroactive HOOK_START: if main() set a context path, re-emit HOOK_START
312
- # to the per-context log (original HOOK_START went to global log before
313
- # context was resolved).
314
- resolved_after = _get_context_path()
315
- if resolved_after and resolved_after.exists():
316
- hook_log("info", hook_name, "HOOK_START", data=start_data,
317
- context_path=resolved_after, stderr=False)
318
-
319
- # HOOK_END
320
- duration_ms = round((time.monotonic() - start_time) * 1000, 1)
321
- end_data: Dict[str, Any] = {
322
- "lifecycle": "end", "status": status,
323
- "duration_ms": duration_ms, "exit_code": exit_code,
324
- "template": template,
325
- }
326
- end_event = _last_hook_event or event # Re-read after main() populated it
327
- end_tool = _last_tool_name or tool
328
- end_data["event"] = end_event
329
- if end_tool:
330
- end_data["tool"] = end_tool
331
- if error_info:
332
- e, tb = error_info
333
- end_data["error_type"] = type(e).__name__
334
- log_hook_error(hook_name, e, traceback_str=tb)
335
- log_error(hook_name, f"HOOK_END: {e}", data=end_data, traceback_str=tb)
336
- elif status == "blocked":
337
- log_warn(hook_name, "HOOK_END", data=end_data)
338
- else:
339
- log_info(hook_name, "HOOK_END", data=end_data)
340
-
341
- raise SystemExit(exit_code)