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