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