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,357 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
_agents.py — Dynamic agent loader.
|
|
4
|
+
|
|
5
|
+
Loads agent definitions from disk:
|
|
6
|
+
- Disk agents: agents/{domain}/*.md files shipped with the plugin.
|
|
7
|
+
Always available.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from _agents import AgentLoader
|
|
11
|
+
|
|
12
|
+
loader = AgentLoader(agents_dir=Path("agents/"))
|
|
13
|
+
loader.load_disk_agents(agents_dir)
|
|
14
|
+
profile = loader.get("senior-engineer")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import sys
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# AgentProfile dataclass
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class AgentProfile:
|
|
30
|
+
"""Normalized agent definition — common format for disk and CP sources.
|
|
31
|
+
|
|
32
|
+
Fields:
|
|
33
|
+
name: Unique agent identifier (kebab-case).
|
|
34
|
+
domain: Domain namespace (e.g. "engineering", "platform").
|
|
35
|
+
system_prompt: Full system prompt text.
|
|
36
|
+
capabilities: List of capability strings (e.g. ["code-review"]).
|
|
37
|
+
traits: List of trait strings (e.g. ["pragmatic", "concise"]).
|
|
38
|
+
metadata: Arbitrary key/value pairs; includes "source" and others.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
domain: str = ""
|
|
43
|
+
system_prompt: str = ""
|
|
44
|
+
capabilities: list[str] = field(default_factory=list)
|
|
45
|
+
traits: list[str] = field(default_factory=list)
|
|
46
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> dict:
|
|
49
|
+
return {
|
|
50
|
+
"name": self.name,
|
|
51
|
+
"domain": self.domain,
|
|
52
|
+
"system_prompt": self.system_prompt,
|
|
53
|
+
"capabilities": self.capabilities,
|
|
54
|
+
"traits": self.traits,
|
|
55
|
+
"metadata": self.metadata,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# AgentLoader
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AgentLoader:
|
|
65
|
+
"""Load and retrieve agent profiles from disk.
|
|
66
|
+
|
|
67
|
+
Lifecycle (called once in bootstrap.py):
|
|
68
|
+
loader = AgentLoader()
|
|
69
|
+
loader.load_disk_agents(agents_dir)
|
|
70
|
+
profile = loader.get("senior-engineer")
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self) -> None:
|
|
74
|
+
self._agents: dict[str, AgentProfile] = {}
|
|
75
|
+
|
|
76
|
+
# ------------------------------------------------------------------
|
|
77
|
+
# Loading
|
|
78
|
+
# ------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
def load_disk_agents(self, agents_dir: Path) -> dict[str, AgentProfile]:
|
|
81
|
+
"""Scan agents_dir recursively for *.md agent files and load them.
|
|
82
|
+
|
|
83
|
+
Each markdown file must have YAML frontmatter with at least a `name`
|
|
84
|
+
field. Additional recognized frontmatter keys: domain, description,
|
|
85
|
+
capabilities (list), traits (list), model, color.
|
|
86
|
+
|
|
87
|
+
The body of the markdown file (after frontmatter) becomes system_prompt.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
agents_dir: Path to the agents/ directory (may contain subdirs).
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Dict mapping agent name -> AgentProfile. Also stored internally.
|
|
94
|
+
"""
|
|
95
|
+
if not agents_dir.exists():
|
|
96
|
+
return {}
|
|
97
|
+
|
|
98
|
+
for md_file in sorted(agents_dir.rglob("*.md")):
|
|
99
|
+
profile = _parse_agent_md(md_file)
|
|
100
|
+
if profile is None:
|
|
101
|
+
continue
|
|
102
|
+
self._agents[profile.name] = profile
|
|
103
|
+
|
|
104
|
+
return dict(self._agents)
|
|
105
|
+
|
|
106
|
+
# ------------------------------------------------------------------
|
|
107
|
+
# Retrieval
|
|
108
|
+
# ------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
def get(self, name: str) -> AgentProfile | None:
|
|
111
|
+
"""Look up an agent by name.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
name: Agent name (kebab-case, e.g. "senior-engineer").
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
AgentProfile, or None if no agent with that name is loaded.
|
|
118
|
+
"""
|
|
119
|
+
return self._agents.get(name)
|
|
120
|
+
|
|
121
|
+
def all(self) -> dict[str, AgentProfile]:
|
|
122
|
+
"""Return a shallow copy of all loaded agents."""
|
|
123
|
+
return dict(self._agents)
|
|
124
|
+
|
|
125
|
+
def count(self) -> int:
|
|
126
|
+
return len(self._agents)
|
|
127
|
+
|
|
128
|
+
def serialize(self) -> list[dict]:
|
|
129
|
+
"""Serialize all agents to a JSON-safe list (for session state file)."""
|
|
130
|
+
return [p.to_dict() for p in self._agents.values()]
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def deserialize(cls, data: list[dict]) -> "AgentLoader":
|
|
134
|
+
"""Reconstruct an AgentLoader from a previously serialized list."""
|
|
135
|
+
loader = cls()
|
|
136
|
+
for rec in data:
|
|
137
|
+
name = rec.get("name")
|
|
138
|
+
if not name:
|
|
139
|
+
continue
|
|
140
|
+
loader._agents[name] = AgentProfile(
|
|
141
|
+
name=name,
|
|
142
|
+
domain=rec.get("domain", ""),
|
|
143
|
+
system_prompt=rec.get("system_prompt", ""),
|
|
144
|
+
capabilities=rec.get("capabilities") or [],
|
|
145
|
+
traits=rec.get("traits") or [],
|
|
146
|
+
metadata=rec.get("metadata") or {},
|
|
147
|
+
)
|
|
148
|
+
return loader
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
# Markdown frontmatter parser (stdlib-only, no PyYAML)
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
_FRONTMATTER_START = "---"
|
|
156
|
+
_FRONTMATTER_END = "---"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _parse_agent_md(path: Path) -> AgentProfile | None:
|
|
160
|
+
"""Parse a Claude Code agent markdown file into an AgentProfile.
|
|
161
|
+
|
|
162
|
+
Format:
|
|
163
|
+
---
|
|
164
|
+
name: agent-name
|
|
165
|
+
description: |
|
|
166
|
+
Optional description
|
|
167
|
+
domain: engineering # optional
|
|
168
|
+
capabilities: # optional list
|
|
169
|
+
- code-review
|
|
170
|
+
traits: # optional list
|
|
171
|
+
- pragmatic
|
|
172
|
+
model: sonnet # stored in metadata
|
|
173
|
+
color: blue # stored in metadata
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
# Agent Title
|
|
177
|
+
|
|
178
|
+
System prompt body...
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
AgentProfile, or None if the file lacks a valid name field.
|
|
182
|
+
"""
|
|
183
|
+
try:
|
|
184
|
+
raw = path.read_text(encoding="utf-8")
|
|
185
|
+
except OSError:
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
frontmatter, body = _split_frontmatter(raw)
|
|
189
|
+
if frontmatter is None:
|
|
190
|
+
# No frontmatter — use filename as name, whole file as system_prompt
|
|
191
|
+
name = path.stem
|
|
192
|
+
domain = _infer_domain(path)
|
|
193
|
+
return AgentProfile(
|
|
194
|
+
name=name,
|
|
195
|
+
domain=domain,
|
|
196
|
+
system_prompt=raw.strip(),
|
|
197
|
+
metadata={"source": "disk", "file": str(path)},
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
parsed = _parse_simple_yaml(frontmatter)
|
|
201
|
+
name = parsed.get("name") or path.stem
|
|
202
|
+
if not name:
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
domain = parsed.get("domain") or _infer_domain(path)
|
|
206
|
+
|
|
207
|
+
# Build metadata from remaining frontmatter keys
|
|
208
|
+
metadata_keys = {"model", "color", "version", "author", "allowed-tools"}
|
|
209
|
+
extra_meta = {k: v for k, v in parsed.items() if k in metadata_keys}
|
|
210
|
+
extra_meta["source"] = "disk"
|
|
211
|
+
extra_meta["file"] = str(path)
|
|
212
|
+
|
|
213
|
+
# Extract tool-capabilities for capability-based dynamic tool routing
|
|
214
|
+
tool_caps = _to_list(parsed.get("tool-capabilities"))
|
|
215
|
+
if tool_caps:
|
|
216
|
+
extra_meta["tool_capabilities"] = tool_caps
|
|
217
|
+
|
|
218
|
+
# Parse allowed-tools into a list for the resolver
|
|
219
|
+
allowed_tools_list = _to_list(parsed.get("allowed-tools"))
|
|
220
|
+
if allowed_tools_list:
|
|
221
|
+
extra_meta["allowed_tools_list"] = allowed_tools_list
|
|
222
|
+
|
|
223
|
+
return AgentProfile(
|
|
224
|
+
name=name,
|
|
225
|
+
domain=domain,
|
|
226
|
+
system_prompt=body.strip(),
|
|
227
|
+
capabilities=_to_list(parsed.get("capabilities")),
|
|
228
|
+
traits=_to_list(parsed.get("traits")),
|
|
229
|
+
metadata=extra_meta,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _split_frontmatter(raw: str) -> tuple[str | None, str]:
|
|
234
|
+
"""Split raw markdown into (frontmatter, body).
|
|
235
|
+
|
|
236
|
+
Returns (None, raw) if no YAML frontmatter block is found.
|
|
237
|
+
"""
|
|
238
|
+
lines = raw.split("\n")
|
|
239
|
+
if not lines or lines[0].strip() != _FRONTMATTER_START:
|
|
240
|
+
return None, raw
|
|
241
|
+
|
|
242
|
+
end_idx = None
|
|
243
|
+
for i, line in enumerate(lines[1:], start=1):
|
|
244
|
+
if line.strip() == _FRONTMATTER_END:
|
|
245
|
+
end_idx = i
|
|
246
|
+
break
|
|
247
|
+
|
|
248
|
+
if end_idx is None:
|
|
249
|
+
return None, raw
|
|
250
|
+
|
|
251
|
+
frontmatter = "\n".join(lines[1:end_idx])
|
|
252
|
+
body = "\n".join(lines[end_idx + 1 :])
|
|
253
|
+
return frontmatter, body
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _parse_simple_yaml(text: str) -> dict[str, Any]:
|
|
257
|
+
"""Minimal YAML parser for Claude Code agent frontmatter.
|
|
258
|
+
|
|
259
|
+
Handles the subset of YAML used in agent files:
|
|
260
|
+
- key: scalar_value
|
|
261
|
+
- key: |
|
|
262
|
+
multi-line block (dedented)
|
|
263
|
+
- key:
|
|
264
|
+
- list_item_1
|
|
265
|
+
- list_item_2
|
|
266
|
+
|
|
267
|
+
Does NOT handle nested dicts, anchors, or other YAML features.
|
|
268
|
+
"""
|
|
269
|
+
result: dict[str, Any] = {}
|
|
270
|
+
lines = text.split("\n")
|
|
271
|
+
i = 0
|
|
272
|
+
|
|
273
|
+
while i < len(lines):
|
|
274
|
+
line = lines[i]
|
|
275
|
+
|
|
276
|
+
# Skip blank lines and comments
|
|
277
|
+
if not line.strip() or line.strip().startswith("#"):
|
|
278
|
+
i += 1
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
# Key: value or Key:
|
|
282
|
+
if ":" not in line:
|
|
283
|
+
i += 1
|
|
284
|
+
continue
|
|
285
|
+
|
|
286
|
+
key_part, _, value_part = line.partition(":")
|
|
287
|
+
key = key_part.strip()
|
|
288
|
+
value = value_part.strip()
|
|
289
|
+
|
|
290
|
+
if not key:
|
|
291
|
+
i += 1
|
|
292
|
+
continue
|
|
293
|
+
|
|
294
|
+
# Block scalar (|)
|
|
295
|
+
if value == "|":
|
|
296
|
+
block_lines: list[str] = []
|
|
297
|
+
i += 1
|
|
298
|
+
while i < len(lines):
|
|
299
|
+
bl = lines[i]
|
|
300
|
+
# Block ends when we hit a line at root indentation (no leading space)
|
|
301
|
+
if bl and not bl.startswith(" ") and not bl.startswith("\t"):
|
|
302
|
+
break
|
|
303
|
+
block_lines.append(bl)
|
|
304
|
+
i += 1
|
|
305
|
+
result[key] = "\n".join(line.rstrip() for line in block_lines).strip()
|
|
306
|
+
continue
|
|
307
|
+
|
|
308
|
+
# Inline list (next lines starting with " -")
|
|
309
|
+
if value == "":
|
|
310
|
+
list_items: list[str] = []
|
|
311
|
+
j = i + 1
|
|
312
|
+
while j < len(lines):
|
|
313
|
+
candidate = lines[j].strip()
|
|
314
|
+
if candidate.startswith("- "):
|
|
315
|
+
list_items.append(candidate[2:].strip())
|
|
316
|
+
j += 1
|
|
317
|
+
elif candidate == "-":
|
|
318
|
+
list_items.append("")
|
|
319
|
+
j += 1
|
|
320
|
+
else:
|
|
321
|
+
break
|
|
322
|
+
if list_items:
|
|
323
|
+
result[key] = list_items
|
|
324
|
+
i = j
|
|
325
|
+
continue
|
|
326
|
+
|
|
327
|
+
# Plain scalar — strip surrounding quotes
|
|
328
|
+
if value.startswith(('"', "'")):
|
|
329
|
+
value = value[1:-1] if len(value) > 1 and value[-1] == value[0] else value
|
|
330
|
+
result[key] = value
|
|
331
|
+
i += 1
|
|
332
|
+
|
|
333
|
+
return result
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _infer_domain(path: Path) -> str:
|
|
337
|
+
"""Infer domain from path: agents/{domain}/agent.md -> domain."""
|
|
338
|
+
parts = path.parts
|
|
339
|
+
# Look for the 'agents' segment and take the next part as domain
|
|
340
|
+
for idx, part in enumerate(parts):
|
|
341
|
+
if part == "agents" and idx + 2 < len(parts):
|
|
342
|
+
return parts[idx + 1]
|
|
343
|
+
return ""
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _to_list(value: Any) -> list[str]:
|
|
347
|
+
"""Normalize a YAML value to a list of strings."""
|
|
348
|
+
if value is None:
|
|
349
|
+
return []
|
|
350
|
+
if isinstance(value, list):
|
|
351
|
+
return [str(v) for v in value]
|
|
352
|
+
if isinstance(value, str):
|
|
353
|
+
# Comma-separated or single value
|
|
354
|
+
if "," in value:
|
|
355
|
+
return [v.strip() for v in value.split(",") if v.strip()]
|
|
356
|
+
return [value.strip()] if value.strip() else []
|
|
357
|
+
return []
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Session-fact extractor over native Claude tasks.
|
|
4
|
+
|
|
5
|
+
Replaces the smaht/v2 FactExtractor + HistoryCondenser turn-log pipeline
|
|
6
|
+
that was deleted in Gate 4 Phase 2 of the v6 rebuild (#428). Reads native
|
|
7
|
+
tasks written by TaskCreate/TaskUpdate to
|
|
8
|
+
${CLAUDE_CONFIG_DIR:-~/.claude}/tasks/{session_id}/*.json and extracts
|
|
9
|
+
session-level facts using the same heuristic patterns as the old
|
|
10
|
+
FactExtractor.
|
|
11
|
+
|
|
12
|
+
Source of truth shift:
|
|
13
|
+
- v5: wicked-smaht session directory turn log (user/assistant messages)
|
|
14
|
+
- v6: native task records (subject + description, filtered to completed)
|
|
15
|
+
|
|
16
|
+
The emission shape on wicked-bus stays the same — `wicked.fact.extracted`
|
|
17
|
+
events with {type, content, entities, source, session_id} — so the brain
|
|
18
|
+
auto-memorize subscriber is unaffected.
|
|
19
|
+
|
|
20
|
+
Fact types (mirrors brain auto-memorize policy):
|
|
21
|
+
- decision — "decided on X", "chose Y", "let's use Z"
|
|
22
|
+
- discovery — "found that X", "turns out Y"
|
|
23
|
+
- artifact — "created X", "wrote Y"
|
|
24
|
+
- problem_solved — "fixed X", "resolved Y"
|
|
25
|
+
- context — "the system uses X", "currently running Y"
|
|
26
|
+
|
|
27
|
+
Only `decision` and `discovery` are emittable (brain re-filters).
|
|
28
|
+
|
|
29
|
+
stdlib-only. Fails open — returns [] on any I/O or parse error.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import json
|
|
35
|
+
import os
|
|
36
|
+
import re
|
|
37
|
+
import sys
|
|
38
|
+
import uuid
|
|
39
|
+
from dataclasses import dataclass, field, asdict
|
|
40
|
+
from datetime import datetime, timezone
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
from typing import Iterable, Optional
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
FACT_TYPES = ("decision", "discovery", "artifact", "problem_solved", "context")
|
|
46
|
+
EMITTABLE_FACT_TYPES = frozenset({"decision", "discovery"})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class SessionFact:
|
|
51
|
+
"""A structured fact extracted from a native task record."""
|
|
52
|
+
|
|
53
|
+
id: str
|
|
54
|
+
type: str
|
|
55
|
+
content: str
|
|
56
|
+
entities: list = field(default_factory=list)
|
|
57
|
+
source: str = "task" # always "task" in v6 — native task origin
|
|
58
|
+
timestamp: str = ""
|
|
59
|
+
task_id: str = ""
|
|
60
|
+
|
|
61
|
+
def __post_init__(self) -> None:
|
|
62
|
+
if not self.id:
|
|
63
|
+
self.id = str(uuid.uuid4())[:8]
|
|
64
|
+
if not self.timestamp:
|
|
65
|
+
self.timestamp = datetime.now(timezone.utc).isoformat()
|
|
66
|
+
|
|
67
|
+
def to_dict(self) -> dict:
|
|
68
|
+
return asdict(self)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# --- Extraction patterns (ported verbatim from smaht/v2/fact_extractor.py) ---
|
|
72
|
+
|
|
73
|
+
_DECISION_PATTERNS = [
|
|
74
|
+
(r"let's (?:go with|use|do) ([^.!?\n]{10,100})", "decision"),
|
|
75
|
+
(r"decided (?:on|to) ([^.!?\n]{10,100})", "decision"),
|
|
76
|
+
(r"chose ([^.!?\n]{10,100})", "decision"),
|
|
77
|
+
(r"we(?:'ll| will) use ([^.!?\n]{10,100})", "decision"),
|
|
78
|
+
(r"going (?:to|with) ([^.!?\n]{10,100})", "decision"),
|
|
79
|
+
(r"switched to ([^.!?\n]{10,100})", "decision"),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
_DISCOVERY_PATTERNS = [
|
|
83
|
+
(r"(?:found|discovered) (?:that |out )([^.!?\n]{10,100})", "discovery"),
|
|
84
|
+
(r"turns out ([^.!?\n]{10,100})", "discovery"),
|
|
85
|
+
(r"(?:realized|noticed) (?:that )?([^.!?\n]{10,100})", "discovery"),
|
|
86
|
+
(r"the (?:issue|problem|root cause) (?:is|was) ([^.!?\n]{10,100})", "discovery"),
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
_ARTIFACT_PATTERNS = [
|
|
90
|
+
(r"(?:created|wrote|generated|built) ([^.!?\n]{5,80})", "artifact"),
|
|
91
|
+
(r"(?:added|implemented) ([^.!?\n]{5,80})", "artifact"),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
_PROBLEM_SOLVED_PATTERNS = [
|
|
95
|
+
(r"(?:fixed|resolved|solved) ([^.!?\n]{10,100})", "problem_solved"),
|
|
96
|
+
(r"(?:the fix|solution) (?:is|was) ([^.!?\n]{10,100})", "problem_solved"),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
_CONTEXT_PATTERNS = [
|
|
100
|
+
(r"the system (?:uses|has|runs) ([^.!?\n]{10,80})", "context"),
|
|
101
|
+
(r"currently (?:using|running|on) ([^.!?\n]{10,80})", "context"),
|
|
102
|
+
(r"our (?:stack|setup|architecture) (?:is|includes) ([^.!?\n]{10,80})", "context"),
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
_ALL_PATTERNS = (
|
|
106
|
+
_DECISION_PATTERNS
|
|
107
|
+
+ _DISCOVERY_PATTERNS
|
|
108
|
+
+ _ARTIFACT_PATTERNS
|
|
109
|
+
+ _PROBLEM_SOLVED_PATTERNS
|
|
110
|
+
+ _CONTEXT_PATTERNS
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
_TECH_NAMES = (
|
|
115
|
+
"Redis", "Postgres", "PostgreSQL", "MySQL", "MongoDB", "SQLite",
|
|
116
|
+
"Docker", "Kubernetes", "AWS", "GCP", "Azure",
|
|
117
|
+
"React", "Vue", "Angular", "FastAPI", "Django", "Flask", "Express",
|
|
118
|
+
"JWT", "OAuth", "GraphQL", "REST", "gRPC",
|
|
119
|
+
"Stripe", "Kafka", "RabbitMQ", "Elasticsearch",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
_FILE_PATH_RE = re.compile(
|
|
123
|
+
r"([a-zA-Z_][a-zA-Z0-9_/.-]*\."
|
|
124
|
+
r"(?:py|ts|js|tsx|jsx|json|yaml|yml|md|sql|java|go|rs|sh))\b"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _extract_entities(text: str) -> list:
|
|
129
|
+
"""Extract entity references (files, technologies, services) from text."""
|
|
130
|
+
entities: list = []
|
|
131
|
+
|
|
132
|
+
files = _FILE_PATH_RE.findall(text)
|
|
133
|
+
entities.extend(files[:5])
|
|
134
|
+
|
|
135
|
+
for tech in _TECH_NAMES:
|
|
136
|
+
if re.search(rf"\b{re.escape(tech)}\b", text, re.IGNORECASE) and tech not in entities:
|
|
137
|
+
entities.append(tech)
|
|
138
|
+
|
|
139
|
+
return entities[:8]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _tasks_dir(session_id: str) -> Path:
|
|
143
|
+
"""Resolve the native tasks directory for a session.
|
|
144
|
+
|
|
145
|
+
Honors CLAUDE_CONFIG_DIR; defaults to ~/.claude.
|
|
146
|
+
"""
|
|
147
|
+
base = os.environ.get("CLAUDE_CONFIG_DIR")
|
|
148
|
+
if base:
|
|
149
|
+
root = Path(base).expanduser()
|
|
150
|
+
else:
|
|
151
|
+
root = Path.home() / ".claude"
|
|
152
|
+
# Sanitize session_id the same way hooks/scripts/stop.py does.
|
|
153
|
+
safe = session_id.replace("/", "_").replace("\\", "_").replace("..", "_")
|
|
154
|
+
return root / "tasks" / safe
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _iter_task_records_direct(session_id: str) -> Iterable[dict]:
|
|
158
|
+
"""Yield completed task records via direct file read (pre-PR-2 behaviour)."""
|
|
159
|
+
tdir = _tasks_dir(session_id)
|
|
160
|
+
if not tdir.exists() or not tdir.is_dir():
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# Native task filenames are the numeric task id + .json. Sort numerically
|
|
164
|
+
# so earlier tasks produce earlier facts (stable order for dedup).
|
|
165
|
+
def _sort_key(p: Path) -> tuple:
|
|
166
|
+
stem = p.stem
|
|
167
|
+
try:
|
|
168
|
+
return (0, int(stem))
|
|
169
|
+
except ValueError:
|
|
170
|
+
return (1, stem)
|
|
171
|
+
|
|
172
|
+
for path in sorted(tdir.glob("*.json"), key=_sort_key):
|
|
173
|
+
try:
|
|
174
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
175
|
+
except (OSError, ValueError, json.JSONDecodeError):
|
|
176
|
+
continue
|
|
177
|
+
if not isinstance(data, dict):
|
|
178
|
+
continue
|
|
179
|
+
if data.get("status") != "completed":
|
|
180
|
+
continue
|
|
181
|
+
yield data
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _iter_task_records(session_id: str) -> Iterable[dict]:
|
|
185
|
+
"""Yield native task JSON records for the session, oldest-first by numeric id.
|
|
186
|
+
|
|
187
|
+
Only yields tasks whose status == 'completed'.
|
|
188
|
+
|
|
189
|
+
Routing: WG_DAEMON_ENABLED=false → direct file read (unchanged);
|
|
190
|
+
WG_DAEMON_ENABLED=true → daemon HTTP with fallback (#596 v8-PR-2).
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
# _SCRIPTS_ROOT is the parent of this file's directory.
|
|
194
|
+
_scripts = str(Path(__file__).resolve().parents[1])
|
|
195
|
+
if _scripts not in sys.path:
|
|
196
|
+
sys.path.insert(0, _scripts)
|
|
197
|
+
from crew._task_reader import read_session_tasks # type: ignore[import]
|
|
198
|
+
tasks = read_session_tasks(session_id, limit=500)
|
|
199
|
+
for t in tasks:
|
|
200
|
+
if isinstance(t, dict) and t.get("status") == "completed":
|
|
201
|
+
yield t
|
|
202
|
+
return
|
|
203
|
+
except Exception:
|
|
204
|
+
pass # fail open — _task_reader unavailable; fall through to direct file read
|
|
205
|
+
# Fallback to direct file read if import fails.
|
|
206
|
+
yield from _iter_task_records_direct(session_id)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _extract_from_text(text: str, task_id: str) -> list:
|
|
210
|
+
"""Extract facts from a text block (task subject + description)."""
|
|
211
|
+
if not text:
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
facts: list = []
|
|
215
|
+
text_lower = text.lower()
|
|
216
|
+
entities = _extract_entities(text)
|
|
217
|
+
|
|
218
|
+
for pattern, fact_type in _ALL_PATTERNS:
|
|
219
|
+
matches = re.findall(pattern, text_lower)
|
|
220
|
+
for match in matches[:2]:
|
|
221
|
+
if isinstance(match, tuple):
|
|
222
|
+
match = " ".join(match)
|
|
223
|
+
match = match.strip()
|
|
224
|
+
if not match or len(match) < 5:
|
|
225
|
+
continue
|
|
226
|
+
facts.append(
|
|
227
|
+
SessionFact(
|
|
228
|
+
id=str(uuid.uuid4())[:8],
|
|
229
|
+
type=fact_type,
|
|
230
|
+
content=match,
|
|
231
|
+
entities=entities,
|
|
232
|
+
source="task",
|
|
233
|
+
task_id=task_id,
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
# Match the old FactExtractor cap of 5 per text block.
|
|
237
|
+
return facts[:5]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def extract_session_facts(session_id: str, limit: int = 10) -> list:
|
|
241
|
+
"""Extract session-level facts from native task records.
|
|
242
|
+
|
|
243
|
+
Returns a list of SessionFact objects (up to `limit`), deduplicated by
|
|
244
|
+
lowercased content. Fails open — returns [] on any error.
|
|
245
|
+
|
|
246
|
+
The returned shape matches what hooks/scripts/stop.py::_run_memory_promotion
|
|
247
|
+
consumes: objects with `.type`, `.content`, `.entities`, `.source`.
|
|
248
|
+
"""
|
|
249
|
+
if not session_id or limit <= 0:
|
|
250
|
+
return []
|
|
251
|
+
|
|
252
|
+
facts: list = []
|
|
253
|
+
seen: set = set()
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
for record in _iter_task_records(session_id):
|
|
257
|
+
task_id = str(record.get("id", ""))
|
|
258
|
+
subject = str(record.get("subject", "") or "")
|
|
259
|
+
description = str(record.get("description", "") or "")
|
|
260
|
+
# Concatenate so patterns can match across subject+description;
|
|
261
|
+
# keeps entity extraction coherent within a single task.
|
|
262
|
+
text = f"{subject}. {description}".strip()
|
|
263
|
+
|
|
264
|
+
for fact in _extract_from_text(text, task_id):
|
|
265
|
+
key = fact.content.lower()
|
|
266
|
+
if key in seen:
|
|
267
|
+
continue
|
|
268
|
+
seen.add(key)
|
|
269
|
+
facts.append(fact)
|
|
270
|
+
if len(facts) >= limit:
|
|
271
|
+
return facts
|
|
272
|
+
except Exception as exc: # pragma: no cover - defensive fail-open
|
|
273
|
+
print(f"[wicked-mem] session fact extraction error: {exc}", file=sys.stderr)
|
|
274
|
+
return facts
|
|
275
|
+
|
|
276
|
+
return facts
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _self_test() -> int:
|
|
280
|
+
"""Inline self-test with a synthetic task directory."""
|
|
281
|
+
import tempfile
|
|
282
|
+
|
|
283
|
+
tmp = Path(tempfile.mkdtemp(prefix="wg-sfe-"))
|
|
284
|
+
try:
|
|
285
|
+
session_id = "selftest"
|
|
286
|
+
sdir = tmp / "tasks" / session_id
|
|
287
|
+
sdir.mkdir(parents=True)
|
|
288
|
+
|
|
289
|
+
# Task 1: contains a decision + an artifact
|
|
290
|
+
(sdir / "1.json").write_text(json.dumps({
|
|
291
|
+
"id": "1",
|
|
292
|
+
"subject": "Wire facilitator",
|
|
293
|
+
"description": "We decided to use the rubric approach and created facilitator.md",
|
|
294
|
+
"status": "completed",
|
|
295
|
+
}))
|
|
296
|
+
# Task 2: pending — should be ignored
|
|
297
|
+
(sdir / "2.json").write_text(json.dumps({
|
|
298
|
+
"id": "2",
|
|
299
|
+
"subject": "Write docs",
|
|
300
|
+
"description": "Not done yet",
|
|
301
|
+
"status": "pending",
|
|
302
|
+
}))
|
|
303
|
+
# Task 3: a discovery
|
|
304
|
+
(sdir / "3.json").write_text(json.dumps({
|
|
305
|
+
"id": "3",
|
|
306
|
+
"subject": "Root cause",
|
|
307
|
+
"description": "The issue was a race condition in the event loop",
|
|
308
|
+
"status": "completed",
|
|
309
|
+
}))
|
|
310
|
+
|
|
311
|
+
os.environ["CLAUDE_CONFIG_DIR"] = str(tmp)
|
|
312
|
+
facts = extract_session_facts(session_id, limit=10)
|
|
313
|
+
|
|
314
|
+
types = [f.type for f in facts]
|
|
315
|
+
contents = [f.content for f in facts]
|
|
316
|
+
|
|
317
|
+
assert facts, f"expected at least one fact, got {facts}"
|
|
318
|
+
assert "decision" in types, f"expected a decision fact, got types={types}"
|
|
319
|
+
assert "discovery" in types, f"expected a discovery fact, got types={types}"
|
|
320
|
+
# pending task must not leak into facts
|
|
321
|
+
assert not any("not done" in c for c in contents), (
|
|
322
|
+
f"pending task leaked into facts: {contents}"
|
|
323
|
+
)
|
|
324
|
+
# Every fact must have a task_id linking it to its source
|
|
325
|
+
assert all(f.task_id for f in facts), (
|
|
326
|
+
f"facts missing task_id: {[f.to_dict() for f in facts]}"
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
emittable = [f for f in facts if f.type in EMITTABLE_FACT_TYPES]
|
|
330
|
+
assert emittable, "expected at least one emittable (decision/discovery) fact"
|
|
331
|
+
|
|
332
|
+
print(f"session_fact_extractor self-test: PASS ({len(facts)} facts, {len(emittable)} emittable)")
|
|
333
|
+
return 0
|
|
334
|
+
finally:
|
|
335
|
+
import shutil
|
|
336
|
+
shutil.rmtree(tmp, ignore_errors=True)
|
|
337
|
+
os.environ.pop("CLAUDE_CONFIG_DIR", None)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
if __name__ == "__main__":
|
|
341
|
+
sys.exit(_self_test())
|