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,324 @@
1
+ """scripts/_heavy_cadence.py — session-end heavy cadence work + 60-min stop fallback.
2
+
3
+ Provenance: v9.2.15 redesign of stop.py per-turn cadence. Pre-fix, four heavy
4
+ functions ran on EVERY Stop hook (per turn end):
5
+
6
+ - _run_memory_decay (brain `lint`)
7
+ - _run_working_consolidation (brain `compile` + `lint`, 10s heaviest call)
8
+ - _run_quality_telemetry (timeline.jsonl append + drift bus event)
9
+ - _run_guard_pipeline (scalpel/standard profile + findings.json + bus)
10
+
11
+ Per-turn cadence costs:
12
+ ~90 brain HTTP calls per 30-turn session (vs 3 if session-end-scoped)
13
+ 30 timeline.jsonl appends (vs 1)
14
+ 30 findings.json overwrites (vs 1; mid-session signal lost)
15
+ 4-session drift baseline → 4-turn baseline (CORRECTNESS BUG, not just perf)
16
+
17
+ The v9.2.11 audit + v9.2.15 quick brainstorm settled on Option C (hybrid):
18
+ - SessionEnd hook is the primary cadence (new in this PR — was unwired).
19
+ - stop.py keeps a 60-min time-gated fallback for the partial-session
20
+ failure mode (CLI killed, network drop, user walks away — SessionEnd
21
+ never fires).
22
+
23
+ A sidecar file at <local store>/wicked-garden/heavy-cadence/last_run.json
24
+ records `last_heavy_run_ts` + `trigger` so the Stop fallback is deterministic.
25
+ SessionState is per-session (resets on new session), so it can't gate cross-
26
+ session fallback — sidecar is the right persistence boundary.
27
+
28
+ Brain calls themselves are unchanged. Only WHEN they fire changed.
29
+
30
+ v9.2.15 council mitigation (3-of-4 reviewers cited TOCTOU race as top risk):
31
+ sidecar writes use the write-temp-then-os.replace pattern instead of bare
32
+ write_text. os.replace is atomic on both POSIX and Windows, so two parallel
33
+ Stop calls passing the should_run_fallback() check simultaneously cannot
34
+ produce a corrupt sidecar JSON. The heavy operations themselves are
35
+ idempotent (per the inventory), so duplicate runs waste work but don't
36
+ corrupt state.
37
+
38
+ R1: no dead code — every helper is called from main flow + tests.
39
+ R3: constants named (FALLBACK_INTERVAL_SECS, SIDECAR_FILENAME).
40
+ R5: subprocess-free in module body (subprocess only via _brain_api).
41
+ """
42
+ from __future__ import annotations
43
+
44
+ import json
45
+ import os
46
+ import sys
47
+ import time
48
+ from datetime import datetime, timezone
49
+ from pathlib import Path
50
+ from typing import List, Optional
51
+
52
+ # 60-minute fallback interval. Picked because:
53
+ # - Long enough that a normal multi-turn session never crosses it (typical
54
+ # "I'm doing real work" sessions complete in <30 min and SessionEnd fires).
55
+ # - Short enough that abruptly-killed sessions get caught up the next time
56
+ # a Stop fires after the user resumes work — no >1hr gap in decay/
57
+ # consolidation activity.
58
+ # - Same magnitude as wicked-brain's own stale-content thresholds so brain
59
+ # decay decisions remain timely even under fallback.
60
+ FALLBACK_INTERVAL_SECS: int = 60 * 60
61
+
62
+ SIDECAR_DIR_NAME: str = "heavy-cadence"
63
+ SIDECAR_FILENAME: str = "last_run.json"
64
+
65
+ # Trigger strings recorded in the sidecar — surfaced in observability if we
66
+ # ever need to debug "why did decay fire here". Not load-bearing for behavior.
67
+ TRIGGER_SESSION_END: str = "session_end"
68
+ TRIGGER_STOP_FALLBACK: str = "stop_fallback"
69
+
70
+
71
+ def _utc_iso_now() -> str:
72
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
73
+
74
+
75
+ def _sidecar_path() -> Optional[Path]:
76
+ """Resolve the per-project sidecar file path. None on resolution failure.
77
+
78
+ Per-project automatic because get_local_path("wicked-garden", ...) is
79
+ scoped under the active project's slug. A user with multiple projects
80
+ gets one sidecar per project — fallback gating is correctly isolated.
81
+ """
82
+ try:
83
+ from _domain_store import get_local_path # type: ignore
84
+ return get_local_path("wicked-garden", SIDECAR_DIR_NAME) / SIDECAR_FILENAME
85
+ except Exception:
86
+ return None
87
+
88
+
89
+ def _read_sidecar() -> dict:
90
+ """Return the sidecar dict or {} if missing/corrupt. Best-effort, no raise."""
91
+ p = _sidecar_path()
92
+ if p is None or not p.exists():
93
+ return {}
94
+ try:
95
+ return json.loads(p.read_text(encoding="utf-8"))
96
+ except (json.JSONDecodeError, OSError):
97
+ return {}
98
+
99
+
100
+ def _write_sidecar(trigger: str, session_id: Optional[str] = None) -> None:
101
+ """Record `last_heavy_run_ts` + trigger. Best-effort, no raise.
102
+
103
+ Atomic write via temp-file + os.replace (v9.2.15 council mitigation,
104
+ 3-of-4 reviewers cited TOCTOU race as top risk):
105
+
106
+ 1. Write payload to `<sidecar>.tmp.<pid>` (PID disambiguates parallel
107
+ writers — two processes won't clobber each other's temp files).
108
+ 2. `os.replace(tmp, sidecar)` — atomic on both POSIX and Windows.
109
+ Either the old sidecar OR the new sidecar is observable by any
110
+ concurrent reader; never a half-written file.
111
+
112
+ Heavy operations are idempotent (decay is set-difference, compile is
113
+ deterministic given input chunks, telemetry appends are tagged with
114
+ session_id, guard overwrites a session-scoped findings file), so two
115
+ parallel runs waste work but cannot corrupt domain state.
116
+ """
117
+ p = _sidecar_path()
118
+ if p is None:
119
+ return
120
+ try:
121
+ p.parent.mkdir(parents=True, exist_ok=True)
122
+ payload = json.dumps({
123
+ "last_heavy_run_ts": _utc_iso_now(),
124
+ "trigger": trigger,
125
+ "session_id": session_id or "",
126
+ })
127
+ # PID-disambiguated temp path so two parallel writers don't collide
128
+ # on the same temp file before either calls os.replace.
129
+ tmp = p.with_name(f"{p.name}.tmp.{os.getpid()}")
130
+ tmp.write_text(payload, encoding="utf-8")
131
+ os.replace(tmp, p)
132
+ except OSError:
133
+ # Best-effort cleanup of orphaned temp file. Failure to clean up is
134
+ # not load-bearing — the next successful write displaces it.
135
+ try:
136
+ tmp.unlink(missing_ok=True) # type: ignore[name-defined]
137
+ except (NameError, OSError):
138
+ pass
139
+
140
+
141
+ def should_run_fallback(now_ts: Optional[float] = None) -> bool:
142
+ """Return True if Stop should run the heavy cadence as a fallback.
143
+
144
+ Triggers True when:
145
+ - sidecar file missing (first run ever for this project)
146
+ - sidecar exists but `last_heavy_run_ts` is unparseable
147
+ - last run was more than FALLBACK_INTERVAL_SECS ago
148
+
149
+ Defaults False when the sidecar is fresh — most Stop calls are per-turn
150
+ and should NOT trigger heavy work.
151
+ """
152
+ data = _read_sidecar()
153
+ raw_ts = data.get("last_heavy_run_ts")
154
+ if not raw_ts:
155
+ return True # never run — fire fallback to seed the sidecar
156
+ try:
157
+ last_dt = datetime.fromisoformat(raw_ts.replace("Z", "+00:00"))
158
+ except (ValueError, AttributeError):
159
+ return True # corrupt timestamp — treat as never-run
160
+ now_dt = datetime.fromtimestamp(now_ts, tz=timezone.utc) if now_ts else datetime.now(timezone.utc)
161
+ elapsed = (now_dt - last_dt).total_seconds()
162
+ return elapsed >= FALLBACK_INTERVAL_SECS
163
+
164
+
165
+ def run_heavy_cadence(trigger: str, session_id: Optional[str] = None,
166
+ plugin_root: Optional[Path] = None) -> List[str]:
167
+ """Run all four heavy functions + record the run in the sidecar.
168
+
169
+ Used by:
170
+ - hooks/scripts/session_end.py (primary, trigger=session_end)
171
+ - hooks/scripts/stop.py (fallback when should_run_fallback() True,
172
+ trigger=stop_fallback)
173
+
174
+ Returns aggregated message strings (decay + consolidation + telemetry +
175
+ guard) suitable for a systemMessage emit. Each underlying function fails
176
+ open — a brain outage doesn't block the others.
177
+ """
178
+ if plugin_root is None:
179
+ # Default — used when called from a hook. Tests pass plugin_root.
180
+ plugin_root = Path(__file__).resolve().parents[1]
181
+
182
+ messages: List[str] = []
183
+ messages.extend(_run_memory_consolidation(plugin_root))
184
+ messages.extend(_run_memory_decay(plugin_root))
185
+ messages.extend(_run_quality_telemetry(plugin_root, session_id or ""))
186
+ messages.extend(_run_guard_pipeline(plugin_root))
187
+
188
+ _write_sidecar(trigger, session_id=session_id)
189
+ return messages
190
+
191
+
192
+ # ---------------------------------------------------------------------------
193
+ # The four heavy functions — extracted from stop.py and unchanged in behavior.
194
+ # Brain APIs called identically; what changed is only WHEN they fire.
195
+ # ---------------------------------------------------------------------------
196
+
197
+ def _brain_api_call(plugin_root: Path, action: str, params: dict, timeout: int = 5):
198
+ """Lightweight brain HTTP wrapper — same shape as stop.py::_brain_api but
199
+ importable without pulling in stop.py's hook-specific dependencies.
200
+ """
201
+ try:
202
+ sys.path.insert(0, str(plugin_root / "scripts"))
203
+ from _brain_port import resolve_port
204
+ import urllib.request
205
+ import urllib.error
206
+
207
+ port = resolve_port()
208
+ url = f"http://localhost:{port}/api/{action}"
209
+ data = json.dumps(params or {}).encode("utf-8")
210
+ req = urllib.request.Request(
211
+ url, data=data, headers={"Content-Type": "application/json"}, method="POST"
212
+ )
213
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
214
+ return json.loads(resp.read().decode("utf-8"))
215
+ except Exception:
216
+ return None
217
+
218
+
219
+ def _run_memory_decay(plugin_root: Path) -> List[str]:
220
+ """Run decay maintenance via brain lint API. Idempotent."""
221
+ messages: List[str] = []
222
+ try:
223
+ result = _brain_api_call(plugin_root, "lint", {}, timeout=5)
224
+ if result and (result.get("archived", 0) > 0 or result.get("deleted", 0) > 0):
225
+ messages.append(
226
+ f"[Memory] Decay: {result.get('archived', 0)} archived, {result.get('deleted', 0)} cleaned"
227
+ )
228
+ except Exception as e:
229
+ print(f"[wicked-garden] decay error: {e}", file=sys.stderr)
230
+ return messages
231
+
232
+
233
+ def _run_memory_consolidation(plugin_root: Path) -> List[str]:
234
+ """Consolidate working-tier memories via brain compile + lint.
235
+
236
+ Heaviest call in the cadence (10s timeout on compile). Idempotent —
237
+ same chunks won't re-synthesize the same wiki article.
238
+ """
239
+ messages: List[str] = []
240
+ try:
241
+ compile_result = _brain_api_call(plugin_root, "compile", {}, timeout=10)
242
+ lint_result = _brain_api_call(plugin_root, "lint", {}, timeout=5)
243
+ compiled = compile_result.get("compiled", 0) if compile_result else 0
244
+ cleaned = lint_result.get("deleted", 0) if lint_result else 0
245
+ if compiled > 0 or cleaned > 0:
246
+ messages.append(
247
+ f"[Memory] Consolidation: {compiled} compiled, {cleaned} cleaned"
248
+ )
249
+ except Exception as e:
250
+ print(f"[wicked-garden] consolidation error: {e}", file=sys.stderr)
251
+ return messages
252
+
253
+
254
+ def _run_quality_telemetry(plugin_root: Path, session_id: str) -> List[str]:
255
+ """Append a timeline record + detect drift. Returns any messages.
256
+
257
+ Per-session-end cadence is REQUIRED for correctness — per-turn capture
258
+ inflates timeline.jsonl 30x and reduces the 4-session drift baseline to
259
+ a 4-turn baseline (signal dilution).
260
+ """
261
+ messages: List[str] = []
262
+ try:
263
+ sys.path.insert(0, str(plugin_root / "scripts" / "delivery"))
264
+ import telemetry as _telemetry # type: ignore
265
+ import drift as _drift # type: ignore
266
+
267
+ record = _telemetry.capture_session(session_id)
268
+ if record is None:
269
+ return messages
270
+
271
+ project = record.get("project") or "_global"
272
+ records = _telemetry.read_timeline(project)
273
+ cls = _drift.classify(records)
274
+ emitted = _drift.emit_drift_event(project, cls)
275
+ if cls.get("drift"):
276
+ messages.append(
277
+ f"[Telemetry] Drift detected for {project}: " + _drift.summarize(cls)
278
+ + (" (emitted to wicked-bus)" if emitted else "")
279
+ )
280
+ elif cls.get("zone") == "warn":
281
+ messages.append(
282
+ f"[Telemetry] Watch signal for {project}: " + _drift.summarize(cls)
283
+ )
284
+ except Exception as e:
285
+ print(f"[wicked-garden] telemetry error: {e}", file=sys.stderr)
286
+ return messages
287
+
288
+
289
+ def _run_guard_pipeline(plugin_root: Path) -> List[str]:
290
+ """Run the autonomous session-close guard pipeline.
291
+
292
+ Writes findings.json (consumed by next-session bootstrap briefing) and
293
+ emits wicked.guard.findings on the bus. Per-session-end cadence avoids
294
+ overwriting mid-session findings before bootstrap can read them.
295
+ """
296
+ try:
297
+ sys.path.insert(0, str(plugin_root / "scripts" / "platform"))
298
+ from guard_pipeline import ( # type: ignore
299
+ run_pipeline, write_briefing_file, emit_findings_event, render_summary,
300
+ )
301
+ except Exception as exc:
302
+ print(f"[wicked-garden] guard pipeline unavailable: {exc}", file=sys.stderr)
303
+ return []
304
+
305
+ try:
306
+ build_just_closed = False
307
+ try:
308
+ sys.path.insert(0, str(plugin_root / "scripts"))
309
+ from _session import SessionState # type: ignore
310
+ state = SessionState.load()
311
+ build_just_closed = bool(getattr(state, "last_phase_approved", None) == "build")
312
+ except Exception:
313
+ pass
314
+
315
+ report = run_pipeline(build_phase_just_closed=build_just_closed)
316
+ write_briefing_file(report)
317
+ emit_findings_event(report)
318
+
319
+ if report.total_findings > 0 or report.status != "ok":
320
+ return [render_summary(report)]
321
+ return []
322
+ except Exception as exc:
323
+ print(f"[wicked-garden] guard pipeline error: {exc}", file=sys.stderr)
324
+ return []
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ _integration_resolver.py — Integration tool resolution for DomainStore.
4
+
5
+ Resolves which external tool (if any) should be used for a given plugin domain.
6
+ Called by DomainStore._init_routing() during initialization, and also testable
7
+ in isolation.
8
+
9
+ Resolution order:
10
+ 1. ~/.something-wicked/wicked-garden/config.json key tool_preferences.{domain}
11
+ 2. Stored memory preference (tagged "tool-preference" + domain name)
12
+ 3. MCP discovery (stub — deferred until MCP-from-Python approach is determined)
13
+ 4. Interactive prompt if multiple matches (not reachable until step 3 is wired)
14
+
15
+ Returns:
16
+ Tool name string ("linear", "jira", "notion", …) or None for local-only.
17
+
18
+ stdlib-only — no external dependencies.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import sys
24
+ from pathlib import Path
25
+ from typing import Optional
26
+
27
+ # Config location — same root as the local data store
28
+ _CONFIG_PATH = Path.home() / ".something-wicked" / "wicked-garden" / "config.json"
29
+
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Public API
33
+ # ---------------------------------------------------------------------------
34
+
35
+
36
+ def resolve_tool(domain: str, hook_mode: bool = False) -> Optional[str]:
37
+ """Return the external tool name to use for *domain*, or None for local.
38
+
39
+ Args:
40
+ domain: Plugin domain, e.g. "wicked-crew", "wicked-garden:mem".
41
+ hook_mode: When True, skip discovery and return None immediately so
42
+ hook scripts stay within the 5-second timing budget.
43
+
44
+ Returns:
45
+ Tool name string or None (meaning: use local JSON storage).
46
+ """
47
+ # Import here to avoid a circular import at module load time — _domain_store
48
+ # imports this module, and DOMAIN_MCP_PATTERNS lives in _domain_store.
49
+ try:
50
+ _scripts_dir = str(Path(__file__).parent)
51
+ if _scripts_dir not in sys.path:
52
+ sys.path.insert(0, _scripts_dir)
53
+ from _domain_store import DOMAIN_MCP_PATTERNS
54
+ except ImportError:
55
+ DOMAIN_MCP_PATTERNS = {}
56
+
57
+ # Domains not in the pattern registry are always local-only
58
+ if domain not in DOMAIN_MCP_PATTERNS:
59
+ return None
60
+
61
+ # Hook scripts skip discovery to meet timing budgets
62
+ if hook_mode:
63
+ return None
64
+
65
+ # ── Step 1: explicit user preference in config.json ──────────────────
66
+ preference = _read_config_preference(domain)
67
+ if preference:
68
+ return preference
69
+
70
+ # ── Step 2: stored memory preference ─────────────────────────────────
71
+ mem_preference = _check_mem_preference(domain)
72
+ if mem_preference:
73
+ return mem_preference
74
+
75
+ # ── Step 3: MCP discovery (stub — always returns []) ─────────────────
76
+ matches = _discover_mcp_tools(domain)
77
+
78
+ # ── Step 4: interactive prompt when multiple tools are found ──────────
79
+ if len(matches) == 1:
80
+ tool = matches[0]
81
+ _store_preference(domain, tool)
82
+ return tool
83
+
84
+ if len(matches) > 1:
85
+ tool = _prompt_user_choice(domain, matches)
86
+ return tool
87
+
88
+ # No external tool found — use local JSON
89
+ return None
90
+
91
+
92
+ # ---------------------------------------------------------------------------
93
+ # Step 1 helpers
94
+ # ---------------------------------------------------------------------------
95
+
96
+
97
+ def _read_config_preference(domain: str) -> Optional[str]:
98
+ """Read tool_preferences.{domain} from config.json.
99
+
100
+ Returns the configured tool name, or None if not set or on any error.
101
+ """
102
+ try:
103
+ if not _CONFIG_PATH.exists():
104
+ return None
105
+ data = json.loads(_CONFIG_PATH.read_text(encoding="utf-8"))
106
+ prefs = data.get("tool_preferences")
107
+ if not isinstance(prefs, dict):
108
+ return None
109
+ value = prefs.get(domain)
110
+ if isinstance(value, str) and value:
111
+ return value
112
+ return None
113
+ except (json.JSONDecodeError, OSError):
114
+ return None
115
+
116
+
117
+ # ---------------------------------------------------------------------------
118
+ # Step 2 helpers
119
+ # ---------------------------------------------------------------------------
120
+
121
+
122
+ def _brain_api(action, params=None, timeout=3):
123
+ """Call brain API. Returns parsed JSON or None."""
124
+ try:
125
+ import urllib.request
126
+ from _brain_port import resolve_port
127
+ port = resolve_port()
128
+ payload = json.dumps({"action": action, "params": params or {}}).encode("utf-8")
129
+ req = urllib.request.Request(
130
+ f"http://localhost:{port}/api",
131
+ data=payload,
132
+ headers={"Content-Type": "application/json"},
133
+ method="POST",
134
+ )
135
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
136
+ return json.loads(resp.read().decode("utf-8"))
137
+ except Exception:
138
+ return None
139
+
140
+
141
+ def _check_mem_preference(domain: str) -> Optional[str]:
142
+ """Query brain for a previously stored tool preference.
143
+
144
+ Searches for memories tagged with "tool-preference" and the domain
145
+ name via the brain search API. Returns the tool name found in the
146
+ memory content, or None if not found or brain is unavailable.
147
+
148
+ Fails gracefully so callers that run before brain is initialized
149
+ (e.g. during onboarding) continue to work.
150
+ """
151
+ try:
152
+ result = _brain_api("search", {"query": f"tool-preference {domain}", "limit": 5}, timeout=3)
153
+ if not result:
154
+ return None
155
+
156
+ # Handle both list and dict response formats
157
+ results = result if isinstance(result, list) else result.get("results", [])
158
+ for r in results:
159
+ # Look for tool-preference memories that match this domain
160
+ content = r.get("content", "") or ""
161
+ path = r.get("path", "") or ""
162
+ if "tool-preference" in content and domain in content:
163
+ # Extract the tool name — it's the short content of the memory
164
+ # Try to read the actual chunk file for the definitive content
165
+ try:
166
+ chunk_path = Path.home() / ".wicked-brain" / path
167
+ if chunk_path.exists():
168
+ text = chunk_path.read_text(encoding="utf-8")
169
+ # Content is after the frontmatter and title
170
+ parts = text.split("---", 2)
171
+ if len(parts) >= 3:
172
+ body = parts[2].strip()
173
+ # Skip the title line (# ...)
174
+ body_lines = [l for l in body.splitlines() if l.strip() and not l.startswith("#")]
175
+ if body_lines:
176
+ tool = body_lines[-1].strip()
177
+ if tool:
178
+ return tool
179
+ except Exception:
180
+ pass # fail open
181
+ return None
182
+ except Exception:
183
+ # Brain may be unavailable — always fail gracefully
184
+ return None
185
+
186
+
187
+ # ---------------------------------------------------------------------------
188
+ # Step 3 helpers
189
+ # ---------------------------------------------------------------------------
190
+
191
+
192
+ def _discover_mcp_tools(domain: str) -> list:
193
+ """Discover MCP tools available for *domain*.
194
+
195
+ MCP invocation from Python is unresolved (see design/architecture.md
196
+ Open Questions). This is a stub that always returns an empty list.
197
+ Real discovery will be wired in a follow-on task once the approach is
198
+ determined.
199
+
200
+ Args:
201
+ domain: Plugin domain name (e.g. "wicked-crew").
202
+
203
+ Returns:
204
+ List of matching tool name strings (empty until real discovery is wired).
205
+ """
206
+ return []
207
+
208
+
209
+ # ---------------------------------------------------------------------------
210
+ # Step 4 helpers
211
+ # ---------------------------------------------------------------------------
212
+
213
+
214
+ def _prompt_user_choice(domain: str, matches: list) -> str:
215
+ """Interactively ask the user to choose from multiple matching tools.
216
+
217
+ Prints a numbered list to stdout and reads a line from stdin.
218
+ Stores the choice via _store_preference() and returns the selected tool.
219
+
220
+ Args:
221
+ domain: Plugin domain name.
222
+ matches: Non-empty list of discovered tool name strings.
223
+
224
+ Returns:
225
+ Selected tool name string.
226
+ """
227
+ # Guard against non-interactive contexts (hooks, dangerous mode, CI).
228
+ if not sys.stdin.isatty():
229
+ return _local_fallback_and_store(domain, matches)
230
+
231
+ print(f"\n[wicked-garden] Multiple integration tools found for {domain}:")
232
+ for idx, tool in enumerate(matches, start=1):
233
+ print(f" {idx}. {tool}")
234
+ print(f" {len(matches) + 1}. Use local storage (no external tool)")
235
+ print("Enter a number to select: ", end="", flush=True)
236
+
237
+ try:
238
+ raw = sys.stdin.readline().strip()
239
+ choice_num = int(raw)
240
+ except (ValueError, EOFError, OSError):
241
+ # Default to local on any input error
242
+ return _local_fallback_and_store(domain, matches)
243
+
244
+ if 1 <= choice_num <= len(matches):
245
+ tool = matches[choice_num - 1]
246
+ _store_preference(domain, tool)
247
+ return tool
248
+
249
+ # Choice was "local" or out of range — store "local" sentinel so we don't
250
+ # ask again this session; return None via the caller's None path.
251
+ # We can't return None here (return type is str), so store and return the
252
+ # first match; caller will treat any truthy return as an external tool.
253
+ # Instead, write a special "local" marker and return the first match as a
254
+ # no-op — the adapter will return None and fall through to local JSON.
255
+ # Realistically this branch is unreachable until _discover_mcp_tools returns
256
+ # real results, so correctness here is secondary to not crashing.
257
+ return _local_fallback_and_store(domain, matches)
258
+
259
+
260
+ def _local_fallback_and_store(domain: str, matches: list) -> str:
261
+ """Store 'local' preference and return the first match as a no-op tool name.
262
+
263
+ Internal helper: used when the user selects the 'local' option or
264
+ input parsing fails. Returns first match so the caller has a valid string;
265
+ the adapter's stub will return None → local JSON path is taken.
266
+ """
267
+ _store_preference(domain, "local")
268
+ # Return first match — stub adapters return None so local JSON is used anyway.
269
+ return matches[0] if matches else "local"
270
+
271
+
272
+ # ---------------------------------------------------------------------------
273
+ # Preference storage
274
+ # ---------------------------------------------------------------------------
275
+
276
+
277
+ def _store_preference(domain: str, tool: str) -> None:
278
+ """Persist a tool preference to brain as a memory chunk.
279
+
280
+ Writes a memory chunk with tags=["tool-preference", domain] so future
281
+ sessions can skip the discovery step.
282
+
283
+ Fails silently on any error so a broken brain never blocks writes.
284
+ """
285
+ try:
286
+ import uuid
287
+ import os as _os
288
+ from datetime import datetime, timezone
289
+
290
+ mem_id = str(uuid.uuid4())
291
+ chunk_id = f"memories/semantic/mem-{mem_id}"
292
+ chunk_path = Path.home() / ".wicked-brain" / f"{chunk_id}.md"
293
+ chunk_path.parent.mkdir(parents=True, exist_ok=True)
294
+
295
+ tags_list = ["tool-preference", domain]
296
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
297
+ title = f"Tool preference: {domain} → {tool}"
298
+
299
+ lines = ["---"]
300
+ lines.append("source: wicked-brain:memory")
301
+ lines.append("memory_type: preference")
302
+ lines.append("memory_tier: semantic")
303
+ lines.append(f"title: {title}")
304
+ lines.append("importance: 5")
305
+ lines.append("contains:")
306
+ for t in tags_list:
307
+ lines.append(f" - {t}")
308
+ lines.append(f'indexed_at: "{now}"')
309
+ lines.append("---")
310
+ lines.append("")
311
+ lines.append(f"# {title}")
312
+ lines.append("")
313
+ lines.append(tool)
314
+
315
+ chunk_path.write_text("\n".join(lines), encoding="utf-8")
316
+
317
+ # Index in brain FTS5
318
+ search_text = f"{title} {tool} {' '.join(tags_list)}"
319
+ _brain_api("index", {"id": f"{chunk_id}.md", "path": f"{chunk_id}.md", "content": search_text, "brain_id": "wicked-brain"})
320
+ except Exception:
321
+ pass # fail open: brain may be unavailable