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,843 @@
|
|
|
1
|
+
# wicked-loom — Expand Phase, Plan A (compose skeleton) — Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Stand up the `wicked-loom` standalone package with its `compose` surface — peer manifest, the runtime resolution ladder, version-check, and install orchestration — publishable as `wicked-loom@0.1.0` and runnable as `npx wicked-loom`, with garden untouched.
|
|
6
|
+
|
|
7
|
+
**Architecture:** An **npm package** whose `bin` is a thin Node shim (`bin/loom.mjs`) that execs `python3` on a bundled Python module (`python/loom/`). The Python core holds all logic (CLI dispatch + compose). This matches garden's existing `npx wicked-*` consumption + resolution ladder while reusing garden's Python resolver code. **Compose only** — gate/flow/conduct is Plan B (do not build it here; YAGNI).
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Python 3.9+ (stdlib only — no third-party deps, mirroring garden's hook discipline), `pytest` for tests, a ~30-line Node ESM shim, `npm` packaging.
|
|
10
|
+
|
|
11
|
+
**Decisions locked for this plan (override in review):**
|
|
12
|
+
- Distribution: npm package + Node bin shim → bundled Python. (Alt: pip/pipx.)
|
|
13
|
+
- Scope: compose surface only — `loom resolve`, `loom doctor`, `loom compose install`. Conduct = Plan B.
|
|
14
|
+
- Target repo dir for execution: `~/Projects/wicked-loom` (a NEW git repo, sibling to wicked-garden). Adjust paths if you place it elsewhere.
|
|
15
|
+
|
|
16
|
+
**Source material to port (read these in wicked-garden before Task 3+):**
|
|
17
|
+
- `scripts/_integration_resolver.py`, `scripts/_capability_resolver.py` — the resolution ladder + kill-switch.
|
|
18
|
+
- `scripts/qe/vault_gate.py` (the `_resolve_vault` portion only) — the `WICKED_VAULT_BIN → config → PATH → node_modules → npx` ladder, verbatim pattern.
|
|
19
|
+
- `docs/required-peers.md` + `.claude-plugin/plugin.json` — the peer set, version pins (`testing ^0.3`, `vault ^0.3`, `brain ^0.14`, `bus ^2.0`) and install commands.
|
|
20
|
+
|
|
21
|
+
**Bulletproof standards:** R1–R6 (no dead code, no bare panics, no magic values, no swallowed errors, no unbounded ops, no god functions) and T1–T6 (determinism, no sleep-based sync, isolation, single-assertion focus, descriptive names, provenance). The resolution ladder must be deterministic and fully mockable — never hit the network or real `npx` in a unit test.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## File Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
wicked-loom/
|
|
29
|
+
├── package.json # npm metadata + bin: { "loom": "bin/loom.mjs" }
|
|
30
|
+
├── bin/loom.mjs # Node ESM shim → spawns python3 on python/loom
|
|
31
|
+
├── python/loom/
|
|
32
|
+
│ ├── __init__.py # version constant
|
|
33
|
+
│ ├── __main__.py # `python3 -m loom` entry → cli.main(argv)
|
|
34
|
+
│ ├── cli.py # arg dispatch: resolve | doctor | compose
|
|
35
|
+
│ ├── manifest.py # Peer dataclass + PEERS registry (the peer set)
|
|
36
|
+
│ ├── resolve.py # the runtime resolution ladder + kill-switch
|
|
37
|
+
│ └── compose.py # version-check + install orchestration
|
|
38
|
+
├── tests/
|
|
39
|
+
│ ├── test_manifest.py
|
|
40
|
+
│ ├── test_resolve.py
|
|
41
|
+
│ ├── test_compose.py
|
|
42
|
+
│ └── test_cli.py
|
|
43
|
+
├── .gitignore
|
|
44
|
+
└── README.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Responsibilities: `manifest` = *what the peers are* (data). `resolve` = *find a peer's runnable command* (the ladder). `compose` = *check versions + install* (actions, built on resolve+manifest). `cli` = *argument dispatch only* (no logic). `bin/loom.mjs` = *launch python3* (no logic). One responsibility per file; `cli` and the shim stay logic-free so the Python modules are unit-testable without a process boundary.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Task 1: Repo + package skeleton
|
|
52
|
+
|
|
53
|
+
**Files:**
|
|
54
|
+
- Create: `~/Projects/wicked-loom/package.json`
|
|
55
|
+
- Create: `~/Projects/wicked-loom/.gitignore`
|
|
56
|
+
- Create: `~/Projects/wicked-loom/python/loom/__init__.py`
|
|
57
|
+
|
|
58
|
+
- [ ] **Step 1: Init the repo**
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
mkdir -p ~/Projects/wicked-loom/python/loom ~/Projects/wicked-loom/bin ~/Projects/wicked-loom/tests
|
|
62
|
+
cd ~/Projects/wicked-loom && git init
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- [ ] **Step 2: Write `package.json`**
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"name": "wicked-loom",
|
|
70
|
+
"version": "0.1.0",
|
|
71
|
+
"description": "Local-first orchestration runtime for agent ecosystems: resolves, version-checks, and installs the wicked-* peer set (compose). Conduct (gate/flow) ships separately.",
|
|
72
|
+
"bin": { "loom": "bin/loom.mjs" },
|
|
73
|
+
"files": ["bin/", "python/", "README.md"],
|
|
74
|
+
"type": "module",
|
|
75
|
+
"engines": { "node": ">=18" },
|
|
76
|
+
"license": "MIT",
|
|
77
|
+
"repository": "https://github.com/mikeparcewski/wicked-loom",
|
|
78
|
+
"keywords": ["orchestration", "agents", "local-first", "compose", "wicked"]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- [ ] **Step 3: Write `.gitignore`**
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
__pycache__/
|
|
86
|
+
*.pyc
|
|
87
|
+
node_modules/
|
|
88
|
+
.pytest_cache/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- [ ] **Step 4: Write `python/loom/__init__.py`**
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
"""wicked-loom — orchestration runtime (compose surface)."""
|
|
95
|
+
|
|
96
|
+
__version__ = "0.1.0"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- [ ] **Step 5: Commit**
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
cd ~/Projects/wicked-loom
|
|
103
|
+
git add package.json .gitignore python/loom/__init__.py
|
|
104
|
+
git commit -m "chore: wicked-loom package skeleton"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Task 2: Node bin shim (launch python3)
|
|
110
|
+
|
|
111
|
+
**Files:**
|
|
112
|
+
- Create: `~/Projects/wicked-loom/bin/loom.mjs`
|
|
113
|
+
- Test: `~/Projects/wicked-loom/tests/test_cli.py` (the shim is smoke-tested via the CLI task; here we only assert it exists + is wired)
|
|
114
|
+
|
|
115
|
+
- [ ] **Step 1: Write the shim**
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
#!/usr/bin/env node
|
|
119
|
+
// Thin launcher: find python3 and exec `python3 -m loom <args>` with python/ on PYTHONPATH.
|
|
120
|
+
// All logic lives in the Python module; this file only bridges npx → python3.
|
|
121
|
+
import { spawnSync } from "node:child_process";
|
|
122
|
+
import { fileURLToPath } from "node:url";
|
|
123
|
+
import { dirname, join } from "node:path";
|
|
124
|
+
|
|
125
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
126
|
+
const pyRoot = join(here, "..", "python");
|
|
127
|
+
|
|
128
|
+
function findPython() {
|
|
129
|
+
for (const cand of ["python3", "python", "py"]) {
|
|
130
|
+
const r = spawnSync(cand, ["--version"], { stdio: "ignore" });
|
|
131
|
+
if (r.status === 0) return cand;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const python = findPython();
|
|
137
|
+
if (!python) {
|
|
138
|
+
console.error("[wicked-loom] python3 is required but was not found on PATH.");
|
|
139
|
+
process.exit(127);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const env = { ...process.env, PYTHONPATH: pyRoot + (process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : "") };
|
|
143
|
+
const res = spawnSync(python, ["-m", "loom", ...process.argv.slice(2)], { stdio: "inherit", env });
|
|
144
|
+
process.exit(res.status === null ? 1 : res.status);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- [ ] **Step 2: Make it executable + smoke it (will fail until `__main__` exists)**
|
|
148
|
+
|
|
149
|
+
Run: `chmod +x ~/Projects/wicked-loom/bin/loom.mjs && node ~/Projects/wicked-loom/bin/loom.mjs --help`
|
|
150
|
+
Expected: a Python error (`No module named loom.__main__`) — proves the shim finds python3 and dispatches. (Green after Task 6.)
|
|
151
|
+
|
|
152
|
+
- [ ] **Step 3: Commit**
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
cd ~/Projects/wicked-loom
|
|
156
|
+
git add bin/loom.mjs
|
|
157
|
+
git commit -m "feat: node bin shim launches bundled python"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Task 3: Peer manifest
|
|
163
|
+
|
|
164
|
+
**Files:**
|
|
165
|
+
- Create: `~/Projects/wicked-loom/python/loom/manifest.py`
|
|
166
|
+
- Test: `~/Projects/wicked-loom/tests/test_manifest.py`
|
|
167
|
+
|
|
168
|
+
- [ ] **Step 1: Write the failing test**
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from loom import manifest
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_known_peers_present():
|
|
175
|
+
assert set(manifest.PEERS) == {"vault", "testing", "brain", "bus"}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_each_peer_has_required_fields():
|
|
179
|
+
for name, peer in manifest.PEERS.items():
|
|
180
|
+
assert peer.name == name
|
|
181
|
+
assert peer.npm_package.startswith("wicked-")
|
|
182
|
+
assert peer.env_var.startswith("WICKED_") and peer.env_var.endswith("_BIN")
|
|
183
|
+
assert peer.version_pin # non-empty
|
|
184
|
+
assert isinstance(peer.install_cmd, list) and peer.install_cmd
|
|
185
|
+
assert isinstance(peer.probe_cmd, list) and peer.probe_cmd
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_vault_pins_match_garden():
|
|
189
|
+
assert manifest.PEERS["vault"].version_pin == "0.3"
|
|
190
|
+
assert manifest.PEERS["vault"].env_var == "WICKED_VAULT_BIN"
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_get_unknown_peer_returns_none():
|
|
194
|
+
assert manifest.get("nope") is None
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
198
|
+
|
|
199
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_manifest.py -v`
|
|
200
|
+
Expected: FAIL — `ModuleNotFoundError: No module named 'loom.manifest'`
|
|
201
|
+
|
|
202
|
+
- [ ] **Step 3: Write `manifest.py`**
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
"""manifest.py — the wicked-* peer set: what each peer is and how to reach it.
|
|
206
|
+
|
|
207
|
+
Source of truth ported from wicked-garden docs/required-peers.md + plugin.json.
|
|
208
|
+
Version pins are the MAJOR.MINOR floor (the `^x.y` in plugin.json), compared by
|
|
209
|
+
compose.py. Install commands are headless (npm/npx) — the `/plugin install`
|
|
210
|
+
path is CC-UX sugar, not the only route.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
from __future__ import annotations
|
|
214
|
+
|
|
215
|
+
from dataclasses import dataclass
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass(frozen=True)
|
|
219
|
+
class Peer:
|
|
220
|
+
name: str
|
|
221
|
+
npm_package: str
|
|
222
|
+
env_var: str # runtime override env var, e.g. WICKED_VAULT_BIN
|
|
223
|
+
version_pin: str # MAJOR.MINOR floor, e.g. "0.3"
|
|
224
|
+
install_cmd: list[str] # headless install command
|
|
225
|
+
probe_cmd: list[str] # command to print the installed version
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
PEERS: dict[str, Peer] = {
|
|
229
|
+
"vault": Peer(
|
|
230
|
+
name="vault",
|
|
231
|
+
npm_package="wicked-vault",
|
|
232
|
+
env_var="WICKED_VAULT_BIN",
|
|
233
|
+
version_pin="0.3",
|
|
234
|
+
install_cmd=["npx", "wicked-vault-install"],
|
|
235
|
+
probe_cmd=["wicked-vault", "--version"],
|
|
236
|
+
),
|
|
237
|
+
"testing": Peer(
|
|
238
|
+
name="testing",
|
|
239
|
+
npm_package="wicked-testing",
|
|
240
|
+
env_var="WICKED_TESTING_BIN",
|
|
241
|
+
version_pin="0.3",
|
|
242
|
+
install_cmd=["npx", "wicked-testing", "install"],
|
|
243
|
+
probe_cmd=["wicked-testing", "--version"],
|
|
244
|
+
),
|
|
245
|
+
"brain": Peer(
|
|
246
|
+
name="brain",
|
|
247
|
+
npm_package="wicked-brain",
|
|
248
|
+
env_var="WICKED_BRAIN_BIN",
|
|
249
|
+
version_pin="0.14",
|
|
250
|
+
install_cmd=["npm", "install", "-g", "wicked-brain@latest"],
|
|
251
|
+
probe_cmd=["wicked-brain-server", "--version"],
|
|
252
|
+
),
|
|
253
|
+
"bus": Peer(
|
|
254
|
+
name="bus",
|
|
255
|
+
npm_package="wicked-bus",
|
|
256
|
+
env_var="WICKED_BUS_BIN",
|
|
257
|
+
version_pin="2.0",
|
|
258
|
+
install_cmd=["npm", "install", "-g", "wicked-bus@latest"],
|
|
259
|
+
probe_cmd=["wicked-bus", "--version"],
|
|
260
|
+
),
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def get(name: str) -> "Peer | None":
|
|
265
|
+
"""Return the Peer for ``name`` or None if unknown."""
|
|
266
|
+
return PEERS.get(name)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
270
|
+
|
|
271
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_manifest.py -v`
|
|
272
|
+
Expected: PASS (4 tests)
|
|
273
|
+
|
|
274
|
+
- [ ] **Step 5: Commit**
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
cd ~/Projects/wicked-loom
|
|
278
|
+
git add python/loom/manifest.py tests/test_manifest.py
|
|
279
|
+
git commit -m "feat: peer manifest (vault/testing/brain/bus)"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Task 4: Resolution ladder
|
|
285
|
+
|
|
286
|
+
**Files:**
|
|
287
|
+
- Create: `~/Projects/wicked-loom/python/loom/resolve.py`
|
|
288
|
+
- Test: `~/Projects/wicked-loom/tests/test_resolve.py`
|
|
289
|
+
|
|
290
|
+
The ladder, ported from `vault_gate.py`: `WICKED_<PEER>_BIN` env → `PATH` (shutil.which) → `npx <package>` fallback. Empty env var (`WICKED_VAULT_BIN=""`) is the **kill-switch** → returns None. (Garden also checks a config file + node_modules; omitted in 0.1 as YAGNI — add when a consumer needs them.)
|
|
291
|
+
|
|
292
|
+
- [ ] **Step 1: Write the failing test**
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
import shutil
|
|
296
|
+
from unittest.mock import patch
|
|
297
|
+
|
|
298
|
+
from loom import resolve
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def test_env_var_override_wins(monkeypatch):
|
|
302
|
+
monkeypatch.setenv("WICKED_VAULT_BIN", "/opt/custom/vault")
|
|
303
|
+
assert resolve.resolve("vault") == ["/opt/custom/vault"]
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def test_empty_env_var_is_killswitch(monkeypatch):
|
|
307
|
+
monkeypatch.setenv("WICKED_VAULT_BIN", "")
|
|
308
|
+
assert resolve.resolve("vault") is None
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def test_path_lookup_when_no_env(monkeypatch):
|
|
312
|
+
monkeypatch.delenv("WICKED_VAULT_BIN", raising=False)
|
|
313
|
+
with patch.object(shutil, "which", return_value="/usr/local/bin/wicked-vault"):
|
|
314
|
+
assert resolve.resolve("vault") == ["/usr/local/bin/wicked-vault"]
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_npx_fallback_when_not_on_path(monkeypatch):
|
|
318
|
+
monkeypatch.delenv("WICKED_VAULT_BIN", raising=False)
|
|
319
|
+
with patch.object(shutil, "which", return_value=None):
|
|
320
|
+
assert resolve.resolve("vault") == ["npx", "wicked-vault"]
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def test_unknown_peer_returns_none():
|
|
324
|
+
assert resolve.resolve("nope") is None
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
328
|
+
|
|
329
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_resolve.py -v`
|
|
330
|
+
Expected: FAIL — `ModuleNotFoundError: No module named 'loom.resolve'`
|
|
331
|
+
|
|
332
|
+
- [ ] **Step 3: Write `resolve.py`**
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
"""resolve.py — the runtime resolution ladder for a peer's runnable command.
|
|
336
|
+
|
|
337
|
+
Ladder (highest priority first):
|
|
338
|
+
1. WICKED_<PEER>_BIN env var — explicit override. Empty string is a
|
|
339
|
+
deliberate kill-switch (returns None, resolution short-circuits cleanly).
|
|
340
|
+
2. PATH — shutil.which(<npm_package>).
|
|
341
|
+
3. npx fallback — ["npx", "<npm_package>"].
|
|
342
|
+
|
|
343
|
+
Returns a command as a list[str] (argv-ready) or None when unresolvable /
|
|
344
|
+
killed / unknown peer. Pure + deterministic: no process is spawned here.
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
from __future__ import annotations
|
|
348
|
+
|
|
349
|
+
import os
|
|
350
|
+
import shutil
|
|
351
|
+
|
|
352
|
+
from loom import manifest
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def resolve(peer_name: str) -> "list[str] | None":
|
|
356
|
+
peer = manifest.get(peer_name)
|
|
357
|
+
if peer is None:
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
if peer.env_var in os.environ:
|
|
361
|
+
override = os.environ[peer.env_var].strip()
|
|
362
|
+
if override == "":
|
|
363
|
+
return None # kill-switch
|
|
364
|
+
return [override]
|
|
365
|
+
|
|
366
|
+
on_path = shutil.which(peer.npm_package)
|
|
367
|
+
if on_path:
|
|
368
|
+
return [on_path]
|
|
369
|
+
|
|
370
|
+
return ["npx", peer.npm_package]
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
374
|
+
|
|
375
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_resolve.py -v`
|
|
376
|
+
Expected: PASS (5 tests)
|
|
377
|
+
|
|
378
|
+
- [ ] **Step 5: Commit**
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
cd ~/Projects/wicked-loom
|
|
382
|
+
git add python/loom/resolve.py tests/test_resolve.py
|
|
383
|
+
git commit -m "feat: peer resolution ladder + kill-switch"
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Task 5: Version-check + install orchestration
|
|
389
|
+
|
|
390
|
+
**Files:**
|
|
391
|
+
- Create: `~/Projects/wicked-loom/python/loom/compose.py`
|
|
392
|
+
- Test: `~/Projects/wicked-loom/tests/test_compose.py`
|
|
393
|
+
|
|
394
|
+
`check_peer` resolves the peer, runs its `probe_cmd`, parses a semver-ish version, and compares MAJOR.MINOR against the pin. `install_peer` runs the headless `install_cmd`. Both return structured dicts; neither raises (R4 — surface errors as data). A subprocess runner is injected so tests never spawn real processes (T1/T3).
|
|
395
|
+
|
|
396
|
+
- [ ] **Step 1: Write the failing test**
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
from unittest.mock import patch
|
|
400
|
+
|
|
401
|
+
from loom import compose
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _runner(stdout="", code=0):
|
|
405
|
+
def run(cmd, timeout=None):
|
|
406
|
+
return compose.RunResult(returncode=code, stdout=stdout, stderr="")
|
|
407
|
+
return run
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def test_check_peer_satisfied_when_version_meets_pin():
|
|
411
|
+
with patch.object(compose, "resolve", return_value=["wicked-vault"]):
|
|
412
|
+
r = compose.check_peer("vault", run=_runner(stdout="wicked-vault 0.3.2\n"))
|
|
413
|
+
assert r["status"] == "ok"
|
|
414
|
+
assert r["installed"] == "0.3.2"
|
|
415
|
+
assert r["pin"] == "0.3"
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def test_check_peer_below_pin_is_drift():
|
|
419
|
+
with patch.object(compose, "resolve", return_value=["wicked-vault"]):
|
|
420
|
+
r = compose.check_peer("vault", run=_runner(stdout="0.2.9"))
|
|
421
|
+
assert r["status"] == "drift"
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def test_check_peer_unresolvable_is_missing():
|
|
425
|
+
with patch.object(compose, "resolve", return_value=None):
|
|
426
|
+
r = compose.check_peer("vault", run=_runner())
|
|
427
|
+
assert r["status"] == "missing"
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def test_check_peer_probe_failure_is_error():
|
|
431
|
+
with patch.object(compose, "resolve", return_value=["wicked-vault"]):
|
|
432
|
+
r = compose.check_peer("vault", run=_runner(code=1))
|
|
433
|
+
assert r["status"] == "error"
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def test_install_peer_runs_install_cmd_and_reports():
|
|
437
|
+
calls = []
|
|
438
|
+
|
|
439
|
+
def run(cmd, timeout=None):
|
|
440
|
+
calls.append(cmd)
|
|
441
|
+
return compose.RunResult(returncode=0, stdout="ok", stderr="")
|
|
442
|
+
|
|
443
|
+
r = compose.install_peer("vault", run=run)
|
|
444
|
+
assert calls == [["npx", "wicked-vault-install"]]
|
|
445
|
+
assert r["status"] == "installed"
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def test_install_unknown_peer_is_error():
|
|
449
|
+
r = compose.install_peer("nope", run=_runner())
|
|
450
|
+
assert r["status"] == "error"
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def test_check_all_returns_one_row_per_peer():
|
|
454
|
+
with patch.object(compose, "resolve", return_value=["x"]):
|
|
455
|
+
rows = compose.check_all(run=_runner(stdout="9.9.9"))
|
|
456
|
+
assert {row["peer"] for row in rows} == {"vault", "testing", "brain", "bus"}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
460
|
+
|
|
461
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_compose.py -v`
|
|
462
|
+
Expected: FAIL — `ModuleNotFoundError: No module named 'loom.compose'`
|
|
463
|
+
|
|
464
|
+
- [ ] **Step 3: Write `compose.py`**
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
"""compose.py — version-check + install orchestration over the peer set.
|
|
468
|
+
|
|
469
|
+
check_peer: resolve → probe → parse version → compare MAJOR.MINOR to the pin.
|
|
470
|
+
install_peer: run the peer's headless install command.
|
|
471
|
+
check_all: one check_peer row per known peer.
|
|
472
|
+
|
|
473
|
+
Subprocess execution is injected (the ``run`` parameter) so callers (and tests)
|
|
474
|
+
control side effects. Nothing here raises — failures are returned as status
|
|
475
|
+
rows ("ok" | "drift" | "missing" | "error" | "installed" | "install-failed").
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
from __future__ import annotations
|
|
479
|
+
|
|
480
|
+
import re
|
|
481
|
+
import subprocess
|
|
482
|
+
from dataclasses import dataclass
|
|
483
|
+
from typing import Callable
|
|
484
|
+
|
|
485
|
+
from loom import manifest
|
|
486
|
+
from loom.resolve import resolve
|
|
487
|
+
|
|
488
|
+
_VERSION_RE = re.compile(r"(\d+)\.(\d+)(?:\.(\d+))?")
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@dataclass
|
|
492
|
+
class RunResult:
|
|
493
|
+
returncode: int
|
|
494
|
+
stdout: str
|
|
495
|
+
stderr: str
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
Runner = Callable[..., RunResult]
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def _default_run(cmd: list[str], timeout: int = 30) -> RunResult:
|
|
502
|
+
p = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
|
503
|
+
return RunResult(returncode=p.returncode, stdout=p.stdout, stderr=p.stderr)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _parse_version(text: str) -> "str | None":
|
|
507
|
+
m = _VERSION_RE.search(text or "")
|
|
508
|
+
if not m:
|
|
509
|
+
return None
|
|
510
|
+
major, minor, patch = m.group(1), m.group(2), m.group(3) or "0"
|
|
511
|
+
return f"{major}.{minor}.{patch}"
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _meets_pin(installed: str, pin: str) -> bool:
|
|
515
|
+
iv = _parse_version(installed)
|
|
516
|
+
pv = _parse_version(pin) or pin
|
|
517
|
+
if iv is None:
|
|
518
|
+
return False
|
|
519
|
+
ip = [int(x) for x in iv.split(".")]
|
|
520
|
+
pp = [int(x) for x in (_parse_version(pin) or "0.0.0").split(".")]
|
|
521
|
+
# Compare MAJOR.MINOR floor.
|
|
522
|
+
return (ip[0], ip[1]) >= (pp[0], pp[1])
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def check_peer(name: str, run: Runner = _default_run) -> dict:
|
|
526
|
+
peer = manifest.get(name)
|
|
527
|
+
if peer is None:
|
|
528
|
+
return {"peer": name, "status": "error", "detail": "unknown peer"}
|
|
529
|
+
|
|
530
|
+
cmd = resolve(name)
|
|
531
|
+
if cmd is None:
|
|
532
|
+
return {"peer": name, "status": "missing", "pin": peer.version_pin}
|
|
533
|
+
|
|
534
|
+
try:
|
|
535
|
+
result = run(cmd[:1] + peer.probe_cmd[1:] if cmd[0] == peer.probe_cmd[0]
|
|
536
|
+
else cmd + peer.probe_cmd[1:])
|
|
537
|
+
except Exception as e: # noqa: BLE001 — surface as data, never crash (R4)
|
|
538
|
+
return {"peer": name, "status": "error", "detail": str(e), "pin": peer.version_pin}
|
|
539
|
+
|
|
540
|
+
if result.returncode != 0:
|
|
541
|
+
return {"peer": name, "status": "error", "detail": result.stderr.strip(),
|
|
542
|
+
"pin": peer.version_pin}
|
|
543
|
+
|
|
544
|
+
installed = _parse_version(result.stdout)
|
|
545
|
+
if installed is None:
|
|
546
|
+
return {"peer": name, "status": "error", "detail": "unparseable version",
|
|
547
|
+
"pin": peer.version_pin}
|
|
548
|
+
|
|
549
|
+
status = "ok" if _meets_pin(installed, peer.version_pin) else "drift"
|
|
550
|
+
return {"peer": name, "status": status, "installed": installed, "pin": peer.version_pin}
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def install_peer(name: str, run: Runner = _default_run) -> dict:
|
|
554
|
+
peer = manifest.get(name)
|
|
555
|
+
if peer is None:
|
|
556
|
+
return {"peer": name, "status": "error", "detail": "unknown peer"}
|
|
557
|
+
try:
|
|
558
|
+
result = run(peer.install_cmd, timeout=300)
|
|
559
|
+
except Exception as e: # noqa: BLE001
|
|
560
|
+
return {"peer": name, "status": "install-failed", "detail": str(e)}
|
|
561
|
+
if result.returncode != 0:
|
|
562
|
+
return {"peer": name, "status": "install-failed", "detail": result.stderr.strip()}
|
|
563
|
+
return {"peer": name, "status": "installed"}
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def check_all(run: Runner = _default_run) -> list[dict]:
|
|
567
|
+
return [check_peer(name, run=run) for name in manifest.PEERS]
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
> Note for the implementer: the `check_peer` `run(...)` argument constructs the probe command from the resolved command + the probe's trailing args. Keep it simple — if `resolve` returned `["npx", "wicked-vault"]`, the probe is `["npx", "wicked-vault", "--version"]`; if it returned `["/usr/local/bin/wicked-vault"]`, the probe is `["/usr/local/bin/wicked-vault", "--version"]`. The expression above does exactly that; if it reads awkwardly, refactor to an explicit `_probe_command(cmd, peer)` helper with its own test — but keep behavior identical.
|
|
571
|
+
|
|
572
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
573
|
+
|
|
574
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_compose.py -v`
|
|
575
|
+
Expected: PASS (7 tests)
|
|
576
|
+
|
|
577
|
+
- [ ] **Step 5: Commit**
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
cd ~/Projects/wicked-loom
|
|
581
|
+
git add python/loom/compose.py tests/test_compose.py
|
|
582
|
+
git commit -m "feat: compose version-check + install orchestration"
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Task 6: CLI dispatch + `__main__`
|
|
588
|
+
|
|
589
|
+
**Files:**
|
|
590
|
+
- Create: `~/Projects/wicked-loom/python/loom/cli.py`
|
|
591
|
+
- Create: `~/Projects/wicked-loom/python/loom/__main__.py`
|
|
592
|
+
- Test: `~/Projects/wicked-loom/tests/test_cli.py`
|
|
593
|
+
|
|
594
|
+
`cli.main(argv)` dispatches `resolve <peer>`, `doctor`, `compose install [--peer X]` and prints JSON to stdout, returning an exit code. Logic stays in resolve/compose; cli only parses args + formats output (R6 — no god function).
|
|
595
|
+
|
|
596
|
+
- [ ] **Step 1: Write the failing test**
|
|
597
|
+
|
|
598
|
+
```python
|
|
599
|
+
import io
|
|
600
|
+
import json
|
|
601
|
+
from contextlib import redirect_stdout
|
|
602
|
+
from unittest.mock import patch
|
|
603
|
+
|
|
604
|
+
from loom import cli
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def _run(argv):
|
|
608
|
+
buf = io.StringIO()
|
|
609
|
+
with redirect_stdout(buf):
|
|
610
|
+
code = cli.main(argv)
|
|
611
|
+
return code, buf.getvalue()
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def test_resolve_prints_command():
|
|
615
|
+
with patch("loom.cli.resolve", return_value=["npx", "wicked-vault"]):
|
|
616
|
+
code, out = _run(["resolve", "vault"])
|
|
617
|
+
assert code == 0
|
|
618
|
+
assert json.loads(out)["command"] == ["npx", "wicked-vault"]
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def test_resolve_unresolvable_exits_nonzero():
|
|
622
|
+
with patch("loom.cli.resolve", return_value=None):
|
|
623
|
+
code, out = _run(["resolve", "vault"])
|
|
624
|
+
assert code == 1
|
|
625
|
+
assert json.loads(out)["command"] is None
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def test_doctor_prints_all_rows():
|
|
629
|
+
rows = [{"peer": "vault", "status": "ok"}]
|
|
630
|
+
with patch("loom.cli.check_all", return_value=rows):
|
|
631
|
+
code, out = _run(["doctor"])
|
|
632
|
+
assert code == 0
|
|
633
|
+
assert json.loads(out)["peers"] == rows
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def test_doctor_exits_nonzero_on_missing_peer():
|
|
637
|
+
rows = [{"peer": "vault", "status": "missing"}]
|
|
638
|
+
with patch("loom.cli.check_all", return_value=rows):
|
|
639
|
+
code, _ = _run(["doctor"])
|
|
640
|
+
assert code == 1
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def test_compose_install_targets_one_peer():
|
|
644
|
+
with patch("loom.cli.install_peer", return_value={"peer": "vault", "status": "installed"}) as m:
|
|
645
|
+
code, out = _run(["compose", "install", "--peer", "vault"])
|
|
646
|
+
assert code == 0
|
|
647
|
+
m.assert_called_once_with("vault")
|
|
648
|
+
assert json.loads(out)["results"][0]["status"] == "installed"
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def test_unknown_command_exits_two():
|
|
652
|
+
code, _ = _run(["frobnicate"])
|
|
653
|
+
assert code == 2
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
657
|
+
|
|
658
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_cli.py -v`
|
|
659
|
+
Expected: FAIL — `ModuleNotFoundError: No module named 'loom.cli'`
|
|
660
|
+
|
|
661
|
+
- [ ] **Step 3: Write `cli.py`**
|
|
662
|
+
|
|
663
|
+
```python
|
|
664
|
+
"""cli.py — argument dispatch + JSON output for the compose surface.
|
|
665
|
+
|
|
666
|
+
Commands:
|
|
667
|
+
loom resolve <peer> -> {"peer","command"} exit 0/1
|
|
668
|
+
loom doctor -> {"peers":[check rows]} exit 0/1
|
|
669
|
+
loom compose install [--peer X] -> {"results":[install rows]} exit 0/1
|
|
670
|
+
|
|
671
|
+
No business logic lives here — only parsing + formatting.
|
|
672
|
+
"""
|
|
673
|
+
|
|
674
|
+
from __future__ import annotations
|
|
675
|
+
|
|
676
|
+
import json
|
|
677
|
+
import sys
|
|
678
|
+
|
|
679
|
+
from loom import manifest
|
|
680
|
+
from loom.compose import check_all, install_peer
|
|
681
|
+
from loom.resolve import resolve
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _emit(obj: dict) -> None:
|
|
685
|
+
sys.stdout.write(json.dumps(obj) + "\n")
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def _cmd_resolve(args: list[str]) -> int:
|
|
689
|
+
if not args:
|
|
690
|
+
_emit({"error": "usage: loom resolve <peer>"})
|
|
691
|
+
return 2
|
|
692
|
+
cmd = resolve(args[0])
|
|
693
|
+
_emit({"peer": args[0], "command": cmd})
|
|
694
|
+
return 0 if cmd is not None else 1
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def _cmd_doctor(_args: list[str]) -> int:
|
|
698
|
+
rows = check_all()
|
|
699
|
+
_emit({"peers": rows})
|
|
700
|
+
return 0 if all(r.get("status") == "ok" for r in rows) else 1
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def _cmd_compose(args: list[str]) -> int:
|
|
704
|
+
if not args or args[0] != "install":
|
|
705
|
+
_emit({"error": "usage: loom compose install [--peer <name>]"})
|
|
706
|
+
return 2
|
|
707
|
+
target = None
|
|
708
|
+
if "--peer" in args:
|
|
709
|
+
i = args.index("--peer")
|
|
710
|
+
if i + 1 < len(args):
|
|
711
|
+
target = args[i + 1]
|
|
712
|
+
names = [target] if target else list(manifest.PEERS)
|
|
713
|
+
results = [install_peer(n) for n in names]
|
|
714
|
+
_emit({"results": results})
|
|
715
|
+
return 0 if all(r.get("status") == "installed" for r in results) else 1
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
_DISPATCH = {"resolve": _cmd_resolve, "doctor": _cmd_doctor, "compose": _cmd_compose}
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def main(argv: "list[str] | None" = None) -> int:
|
|
722
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
723
|
+
if not argv or argv[0] in ("-h", "--help"):
|
|
724
|
+
_emit({"commands": list(_DISPATCH)})
|
|
725
|
+
return 0
|
|
726
|
+
handler = _DISPATCH.get(argv[0])
|
|
727
|
+
if handler is None:
|
|
728
|
+
_emit({"error": f"unknown command: {argv[0]}", "commands": list(_DISPATCH)})
|
|
729
|
+
return 2
|
|
730
|
+
return handler(argv[1:])
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
- [ ] **Step 4: Write `__main__.py`**
|
|
734
|
+
|
|
735
|
+
```python
|
|
736
|
+
"""`python3 -m loom` entry point."""
|
|
737
|
+
|
|
738
|
+
import sys
|
|
739
|
+
|
|
740
|
+
from loom.cli import main
|
|
741
|
+
|
|
742
|
+
if __name__ == "__main__":
|
|
743
|
+
sys.exit(main())
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
- [ ] **Step 5: Run test to verify it passes**
|
|
747
|
+
|
|
748
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest tests/test_cli.py -v`
|
|
749
|
+
Expected: PASS (6 tests)
|
|
750
|
+
|
|
751
|
+
- [ ] **Step 6: Smoke the full path through the Node shim**
|
|
752
|
+
|
|
753
|
+
Run: `cd ~/Projects/wicked-loom && node bin/loom.mjs doctor`
|
|
754
|
+
Expected: a JSON line `{"peers": [...]}` with one row per peer (statuses depend on what's installed locally; the point is the shim → python3 → cli path works end-to-end).
|
|
755
|
+
|
|
756
|
+
- [ ] **Step 7: Commit**
|
|
757
|
+
|
|
758
|
+
```bash
|
|
759
|
+
cd ~/Projects/wicked-loom
|
|
760
|
+
git add python/loom/cli.py python/loom/__main__.py tests/test_cli.py
|
|
761
|
+
git commit -m "feat: compose CLI (resolve/doctor/compose install) + __main__"
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## Task 7: README + full suite + publish-readiness
|
|
767
|
+
|
|
768
|
+
**Files:**
|
|
769
|
+
- Create: `~/Projects/wicked-loom/README.md`
|
|
770
|
+
|
|
771
|
+
- [ ] **Step 1: Write `README.md`**
|
|
772
|
+
|
|
773
|
+
```markdown
|
|
774
|
+
# wicked-loom
|
|
775
|
+
|
|
776
|
+
Local-first orchestration runtime for agent ecosystems. **0.1 ships the
|
|
777
|
+
`compose` surface** — it resolves, version-checks, and installs the `wicked-*`
|
|
778
|
+
peer set. Conduct (gate/flow) ships separately.
|
|
779
|
+
|
|
780
|
+
## Use
|
|
781
|
+
|
|
782
|
+
npx wicked-loom doctor # check every peer
|
|
783
|
+
npx wicked-loom resolve vault # print vault's runnable command
|
|
784
|
+
npx wicked-loom compose install --peer bus # install one peer
|
|
785
|
+
|
|
786
|
+
Requires `python3` on PATH (the npm package launches a bundled Python core).
|
|
787
|
+
|
|
788
|
+
## Resolution ladder
|
|
789
|
+
|
|
790
|
+
For each peer: `WICKED_<PEER>_BIN` env (empty = kill-switch) → `PATH` →
|
|
791
|
+
`npx <package>`.
|
|
792
|
+
|
|
793
|
+
## Peers
|
|
794
|
+
|
|
795
|
+
vault · testing · brain · bus — pins mirror wicked-garden's `required-peers`.
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
- [ ] **Step 2: Run the full suite**
|
|
799
|
+
|
|
800
|
+
Run: `cd ~/Projects/wicked-loom && PYTHONPATH=python python3 -m pytest -v`
|
|
801
|
+
Expected: PASS — 22 tests (4 manifest + 5 resolve + 7 compose + 6 cli).
|
|
802
|
+
|
|
803
|
+
- [ ] **Step 3: Dry-run the npm package contents**
|
|
804
|
+
|
|
805
|
+
Run: `cd ~/Projects/wicked-loom && npm pack --dry-run`
|
|
806
|
+
Expected: the tarball lists `bin/`, `python/`, `README.md`, `package.json` — and NOT `tests/` or `__pycache__/`.
|
|
807
|
+
|
|
808
|
+
- [ ] **Step 4: Commit**
|
|
809
|
+
|
|
810
|
+
```bash
|
|
811
|
+
cd ~/Projects/wicked-loom
|
|
812
|
+
git add README.md
|
|
813
|
+
git commit -m "docs: README + publish-readiness"
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
- [ ] **Step 5: STOP — publish is a human-gated outward action**
|
|
817
|
+
|
|
818
|
+
Do **not** run `npm publish` autonomously. Report to the operator that `wicked-loom@0.1.0` is publish-ready (suite green, `npm pack` clean) and let them publish + create the GitHub repo. Garden integration (the resolve cutover) is a later plan.
|
|
819
|
+
|
|
820
|
+
---
|
|
821
|
+
|
|
822
|
+
## Self-Review
|
|
823
|
+
|
|
824
|
+
**Spec coverage (Plan A scope = compose):**
|
|
825
|
+
- COMPOSE: declare (manifest, Task 3) · resolve ladder + kill-switch (Task 4) · version-check + install (Task 5) · CLI surface `resolve`/`doctor`/`compose install` (Task 6). ✓ matches spec §4.1 / §4.3.
|
|
826
|
+
- D5 (npm + python via subprocess): package.json bin → Node shim → python3 (Tasks 1–2, 6). ✓
|
|
827
|
+
- Standalone-usable by a non-garden consumer (success criterion #1): `npx wicked-loom doctor` works with zero garden present. ✓
|
|
828
|
+
- **Deferred (correctly out of scope for Plan A):** conduct/gate/flow/park (§4.2), bus event contract (§4.3 #2/#3), the archetype→flow seam (§3.1), garden cutover (§7 step 2). These belong to Plan B + the cutover plan. No gap — intentional decomposition.
|
|
829
|
+
|
|
830
|
+
**Placeholder scan:** No TBD/TODO; every code step has complete code; the one prose note (Task 5 probe-command construction) shows the exact expression + the refactor option with identical behavior. ✓
|
|
831
|
+
|
|
832
|
+
**Type consistency:** `Peer` fields (`name`, `npm_package`, `env_var`, `version_pin`, `install_cmd`, `probe_cmd`) are used identically in manifest/resolve/compose/cli. `resolve()` returns `list[str] | None` everywhere. `RunResult(returncode, stdout, stderr)` consistent across compose + tests. `check_peer`/`install_peer`/`check_all` signatures match their cli call sites. Status vocabulary (`ok`/`drift`/`missing`/`error`/`installed`/`install-failed`) consistent. ✓
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
## Execution Handoff
|
|
837
|
+
|
|
838
|
+
Plan complete. Two execution options:
|
|
839
|
+
|
|
840
|
+
1. **Subagent-Driven (recommended)** — fresh subagent per task, review between tasks.
|
|
841
|
+
2. **Inline Execution** — execute in this session with checkpoints.
|
|
842
|
+
|
|
843
|
+
Note: execution happens in a **new repo** (`~/Projects/wicked-loom`), not in wicked-garden.
|