wicked-garden 12.27.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 (640) hide show
  1. package/.claude-plugin/archetypes.json +166 -0
  2. package/.claude-plugin/components.json +75 -0
  3. package/.claude-plugin/marketplace.json +15 -0
  4. package/.claude-plugin/plugin.json +174 -0
  5. package/.claude-plugin/specialist.json +48 -0
  6. package/.claude-plugin/stack-registry.json +96 -0
  7. package/AGENTS.md +13 -0
  8. package/CHANGELOG.md +712 -0
  9. package/CONTRIBUTING.md +99 -0
  10. package/ETHOS.md +83 -0
  11. package/LICENSE +21 -0
  12. package/README.md +115 -0
  13. package/WICKED_GARDEN_BUS_EVENTS.md +149 -0
  14. package/agents/agentic/architect.md +449 -0
  15. package/agents/agentic/performance-analyst.md +702 -0
  16. package/agents/agentic/safety-reviewer.md +662 -0
  17. package/agents/crew/implementer.md +163 -0
  18. package/agents/crew/researcher.md +94 -0
  19. package/agents/crew/reviewer.md +184 -0
  20. package/agents/data/data-engineer.md +227 -0
  21. package/agents/engineering/api-documentarian.md +440 -0
  22. package/agents/engineering/migration-engineer.md +301 -0
  23. package/agents/engineering/solution-architect.md +208 -0
  24. package/agents/jam/brainstorm-facilitator.md +297 -0
  25. package/agents/jam/council.md +322 -0
  26. package/agents/jam/quick-facilitator.md +85 -0
  27. package/agents/persona/persona-agent.md +121 -0
  28. package/agents/platform/compliance-officer.md +266 -0
  29. package/agents/platform/privacy-expert.md +496 -0
  30. package/agents/platform/security-engineer.md +287 -0
  31. package/agents/product/a11y-expert.md +323 -0
  32. package/agents/product/requirements-analyst.md +238 -0
  33. package/agents/product/ui-reviewer.md +180 -0
  34. package/agents/product/ux-designer.md +199 -0
  35. package/agents/product/value-strategist.md +269 -0
  36. package/agents/qe/semantic-reviewer.md +78 -0
  37. package/commands/agentic/audit.md +24 -0
  38. package/commands/agentic/design.md +24 -0
  39. package/commands/agentic/frameworks.md +25 -0
  40. package/commands/agentic/review.md +27 -0
  41. package/commands/archetype/build.md +12 -0
  42. package/commands/archetype/decide.md +12 -0
  43. package/commands/archetype/explore.md +12 -0
  44. package/commands/archetype/incident.md +12 -0
  45. package/commands/archetype/migrate.md +12 -0
  46. package/commands/archetype/modernize.md +12 -0
  47. package/commands/archetype/review.md +14 -0
  48. package/commands/archetype/ship.md +12 -0
  49. package/commands/archetype/specify.md +12 -0
  50. package/commands/archetype/triage.md +12 -0
  51. package/commands/compile.md +37 -0
  52. package/commands/data/analyze.md +18 -0
  53. package/commands/data/data.md +21 -0
  54. package/commands/data/ml.md +18 -0
  55. package/commands/data/ontology.md +22 -0
  56. package/commands/data/pipeline.md +17 -0
  57. package/commands/deliberate.md +181 -0
  58. package/commands/engineering/add-field.md +77 -0
  59. package/commands/engineering/apply.md +58 -0
  60. package/commands/engineering/arch.md +26 -0
  61. package/commands/engineering/debug.md +27 -0
  62. package/commands/engineering/docs.md +27 -0
  63. package/commands/engineering/new-generator.md +257 -0
  64. package/commands/engineering/patch-plan.md +59 -0
  65. package/commands/engineering/plan.md +29 -0
  66. package/commands/engineering/remove.md +48 -0
  67. package/commands/engineering/rename.md +44 -0
  68. package/commands/engineering/review.md +32 -0
  69. package/commands/help.md +128 -0
  70. package/commands/install.md +118 -0
  71. package/commands/intent.md +54 -0
  72. package/commands/jam/brainstorm.md +37 -0
  73. package/commands/jam/council.md +49 -0
  74. package/commands/jam/quick.md +21 -0
  75. package/commands/jam/revisit.md +79 -0
  76. package/commands/persona/as.md +136 -0
  77. package/commands/persona/define.md +24 -0
  78. package/commands/persona/list.md +118 -0
  79. package/commands/platform/actions.md +20 -0
  80. package/commands/platform/assert.md +18 -0
  81. package/commands/platform/audit.md +23 -0
  82. package/commands/platform/compliance.md +20 -0
  83. package/commands/platform/gh.md +22 -0
  84. package/commands/platform/health.md +22 -0
  85. package/commands/platform/incident.md +23 -0
  86. package/commands/platform/infra.md +24 -0
  87. package/commands/platform/peer-health.md +103 -0
  88. package/commands/platform/plugin-health.md +58 -0
  89. package/commands/platform/security.md +38 -0
  90. package/commands/platform/toolchain.md +96 -0
  91. package/commands/platform/traces.md +20 -0
  92. package/commands/product/a11y.md +19 -0
  93. package/commands/product/acceptance.md +19 -0
  94. package/commands/product/align.md +20 -0
  95. package/commands/product/analyze.md +23 -0
  96. package/commands/product/elicit.md +19 -0
  97. package/commands/product/listen.md +24 -0
  98. package/commands/product/mockup.md +20 -0
  99. package/commands/product/screenshot.md +19 -0
  100. package/commands/product/strategy.md +40 -0
  101. package/commands/product/synthesize.md +23 -0
  102. package/commands/product/ux-review.md +42 -0
  103. package/commands/product/ux.md +20 -0
  104. package/commands/product/visual-direction.md +34 -0
  105. package/commands/prove.md +47 -0
  106. package/commands/report-issue.md +243 -0
  107. package/commands/reset.md +144 -0
  108. package/commands/search/blast-radius.md +58 -0
  109. package/commands/search/hotspots.md +55 -0
  110. package/commands/search/index.md +38 -0
  111. package/commands/search/lineage.md +47 -0
  112. package/commands/search/service-map.md +79 -0
  113. package/commands/setup.md +363 -0
  114. package/commands/smaht/briefing.md +200 -0
  115. package/commands/smaht/events-import.md +95 -0
  116. package/commands/smaht/state.md +19 -0
  117. package/commands/where-am-i.md +58 -0
  118. package/docs/README.md +32 -0
  119. package/docs/adr/0001-code-relationship-graph-engine.md +116 -0
  120. package/docs/adr/0002-major-cleanup-keep-cut.md +91 -0
  121. package/docs/adr/0003-peers-gate-required-layers-optin.md +41 -0
  122. package/docs/adr/0004-code-graph-moves-to-wicked-brain.md +47 -0
  123. package/docs/brain-chunk-format.md +135 -0
  124. package/docs/compiler.md +95 -0
  125. package/docs/daemon-api.md +70 -0
  126. package/docs/domains.md +197 -0
  127. package/docs/getting-started.md +208 -0
  128. package/docs/plans/2026-06-08-wicked-loom-conduct.md +1337 -0
  129. package/docs/plans/2026-06-08-wicked-loom-contract.md +638 -0
  130. package/docs/plans/2026-06-08-wicked-loom-cutover.md +1300 -0
  131. package/docs/plans/2026-06-08-wicked-loom-expand-compose.md +843 -0
  132. package/docs/plans/2026-06-08-wicked-loom-flow-cutover.md +403 -0
  133. package/docs/plans/2026-06-10-codegraph-to-brain-phase1a-graph-core.md +838 -0
  134. package/docs/plans/2026-06-10-codegraph-to-brain-phase1b-injected-edges.md +44 -0
  135. package/docs/required-peers.md +60 -0
  136. package/docs/specs/2026-06-08-wicked-loom-extraction.md +225 -0
  137. package/docs/specs/2026-06-10-codegraph-to-brain-migration.md +168 -0
  138. package/docs/v11/archetypes.md +231 -0
  139. package/hooks/hooks.json +275 -0
  140. package/hooks/scripts/bootstrap.py +1797 -0
  141. package/hooks/scripts/invoke.py +62 -0
  142. package/hooks/scripts/notification.py +166 -0
  143. package/hooks/scripts/permission_request.py +223 -0
  144. package/hooks/scripts/post_tool.py +1374 -0
  145. package/hooks/scripts/pre_compact.py +272 -0
  146. package/hooks/scripts/pre_tool.py +1278 -0
  147. package/hooks/scripts/prompt_submit.py +1109 -0
  148. package/hooks/scripts/session_end.py +95 -0
  149. package/hooks/scripts/stop.py +624 -0
  150. package/hooks/scripts/subagent_lifecycle.py +464 -0
  151. package/hooks/scripts/subscribers/on_gate_decided.py +84 -0
  152. package/hooks/scripts/task_completed.py +322 -0
  153. package/hooks/scripts/worktree_guard.py +148 -0
  154. package/hooks/subscriptions/on_gate_decided.json +9 -0
  155. package/install.mjs +128 -0
  156. package/package.json +41 -0
  157. package/pyproject.toml +59 -0
  158. package/schemas/README.md +32 -0
  159. package/schemas/assert_contracts.json +37 -0
  160. package/schemas/health_probe.json +46 -0
  161. package/schemas/plugin_status.json +37 -0
  162. package/scripts/_adapters/__init__.py +166 -0
  163. package/scripts/_adapters/jira_adapter.py +18 -0
  164. package/scripts/_adapters/linear_adapter.py +18 -0
  165. package/scripts/_adapters/miro_adapter.py +18 -0
  166. package/scripts/_adapters/notion_adapter.py +18 -0
  167. package/scripts/_agents.py +357 -0
  168. package/scripts/_brain_ingest/__init__.py +2 -0
  169. package/scripts/_brain_ingest/session_fact_extractor.py +341 -0
  170. package/scripts/_brain_port.py +163 -0
  171. package/scripts/_bus.py +1081 -0
  172. package/scripts/_bus_catalog_gen.py +93 -0
  173. package/scripts/_bus_consumers.json +46 -0
  174. package/scripts/_bus_emit.py +43 -0
  175. package/scripts/_capability_registry.py +334 -0
  176. package/scripts/_capability_resolver.py +322 -0
  177. package/scripts/_domain_store.py +559 -0
  178. package/scripts/_event_log_reader.py +154 -0
  179. package/scripts/_event_schema.py +305 -0
  180. package/scripts/_event_store.py +397 -0
  181. package/scripts/_heavy_cadence.py +324 -0
  182. package/scripts/_integration_resolver.py +321 -0
  183. package/scripts/_logger.py +151 -0
  184. package/scripts/_loom.py +482 -0
  185. package/scripts/_paths.py +133 -0
  186. package/scripts/_python.sh +22 -0
  187. package/scripts/_run.py +137 -0
  188. package/scripts/_session.py +502 -0
  189. package/scripts/_validate_registry.py +1042 -0
  190. package/scripts/_wicked_testing_probe.py +277 -0
  191. package/scripts/agentic/analyze_agents.py +391 -0
  192. package/scripts/agentic/detect_framework.py +420 -0
  193. package/scripts/agentic/issue_taxonomy.py +357 -0
  194. package/scripts/agentic/pattern_scorer.py +434 -0
  195. package/scripts/classify/persist.py +169 -0
  196. package/scripts/compiler/compile.py +552 -0
  197. package/scripts/compiler/phase0/VERDICT.md +60 -0
  198. package/scripts/compiler/phase0/detect.py +292 -0
  199. package/scripts/compiler/phase0/emit.py +228 -0
  200. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.bindings.json +30 -0
  201. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.lint-output.txt +17 -0
  202. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/failing-tests.txt +4 -0
  203. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/.wg/evidence/honest-tests.txt +2 -0
  204. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-fail.build.md +8 -0
  205. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/fab-missing.build.md +8 -0
  206. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/honest.build.md +8 -0
  207. package/scripts/compiler/phase0/evidence/Enterprise-AISDLC.stage/wg_check_claims.py +174 -0
  208. package/scripts/compiler/phase0/evidence/command_iq.bindings.json +45 -0
  209. package/scripts/compiler/phase0/evidence/command_iq.lint-output.txt +17 -0
  210. package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/failing-tests.txt +4 -0
  211. package/scripts/compiler/phase0/evidence/command_iq.stage/.projects/evidence-domain/phase-1-clarify/evidence/honest-tests.txt +3 -0
  212. package/scripts/compiler/phase0/evidence/command_iq.stage/fab-fail.build.md +8 -0
  213. package/scripts/compiler/phase0/evidence/command_iq.stage/fab-missing.build.md +8 -0
  214. package/scripts/compiler/phase0/evidence/command_iq.stage/honest.build.md +8 -0
  215. package/scripts/compiler/phase0/evidence/command_iq.stage/wg_check_claims.py +174 -0
  216. package/scripts/compiler/phase0/evidence/memos.bindings.json +28 -0
  217. package/scripts/compiler/phase0/evidence/memos.lint-output.txt +17 -0
  218. package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/failing-tests.txt +4 -0
  219. package/scripts/compiler/phase0/evidence/memos.stage/.wg/evidence/honest-tests.txt +2 -0
  220. package/scripts/compiler/phase0/evidence/memos.stage/fab-fail.build.md +8 -0
  221. package/scripts/compiler/phase0/evidence/memos.stage/fab-missing.build.md +8 -0
  222. package/scripts/compiler/phase0/evidence/memos.stage/honest.build.md +8 -0
  223. package/scripts/compiler/phase0/evidence/memos.stage/wg_check_claims.py +174 -0
  224. package/scripts/compiler/phase0/evidence/verdict.json +104 -0
  225. package/scripts/compiler/phase0/evidence/wicked-bus.bindings.json +29 -0
  226. package/scripts/compiler/phase0/evidence/wicked-bus.lint-output.txt +17 -0
  227. package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/failing-tests.txt +4 -0
  228. package/scripts/compiler/phase0/evidence/wicked-bus.stage/coverage/honest-tests.txt +3 -0
  229. package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-fail.build.md +8 -0
  230. package/scripts/compiler/phase0/evidence/wicked-bus.stage/fab-missing.build.md +8 -0
  231. package/scripts/compiler/phase0/evidence/wicked-bus.stage/honest.build.md +8 -0
  232. package/scripts/compiler/phase0/evidence/wicked-bus.stage/wg_check_claims.py +174 -0
  233. package/scripts/compiler/phase0/evidence/wire-vault-transcript.txt +60 -0
  234. package/scripts/compiler/phase0/run_probe.py +234 -0
  235. package/scripts/compiler/phase0/wire_vault.py +310 -0
  236. package/scripts/crew/__init__.py +0 -0
  237. package/scripts/crew/_task_audit_writer.py +243 -0
  238. package/scripts/crew/_task_reader.py +389 -0
  239. package/scripts/crew/archetypes_v11.py +348 -0
  240. package/scripts/crew/detectors/__init__.py +12 -0
  241. package/scripts/crew/flow_compiler.py +121 -0
  242. package/scripts/crew/phase_manager.py +703 -0
  243. package/scripts/crew/scope_delta.py +264 -0
  244. package/scripts/crew/specialist_resolver.py +218 -0
  245. package/scripts/crew/stack_registry.py +189 -0
  246. package/scripts/data/data_profiler.py +271 -0
  247. package/scripts/data/ontology_recommender.py +231 -0
  248. package/scripts/data/schema_validator.py +267 -0
  249. package/scripts/delivery/drift.py +739 -0
  250. package/scripts/delivery/telemetry.py +614 -0
  251. package/scripts/engineering/patch/codegraph_db.py +125 -0
  252. package/scripts/engineering/patch/generators/__init__.py +56 -0
  253. package/scripts/engineering/patch/generators/base.py +373 -0
  254. package/scripts/engineering/patch/generators/csharp_generator.py +494 -0
  255. package/scripts/engineering/patch/generators/go_generator.py +430 -0
  256. package/scripts/engineering/patch/generators/java_generator.py +928 -0
  257. package/scripts/engineering/patch/generators/jsp_generator.py +454 -0
  258. package/scripts/engineering/patch/generators/kotlin_generator.py +561 -0
  259. package/scripts/engineering/patch/generators/perl_generator.py +510 -0
  260. package/scripts/engineering/patch/generators/php_generator.py +644 -0
  261. package/scripts/engineering/patch/generators/propagation_engine.py +929 -0
  262. package/scripts/engineering/patch/generators/python_generator.py +487 -0
  263. package/scripts/engineering/patch/generators/ruby_generator.py +463 -0
  264. package/scripts/engineering/patch/generators/rust_generator.py +400 -0
  265. package/scripts/engineering/patch/generators/sql_generator.py +594 -0
  266. package/scripts/engineering/patch/generators/typescript_generator.py +439 -0
  267. package/scripts/engineering/patch/patch.py +800 -0
  268. package/scripts/engineering/patch/safety.py +429 -0
  269. package/scripts/jam/__init__.py +0 -0
  270. package/scripts/jam/_bus_consumers.py +271 -0
  271. package/scripts/jam/agentic_cli_registry.py +703 -0
  272. package/scripts/jam/consensus.py +737 -0
  273. package/scripts/jam/detect_clis.py +409 -0
  274. package/scripts/jam/jam.py +283 -0
  275. package/scripts/jam/save_transcript.py +171 -0
  276. package/scripts/loom/__init__.py +22 -0
  277. package/scripts/loom/cli.py +170 -0
  278. package/scripts/loom/compose.py +180 -0
  279. package/scripts/loom/gate.py +120 -0
  280. package/scripts/loom/manifest.py +140 -0
  281. package/scripts/loom/resolve.py +59 -0
  282. package/scripts/persona/__init__.py +0 -0
  283. package/scripts/persona/registry.py +688 -0
  284. package/scripts/platform/guard_pipeline.py +1214 -0
  285. package/scripts/platform/guard_profiles.py +216 -0
  286. package/scripts/platform/observability/assert_contracts.py +434 -0
  287. package/scripts/platform/observability/health_probe.py +915 -0
  288. package/scripts/platform/observability/ops_log_viewer.py +209 -0
  289. package/scripts/platform/prereq_doctor.py +567 -0
  290. package/scripts/qe/conditions_manifest.py +260 -0
  291. package/scripts/qe/content_sanitizer.py +160 -0
  292. package/scripts/qe/coverage_tracker.py +535 -0
  293. package/scripts/qe/discover_scenarios.py +243 -0
  294. package/scripts/qe/evidence_tracker.py +232 -0
  295. package/scripts/qe/prove.py +196 -0
  296. package/scripts/qe/registry_lookup.py +80 -0
  297. package/scripts/qe/registry_store.py +176 -0
  298. package/scripts/qe/semantic_review.py +1005 -0
  299. package/scripts/qe/vault_gate.py +382 -0
  300. package/scripts/qe/verdict_audit.py +175 -0
  301. package/scripts/qe/verdict_schema.py +335 -0
  302. package/scripts/reset.py +220 -0
  303. package/scripts/resolve_path.py +22 -0
  304. package/scripts/sentinel/invariants.py +402 -0
  305. package/scripts/sentinel/pre_push.py +73 -0
  306. package/scripts/setup/__init__.py +1 -0
  307. package/scripts/setup/detect_state.py +114 -0
  308. package/scripts/setup/migrate_legacy.py +67 -0
  309. package/scripts/setup/migrate_v6_projects.py +232 -0
  310. package/scripts/setup/onboarding.py +114 -0
  311. package/scripts/smaht/_bus_consumers.py +98 -0
  312. package/scripts/smaht/adapters/__init__.py +83 -0
  313. package/scripts/smaht/adapters/brain_adapter.py +254 -0
  314. package/scripts/smaht/adapters/context7_adapter.py +438 -0
  315. package/scripts/smaht/adapters/delegation_adapter.py +104 -0
  316. package/scripts/smaht/adapters/domain_adapter.py +206 -0
  317. package/scripts/smaht/adapters/events_adapter.py +231 -0
  318. package/scripts/smaht/adapters/tools_adapter.py +49 -0
  319. package/scripts/smaht/cheatsheet_store.py +236 -0
  320. package/scripts/smaht/context_package.py +386 -0
  321. package/scripts/smaht/knowledge_graph.py +395 -0
  322. package/scripts/smaht/propose_skills.py +856 -0
  323. package/scripts/smaht/test_cheatsheet_store.py +428 -0
  324. package/scripts/statusline.py +151 -0
  325. package/scripts/where_am_i.py +236 -0
  326. package/skills/agentic/agentic-patterns/SKILL.md +91 -0
  327. package/skills/agentic/agentic-patterns/refs/anti-patterns-design.md +283 -0
  328. package/skills/agentic/agentic-patterns/refs/anti-patterns-operational.md +216 -0
  329. package/skills/agentic/agentic-patterns/refs/pattern-catalog.md +329 -0
  330. package/skills/agentic/context-engineering/SKILL.md +142 -0
  331. package/skills/agentic/context-engineering/refs/caching-and-optimization.md +251 -0
  332. package/skills/agentic/context-engineering/refs/compression-techniques.md +171 -0
  333. package/skills/agentic/context-engineering/refs/cost-calculation-budget.md +250 -0
  334. package/skills/agentic/context-engineering/refs/cost-optimization-reporting.md +300 -0
  335. package/skills/agentic/context-engineering/refs/selective-loading.md +179 -0
  336. package/skills/agentic/frameworks/SKILL.md +191 -0
  337. package/skills/agentic/frameworks/refs/framework-profiles-1.md +209 -0
  338. package/skills/agentic/frameworks/refs/framework-profiles-2.md +214 -0
  339. package/skills/agentic/frameworks/refs/migration-patterns-paths.md +267 -0
  340. package/skills/agentic/frameworks/refs/migration-patterns-testing.md +208 -0
  341. package/skills/agentic/refs/audit.md +134 -0
  342. package/skills/agentic/refs/design.md +137 -0
  343. package/skills/agentic/refs/frameworks.md +102 -0
  344. package/skills/agentic/refs/review.md +160 -0
  345. package/skills/agentic/review-methodology/SKILL.md +186 -0
  346. package/skills/agentic/review-methodology/refs/deliverable-templates.md +246 -0
  347. package/skills/agentic/review-methodology/refs/issue-taxonomy-quality-testing.md +277 -0
  348. package/skills/agentic/review-methodology/refs/issue-taxonomy-reliability-safety.md +253 -0
  349. package/skills/agentic/trust-and-safety/SKILL.md +177 -0
  350. package/skills/agentic/trust-and-safety/refs/guardrails-actions.md +246 -0
  351. package/skills/agentic/trust-and-safety/refs/guardrails-input-output.md +238 -0
  352. package/skills/agentic/trust-and-safety/refs/guardrails-resources.md +209 -0
  353. package/skills/agentic/trust-and-safety/refs/safety-checklist-advanced.md +209 -0
  354. package/skills/agentic/trust-and-safety/refs/safety-checklist-core.md +215 -0
  355. package/skills/archetype/SKILL.md +104 -0
  356. package/skills/archetype/refs/build.md +142 -0
  357. package/skills/archetype/refs/decide.md +117 -0
  358. package/skills/archetype/refs/explore.md +66 -0
  359. package/skills/archetype/refs/incident.md +149 -0
  360. package/skills/archetype/refs/migrate.md +170 -0
  361. package/skills/archetype/refs/modernize.md +187 -0
  362. package/skills/archetype/refs/review.md +145 -0
  363. package/skills/archetype/refs/ship.md +117 -0
  364. package/skills/archetype/refs/specify.md +108 -0
  365. package/skills/archetype/refs/triage.md +62 -0
  366. package/skills/classify/SKILL.md +157 -0
  367. package/skills/data/analysis/SKILL.md +125 -0
  368. package/skills/data/analysis/refs/templates.md +170 -0
  369. package/skills/data/data/SKILL.md +118 -0
  370. package/skills/data/data/refs/examples.md +235 -0
  371. package/skills/data/ml/SKILL.md +125 -0
  372. package/skills/data/ml/refs/templates.md +172 -0
  373. package/skills/data/pipeline/SKILL.md +136 -0
  374. package/skills/data/pipeline/refs/templates.md +175 -0
  375. package/skills/data/refs/analyze.md +123 -0
  376. package/skills/data/refs/data.md +95 -0
  377. package/skills/data/refs/ml.md +118 -0
  378. package/skills/data/refs/pipeline.md +92 -0
  379. package/skills/deliberate/SKILL.md +125 -0
  380. package/skills/deliberate/refs/architecture-lens.md +37 -0
  381. package/skills/deliberate/refs/assumptions-lens.md +43 -0
  382. package/skills/deliberate/refs/code-lens.md +38 -0
  383. package/skills/deliberate/refs/content-lens.md +37 -0
  384. package/skills/deliberate/refs/data-lens.md +43 -0
  385. package/skills/deliberate/refs/design-lens.md +37 -0
  386. package/skills/deliberate/refs/opportunity-patterns.md +107 -0
  387. package/skills/deliberate/refs/ops-lens.md +43 -0
  388. package/skills/deliberate/refs/product-lens.md +46 -0
  389. package/skills/deliberate/refs/rethink-framework.md +77 -0
  390. package/skills/deliberate/refs/security-lens.md +47 -0
  391. package/skills/deliberate/refs/testing-lens.md +45 -0
  392. package/skills/deliberate/refs/timing-lens.md +42 -0
  393. package/skills/engineering/architecture/SKILL.md +177 -0
  394. package/skills/engineering/architecture/refs/adr-template.md +268 -0
  395. package/skills/engineering/architecture/refs/architecture-template-deployment.md +176 -0
  396. package/skills/engineering/architecture/refs/architecture-template-design.md +300 -0
  397. package/skills/engineering/architecture/refs/examples-ecommerce-analytics.md +299 -0
  398. package/skills/engineering/architecture/refs/examples-saas-trading.md +291 -0
  399. package/skills/engineering/architecture/refs/patterns-advanced.md +238 -0
  400. package/skills/engineering/architecture/refs/patterns-structural.md +303 -0
  401. package/skills/engineering/backend/SKILL.md +102 -0
  402. package/skills/engineering/backend/refs/checklists.md +146 -0
  403. package/skills/engineering/backend/refs/patterns.md +181 -0
  404. package/skills/engineering/debugging/SKILL.md +99 -0
  405. package/skills/engineering/debugging/refs/patterns.md +248 -0
  406. package/skills/engineering/debugging/refs/process.md +203 -0
  407. package/skills/engineering/docs/SKILL.md +97 -0
  408. package/skills/engineering/engineering/SKILL.md +88 -0
  409. package/skills/engineering/engineering/refs/checklists.md +91 -0
  410. package/skills/engineering/engineering/refs/templates.md +71 -0
  411. package/skills/engineering/frontend/SKILL.md +92 -0
  412. package/skills/engineering/frontend/refs/checklists.md +96 -0
  413. package/skills/engineering/frontend/refs/patterns.md +167 -0
  414. package/skills/engineering/integration/SKILL.md +183 -0
  415. package/skills/engineering/integration/refs/error-handling-best-practices.md +139 -0
  416. package/skills/engineering/integration/refs/error-handling-error-classes-typescript.md +212 -0
  417. package/skills/engineering/integration/refs/error-handling-error-response-format.md +301 -0
  418. package/skills/engineering/integration/refs/error-handling-retry-strategies.md +190 -0
  419. package/skills/engineering/integration/refs/event-schemas-best-practices.md +225 -0
  420. package/skills/engineering/integration/refs/event-schemas-event-naming-conventions.md +303 -0
  421. package/skills/engineering/integration/refs/event-schemas-event-structure.md +288 -0
  422. package/skills/engineering/integration/refs/graphql-best-practices.md +138 -0
  423. package/skills/engineering/integration/refs/graphql-error-handling.md +198 -0
  424. package/skills/engineering/integration/refs/graphql-graphql-basics.md +197 -0
  425. package/skills/engineering/integration/refs/graphql-query-examples.md +226 -0
  426. package/skills/engineering/integration/refs/graphql-schema-design-patterns.md +280 -0
  427. package/skills/engineering/integration/refs/grpc-grpc-overview.md +246 -0
  428. package/skills/engineering/integration/refs/grpc-implementation-examples.md +269 -0
  429. package/skills/engineering/integration/refs/grpc-metadata-headers.md +304 -0
  430. package/skills/engineering/integration/refs/grpc-testing.md +49 -0
  431. package/skills/engineering/integration/refs/openapi-template.yaml +682 -0
  432. package/skills/engineering/integration/refs/resilience-best-practices.md +109 -0
  433. package/skills/engineering/integration/refs/resilience-circuit-breaker-pattern.md +302 -0
  434. package/skills/engineering/integration/refs/resilience-rate-limiting.md +245 -0
  435. package/skills/engineering/integration/refs/resilience-timeout-pattern.md +212 -0
  436. package/skills/engineering/integration/refs/rest-api-fundamentals.md +299 -0
  437. package/skills/engineering/integration/refs/rest-api-responses-security.md +302 -0
  438. package/skills/engineering/integration/refs/versioning-breaking-changes.md +237 -0
  439. package/skills/engineering/integration/refs/versioning-migration-strategies.md +257 -0
  440. package/skills/engineering/integration/refs/versioning-versioning-strategies.md +296 -0
  441. package/skills/engineering/large-scale-migration/SKILL.md +65 -0
  442. package/skills/engineering/patch/SKILL.md +184 -0
  443. package/skills/engineering/refs/arch.md +126 -0
  444. package/skills/engineering/refs/debug.md +142 -0
  445. package/skills/engineering/refs/docs.md +190 -0
  446. package/skills/engineering/refs/plan.md +109 -0
  447. package/skills/engineering/refs/review.md +138 -0
  448. package/skills/engineering/system-design/SKILL.md +154 -0
  449. package/skills/engineering/system-design/refs/anti-patterns-big-ball-of-mud.md +254 -0
  450. package/skills/engineering/system-design/refs/anti-patterns-circular-dependencies.md +241 -0
  451. package/skills/engineering/system-design/refs/anti-patterns-feature-envy.md +168 -0
  452. package/skills/engineering/system-design/refs/anti-patterns-god-object.md +247 -0
  453. package/skills/engineering/system-design/refs/component-template-lifecycle.md +210 -0
  454. package/skills/engineering/system-design/refs/component-template-maintenance.md +219 -0
  455. package/skills/engineering/system-design/refs/component-template-structure.md +276 -0
  456. package/skills/engineering/system-design/refs/dependencies-best-practices.md +260 -0
  457. package/skills/engineering/system-design/refs/dependencies-dependency-principles.md +295 -0
  458. package/skills/engineering/system-design/refs/dependencies-dependency-tracking.md +261 -0
  459. package/skills/engineering/system-design/refs/interface-template-lifecycle.md +211 -0
  460. package/skills/engineering/system-design/refs/interface-template-maintenance.md +212 -0
  461. package/skills/engineering/system-design/refs/interface-template-structure.md +269 -0
  462. package/skills/engineering/system-design/refs/patterns-dependency-injection.md +151 -0
  463. package/skills/engineering/system-design/refs/patterns-facade-pattern.md +257 -0
  464. package/skills/engineering/system-design/refs/patterns-layered-pattern.md +204 -0
  465. package/skills/engineering/system-design/refs/patterns-plugin-architecture.md +229 -0
  466. package/skills/engineering/unit-test-quality/SKILL.md +194 -0
  467. package/skills/ground/SKILL.md +130 -0
  468. package/skills/integration-discovery/SKILL.md +168 -0
  469. package/skills/integration-discovery/refs/capabilities.md +282 -0
  470. package/skills/integration-discovery/refs/cli-detection.md +295 -0
  471. package/skills/integration-discovery/refs/discovery-sources.md +233 -0
  472. package/skills/integration-discovery/refs/task-patterns.md +206 -0
  473. package/skills/jam/SKILL.md +45 -0
  474. package/skills/jam/refs/facilitation-patterns.md +125 -0
  475. package/skills/jam/refs/quick.md +64 -0
  476. package/skills/jam/refs/synthesis-patterns.md +114 -0
  477. package/skills/multi-model/SKILL.md +168 -0
  478. package/skills/multi-model/refs/aichat.md +119 -0
  479. package/skills/multi-model/refs/aider.md +100 -0
  480. package/skills/multi-model/refs/auditability.md +238 -0
  481. package/skills/multi-model/refs/codex.md +169 -0
  482. package/skills/multi-model/refs/context.md +306 -0
  483. package/skills/multi-model/refs/copilot.md +161 -0
  484. package/skills/multi-model/refs/examples.md +224 -0
  485. package/skills/multi-model/refs/gemini.md +185 -0
  486. package/skills/multi-model/refs/goose.md +112 -0
  487. package/skills/multi-model/refs/llm.md +124 -0
  488. package/skills/multi-model/refs/opencode.md +196 -0
  489. package/skills/multi-model/refs/orchestration.md +188 -0
  490. package/skills/multi-model/refs/pi.md +208 -0
  491. package/skills/persona/SKILL.md +144 -0
  492. package/skills/persona/refs/define.md +133 -0
  493. package/skills/platform/actions/refs/actions.md +114 -0
  494. package/skills/platform/audit/SKILL.md +174 -0
  495. package/skills/platform/audit/refs/audit.md +145 -0
  496. package/skills/platform/audit/refs/checklists-evidence-operations.md +144 -0
  497. package/skills/platform/audit/refs/checklists-gdpr-pci-evidence.md +213 -0
  498. package/skills/platform/audit/refs/checklists-soc2-hipaa.md +120 -0
  499. package/skills/platform/audit/refs/frameworks-gdpr-pci.md +225 -0
  500. package/skills/platform/audit/refs/frameworks-soc2-hipaa.md +228 -0
  501. package/skills/platform/compliance/SKILL.md +169 -0
  502. package/skills/platform/compliance/refs/checklists.md +303 -0
  503. package/skills/platform/compliance/refs/compliance.md +117 -0
  504. package/skills/platform/compliance/refs/frameworks.md +282 -0
  505. package/skills/platform/errors/SKILL.md +183 -0
  506. package/skills/platform/errors/refs/patterns.md +271 -0
  507. package/skills/platform/errors/refs/severity.md +264 -0
  508. package/skills/platform/gate-benchmark-rebaseline/SKILL.md +108 -0
  509. package/skills/platform/gh/refs/gh.md +105 -0
  510. package/skills/platform/gh-cli/SKILL.md +120 -0
  511. package/skills/platform/gh-cli/refs/automation.md +63 -0
  512. package/skills/platform/gh-cli/refs/gh-ops.md +103 -0
  513. package/skills/platform/gh-cli/refs/patterns.md +132 -0
  514. package/skills/platform/gh-cli/scripts/gh_ops.py +454 -0
  515. package/skills/platform/github-actions/SKILL.md +159 -0
  516. package/skills/platform/github-actions/refs/security.md +150 -0
  517. package/skills/platform/github-actions/refs/templates.md +230 -0
  518. package/skills/platform/github-actions/refs/troubleshooting.md +174 -0
  519. package/skills/platform/github-actions/scripts/workflow_gen.py +215 -0
  520. package/skills/platform/gitlab-ci/SKILL.md +140 -0
  521. package/skills/platform/gitlab-ci/refs/templates.md +223 -0
  522. package/skills/platform/gitlab-ci/refs/troubleshooting.md +213 -0
  523. package/skills/platform/glab-cli/SKILL.md +133 -0
  524. package/skills/platform/glab-cli/refs/ci-debugging.md +95 -0
  525. package/skills/platform/glab-cli/refs/glab-ops.md +78 -0
  526. package/skills/platform/glab-cli/refs/patterns.md +131 -0
  527. package/skills/platform/glab-cli/scripts/glab_ops.py +313 -0
  528. package/skills/platform/health/SKILL.md +177 -0
  529. package/skills/platform/health/refs/health.md +72 -0
  530. package/skills/platform/health/refs/sources.md +302 -0
  531. package/skills/platform/incident/refs/incident.md +136 -0
  532. package/skills/platform/infra/refs/infra.md +104 -0
  533. package/skills/platform/observability/SKILL.md +128 -0
  534. package/skills/platform/observability/refs/toolchain-discovery.md +243 -0
  535. package/skills/platform/prereq-doctor/SKILL.md +57 -0
  536. package/skills/platform/prereq-doctor/refs/tool-registry.md +58 -0
  537. package/skills/platform/traces/refs/traces.md +101 -0
  538. package/skills/product/acceptance-criteria/SKILL.md +193 -0
  539. package/skills/product/acceptance-criteria/refs/ac-anti-patterns.md +291 -0
  540. package/skills/product/acceptance-criteria/refs/ac-examples.md +277 -0
  541. package/skills/product/accessibility/SKILL.md +197 -0
  542. package/skills/product/accessibility/refs/aria-patterns-basics.md +250 -0
  543. package/skills/product/accessibility/refs/aria-patterns-dynamic.md +187 -0
  544. package/skills/product/accessibility/refs/aria-patterns-interactive.md +246 -0
  545. package/skills/product/accessibility/refs/audit-report-findings.md +248 -0
  546. package/skills/product/accessibility/refs/audit-report-results.md +276 -0
  547. package/skills/product/accessibility/refs/common-violations-html-css.md +270 -0
  548. package/skills/product/accessibility/refs/common-violations-interactive.md +303 -0
  549. package/skills/product/accessibility/refs/keyboard-testing-basics.md +222 -0
  550. package/skills/product/accessibility/refs/keyboard-testing-patterns.md +173 -0
  551. package/skills/product/accessibility/refs/screen-reader-testing-advanced.md +294 -0
  552. package/skills/product/accessibility/refs/screen-reader-testing-commands.md +255 -0
  553. package/skills/product/accessibility/refs/wcag-checklist.md +262 -0
  554. package/skills/product/analyze/SKILL.md +164 -0
  555. package/skills/product/analyze/refs/algorithms.md +238 -0
  556. package/skills/product/analyze/refs/sentiment-patterns.md +180 -0
  557. package/skills/product/imagery/SKILL.md +97 -0
  558. package/skills/product/imagery/alter/SKILL.md +105 -0
  559. package/skills/product/imagery/alter/refs/editing-reference.md +146 -0
  560. package/skills/product/imagery/create/SKILL.md +109 -0
  561. package/skills/product/imagery/create/refs/prompt-engineering.md +112 -0
  562. package/skills/product/imagery/create/refs/provider-reference.md +219 -0
  563. package/skills/product/imagery/review/SKILL.md +101 -0
  564. package/skills/product/imagery/review/refs/analysis-lenses.md +136 -0
  565. package/skills/product/imagery/review/refs/quality-gates.md +153 -0
  566. package/skills/product/imagery/scripts/analyze.sh +23 -0
  567. package/skills/product/imagery/scripts/edit.sh +14 -0
  568. package/skills/product/imagery/scripts/generate.sh +13 -0
  569. package/skills/product/imagery/scripts/provider.py +787 -0
  570. package/skills/product/listen/SKILL.md +175 -0
  571. package/skills/product/listen/refs/channels.md +220 -0
  572. package/skills/product/mockup/SKILL.md +106 -0
  573. package/skills/product/mockup/refs/templates.md +95 -0
  574. package/skills/product/refs/a11y.md +109 -0
  575. package/skills/product/refs/acceptance.md +79 -0
  576. package/skills/product/refs/align.md +67 -0
  577. package/skills/product/refs/analyze.md +77 -0
  578. package/skills/product/refs/elicit.md +67 -0
  579. package/skills/product/refs/listen.md +58 -0
  580. package/skills/product/refs/mockup.md +77 -0
  581. package/skills/product/refs/screenshot.md +59 -0
  582. package/skills/product/refs/strategy.md +77 -0
  583. package/skills/product/refs/synthesize.md +54 -0
  584. package/skills/product/refs/ux-review.md +68 -0
  585. package/skills/product/refs/ux.md +101 -0
  586. package/skills/product/requirements-analysis/SKILL.md +136 -0
  587. package/skills/product/requirements-analysis/refs/requirements-example-auth.md +264 -0
  588. package/skills/product/requirements-analysis/refs/requirements-example-export-integration.md +88 -0
  589. package/skills/product/requirements-analysis/refs/requirements-output-format-template.md +247 -0
  590. package/skills/product/requirements-analysis/refs/user-story-guide.md +296 -0
  591. package/skills/product/requirements-graph/SKILL.md +155 -0
  592. package/skills/product/requirements-graph/refs/examples.md +223 -0
  593. package/skills/product/requirements-graph/refs/schema.md +162 -0
  594. package/skills/product/requirements-migrate/SKILL.md +90 -0
  595. package/skills/product/requirements-navigate/SKILL.md +141 -0
  596. package/skills/product/requirements-navigate/refs/meta-templates.md +141 -0
  597. package/skills/product/screenshot/SKILL.md +147 -0
  598. package/skills/product/strategy/SKILL.md +198 -0
  599. package/skills/product/strategy/refs/frameworks.md +295 -0
  600. package/skills/product/strategy/refs/templates-competitive-market.md +299 -0
  601. package/skills/product/strategy/refs/templates-roi-value.md +207 -0
  602. package/skills/product/synthesize/SKILL.md +116 -0
  603. package/skills/product/synthesize/refs/journey-mapping.md +212 -0
  604. package/skills/product/synthesize/refs/prioritization.md +193 -0
  605. package/skills/product/ux-review/SKILL.md +164 -0
  606. package/skills/product/visual-direction/SKILL.md +137 -0
  607. package/skills/product/visual-review/SKILL.md +131 -0
  608. package/skills/runtime-exec/SKILL.md +138 -0
  609. package/skills/search/codebase-narrator/SKILL.md +131 -0
  610. package/skills/search/codebase-narrator/refs/output-template.md +100 -0
  611. package/skills/smaht/SKILL.md +72 -0
  612. package/skills/smaht/discovery/SKILL.md +65 -0
  613. package/skills/smaht/intent/SKILL.md +89 -0
  614. package/skills/smaht/propose-skills/SKILL.md +142 -0
  615. package/skills/smaht/refs/state.md +76 -0
  616. package/skills/swarm/SKILL.md +132 -0
  617. package/skills/swarm/refs/fan-out.md +104 -0
  618. package/skills/swarm/refs/independent-verification.md +86 -0
  619. package/skills/swarm/refs/receipts-and-evidence.md +69 -0
  620. package/skills/swarm/refs/recon-synthesis.md +87 -0
  621. package/skills/swarm/refs/ship-discipline.md +90 -0
  622. package/skills/wickedizer/SKILL.md +147 -0
  623. package/skills/wickedizer/refs/artifact-types.md +238 -0
  624. package/skills/wickedizer/refs/complexity.md +278 -0
  625. package/skills/wickedizer/refs/de-ai-pass.md +140 -0
  626. package/skills/wickedizer/refs/failure-modes.md +249 -0
  627. package/skills/wickedizer/refs/patterns.md +282 -0
  628. package/skills/wickedizer/refs/review.md +221 -0
  629. package/skills/wickedizer/refs/structure.md +282 -0
  630. package/skills/wickedizer/refs/voice-profile.md +125 -0
  631. package/skills/workflow/SKILL.md +132 -0
  632. package/skills/workflow/refs/archetype-adjustments.md +205 -0
  633. package/skills/workflow/refs/evidence.md +58 -0
  634. package/skills/workflow/refs/integration.md +130 -0
  635. package/skills/workflow/refs/risk-dimension-signals.md +260 -0
  636. package/skills/workflow/refs/scoring-rubric.md +49 -0
  637. package/skills/workflow/refs/signal-keywords.md +176 -0
  638. package/skills/workflow/refs/specialist-routing-rules.md +159 -0
  639. package/skills/worktrees/SKILL.md +87 -0
  640. package/skills/worktrees/refs/recipes.md +96 -0
@@ -0,0 +1,1109 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ UserPromptSubmit hook — wicked-garden pull-model context assembly.
4
+
5
+ v6 replaced the v5 push-model orchestrator (deleted in #428) with a pull-model
6
+ architecture. The hook is now responsible only for session-level concerns:
7
+ - Setup gate (hard-block / onboarding directive)
8
+ - Facilitator re-evaluation directive (v6 Gate 3, #428)
9
+ - Turn counter increment
10
+ - Session goal capture (turns 1-2)
11
+ - HOT fast-exit on continuation tokens
12
+ - Pull-model directive injection (tiny, ~60-150 chars)
13
+ - Jam / discovery suggestion (signal-gated)
14
+ - Output formatting
15
+
16
+ v9.2.11 removed the periodic [Memory] nudge that fired every 10 turns.
17
+ Periodic reminders without a real signal are noise — Claude knows about
18
+ wicked-brain:memory from CLAUDE.md and uses it when there is something
19
+ to checkpoint. The proper checkpoint is the wicked-brain-session-teardown
20
+ agent at session end, not a 10-turn timer.
21
+
22
+ Subagents pull context on demand via wicked-brain:search/query and
23
+ wicked-garden:search rather than having a briefing pushed every turn.
24
+ The hook is stdlib-only.
25
+
26
+ Always fails open — any unhandled exception returns {"continue": true}.
27
+ """
28
+
29
+ import json
30
+ import os
31
+ import re
32
+ import sys
33
+ import time
34
+ from datetime import datetime, timezone
35
+ from pathlib import Path
36
+ from typing import Mapping
37
+
38
+ # Add shared scripts directory to path.
39
+ _PLUGIN_ROOT = Path(os.environ.get("CLAUDE_PLUGIN_ROOT", Path(__file__).resolve().parents[2]))
40
+ _SCRIPTS_DIR = _PLUGIN_ROOT / "scripts"
41
+ sys.path.insert(0, str(_SCRIPTS_DIR))
42
+
43
+ def _resolve_brain_port():
44
+ try:
45
+ from _brain_port import resolve_port
46
+ return resolve_port()
47
+ except Exception:
48
+ return int(os.environ.get("WICKED_BRAIN_PORT", "4242"))
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Ops logger wrapper — fail-silent, never crashes the hook
53
+ # ---------------------------------------------------------------------------
54
+
55
+ def _log(domain, level, event, ok=True, ms=None, detail=None):
56
+ """Ops logger — fail-silent, never crashes the hook."""
57
+ try:
58
+ from _logger import log
59
+ log(domain, level, event, ok=ok, ms=ms, detail=detail)
60
+ except Exception:
61
+ pass
62
+
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Session goal capture (turns 1-2)
66
+ # ---------------------------------------------------------------------------
67
+
68
+ def _brain_api(action, params=None, timeout=3):
69
+ """Call brain API. Returns parsed JSON or None."""
70
+ try:
71
+ import urllib.request
72
+ port = _resolve_brain_port()
73
+ payload = json.dumps({"action": action, "params": params or {}}).encode("utf-8")
74
+ req = urllib.request.Request(
75
+ f"http://localhost:{port}/api",
76
+ data=payload,
77
+ headers={"Content-Type": "application/json"},
78
+ method="POST",
79
+ )
80
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
81
+ return json.loads(resp.read().decode("utf-8"))
82
+ except Exception:
83
+ return None
84
+
85
+
86
+ def _write_brain_memory(title, content, tier="episodic", tags=None, mem_type="episodic", importance=5):
87
+ """Write a memory chunk to brain. Returns chunk_id or None."""
88
+ try:
89
+ import uuid
90
+ mem_id = str(uuid.uuid4())
91
+ chunk_id = f"memories/{tier}/mem-{mem_id}"
92
+ chunk_path = Path.home() / ".wicked-brain" / f"{chunk_id}.md"
93
+ chunk_path.parent.mkdir(parents=True, exist_ok=True)
94
+
95
+ tags_list = tags or []
96
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
97
+
98
+ lines = ["---"]
99
+ lines.append("source: wicked-brain:memory")
100
+ lines.append(f"memory_type: {mem_type}")
101
+ lines.append(f"memory_tier: {tier}")
102
+ lines.append(f"title: {title}")
103
+ lines.append(f"importance: {importance}")
104
+ lines.append("contains:")
105
+ for t in tags_list:
106
+ lines.append(f" - {t}")
107
+ lines.append(f'indexed_at: "{now}"')
108
+ lines.append("---")
109
+ lines.append("")
110
+ lines.append(f"# {title}")
111
+ lines.append("")
112
+ lines.append(content)
113
+
114
+ chunk_path.write_text("\n".join(lines), encoding="utf-8")
115
+
116
+ # Index in brain FTS5
117
+ search_text = f"{title} {content} {' '.join(tags_list)}"
118
+ _brain_api("index", {"id": f"{chunk_id}.md", "path": f"{chunk_id}.md", "content": search_text, "brain_id": "wicked-brain"})
119
+ return chunk_id
120
+ except Exception:
121
+ return None
122
+
123
+
124
+ def _capture_session_goal(prompt: str, turn_count: int, project: str, session_id: str):
125
+ """On turns 1-2, save the session goal as WORKING memory via brain API."""
126
+ if turn_count > 2:
127
+ return
128
+ if len(prompt.strip()) < 20:
129
+ return
130
+ try:
131
+ _write_brain_memory(
132
+ title=f"Session goal (turn {turn_count})",
133
+ content=prompt[:500],
134
+ tier="working",
135
+ tags=["session-goal", "auto-captured"],
136
+ mem_type="working",
137
+ importance=5,
138
+ )
139
+ except Exception:
140
+ pass
141
+
142
+
143
+ # ---------------------------------------------------------------------------
144
+ # Turn counter
145
+ # ---------------------------------------------------------------------------
146
+
147
+ def _increment_turn(state) -> int:
148
+ """Increment session turn counter, reset turn progress tracking, and return new value."""
149
+ if state is None:
150
+ return 0
151
+ try:
152
+ new_count = (state.turn_count or 0) + 1
153
+ # Reset turn progress visibility fields (Issue #323)
154
+ state.update(
155
+ turn_count=new_count,
156
+ turn_start_ts=datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
157
+ turn_tool_count=0,
158
+ )
159
+ state.save()
160
+ return new_count
161
+ except Exception:
162
+ return 0
163
+
164
+
165
+ # ---------------------------------------------------------------------------
166
+ # Jam suggestion (session-level, hook-local)
167
+ # ---------------------------------------------------------------------------
168
+
169
+ _JAM_AMBIGUITY_SIGNALS = [
170
+ "options", "tradeoffs", "trade-off", "should we", "brainstorm",
171
+ "alternatives", "pros and cons", "compare", "which approach",
172
+ "what are the", "explore", "think through", "uncertain", "unclear",
173
+ "not sure", "ideas", "different ways",
174
+ ]
175
+
176
+
177
+ def _has_ambiguity_signals(prompt: str) -> bool:
178
+ """Return True if prompt contains explicit ambiguity / exploration signals."""
179
+ lower = prompt.lower()
180
+ return any(sig in lower for sig in _JAM_AMBIGUITY_SIGNALS)
181
+
182
+
183
+ # Archetypes whose playbook ends in a produces-gate that re-derives through
184
+ # wicked-loom. For these, steering is only actionable if it names the exact
185
+ # `prove.py` one-liner the agent should reach for at the gate phase. Keyed by
186
+ # archetype -> (claim, gate-phase, hard-gate?). Hard gates (incident/migrate/
187
+ # review) require an INDEPENDENT attestation, so they carry --with-attestations.
188
+ # Archetypes absent from this map (explore, triage) have no produces-gate, so
189
+ # no prove hint is emitted for them (silence is correct).
190
+ _PROVE_GATE = {
191
+ "build": ("tests-pass", "review", False),
192
+ "specify": ("criteria-met", "validate", False),
193
+ "decide": ("adr-recorded", "record", False),
194
+ "ship": ("rollout-healthy", "soak", False),
195
+ "review": ("review-verdict", "remediate-or-accept", True),
196
+ "incident": ("incident-resolved", "resolve", True),
197
+ "migrate": ("rollback-proven", "cutover", True),
198
+ }
199
+
200
+
201
+ def _prove_hint_for(archetype: str) -> str | None:
202
+ """Return the concrete `prove:` one-liner for an archetype's produces-gate,
203
+ or None when the archetype has no gate. Tells the agent exactly how to
204
+ re-derive (not assert) the produce at the gate phase. Fail-open: any
205
+ unexpected input yields None rather than raising.
206
+
207
+ The verify command is a placeholder (`<verify cmd>`) the agent fills in —
208
+ e.g. the project's test command for build, a `cat <artifact>` + --verifier
209
+ for doc-shaped produces. Hard gates append --with-attestations so the gate
210
+ stays REJECT until an independent evaluator attests.
211
+ """
212
+ spec = _PROVE_GATE.get(archetype)
213
+ if not spec:
214
+ return None
215
+ claim, phase, hard = spec
216
+ line = (
217
+ f"prove: scripts/qe/prove.py {claim} --by \"<verify cmd>\" "
218
+ f"--scope {archetype} --phase {phase}"
219
+ )
220
+ if hard:
221
+ line += " --with-attestations"
222
+ return line
223
+
224
+
225
+ def _with_prove_hints(directive: str, archetypes: list[str]) -> str:
226
+ """Append prove-hint line(s) to a directive for any gated archetypes.
227
+
228
+ Dedupes, preserves catalog order, and is a no-op when none of the
229
+ archetypes have a produces-gate. Fail-open on any error (returns the
230
+ original directive unchanged)."""
231
+ try:
232
+ hints = []
233
+ seen = set()
234
+ for a in archetypes:
235
+ if a in seen:
236
+ continue
237
+ seen.add(a)
238
+ h = _prove_hint_for(a)
239
+ if h:
240
+ hints.append(h)
241
+ if not hints:
242
+ return directive
243
+ return directive + "\n" + "\n".join(hints)
244
+ except Exception:
245
+ return directive
246
+
247
+
248
+ def _build_archetype_directive(prompt: str, intent: str, state=None) -> str | None:
249
+ """v11 — emit a slim work-shape archetype steering directive.
250
+
251
+ Two-tier classification:
252
+
253
+ 1. **Persisted (LLM)**: when SessionState carries a recent
254
+ `classified_at` + `archetypes_v11`, prefer that — the model
255
+ already classified through the wicked-garden:classify skill.
256
+ 2. **Fallback (regex)**: when no persisted classification exists,
257
+ run archetypes_v11.detect_archetypes() against the prompt
258
+ directly. If the prompt is non-trivial (intent != simple-edit)
259
+ AND the regex result is weak (only triage matched), emit a
260
+ directive asking the model to invoke the classify skill —
261
+ LLM reasoning beats regex on ambiguous prompts.
262
+
263
+ Skips emission when intent is 'simple-edit' (silence is the signal).
264
+ Fail-open on any error.
265
+ """
266
+ if not prompt or not prompt.strip():
267
+ return None
268
+ if intent == "simple-edit":
269
+ return None
270
+
271
+ # Tier 1: persisted classification from the classify skill
272
+ persisted = (getattr(state, "archetypes_v11", None) or []) if state else []
273
+ if persisted:
274
+ # Filter triage out unless sole match; emit shape-card from persisted.
275
+ real = [a for a in persisted if a.get("name") != "triage"]
276
+ if not real:
277
+ return None
278
+ if len(real) == 1 and float(real[0].get("score", 0)) >= 0.7:
279
+ n = real[0]["name"]
280
+ s = float(real[0]["score"])
281
+ base = f"<wg archetype=\"{n}\" score=\"{s:.2f}\" classified=\"llm\" />"
282
+ return _with_prove_hints(base, [n])
283
+ shape_lines = [
284
+ f" - {a['name']} ({float(a.get('score', 0)):.2f})"
285
+ for a in real[:3]
286
+ ]
287
+ base = (
288
+ "<wg archetypes classified=\"llm\">\n"
289
+ + "\n".join(shape_lines)
290
+ + "\n</wg>"
291
+ )
292
+ return _with_prove_hints(base, [a["name"] for a in real[:3]])
293
+
294
+ # Tier 2: regex fallback
295
+ try:
296
+ from pathlib import Path as _Path
297
+ import sys as _sys
298
+ _scripts = _Path(__file__).resolve().parents[1].parent / "scripts"
299
+ _crew = _scripts / "crew"
300
+ for p in (_scripts, _crew):
301
+ if str(p) not in _sys.path:
302
+ _sys.path.append(str(p))
303
+ from archetypes_v11 import detect_archetypes, HIGH_CONFIDENCE
304
+ except Exception:
305
+ return None
306
+
307
+ try:
308
+ matches = detect_archetypes(prompt)
309
+ except Exception:
310
+ return None
311
+
312
+ real = [(n, s, e) for (n, s, e) in matches if n != "triage"]
313
+
314
+ if not real:
315
+ # Regex couldn't classify cleanly — ask the model to do it instead.
316
+ # This is the LLM-classifier opt-in path: the model invokes the
317
+ # wicked-garden:classify skill, persists the result, and on the
318
+ # next turn the persisted-tier branch above takes over.
319
+ return (
320
+ "<wg classify-due />\n"
321
+ "Regex classifier could not route this prompt to a v11 archetype. "
322
+ "Invoke the `wicked-garden:classify` skill to classify with model "
323
+ "reasoning, then run the chosen archetype's playbook."
324
+ )
325
+
326
+ high = [(n, s, e) for (n, s, e) in real if s >= HIGH_CONFIDENCE]
327
+ if len(high) == 1 and len(real) == 1:
328
+ n, s, _ = high[0]
329
+ base = f"<wg archetype=\"{n}\" score=\"{s:.2f}\" classified=\"regex\" />"
330
+ return _with_prove_hints(base, [n])
331
+
332
+ shape_lines = [f" - {n} ({s:.2f})" for (n, s, _) in real[:3]]
333
+ base = (
334
+ "<wg archetypes classified=\"regex\">\n"
335
+ + "\n".join(shape_lines)
336
+ + "\n</wg>"
337
+ )
338
+ return _with_prove_hints(base, [n for (n, _s, _e) in real[:3]])
339
+
340
+
341
+ def _suggest_jam(prompt: str, state) -> str | None:
342
+ """Return a jam suggestion string if conditions are met, or None.
343
+
344
+ Conditions:
345
+ 1. Prompt contains ambiguity signals
346
+ 2. Prompt does not already reference a jam command
347
+ 3. Hint not shown this session
348
+
349
+ Returns the hint string to append, or None if no suggestion should be shown.
350
+ """
351
+ if ":jam:" in prompt.lower() or "/jam:" in prompt.lower():
352
+ return None # Already using jam — no suggestion needed
353
+ if not _has_ambiguity_signals(prompt):
354
+ return None
355
+ jam_shown = getattr(state, "jam_hint_shown", False) if state else False
356
+ if jam_shown:
357
+ return None
358
+
359
+ try:
360
+ if state:
361
+ state.update(jam_hint_shown=True)
362
+ except Exception:
363
+ pass
364
+ return (
365
+ "[Suggestion] This prompt involves exploring options or tradeoffs. "
366
+ "Consider /wicked-garden:jam:quick for fast structured thinking, "
367
+ "or /wicked-garden:jam:brainstorm for a full multi-perspective session."
368
+ )
369
+
370
+
371
+ # ---------------------------------------------------------------------------
372
+ # Complexity + risk scoring (inline, no API calls, no external imports)
373
+ # ---------------------------------------------------------------------------
374
+
375
+ _SYNTHESIS_THRESHOLD = 0.40 # complexity >= this OR is_risky → trigger synthesis
376
+
377
+ _DEEP_WORK_SIGNALS = frozenset({
378
+ "implement", "architecture", "design", "refactor", "migrate",
379
+ "integrate", "why does", "how does", "why is", "how do", "how should",
380
+ "explain", "compare", "analyze", "review", "investigate", "diagnose",
381
+ "difference between", "best approach", "should i", "what if",
382
+ "trade-off", "tradeoff", "what happens", "what would",
383
+ })
384
+
385
+ _RISK_SIGNALS = frozenset({
386
+ "delete", "drop table", "remove all", "migration", "production", "prod ",
387
+ "deploy", "breaking change", "credentials", "secret key",
388
+ "password", "force push", "hard reset",
389
+ })
390
+ # Risk signals that must match as whole words (not substrings)
391
+ _RISK_WORD_SIGNALS = frozenset({"auth token", "api key", "rollback", "purge", "truncate"})
392
+
393
+ _MULTI_PART_SIGNALS = frozenset({
394
+ "and also", "additionally", "furthermore", "first,", "second,",
395
+ "third,", "as well as", "along with",
396
+ })
397
+
398
+
399
+ def _score_complexity_and_risk(prompt: str, state) -> tuple[float, bool]:
400
+ """Fast inline complexity + risk scoring. No external imports, <1ms."""
401
+ words = prompt.split()
402
+ word_count = len(words)
403
+ prompt_lower = prompt.lower()
404
+
405
+ complexity = 0.0
406
+
407
+ # Length signals (longer = more likely complex)
408
+ if word_count > 8: complexity += 0.10
409
+ if word_count > 25: complexity += 0.10
410
+ if word_count > 60: complexity += 0.10
411
+ if word_count > 110: complexity += 0.05
412
+
413
+ # Multi-part signals
414
+ if any(s in prompt_lower for s in _MULTI_PART_SIGNALS): complexity += 0.15
415
+ if prompt.count("?") > 1: complexity += 0.10
416
+
417
+ # Deep work signals (understanding / analysis / design)
418
+ matches = sum(1 for s in _DEEP_WORK_SIGNALS if s in prompt_lower)
419
+ complexity += min(matches * 0.15, 0.30)
420
+
421
+ # Novelty: prompt topics not in session history → new territory
422
+ try:
423
+ session_topics = set(getattr(state, "topics", None) or [])
424
+ if session_topics:
425
+ prompt_words = {w.lower() for w in words if len(w) > 4}
426
+ if not (prompt_words & session_topics):
427
+ complexity += 0.15 # No overlap = unfamiliar territory
428
+ except Exception:
429
+ pass
430
+
431
+ # Risk detection (separate from complexity)
432
+ is_risky = (
433
+ any(s in prompt_lower for s in _RISK_SIGNALS)
434
+ or any(s in prompt_lower for s in _RISK_WORD_SIGNALS)
435
+ )
436
+
437
+ return min(complexity, 1.0), is_risky
438
+
439
+
440
+ # ---------------------------------------------------------------------------
441
+ # Issue #578 — Context Assembly intent classifier
442
+ #
443
+ # The Context Assembly directive used to fire on every prompt whose complexity
444
+ # score crossed _SYNTHESIS_THRESHOLD, including obviously conversational
445
+ # prompts like "any more feedback?" or "lets do it". Same root cause as #572:
446
+ # the hook fires without a signal Claude cannot already see itself.
447
+ #
448
+ # The classifier below is a pure function — takes a prompt + env mapping and
449
+ # returns (should_emit, reason). It is intentionally conservative: when
450
+ # signals conflict, substantive wins, so a long or technical prompt with a
451
+ # short confirmation prefix still grounds via the directive.
452
+ # ---------------------------------------------------------------------------
453
+
454
+ # Substantive signals — technical verbs that strongly imply real work.
455
+ _IMPERATIVE_TECHNICAL_VERBS = frozenset({
456
+ "implement", "fix", "refactor", "build", "design", "trace",
457
+ "debug", "migrate", "integrate", "deploy", "rollback", "optimize",
458
+ "profile", "benchmark", "explain how", "analyze", "review",
459
+ "investigate", "diagnose",
460
+ })
461
+
462
+ # File-path regex — matches any token that looks like path/to/file.ext where
463
+ # ext is a common code/doc extension. Stdlib re, compiled once at import.
464
+ _FILE_PATH_EXTS = (
465
+ "py", "ts", "tsx", "js", "jsx", "md", "json", "yaml", "yml",
466
+ "toml", "sh", "rs", "go", "java", "kt", "rb", "cpp", "c", "h",
467
+ "hpp", "cs", "swift", "php", "sql", "html", "css", "scss",
468
+ )
469
+ _FILE_PATH_PATTERN = re.compile(
470
+ r"(?:[\w.\-]+[/\\])+[\w.\-]+\.(?:" + "|".join(_FILE_PATH_EXTS) + r")\b",
471
+ re.IGNORECASE,
472
+ )
473
+ # Bare-filename regex — catches 'validate_plan.py' or 'README.md' with no
474
+ # separator. Must have a non-trivial stem (>= 2 chars) so we don't match
475
+ # 'a.b' noise. Used in addition to the path pattern above.
476
+ _BARE_FILENAME_PATTERN = re.compile(
477
+ r"\b[\w\-]{2,}\.(?:" + "|".join(_FILE_PATH_EXTS) + r")\b",
478
+ re.IGNORECASE,
479
+ )
480
+
481
+ # Inline backtick or fenced code block
482
+ _CODE_FENCE_OR_BACKTICK = re.compile(r"```|`[^`\n]{2,}`")
483
+
484
+
485
+ def _has_file_path(prompt: str) -> bool:
486
+ """Return True if the prompt contains a file reference.
487
+
488
+ Matches either ``path/to/file.ext`` (path + separator) or a bare filename
489
+ like ``validate_plan.py`` / ``README.md``. Either form is a strong
490
+ substantive signal — the user is pointing at real code.
491
+ """
492
+ if _FILE_PATH_PATTERN.search(prompt):
493
+ return True
494
+ return bool(_BARE_FILENAME_PATTERN.search(prompt))
495
+
496
+
497
+ def _has_code_markers(prompt: str) -> bool:
498
+ """Return True if the prompt has a code fence or inline backtick code."""
499
+ return bool(_CODE_FENCE_OR_BACKTICK.search(prompt))
500
+
501
+
502
+ def _has_imperative_technical_verb(prompt_lower: str) -> bool:
503
+ """Return True if any imperative technical verb appears in the prompt."""
504
+ return any(verb in prompt_lower for verb in _IMPERATIVE_TECHNICAL_VERBS)
505
+
506
+
507
+ # ---------------------------------------------------------------------------
508
+ # Setup gate
509
+ # ---------------------------------------------------------------------------
510
+
511
+ _GUARD_PASS_PREFIXES = (
512
+ "/wicked-garden:setup",
513
+ "/wicked-garden:help",
514
+ "/setup",
515
+ "/help",
516
+ )
517
+
518
+
519
+ def _check_setup_gate(prompt: str) -> str | None:
520
+ """Check if wicked-garden setup is needed.
521
+
522
+ Returns None if setup is complete (config.json exists with setup_complete=true).
523
+ Calls sys.exit(2) if config is missing or incomplete — this hard-blocks
524
+ the prompt and feeds the error message back to the model.
525
+
526
+ Always checks config.json on disk — no session state caching.
527
+ """
528
+ stripped = prompt.strip().lower()
529
+
530
+ # Exemption: let setup and help commands through
531
+ if stripped.startswith(_GUARD_PASS_PREFIXES):
532
+ try:
533
+ from _session import SessionState
534
+ state = SessionState.load()
535
+ state.update(setup_in_progress=True)
536
+ except Exception:
537
+ pass
538
+ return None
539
+
540
+ # Allow prompts through when setup is actively running — but verify
541
+ # that setup_in_progress isn't stale from a previous session by
542
+ # confirming config.json doesn't already have setup_complete=true.
543
+ try:
544
+ from _session import SessionState
545
+ state = SessionState.load()
546
+ if state.setup_in_progress:
547
+ config_path = Path.home() / ".something-wicked" / "wicked-garden" / "config.json"
548
+ config_done = False
549
+ try:
550
+ if config_path.exists():
551
+ cfg = json.loads(config_path.read_text())
552
+ config_done = cfg.get("setup_complete", False)
553
+ except (json.JSONDecodeError, OSError):
554
+ pass
555
+ if config_done:
556
+ # Config already complete — flag is stale, clear it
557
+ state.update(setup_in_progress=False)
558
+ else:
559
+ # Setup genuinely in progress — let the prompt through
560
+ return None
561
+ except Exception:
562
+ pass
563
+
564
+ # Check config.json on disk — the single source of truth for setup status.
565
+ config_path = Path.home() / ".something-wicked" / "wicked-garden" / "config.json"
566
+ config_ok = False
567
+ try:
568
+ if config_path.exists():
569
+ config = json.loads(config_path.read_text())
570
+ config_ok = config.get("setup_complete", False)
571
+ except (json.JSONDecodeError, OSError):
572
+ pass
573
+
574
+ if not config_ok:
575
+ print(
576
+ "wicked-garden requires setup before first use.\n"
577
+ "Run: /wicked-garden:setup",
578
+ file=sys.stderr,
579
+ )
580
+ sys.exit(2)
581
+
582
+ return None
583
+
584
+
585
+ def _check_brain_gate(prompt: str) -> None:
586
+ """Warn if wicked-brain server is not reachable.
587
+
588
+ Probes the brain API with a 1s timeout. If unreachable, prints a directive
589
+ to stderr — this feeds back to the model via the hook error channel but does
590
+ NOT hard-block (sys.exit(2)) since brain is a server process that may be
591
+ starting up or temporarily unavailable.
592
+
593
+ Exempted on setup/help commands (brain not needed before setup completes).
594
+ """
595
+ stripped = prompt.strip().lower()
596
+ if stripped.startswith(_GUARD_PASS_PREFIXES):
597
+ return
598
+
599
+ try:
600
+ port = _resolve_brain_port()
601
+ payload = json.dumps({"action": "stats", "params": {}}).encode("utf-8")
602
+ import urllib.request
603
+ req = urllib.request.Request(
604
+ f"http://localhost:{port}/api",
605
+ data=payload,
606
+ headers={"Content-Type": "application/json"},
607
+ method="POST",
608
+ )
609
+ with urllib.request.urlopen(req, timeout=1) as _resp:
610
+ pass # Brain is running — no action needed
611
+ except Exception:
612
+ # Deterministic auto-start before nagging: a stopped server is a
613
+ # hook-fixable condition, not something to delegate to the model.
614
+ started = False
615
+ try:
616
+ from _brain_port import ensure_server
617
+ started = ensure_server(wait_secs=2.0)
618
+ except Exception:
619
+ pass
620
+ if not started:
621
+ print(
622
+ "[wicked-brain] Brain server is not running on port "
623
+ f"{_resolve_brain_port()} and auto-start failed.\n"
624
+ "wicked-garden requires wicked-brain for context assembly and memory.\n"
625
+ "Brain skills auto-start the server on every call — do NOT skip "
626
+ "brain usage. Diagnose with: wicked-brain-call --start. "
627
+ "If not installed: claude plugin install wicked-brain --scope project",
628
+ file=sys.stderr,
629
+ )
630
+
631
+
632
+ # v11: _assemble_current_chain, _consume_facilitator_reeval, and
633
+ # _consume_phase_start_gate were removed. They referenced deleted scripts
634
+ # (current_chain.py, propose-process skill, phase_start_gate.py) that lived
635
+ # in the v6 universal-pipeline machinery. v11 archetypes handle re-eval
636
+ # inside their own playbooks.
637
+
638
+ def _check_onboarding_gate(prompt: str) -> str | None:
639
+ """Check if the current project needs onboarding.
640
+
641
+ Returns an onboarding directive string to inject if onboarding is needed,
642
+ or None if onboarding is complete or cannot be determined.
643
+
644
+ Unlike the setup gate, this does NOT hard-block — it returns a directive
645
+ that gets injected into the system message so the model runs setup.
646
+ Uses session state set by bootstrap.py to avoid re-checking on every turn.
647
+ """
648
+ stripped = prompt.strip().lower()
649
+
650
+ # Exemption: setup/help commands handle their own onboarding flow
651
+ if stripped.startswith(_GUARD_PASS_PREFIXES):
652
+ return None
653
+
654
+ try:
655
+ from _session import SessionState
656
+ state = SessionState.load()
657
+
658
+ # If setup is in progress, don't block with onboarding directive
659
+ if state.setup_in_progress:
660
+ return None
661
+
662
+ # If onboarding is already confirmed complete, nothing to do
663
+ if state.onboarding_complete:
664
+ return None
665
+
666
+ # If bootstrap flagged that onboarding is needed, enforce it
667
+ if state.needs_onboarding:
668
+ project = os.environ.get("CLAUDE_PROJECT_NAME") or Path.cwd().name
669
+ return (
670
+ f"[Action Required] Project '{project}' has not been onboarded.\n"
671
+ "You MUST immediately invoke the Skill tool with skill='wicked-garden:setup' "
672
+ "to launch the interactive onboarding wizard.\n"
673
+ "Do NOT ask the user for confirmation — invoke the skill now.\n"
674
+ "Do NOT respond with text first — invoke the skill as your first action."
675
+ )
676
+ except Exception:
677
+ pass # Fail open — don't block on errors
678
+
679
+ return None
680
+
681
+
682
+
683
+ # ---------------------------------------------------------------------------
684
+ # v10 Phase 1 (#813) — intent variable
685
+ # ---------------------------------------------------------------------------
686
+ # The intent variable names what five overlapping classifiers were collectively
687
+ # detecting. Auto-detected on turn 1-2 from existing complexity/risk signals,
688
+ # sticky for the session, overridable via /wicked-garden:intent. Hooks gate
689
+ # directive emission on intent. Design: brainstorms/v10-session-01-intent-and-hook-gating.md.
690
+
691
+ _INTENT_VALUES = ("simple-edit", "feature", "rigor", "research")
692
+ _AUTO_DETECT_TURN_LIMIT = 2 # auto-detect runs only on turns ≤ this; sticky after
693
+
694
+ # Crew/rigor command prefixes — invocations that warrant rigor regardless of
695
+ # prompt complexity (answer to brainstorm Q3: rely on auto-detection signals
696
+ # rather than per-command self-declare ceremony).
697
+ _RIGOR_COMMAND_PREFIXES = (
698
+ "/wicked-garden:crew:",
699
+ "/wg-issue",
700
+ "/wg-test",
701
+ )
702
+
703
+ # Research signal phrases — explanation/analysis prompts that route to research
704
+ # directive even at low complexity.
705
+ _RESEARCH_SIGNALS = (
706
+ "explain how", "explain why", "why does", "how does", "what is the",
707
+ "describe the", "walk me through", "analyze", "investigate",
708
+ )
709
+
710
+
711
+ def _detect_intent(prompt: str, complexity: float, is_risky: bool, state) -> str:
712
+ """Auto-detect session intent from prompt + complexity/risk signals.
713
+
714
+ Called once per session on turn 1-2. Result is sticky — subsequent turns
715
+ read state.intent directly. User overrides via /wicked-garden:intent skill
716
+ set state.intent_explicit=True.
717
+
718
+ Detection priority:
719
+ 1. Rigor commands (/wicked-garden:crew:*, etc.) → rigor
720
+ 2. Research signals (explain how, why does, walk me through) → research
721
+ 3. Risk OR complexity ≥ threshold OR technical verb → feature
722
+ 4. Default → simple-edit (conservative; user can /wicked-garden:intent up)
723
+ """
724
+ if not prompt or not prompt.strip():
725
+ return "simple-edit"
726
+
727
+ prompt_lower = prompt.lower().strip()
728
+
729
+ # 1. Explicit rigor invocations (crew/wg-issue/wg-test commands)
730
+ if any(prompt_lower.startswith(p) for p in _RIGOR_COMMAND_PREFIXES):
731
+ return "rigor"
732
+
733
+ # 2. Research signals (explanation/analysis intent)
734
+ if any(s in prompt_lower for s in _RESEARCH_SIGNALS):
735
+ return "research"
736
+
737
+ # 3. Feature signals (risk, complexity, technical verbs)
738
+ if is_risky or complexity >= _SYNTHESIS_THRESHOLD:
739
+ return "feature"
740
+ if _has_imperative_technical_verb(prompt_lower):
741
+ return "feature"
742
+ if _has_file_path(prompt) or _has_code_markers(prompt):
743
+ return "feature"
744
+
745
+ # 4. Default conservative classification
746
+ return "simple-edit"
747
+
748
+
749
+ def _ensure_intent_set(prompt: str, state, complexity: float, is_risky: bool) -> str:
750
+ """Resolve session intent, auto-detecting if not yet set.
751
+
752
+ Public entrypoint that callers in main() use to populate
753
+ ``state.intent`` for a turn. On turn ≤ _AUTO_DETECT_TURN_LIMIT and
754
+ intent is None → detect via ``_detect_intent``, persist via
755
+ ``state.update``, return. On later turns or when intent is already
756
+ set → return existing value. Never overwrites an explicit override.
757
+
758
+ Returns the effective intent value (always a string from _INTENT_VALUES).
759
+ """
760
+ existing = getattr(state, "intent", None) if state else None
761
+ if existing in _INTENT_VALUES:
762
+ return existing
763
+
764
+ turn = getattr(state, "turn_count", 0) if state else 0
765
+ if turn > _AUTO_DETECT_TURN_LIMIT and existing is None:
766
+ # Past the auto-detect window with no intent set — fall back to
767
+ # conservative default rather than re-classify mid-session.
768
+ return "simple-edit"
769
+
770
+ detected = _detect_intent(prompt, complexity, is_risky, state)
771
+ try:
772
+ if state:
773
+ state.update(intent=detected, intent_explicit=False)
774
+ except Exception:
775
+ pass # fail open — return value still usable for this turn
776
+ return detected
777
+
778
+
779
+ def _build_intent_directive(intent: str, turn_count: int, explicit: bool, state=None) -> str:
780
+ """Build the system-reminder directive for a given intent.
781
+
782
+ Output rules (brainstorm decision):
783
+ simple-edit + auto-detect → empty string (silence is the signal)
784
+ simple-edit + explicit → bare label only ("user said simple-edit")
785
+ feature → synthesis directive (+ label if explicit)
786
+ research → synthesis directive (+ label if explicit)
787
+ rigor → synthesis directive + chain context (+ label if explicit)
788
+
789
+ The label-only-on-explicit rule prevents confirmation-bias drift —
790
+ auto-detected intent stays invisible to the model so it doesn't
791
+ second-guess the framework's silent classification.
792
+ """
793
+ label = f'<wg intent="{intent}" t={turn_count} />' if explicit else ""
794
+
795
+ if intent == "simple-edit":
796
+ return label # empty when auto-detected
797
+
798
+ # synthesis directive shared by feature / research / rigor
799
+ directive_lines = [
800
+ f"[Context Assembly — intent={intent}] Deep context needed before answering.",
801
+ "BEFORE drafting a response:",
802
+ "1. Call wicked-brain:query for conceptual grounding ('how does X work', "
803
+ "'what are the constraints around Y').",
804
+ "2. Call wicked-brain:search for specific symbols, files, or past decisions. "
805
+ "Drill into wiki hits with wicked-brain:read depth=2.",
806
+ ]
807
+ if intent == "rigor":
808
+ directive_lines.append(
809
+ "3. If results cite a wicked-garden:crew project, check its phase and "
810
+ "active_chain_id before committing to an approach."
811
+ )
812
+ directive_lines.append("Only after grounding is complete, answer the original prompt.")
813
+
814
+ directive = "\n".join(directive_lines)
815
+ if label:
816
+ return f"{label}\n{directive}"
817
+ return directive
818
+
819
+
820
+ # ---------------------------------------------------------------------------
821
+ # WIP recovery after context compaction
822
+ # ---------------------------------------------------------------------------
823
+
824
+ def _build_wip_recovery_block(session_id: str, project: str) -> str:
825
+ """Build a WIP recovery block if compaction just happened.
826
+
827
+ v6: the v5 compaction-detection path (PressureTracker.was_just_compacted +
828
+ HistoryCondenser wip_snapshot) was deleted with smaht/v2 in #428. For v6
829
+ we rely on native in-progress tasks as the sole WIP source — if the task
830
+ dir is empty we emit nothing. Acceptable per gate4-deletion-manifest.md §5.2
831
+ ("compaction is rare and facilitator re-evaluate-on-TaskCompleted covers
832
+ the crew case").
833
+
834
+ Returns a formatted markdown block listing in-progress tasks, or an
835
+ empty string when there's no signal worth showing.
836
+ """
837
+ # v6: no compaction-detection; always try to surface native in-progress
838
+ # tasks. Fail-open to empty string on any error.
839
+ wip_data = None
840
+
841
+ # --- Source 3: Native in-progress tasks (optional, fail gracefully) ---
842
+ # Reads the same store SubagentStart uses for procedure-bundle lookup.
843
+ # Routing: WG_DAEMON_ENABLED=false → direct file read (unchanged);
844
+ # WG_DAEMON_ENABLED=true → daemon HTTP with fallback (#596 v8-PR-2).
845
+ in_progress_tasks = []
846
+ try:
847
+ session_id = os.environ.get("CLAUDE_SESSION_ID", "")
848
+ if session_id:
849
+ from crew._task_reader import list_in_progress_tasks # type: ignore[import]
850
+ in_progress_tasks = list_in_progress_tasks(session_id, limit=5)
851
+ except Exception:
852
+ pass # task query is best-effort
853
+
854
+ # --- Format recovery block (budget: <1000 chars) ---
855
+ # v6: only surface in-progress tasks. The v5 ticket-rail fields
856
+ # (current_task, decisions, file_scope, active_constraints, open_questions)
857
+ # are gone. If nothing is in-progress, emit nothing.
858
+ if not in_progress_tasks:
859
+ _log("smaht", "debug", "wip_recovery.empty")
860
+ return ""
861
+
862
+ parts = ["## [Recovery] Work in progress\n"]
863
+ parts.append("### In-Progress Tasks")
864
+ for task in in_progress_tasks:
865
+ parts.append(f"- {task[:80]}")
866
+ parts.append("\n**IMPORTANT**: Review before proceeding. Do NOT repeat completed work.")
867
+
868
+ block = "\n".join(parts)
869
+
870
+ # Enforce budget: truncate to ~1000 chars if needed
871
+ if len(block) > 1000:
872
+ block = block[:997] + "..."
873
+
874
+ _log("smaht", "debug", "wip_recovery.done", detail={"chars": len(block)})
875
+ return block
876
+
877
+
878
+ # ---------------------------------------------------------------------------
879
+ # Main
880
+ # ---------------------------------------------------------------------------
881
+
882
+ def main():
883
+ _t0 = time.monotonic()
884
+ _log("smaht", "debug", "hook.start")
885
+
886
+ try:
887
+ raw = sys.stdin.read()
888
+ input_data = json.loads(raw) if raw.strip() else {}
889
+ except Exception:
890
+ print(json.dumps({"continue": True}))
891
+ return
892
+
893
+ prompt = input_data.get("prompt", "")
894
+ session_id = input_data.get("session_id", os.environ.get("CLAUDE_SESSION_ID", "default"))
895
+ project = os.environ.get("CLAUDE_PROJECT_NAME") or Path.cwd().name
896
+
897
+ if not prompt.strip():
898
+ print(json.dumps({"continue": True}))
899
+ return
900
+
901
+ # Setup gate — hard-block (sys.exit(2)) if no config.
902
+ # MUST run before HOT continuations so setup can never be bypassed.
903
+ _check_setup_gate(prompt)
904
+
905
+ # Brain gate — soft directive if brain server is not reachable.
906
+ # Runs after setup gate (brain irrelevant before setup completes).
907
+ # Does NOT hard-block — brain server may be starting up; user can start it.
908
+ _check_brain_gate(prompt)
909
+
910
+ # Onboarding gate — inject directive if project hasn't been onboarded.
911
+ # Checked after setup gate but before HOT path so continuations during
912
+ # an active setup wizard ("yes", "ok") still pass through quickly.
913
+ onboarding_directive = _check_onboarding_gate(prompt)
914
+
915
+ # Issue #572: drain the wicked-bus cursor BEFORE the re-eval check so the
916
+ # 41k+ lag clears and stale subscriptions don't accumulate further. The
917
+ # drain is fire-and-forget (events discarded, cursor advanced); the re-eval
918
+ # signal still comes from task_completed.py's debounced flag, not the bus.
919
+ # Wrapped in try/except so a bus failure can never break prompt submission.
920
+ try:
921
+ _drain_bus_cursor()
922
+ except Exception:
923
+ pass
924
+
925
+ # v6 Gate 3 (#428) — facilitator re-evaluation directive.
926
+ # v11: legacy facilitator re-eval + phase-start-gate consumers removed.
927
+ # Those were tied to the v6 universal pipeline (propose-process + the
928
+ # phase_start_gate.py script, both deleted in v11). v11 archetypes own
929
+ # their own checkpoint cadence in their playbooks.
930
+ facilitator_reeval_directive = None
931
+ phase_start_gate_directive = None
932
+
933
+ # ---------------------------------------------------------------------------
934
+ # HOT fast-exit: known continuation tokens bypass all imports and any
935
+ # downstream classification. Keeps HOT path p95 well under 100ms SLO.
936
+ # These words carry no new intent — there is nothing for context assembly
937
+ # to add beyond what is already in the conversation window.
938
+ #
939
+ # v6 note: the v5 CONTINUATION_PATTERNS in scripts/smaht/v2/router.py was
940
+ # deleted in #428. This in-hook fast-exit is now the sole continuation
941
+ # detector — it's just a set membership check, no regex needed.
942
+ # ---------------------------------------------------------------------------
943
+ _HOT_CONTINUATIONS = frozenset({
944
+ "yes", "ok", "okay", "sure", "yep", "yup",
945
+ "continue", "proceed", "go", "go ahead", "do it",
946
+ "lgtm", "looks good", "approved", "approve",
947
+ "no", "nope", "cancel", "stop", "skip",
948
+ "next", "done",
949
+ })
950
+ if prompt.strip().lower() in _HOT_CONTINUATIONS:
951
+ # v6: no history condenser to update. The HOT path is a pure no-op
952
+ # bypass — session counter increment still happens via the normal
953
+ # path for non-continuation turns.
954
+
955
+ # Even on HOT path, inject onboarding directive + facilitator re-eval
956
+ # directive so the gate + re-eval can never be bypassed via continuation
957
+ # tokens. Re-eval must fire before the user's acknowledgement drives
958
+ # the next step on a facilitator-owned chain.
959
+ hot_parts = []
960
+ if facilitator_reeval_directive:
961
+ hot_parts.append(facilitator_reeval_directive)
962
+ if phase_start_gate_directive:
963
+ hot_parts.append(phase_start_gate_directive)
964
+ if onboarding_directive:
965
+ hot_parts.append(onboarding_directive)
966
+ if hot_parts:
967
+ print(json.dumps({
968
+ "hookSpecificOutput": {
969
+ "hookEventName": "UserPromptSubmit",
970
+ "additionalContext": "\n\n".join(hot_parts),
971
+ },
972
+ "continue": True,
973
+ }))
974
+ else:
975
+ print(json.dumps({"continue": True}))
976
+ return
977
+
978
+ try:
979
+ from _session import SessionState
980
+ state = SessionState.load()
981
+ except Exception:
982
+ state = None
983
+
984
+ turn_count = _increment_turn(state)
985
+
986
+ # Session goal capture on turns 1-2
987
+ _capture_session_goal(prompt, turn_count, project, session_id)
988
+
989
+ # Complexity + risk gate — inject a deep-context pull directive for complex/risky prompts.
990
+ # v6: the v5 Router + Orchestrator pre-fetch were deleted in #428. Classification
991
+ # is an inline heuristic; v6.3.6 retired the wicked-garden:smaht:synthesize skill
992
+ # and replaced it with a directive pointing at wicked-brain directly.
993
+ # v10 Phase 1 (#813): replace the legacy 5-classifier cascade
994
+ # (complexity scorer → context-assembly classifier → near-HOT guard →
995
+ # synthesis early-return) with a single intent variable. Intent gates
996
+ # directive emission downstream — see _ensure_intent_set + _build_intent_directive.
997
+ if not onboarding_directive:
998
+ complexity, is_risky = _score_complexity_and_risk(prompt, state)
999
+ intent = _ensure_intent_set(prompt, state, complexity, is_risky)
1000
+ _log("prompt", "debug", "intent.resolved",
1001
+ detail={
1002
+ "intent": intent,
1003
+ "explicit": bool(getattr(state, "intent_explicit", False)),
1004
+ "complexity": round(complexity, 2),
1005
+ "risk": is_risky,
1006
+ "turn": turn_count,
1007
+ })
1008
+
1009
+ try:
1010
+ # --- v10 Phase 1 (#813): intent-driven directive ---
1011
+ # The intent variable resolved earlier (or "simple-edit" when this
1012
+ # block runs from the onboarding-pending branch where no intent was
1013
+ # computed) drives directive emission. Auto-detected simple-edit
1014
+ # turns produce an empty directive; feature/research/rigor produce
1015
+ # the synthesis directive. Explicit overrides additionally echo a
1016
+ # bare label so the model knows the user steered the framework.
1017
+ _intent = getattr(state, "intent", None) if state else None
1018
+ _intent_explicit = bool(getattr(state, "intent_explicit", False)) if state else False
1019
+ intent_directive = _build_intent_directive(
1020
+ _intent or "simple-edit", turn_count, _intent_explicit, state
1021
+ )
1022
+
1023
+ # WIP recovery: surface native in-progress tasks (v5 ticket rail gone)
1024
+ wip_block = _build_wip_recovery_block(session_id, project)
1025
+
1026
+ # Build output: single <system-reminder> block with all context parts
1027
+ all_parts = []
1028
+
1029
+ # v6 Gate 3 (#428): facilitator re-evaluation directive has highest
1030
+ # priority — a completed facilitator-owned task must re-plan before
1031
+ # the model addresses the user's prompt.
1032
+ if facilitator_reeval_directive:
1033
+ all_parts.append(facilitator_reeval_directive)
1034
+
1035
+ # AC-11: phase-start gate directive — fires when a phase-transition
1036
+ # event was detected and material changes exist since last re-eval.
1037
+ if phase_start_gate_directive:
1038
+ all_parts.append(phase_start_gate_directive)
1039
+
1040
+ # WIP recovery block goes first so it's the first thing the model sees
1041
+ if wip_block:
1042
+ all_parts.append(wip_block)
1043
+
1044
+ # Onboarding directive goes next for high priority
1045
+ if onboarding_directive:
1046
+ all_parts.append(onboarding_directive)
1047
+
1048
+ # Intent directive (may be empty for auto-detected simple-edit — that
1049
+ # is the v10 win: silence is a signal that the turn is simple).
1050
+ if intent_directive:
1051
+ all_parts.append(intent_directive)
1052
+
1053
+ # v11: work-shape archetype steering. Skips on simple-edit per the
1054
+ # silence-is-the-signal convention. When the prompt routes to a
1055
+ # specific archetype (build / migrate / incident / specify / ...),
1056
+ # emits a slim tag so the agent knows the work shape it's in.
1057
+ archetype_directive = _build_archetype_directive(
1058
+ prompt, _intent or "simple-edit", state=state,
1059
+ )
1060
+ if archetype_directive:
1061
+ all_parts.append(archetype_directive)
1062
+
1063
+ # NOTE (v9.2.11): the periodic [Memory] checkpoint nudge that fired
1064
+ # every 10 turns was removed. It surfaced on every prompt submit at
1065
+ # turns 10, 20, 30 ... regardless of whether the session had produced
1066
+ # anything worth checkpointing — exactly the false-urgency anti-
1067
+ # pattern the v9.2.6 "silent contract drift" wiki article warns
1068
+ # against. CLAUDE.md tells Claude how to use wicked-brain:memory; the
1069
+ # session-teardown agent handles end-of-session synthesis. A turn-
1070
+ # counter timer added no signal.
1071
+
1072
+ # Jam suggestion: when ambiguity signals present
1073
+ jam_hint = _suggest_jam(prompt, state)
1074
+ if jam_hint:
1075
+ all_parts.append(jam_hint)
1076
+
1077
+ # (Prompt-time discovery tips retired — ambient suggestions trained the
1078
+ # agent to ignore the channel; claim-time sentinel signals replaced them.)
1079
+
1080
+ # If nothing material to inject (simple-edit, no WIP, no onboarding,
1081
+ # no hints), suppress the entire <system-reminder> block — this is
1082
+ # the keystone "improves-me" win: silent turns stay silent.
1083
+ if not all_parts:
1084
+ print(json.dumps({"continue": True}))
1085
+ return
1086
+
1087
+ merged_context = f"<system-reminder>\n{chr(10).join(all_parts)}\n</system-reminder>"
1088
+
1089
+ _log("smaht", "debug", "prompt.intent_directive",
1090
+ detail={"intent": _intent, "explicit": _intent_explicit, "turn": turn_count,
1091
+ "directive_bytes": len(intent_directive.encode("utf-8"))})
1092
+
1093
+ output = {
1094
+ "hookSpecificOutput": {
1095
+ "hookEventName": "UserPromptSubmit",
1096
+ "additionalContext": merged_context,
1097
+ },
1098
+ "continue": True,
1099
+ }
1100
+ _log("smaht", "debug", "hook.end", ms=int((time.monotonic() - _t0) * 1000))
1101
+ print(json.dumps(output))
1102
+
1103
+ except Exception as e:
1104
+ print(f"[wicked-garden] prompt_submit error: {e}", file=sys.stderr)
1105
+ print(json.dumps({"continue": True}))
1106
+
1107
+
1108
+ if __name__ == "__main__":
1109
+ main()