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,429 @@
1
+ """
2
+ Safety module for wicked-patch.
3
+
4
+ Provides safety validations before applying patches:
5
+ 1. Git clean working tree check
6
+ 2. Transactional patch application with rollback
7
+ 3. Symbol graph freshness gate
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import shutil
14
+ import sqlite3
15
+ import subprocess
16
+ from dataclasses import dataclass
17
+ from datetime import datetime, timedelta
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Optional, Tuple
20
+ import logging
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ BACKUP_SUFFIX = ".wicked-backup"
25
+
26
+
27
+ @dataclass
28
+ class SafetyCheckResult:
29
+ """Result of a safety check."""
30
+ passed: bool
31
+ message: str
32
+ details: Optional[Dict[str, Any]] = None
33
+
34
+ def __bool__(self) -> bool:
35
+ return self.passed
36
+
37
+
38
+ class SafetyError(Exception):
39
+ """Raised when a safety check fails."""
40
+
41
+
42
+ class GitSafetyChecker:
43
+ """Check git repository state for safety."""
44
+
45
+ @staticmethod
46
+ def check_git_clean(path: Path) -> SafetyCheckResult:
47
+ """
48
+ Check if the git working tree is clean.
49
+
50
+ Args:
51
+ path: Path to check (file or directory)
52
+
53
+ Returns:
54
+ SafetyCheckResult with pass/fail and details
55
+ """
56
+ # Find git root
57
+ git_root = GitSafetyChecker._find_git_root(path)
58
+ if not git_root:
59
+ return SafetyCheckResult(
60
+ passed=False,
61
+ message="Not a git repository. wicked-patch requires git for safety.",
62
+ details={"path": str(path)}
63
+ )
64
+
65
+ try:
66
+ # Check for uncommitted changes
67
+ result = subprocess.run(
68
+ ["git", "status", "--porcelain"],
69
+ cwd=git_root,
70
+ capture_output=True,
71
+ text=True,
72
+ timeout=30
73
+ )
74
+
75
+ if result.returncode != 0:
76
+ return SafetyCheckResult(
77
+ passed=False,
78
+ message=f"Git status failed: {result.stderr}",
79
+ details={"returncode": result.returncode}
80
+ )
81
+
82
+ if result.stdout.strip():
83
+ # There are uncommitted changes
84
+ changed_files = result.stdout.strip().split("\n")
85
+ return SafetyCheckResult(
86
+ passed=False,
87
+ message=f"Working tree has {len(changed_files)} uncommitted changes. Commit or stash first.",
88
+ details={
89
+ "changed_files": changed_files[:10], # Limit to first 10
90
+ "total_changes": len(changed_files)
91
+ }
92
+ )
93
+
94
+ return SafetyCheckResult(
95
+ passed=True,
96
+ message="Git working tree is clean",
97
+ details={"git_root": str(git_root)}
98
+ )
99
+
100
+ except subprocess.TimeoutExpired:
101
+ return SafetyCheckResult(
102
+ passed=False,
103
+ message="Git status timed out",
104
+ )
105
+ except FileNotFoundError:
106
+ return SafetyCheckResult(
107
+ passed=False,
108
+ message="Git not found. Install git to use wicked-patch.",
109
+ )
110
+
111
+ @staticmethod
112
+ def _find_git_root(path: Path) -> Optional[Path]:
113
+ """Find the git repository root."""
114
+ current = path.resolve()
115
+ if current.is_file():
116
+ current = current.parent
117
+
118
+ while current != current.parent:
119
+ if (current / ".git").exists():
120
+ return current
121
+ current = current.parent
122
+
123
+ return None
124
+
125
+
126
+ class FreshnessChecker:
127
+ """Check symbol graph freshness."""
128
+
129
+ DEFAULT_MAX_AGE_HOURS = 24
130
+
131
+ @staticmethod
132
+ def check_freshness(
133
+ db_path: Path,
134
+ max_age_hours: int = DEFAULT_MAX_AGE_HOURS
135
+ ) -> SafetyCheckResult:
136
+ """
137
+ Check if the symbol graph is fresh enough.
138
+
139
+ Args:
140
+ db_path: Path to the symbol database
141
+ max_age_hours: Maximum age in hours before considered stale
142
+
143
+ Returns:
144
+ SafetyCheckResult with pass/fail and age details
145
+ """
146
+ if not db_path.exists():
147
+ return SafetyCheckResult(
148
+ passed=False,
149
+ message=f"Symbol database not found: {db_path}",
150
+ details={"db_path": str(db_path)}
151
+ )
152
+
153
+ try:
154
+ # Try to get index timestamp from database metadata
155
+ conn = sqlite3.connect(str(db_path))
156
+ cursor = conn.cursor()
157
+
158
+ # Check for metadata table
159
+ cursor.execute(
160
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='metadata'"
161
+ )
162
+ if not cursor.fetchone():
163
+ # No metadata table, fall back to file modification time
164
+ index_time = datetime.fromtimestamp(db_path.stat().st_mtime)
165
+ else:
166
+ # Try to get indexed_at from metadata
167
+ cursor.execute(
168
+ "SELECT value FROM metadata WHERE key = 'indexed_at'"
169
+ )
170
+ row = cursor.fetchone()
171
+ if row:
172
+ index_time = datetime.fromisoformat(row[0])
173
+ else:
174
+ index_time = datetime.fromtimestamp(db_path.stat().st_mtime)
175
+
176
+ conn.close()
177
+
178
+ # Calculate age
179
+ age = datetime.now() - index_time
180
+ age_hours = age.total_seconds() / 3600
181
+
182
+ if age_hours > max_age_hours:
183
+ return SafetyCheckResult(
184
+ passed=False,
185
+ message=f"Symbol graph is stale ({age_hours:.1f}h old). "
186
+ f"Run /wicked-garden:search:index or use --force",
187
+ details={
188
+ "index_time": index_time.isoformat(),
189
+ "age_hours": age_hours,
190
+ "max_age_hours": max_age_hours
191
+ }
192
+ )
193
+
194
+ return SafetyCheckResult(
195
+ passed=True,
196
+ message=f"Symbol graph is fresh ({age_hours:.1f}h old)",
197
+ details={
198
+ "index_time": index_time.isoformat(),
199
+ "age_hours": age_hours
200
+ }
201
+ )
202
+
203
+ except Exception as e:
204
+ return SafetyCheckResult(
205
+ passed=False,
206
+ message=f"Failed to check graph freshness: {e}",
207
+ details={"error": str(e)}
208
+ )
209
+
210
+
211
+ @dataclass
212
+ class ApplyResult:
213
+ """Result of patch application."""
214
+ success: bool
215
+ files_modified: List[str]
216
+ files_failed: List[str]
217
+ error: Optional[str] = None
218
+ rolled_back: bool = False
219
+
220
+
221
+ class TransactionalApplicator:
222
+ """
223
+ Apply patches transactionally with rollback on failure.
224
+
225
+ Uses file-level backups to ensure atomicity:
226
+ 1. Backup all files before modification
227
+ 2. Apply all patches
228
+ 3. On failure: restore all backups
229
+ 4. On success: delete all backups
230
+ """
231
+
232
+ def __init__(self, patches: List["Patch"]):
233
+ """
234
+ Initialize with patches to apply.
235
+
236
+ Args:
237
+ patches: List of Patch objects to apply
238
+ """
239
+ self.patches = patches
240
+ self.backups: Dict[str, Path] = {} # original_path -> backup_path
241
+
242
+ def apply(self, dry_run: bool = False) -> ApplyResult:
243
+ """
244
+ Apply all patches transactionally.
245
+
246
+ Args:
247
+ dry_run: If True, only validate without applying
248
+
249
+ Returns:
250
+ ApplyResult with success/failure details
251
+ """
252
+ if dry_run:
253
+ return self._dry_run()
254
+
255
+ files_modified = []
256
+ files_failed = []
257
+
258
+ try:
259
+ # Step 1: Create backups
260
+ self._create_backups()
261
+
262
+ # Step 2: Group patches by file
263
+ patches_by_file = self._group_by_file()
264
+
265
+ # Step 3: Apply patches to each file
266
+ for file_path, file_patches in patches_by_file.items():
267
+ try:
268
+ self._apply_file_patches(file_path, file_patches)
269
+ files_modified.append(file_path)
270
+ except Exception as e:
271
+ files_failed.append(file_path)
272
+ raise SafetyError(f"Failed to patch {file_path}: {e}")
273
+
274
+ # Step 4: Success - clean up backups
275
+ self._cleanup_backups()
276
+
277
+ return ApplyResult(
278
+ success=True,
279
+ files_modified=files_modified,
280
+ files_failed=[],
281
+ error=None,
282
+ rolled_back=False
283
+ )
284
+
285
+ except Exception as e:
286
+ # Step 3b: Failure - restore backups
287
+ logger.error(f"Patch application failed: {e}")
288
+ self._restore_backups()
289
+
290
+ return ApplyResult(
291
+ success=False,
292
+ files_modified=[],
293
+ files_failed=files_failed,
294
+ error=str(e),
295
+ rolled_back=True
296
+ )
297
+
298
+ def _dry_run(self) -> ApplyResult:
299
+ """Validate patches without applying."""
300
+ files = set()
301
+ for patch in self.patches:
302
+ if patch.file_path:
303
+ files.add(patch.file_path)
304
+ if not Path(patch.file_path).exists():
305
+ return ApplyResult(
306
+ success=False,
307
+ files_modified=[],
308
+ files_failed=[patch.file_path],
309
+ error=f"File not found: {patch.file_path}"
310
+ )
311
+
312
+ return ApplyResult(
313
+ success=True,
314
+ files_modified=list(files),
315
+ files_failed=[],
316
+ error=None
317
+ )
318
+
319
+ def _create_backups(self):
320
+ """Create backup copies of all files to be modified."""
321
+ files_to_backup = set(p.file_path for p in self.patches if p.file_path)
322
+
323
+ for file_path in files_to_backup:
324
+ path = Path(file_path)
325
+ if path.exists():
326
+ backup_path = path.with_suffix(path.suffix + BACKUP_SUFFIX)
327
+ shutil.copy2(path, backup_path)
328
+ self.backups[file_path] = backup_path
329
+ logger.debug(f"Backed up {file_path} -> {backup_path}")
330
+
331
+ def _restore_backups(self):
332
+ """Restore all backed up files."""
333
+ for original_path, backup_path in self.backups.items():
334
+ if backup_path.exists():
335
+ shutil.copy2(backup_path, original_path)
336
+ backup_path.unlink()
337
+ logger.info(f"Restored {original_path} from backup")
338
+
339
+ self.backups.clear()
340
+
341
+ def _cleanup_backups(self):
342
+ """Delete all backup files after successful application."""
343
+ for backup_path in self.backups.values():
344
+ if backup_path.exists():
345
+ backup_path.unlink()
346
+ logger.debug(f"Cleaned up backup {backup_path}")
347
+
348
+ self.backups.clear()
349
+
350
+ def _group_by_file(self) -> Dict[str, List["Patch"]]:
351
+ """Group patches by file path."""
352
+ by_file: Dict[str, List] = {}
353
+ for patch in self.patches:
354
+ if patch.file_path:
355
+ if patch.file_path not in by_file:
356
+ by_file[patch.file_path] = []
357
+ by_file[patch.file_path].append(patch)
358
+
359
+ # Sort patches within each file by line number (descending)
360
+ # Apply from bottom to top to preserve line numbers
361
+ for patches in by_file.values():
362
+ patches.sort(key=lambda p: p.line_start, reverse=True)
363
+
364
+ return by_file
365
+
366
+ def _apply_file_patches(self, file_path: str, patches: List["Patch"]):
367
+ """Apply patches to a single file."""
368
+ path = Path(file_path)
369
+ with open(path, "r", encoding="utf-8") as f:
370
+ lines = f.read().split("\n")
371
+
372
+ for patch in patches:
373
+ lines = self._apply_patch(lines, patch)
374
+
375
+ with open(path, "w", encoding="utf-8") as f:
376
+ f.write("\n".join(lines))
377
+
378
+ def _apply_patch(self, lines: List[str], patch: "Patch") -> List[str]:
379
+ """Apply a single patch to lines."""
380
+ if patch.line_start > patch.line_end:
381
+ # Insertion: insert new content after line_start
382
+ new_lines = patch.new_content.split("\n") if patch.new_content else []
383
+ return lines[:patch.line_start] + new_lines + lines[patch.line_start:]
384
+ elif not patch.new_content.strip():
385
+ # Deletion: remove lines
386
+ return lines[:patch.line_start - 1] + lines[patch.line_end:]
387
+ else:
388
+ # Replacement: replace lines
389
+ new_lines = patch.new_content.split("\n")
390
+ return lines[:patch.line_start - 1] + new_lines + lines[patch.line_end:]
391
+
392
+
393
+ def run_safety_checks(
394
+ files: List[str],
395
+ db_path: Path,
396
+ force: bool = False,
397
+ skip_git: bool = False,
398
+ max_age_hours: int = 24,
399
+ ) -> Tuple[bool, List[SafetyCheckResult]]:
400
+ """
401
+ Run all safety checks.
402
+
403
+ Args:
404
+ files: List of files that will be modified
405
+ db_path: Path to symbol database
406
+ force: If True, allow stale graph
407
+ skip_git: If True, skip git clean check
408
+ max_age_hours: Maximum graph age
409
+
410
+ Returns:
411
+ Tuple of (all_passed, list of results)
412
+ """
413
+ results = []
414
+
415
+ # Git clean check
416
+ if not skip_git and files:
417
+ git_result = GitSafetyChecker.check_git_clean(Path(files[0]))
418
+ results.append(git_result)
419
+ if not git_result.passed:
420
+ return False, results
421
+
422
+ # Freshness check — skipped when no db_path (brain-backed mode, no local db)
423
+ if not force and db_path is not None:
424
+ freshness_result = FreshnessChecker.check_freshness(db_path, max_age_hours)
425
+ results.append(freshness_result)
426
+ if not freshness_result.passed:
427
+ return False, results
428
+
429
+ return True, results
File without changes
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ jam/_bus_consumers.py — Bus event consumers for the jam domain.
4
+
5
+ Synthesis-trigger consumer: tracks per-session Round 1 persona contributions
6
+ and emits `wicked.session.synthesis_ready` when either:
7
+ - contribution count for a session reaches its `expected_persona_count`, OR
8
+ - 120 seconds have elapsed since the last contribution for that session.
9
+
10
+ Poll-on-invoke pattern — callable from jam commands (or any startup hook) to
11
+ drain pending events. Fails open: bus errors never propagate.
12
+
13
+ Round 1 only: contributions with round != 1 are ignored so Round 2 sequential
14
+ persona dialogue is not interfered with.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import json
20
+ import logging
21
+ import os
22
+ import sys
23
+ import time
24
+ from pathlib import Path
25
+ from typing import Any, Dict, List, Optional
26
+
27
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
28
+
29
+ logger = logging.getLogger("wicked-jam.bus-consumers")
30
+
31
+ # Timeout: if no new contribution arrives within this window, mark synthesis ready.
32
+ _SYNTHESIS_TIMEOUT_SECONDS = 120.0
33
+
34
+ # Default expected persona count when the session object lacks one. 4 matches the
35
+ # /wicked-garden:jam:quick default; full brainstorms typically override via the
36
+ # session record.
37
+ _DEFAULT_EXPECTED_PERSONAS = 4
38
+
39
+ # Ledger file tracking per-session contribution state. Separate from the global
40
+ # _bus_processed.json ledger because we track *per-session aggregate*, not
41
+ # per-event idempotency.
42
+ _JAM_LEDGER_FILE = os.path.join(
43
+ os.path.expanduser("~"),
44
+ ".something-wicked", "wicked-garden", "local",
45
+ "wicked-jam", "_synthesis_tracker.json",
46
+ )
47
+
48
+
49
+ def _load_tracker() -> Dict[str, Dict[str, Any]]:
50
+ """Load the per-session tracker. Returns {session_id: {...}}."""
51
+ try:
52
+ with open(_JAM_LEDGER_FILE, "r") as f:
53
+ data = json.load(f)
54
+ return data if isinstance(data, dict) else {}
55
+ except (FileNotFoundError, json.JSONDecodeError, OSError):
56
+ return {}
57
+
58
+
59
+ def _save_tracker(tracker: Dict[str, Dict[str, Any]]) -> None:
60
+ """Persist the tracker. Fails open."""
61
+ try:
62
+ os.makedirs(os.path.dirname(_JAM_LEDGER_FILE), exist_ok=True)
63
+ with open(_JAM_LEDGER_FILE, "w") as f:
64
+ json.dump(tracker, f)
65
+ except OSError:
66
+ pass # fail open
67
+
68
+
69
+ def _lookup_expected_persona_count(session_id: str) -> Optional[int]:
70
+ """Look up expected_persona_count from the jam session record, if present."""
71
+ try:
72
+ from _domain_store import DomainStore
73
+ ds = DomainStore("wicked-jam")
74
+ sessions = ds.list("sessions") or []
75
+ for s in sessions:
76
+ if s.get("id") == session_id or s.get("session_id") == session_id:
77
+ n = s.get("expected_persona_count")
78
+ if isinstance(n, int) and n > 0:
79
+ return n
80
+ return None
81
+ except Exception:
82
+ return None
83
+
84
+
85
+ def _resolve_expected(session_id: str, tracker_entry: Dict[str, Any]) -> int:
86
+ """Return expected_persona_count, preferring tracker → store → default."""
87
+ n = tracker_entry.get("expected_persona_count")
88
+ if isinstance(n, int) and n > 0:
89
+ return n
90
+ looked = _lookup_expected_persona_count(session_id)
91
+ if looked:
92
+ return looked
93
+ return _DEFAULT_EXPECTED_PERSONAS
94
+
95
+
96
+ def _emit_synthesis_ready(
97
+ session_id: str,
98
+ received: int,
99
+ expected: int,
100
+ reason: str,
101
+ chain_id: Optional[str],
102
+ ) -> None:
103
+ """Emit wicked.session.synthesis_ready. Fails open."""
104
+ try:
105
+ from _bus import emit_event
106
+ emit_event(
107
+ "wicked.session.synthesis_ready",
108
+ {
109
+ "session_id": session_id,
110
+ "received_count": received,
111
+ "expected_count": expected,
112
+ "reason": reason, # "all_received" | "timeout"
113
+ },
114
+ chain_id=chain_id,
115
+ )
116
+ except Exception as e:
117
+ logger.debug(f"synthesis_ready emit failed (non-blocking): {e}")
118
+
119
+
120
+ def process_pending_events(now: Optional[float] = None) -> List[str]:
121
+ """Poll wicked.persona.contributed events, track per-session counts, and emit
122
+ wicked.session.synthesis_ready once a session reaches expected count or times out.
123
+
124
+ Returns list of action strings (for logging / tests).
125
+ `now` override enables deterministic testing; real calls pass None.
126
+ """
127
+ actions: List[str] = []
128
+ now_ts = time.time() if now is None else now
129
+
130
+ try:
131
+ from _bus import poll_pending, ack_events
132
+ except Exception as e:
133
+ logger.debug(f"bus import failed (non-blocking): {e}")
134
+ return actions
135
+
136
+ try:
137
+ events = poll_pending(event_type_prefix="wicked.persona.") or []
138
+
139
+ tracker = _load_tracker()
140
+ max_event_id = 0
141
+ changed = False
142
+
143
+ for event in events:
144
+ event_id = event.get("event_id", 0)
145
+ if isinstance(event_id, int) and event_id > max_event_id:
146
+ max_event_id = event_id
147
+
148
+ if event.get("event_type") != "wicked.persona.contributed":
149
+ continue
150
+
151
+ payload = event.get("payload") or {}
152
+ session_id = payload.get("session_id")
153
+ if not session_id:
154
+ continue
155
+
156
+ # Round 1 only — Round 2 sequential dialogue must not be triggered.
157
+ round_num = payload.get("round", 1)
158
+ try:
159
+ round_num = int(round_num)
160
+ except (TypeError, ValueError):
161
+ round_num = 1
162
+ if round_num != 1:
163
+ continue
164
+
165
+ metadata = event.get("metadata") or {}
166
+ chain_id = metadata.get("chain_id")
167
+
168
+ entry = tracker.get(session_id) or {
169
+ "received_count": 0,
170
+ "last_contribution_ts": now_ts,
171
+ "processed_event_ids": [],
172
+ "ready_emitted": False,
173
+ "chain_id": None,
174
+ "expected_persona_count": None,
175
+ }
176
+
177
+ # Per-event idempotency inside this session's ledger entry
178
+ processed_ids = entry.get("processed_event_ids") or []
179
+ if event_id and event_id in processed_ids:
180
+ continue
181
+
182
+ entry["received_count"] = int(entry.get("received_count", 0)) + 1
183
+ entry["last_contribution_ts"] = now_ts
184
+ if event_id:
185
+ processed_ids.append(event_id)
186
+ # Cap ledger size — last 64 ids per session is plenty for Round 1.
187
+ entry["processed_event_ids"] = processed_ids[-64:]
188
+ if chain_id and not entry.get("chain_id"):
189
+ entry["chain_id"] = chain_id
190
+ # Allow first-contribution emitter to seed expected count via payload.
191
+ if not entry.get("expected_persona_count"):
192
+ n = payload.get("expected_persona_count")
193
+ if isinstance(n, int) and n > 0:
194
+ entry["expected_persona_count"] = n
195
+
196
+ tracker[session_id] = entry
197
+ changed = True
198
+
199
+ if not entry.get("ready_emitted"):
200
+ expected = _resolve_expected(session_id, entry)
201
+ if entry["received_count"] >= expected:
202
+ _emit_synthesis_ready(
203
+ session_id,
204
+ received=entry["received_count"],
205
+ expected=expected,
206
+ reason="all_received",
207
+ chain_id=entry.get("chain_id"),
208
+ )
209
+ entry["ready_emitted"] = True
210
+ actions.append(
211
+ f"synthesis_ready emitted for {session_id} "
212
+ f"({entry['received_count']}/{expected}, all_received)"
213
+ )
214
+
215
+ # Timeout sweep — any session with unemitted readiness whose last
216
+ # contribution is older than the timeout gets flushed.
217
+ for session_id, entry in list(tracker.items()):
218
+ if entry.get("ready_emitted"):
219
+ continue
220
+ last = float(entry.get("last_contribution_ts") or 0.0)
221
+ if last <= 0:
222
+ continue
223
+ if (now_ts - last) >= _SYNTHESIS_TIMEOUT_SECONDS:
224
+ received = int(entry.get("received_count", 0))
225
+ if received <= 0:
226
+ continue
227
+ expected = _resolve_expected(session_id, entry)
228
+ _emit_synthesis_ready(
229
+ session_id,
230
+ received=received,
231
+ expected=expected,
232
+ reason="timeout",
233
+ chain_id=entry.get("chain_id"),
234
+ )
235
+ entry["ready_emitted"] = True
236
+ tracker[session_id] = entry
237
+ changed = True
238
+ actions.append(
239
+ f"synthesis_ready emitted for {session_id} "
240
+ f"({received}/{expected}, timeout)"
241
+ )
242
+
243
+ # Prune emitted sessions older than 24h to bound ledger growth.
244
+ cutoff = now_ts - 86400
245
+ pruned = {
246
+ sid: e for sid, e in tracker.items()
247
+ if not (e.get("ready_emitted") and float(e.get("last_contribution_ts") or 0.0) < cutoff)
248
+ }
249
+ if len(pruned) != len(tracker):
250
+ tracker = pruned
251
+ changed = True
252
+
253
+ if changed:
254
+ _save_tracker(tracker)
255
+
256
+ if max_event_id > 0:
257
+ ack_events(max_event_id)
258
+
259
+ except Exception as e:
260
+ logger.debug(f"jam bus consumer error (non-blocking): {e}")
261
+
262
+ return actions
263
+
264
+
265
+ if __name__ == "__main__":
266
+ # Manual invocation for testing / debug:
267
+ # python3 scripts/jam/_bus_consumers.py
268
+ logging.basicConfig(level=logging.INFO)
269
+ results = process_pending_events()
270
+ for line in results:
271
+ print(line)