@jaimevalasek/aioson 1.5.1 → 1.7.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/CHANGELOG.md +49 -0
- package/README.md +729 -226
- package/docs/design-previews/aurora-command-ui-website.html +884 -0
- package/docs/design-previews/aurora-command-ui.html +682 -0
- package/docs/design-previews/bold-editorial-ui-website.html +658 -0
- package/docs/design-previews/bold-editorial-ui.html +717 -0
- package/docs/design-previews/clean-saas-ui-website.html +1202 -0
- package/docs/design-previews/clean-saas-ui.html +549 -0
- package/docs/design-previews/cognitive-core-ui-website.html +1009 -0
- package/docs/design-previews/cognitive-core-ui.html +463 -0
- package/docs/design-previews/glassmorphism-ui-website.html +572 -0
- package/docs/design-previews/glassmorphism-ui.html +886 -0
- package/docs/design-previews/index.html +699 -0
- package/docs/design-previews/interface-design-website.html +1187 -0
- package/docs/design-previews/interface-design.html +513 -0
- package/docs/design-previews/neo-brutalist-ui-website.html +621 -0
- package/docs/design-previews/neo-brutalist-ui.html +797 -0
- package/docs/design-previews/premium-command-center-ui-website.html +1217 -0
- package/docs/design-previews/premium-command-center-ui.html +552 -0
- package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
- package/docs/design-previews/warm-craft-ui-website.html +684 -0
- package/docs/design-previews/warm-craft-ui.html +739 -0
- package/docs/en/cli-reference.md +20 -9
- package/docs/integrations/sdlc-genius-boundary.md +76 -0
- package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
- package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
- package/docs/integrations/sdlc-genius-review-samples.md +86 -0
- package/docs/pt/README.md +10 -0
- package/docs/pt/agent-sharding.md +132 -0
- package/docs/pt/agentes.md +9 -2
- package/docs/pt/busca-de-contexto.md +129 -0
- package/docs/pt/cache-de-contexto.md +156 -0
- package/docs/pt/comandos-cli.md +915 -1
- package/docs/pt/design-hybrid-forge.md +356 -0
- package/docs/pt/devlog-pipeline.md +270 -0
- package/docs/pt/fluxo-artefatos.md +178 -0
- package/docs/pt/hooks-session-guard.md +454 -0
- package/docs/pt/inicio-rapido.md +54 -3
- package/docs/pt/inteligencia-adaptativa.md +324 -0
- package/docs/pt/monitor-de-contexto.md +158 -0
- package/docs/pt/recuperacao-de-sessao.md +125 -0
- package/docs/pt/sandbox.md +125 -0
- package/docs/pt/sdd-automation-scripts.md +557 -0
- package/docs/pt/site-forge.md +309 -0
- package/docs/pt/skills.md +98 -6
- package/docs/pt/spec-learnings-pipeline.md +265 -0
- package/package.json +1 -1
- package/src/a2a/client.js +165 -0
- package/src/a2a/server.js +223 -0
- package/src/agent-loader.js +280 -0
- package/src/cli.js +329 -1
- package/src/commands/agent-audit.js +397 -0
- package/src/commands/agent-export-skill.js +229 -0
- package/src/commands/agent-loader.js +85 -0
- package/src/commands/artifact-validate.js +189 -0
- package/src/commands/brief-gen.js +405 -0
- package/src/commands/brief-validate.js +65 -0
- package/src/commands/classify.js +256 -0
- package/src/commands/context-cache.js +90 -0
- package/src/commands/context-compact.js +49 -0
- package/src/commands/context-health.js +175 -0
- package/src/commands/context-monitor.js +163 -0
- package/src/commands/context-search.js +66 -0
- package/src/commands/context-trim.js +177 -0
- package/src/commands/design-hybrid-options.js +385 -0
- package/src/commands/detect-test-runner.js +55 -0
- package/src/commands/devlog-export-brains.js +27 -0
- package/src/commands/devlog-process.js +292 -0
- package/src/commands/devlog-watch.js +131 -0
- package/src/commands/feature-close.js +165 -0
- package/src/commands/gate-check.js +228 -0
- package/src/commands/health.js +214 -0
- package/src/commands/hooks-emit.js +253 -0
- package/src/commands/hooks-install.js +347 -0
- package/src/commands/init.js +54 -13
- package/src/commands/install.js +52 -13
- package/src/commands/learning-auto-promote.js +195 -0
- package/src/commands/learning-evolve.js +364 -0
- package/src/commands/learning-export.js +103 -0
- package/src/commands/learning-rollback.js +164 -0
- package/src/commands/live.js +59 -1
- package/src/commands/pattern-detect.js +33 -0
- package/src/commands/preflight-context.js +30 -0
- package/src/commands/preflight.js +208 -0
- package/src/commands/pulse-update.js +130 -0
- package/src/commands/recovery.js +43 -0
- package/src/commands/runner-daemon.js +274 -0
- package/src/commands/runner-plan.js +70 -0
- package/src/commands/runner-queue-from-plan.js +166 -0
- package/src/commands/runner-queue.js +189 -0
- package/src/commands/runner-run.js +129 -0
- package/src/commands/runtime.js +47 -1
- package/src/commands/sandbox.js +37 -0
- package/src/commands/self-implement-loop.js +256 -0
- package/src/commands/session-guard.js +218 -0
- package/src/commands/setup-context.js +22 -2
- package/src/commands/setup.js +178 -0
- package/src/commands/sizing.js +165 -0
- package/src/commands/skill.js +144 -32
- package/src/commands/spec-checkpoint.js +177 -0
- package/src/commands/spec-status.js +79 -0
- package/src/commands/spec-sync.js +190 -0
- package/src/commands/spec-tasks.js +288 -0
- package/src/commands/squad-autorun.js +1220 -0
- package/src/commands/squad-bus.js +217 -0
- package/src/commands/squad-card.js +149 -0
- package/src/commands/squad-daemon.js +134 -0
- package/src/commands/squad-dependency-graph.js +164 -0
- package/src/commands/squad-review.js +106 -0
- package/src/commands/squad-scaffold.js +55 -0
- package/src/commands/squad-tool-register.js +157 -0
- package/src/commands/state-save.js +122 -0
- package/src/commands/tool-registry-cmd.js +232 -0
- package/src/commands/update.js +9 -0
- package/src/commands/verify-gate.js +572 -0
- package/src/commands/workflow-execute.js +241 -0
- package/src/constants.js +18 -0
- package/src/context-cache.js +159 -0
- package/src/context-search.js +326 -0
- package/src/design-variation-catalog.js +503 -0
- package/src/i18n/messages/en.js +32 -2
- package/src/i18n/messages/es.js +30 -2
- package/src/i18n/messages/fr.js +30 -2
- package/src/i18n/messages/pt-BR.js +32 -2
- package/src/install-animation.js +260 -0
- package/src/install-profile.js +143 -0
- package/src/install-wizard.js +475 -0
- package/src/installer.js +44 -10
- package/src/lib/health-check.js +158 -0
- package/src/lib/hook-protocol.js +76 -0
- package/src/mcp/apps/squad-dashboard/app.js +163 -0
- package/src/mcp/apps/squad-dashboard/index.html +261 -0
- package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
- package/src/mcp/resources/squad-state.js +130 -0
- package/src/parser.js +7 -1
- package/src/preflight-engine.js +443 -0
- package/src/recovery-context-session.js +154 -0
- package/src/runner/cascade.js +97 -0
- package/src/runner/cli-launcher.js +109 -0
- package/src/runner/plan-importer.js +63 -0
- package/src/runner/queue-store.js +159 -0
- package/src/runtime-store.js +158 -4
- package/src/sandbox.js +177 -0
- package/src/squad/agent-teams-adapter.js +264 -0
- package/src/squad/brief-validator.js +350 -0
- package/src/squad/bus-bridge.js +140 -0
- package/src/squad/context-compactor.js +265 -0
- package/src/squad/cross-ai-synthesizer.js +250 -0
- package/src/squad/hooks-generator.js +196 -0
- package/src/squad/inter-squad-events.js +175 -0
- package/src/squad/intra-bus.js +345 -0
- package/src/squad/learning-extractor.js +213 -0
- package/src/squad/pattern-detector.js +365 -0
- package/src/squad/preflight-context.js +296 -0
- package/src/squad/recovery-context.js +242 -71
- package/src/squad/reflection.js +365 -0
- package/src/squad/squad-scaffold.js +177 -0
- package/src/squad/state-manager.js +310 -0
- package/src/squad/task-decomposer.js +652 -0
- package/src/squad/verify-gate.js +303 -0
- package/src/tool-executor.js +94 -0
- package/src/updater.js +10 -3
- package/src/worker-runner.js +186 -1
- package/template/.aioson/agents/analyst.md +119 -3
- package/template/.aioson/agents/architect.md +98 -0
- package/template/.aioson/agents/design-hybrid-forge.md +141 -0
- package/template/.aioson/agents/dev.md +335 -14
- package/template/.aioson/agents/deyvin.md +117 -2
- package/template/.aioson/agents/discovery-design-doc.md +44 -0
- package/template/.aioson/agents/genome.md +14 -0
- package/template/.aioson/agents/neo.md +78 -1
- package/template/.aioson/agents/orache.md +50 -4
- package/template/.aioson/agents/orchestrator.md +197 -1
- package/template/.aioson/agents/pm.md +93 -0
- package/template/.aioson/agents/product.md +77 -4
- package/template/.aioson/agents/profiler-enricher.md +14 -0
- package/template/.aioson/agents/profiler-forge.md +14 -0
- package/template/.aioson/agents/profiler-researcher.md +14 -0
- package/template/.aioson/agents/qa.md +249 -19
- package/template/.aioson/agents/setup.md +144 -12
- package/template/.aioson/agents/sheldon.md +237 -11
- package/template/.aioson/agents/site-forge.md +1753 -0
- package/template/.aioson/agents/squad.md +162 -0
- package/template/.aioson/agents/tester.md +209 -0
- package/template/.aioson/agents/ux-ui.md +34 -1
- package/template/.aioson/brains/README.md +128 -0
- package/template/.aioson/brains/_index.json +16 -0
- package/template/.aioson/brains/scripts/query.js +103 -0
- package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
- package/template/.aioson/config.md +158 -13
- package/template/.aioson/constitution.md +33 -0
- package/template/.aioson/context/forensics/.gitkeep +0 -0
- package/template/.aioson/context/project-pulse.md +34 -0
- package/template/.aioson/context/seeds/seed-example.md +27 -0
- package/template/.aioson/context/user-profile.md +42 -0
- package/template/.aioson/docs/LAYERS.md +79 -0
- package/template/.aioson/docs/README.md +76 -0
- package/template/.aioson/docs/example-external-api-context.md +72 -0
- package/template/.aioson/locales/en/agents/architect.md +17 -0
- package/template/.aioson/locales/en/agents/dev.md +79 -13
- package/template/.aioson/locales/en/agents/orache.md +6 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
- package/template/.aioson/locales/en/agents/product.md +50 -0
- package/template/.aioson/locales/en/agents/setup.md +33 -1
- package/template/.aioson/locales/en/agents/sheldon.md +115 -0
- package/template/.aioson/locales/en/agents/squad.md +14 -0
- package/template/.aioson/locales/en/agents/tester.md +6 -0
- package/template/.aioson/locales/es/agents/analyst.md +2 -0
- package/template/.aioson/locales/es/agents/architect.md +19 -0
- package/template/.aioson/locales/es/agents/dev.md +64 -4
- package/template/.aioson/locales/es/agents/deyvin.md +2 -0
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
- package/template/.aioson/locales/es/agents/genome.md +2 -0
- package/template/.aioson/locales/es/agents/neo.md +2 -0
- package/template/.aioson/locales/es/agents/orache.md +2 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/es/agents/pair.md +2 -0
- package/template/.aioson/locales/es/agents/pm.md +2 -0
- package/template/.aioson/locales/es/agents/product.md +52 -0
- package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
- package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
- package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
- package/template/.aioson/locales/es/agents/qa.md +2 -0
- package/template/.aioson/locales/es/agents/setup.md +35 -1
- package/template/.aioson/locales/es/agents/sheldon.md +117 -0
- package/template/.aioson/locales/es/agents/squad.md +16 -0
- package/template/.aioson/locales/es/agents/tester.md +9 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
- package/template/.aioson/locales/fr/agents/analyst.md +2 -0
- package/template/.aioson/locales/fr/agents/architect.md +19 -0
- package/template/.aioson/locales/fr/agents/dev.md +64 -4
- package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
- package/template/.aioson/locales/fr/agents/genome.md +2 -0
- package/template/.aioson/locales/fr/agents/neo.md +2 -0
- package/template/.aioson/locales/fr/agents/orache.md +2 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
- package/template/.aioson/locales/fr/agents/pair.md +2 -0
- package/template/.aioson/locales/fr/agents/pm.md +2 -0
- package/template/.aioson/locales/fr/agents/product.md +52 -0
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
- package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
- package/template/.aioson/locales/fr/agents/qa.md +2 -0
- package/template/.aioson/locales/fr/agents/setup.md +35 -1
- package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
- package/template/.aioson/locales/fr/agents/squad.md +16 -0
- package/template/.aioson/locales/fr/agents/tester.md +9 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
- package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
- package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
- package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
- package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
- package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
- package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +134 -19
- package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
- package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
- package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
- package/template/.aioson/rules/README.md +69 -0
- package/template/.aioson/rules/data-format-convention.md +136 -0
- package/template/.aioson/rules/example-monetary-values.md +30 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
- package/template/.aioson/skills/design/aurora-command-ui/SKILL.md +243 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/art-direction.md +293 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/components.md +827 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/dashboards.md +250 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/design-tokens.md +585 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/motion.md +365 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/patterns.md +482 -0
- package/template/.aioson/skills/design/aurora-command-ui/references/websites.md +387 -0
- package/template/.aioson/skills/design/glassmorphism-ui/SKILL.md +222 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/art-direction.md +159 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/components.md +498 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/dashboards.md +236 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/design-tokens.md +274 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/motion.md +355 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/patterns.md +198 -0
- package/template/.aioson/skills/design/glassmorphism-ui/references/websites.md +307 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +213 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/art-direction.md +228 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/components.md +855 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/dashboards.md +334 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/design-tokens.md +342 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/motion.md +286 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/patterns.md +458 -0
- package/template/.aioson/skills/design/neo-brutalist-ui/references/websites.md +723 -0
- package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
- package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
- package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +46 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +109 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +44 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +37 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/hardening-lane.md +49 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +101 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +75 -0
- package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +147 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/crossover-protocol.md +221 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/naming-registry.md +88 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +306 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +149 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +208 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/variation-library.md +125 -0
- package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
- package/template/.aioson/skills/static/context-budget-guide.md +46 -0
- package/template/.aioson/skills/static/harness-sensors.md +74 -0
- package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
- package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
- package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
- package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
- package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
- package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
- package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
- package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
- package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
- package/template/.aioson/skills/static/threejs-patterns.md +929 -0
- package/template/.aioson/skills/static/web-research-cache.md +112 -0
- package/template/.aioson/tasks/implementation-plan.md +21 -1
- package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
- package/template/.claude/commands/aioson/agent/orache.md +5 -0
- package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
- package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
- package/template/AGENTS.md +75 -1
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +4 -0
- package/template/researchs/.gitkeep +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Resources: Squad State — Plan 81, Phase 4.2
|
|
5
|
+
*
|
|
6
|
+
* Exposes squad execution state as MCP resources that can be consumed
|
|
7
|
+
* by MCP Apps (dashboard) and MCP clients.
|
|
8
|
+
*
|
|
9
|
+
* Resources:
|
|
10
|
+
* aioson://squad/{slug}/state → STATE.md parsed as JSON
|
|
11
|
+
* aioson://squad/{slug}/bus → last 20 bus messages
|
|
12
|
+
* aioson://squad/{slug}/budget → token budget status
|
|
13
|
+
* aioson://squad/{slug}/waves → execution wave status
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { readState } = require('../../squad/state-manager');
|
|
17
|
+
const {
|
|
18
|
+
getDashboardData,
|
|
19
|
+
readBusState,
|
|
20
|
+
readBudgetState,
|
|
21
|
+
readWavesState
|
|
22
|
+
} = require('../apps/squad-dashboard/app');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* MCP resource URI pattern.
|
|
26
|
+
*/
|
|
27
|
+
const RESOURCE_PATTERNS = [
|
|
28
|
+
{ pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/state$/, handler: 'state' },
|
|
29
|
+
{ pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/bus$/, handler: 'bus' },
|
|
30
|
+
{ pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/budget$/, handler: 'budget' },
|
|
31
|
+
{ pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/waves$/, handler: 'waves' },
|
|
32
|
+
{ pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/dashboard$/, handler: 'dashboard' }
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve an MCP resource URI to its data.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} uri — MCP resource URI
|
|
39
|
+
* @param {string} projectDir — Project root
|
|
40
|
+
* @returns {Promise<object|null>} — Resource data or null if not found
|
|
41
|
+
*/
|
|
42
|
+
async function resolveResource(uri, projectDir) {
|
|
43
|
+
for (const { pattern, handler } of RESOURCE_PATTERNS) {
|
|
44
|
+
const match = uri.match(pattern);
|
|
45
|
+
if (!match) continue;
|
|
46
|
+
|
|
47
|
+
const squadSlug = match[1];
|
|
48
|
+
|
|
49
|
+
switch (handler) {
|
|
50
|
+
case 'state':
|
|
51
|
+
return readState(projectDir, squadSlug);
|
|
52
|
+
|
|
53
|
+
case 'bus':
|
|
54
|
+
return readBusState(projectDir, squadSlug);
|
|
55
|
+
|
|
56
|
+
case 'budget':
|
|
57
|
+
return readBudgetState(projectDir, squadSlug);
|
|
58
|
+
|
|
59
|
+
case 'waves':
|
|
60
|
+
return readWavesState(projectDir, squadSlug);
|
|
61
|
+
|
|
62
|
+
case 'dashboard':
|
|
63
|
+
return getDashboardData(projectDir, squadSlug);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* List available MCP resources for a project.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} projectDir
|
|
74
|
+
* @returns {Promise<object[]>} — Array of { uri, name, description, mimeType }
|
|
75
|
+
*/
|
|
76
|
+
async function listResources(projectDir) {
|
|
77
|
+
const fs = require('node:fs/promises');
|
|
78
|
+
const path = require('node:path');
|
|
79
|
+
const squadsDir = path.join(projectDir, '.aioson', 'squads');
|
|
80
|
+
const resources = [];
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const entries = await fs.readdir(squadsDir, { withFileTypes: true });
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (!entry.isDirectory()) continue;
|
|
86
|
+
const slug = entry.name;
|
|
87
|
+
|
|
88
|
+
resources.push(
|
|
89
|
+
{
|
|
90
|
+
uri: `aioson://squad/${slug}/state`,
|
|
91
|
+
name: `Squad ${slug} — State`,
|
|
92
|
+
description: `Cross-session state for squad "${slug}"`,
|
|
93
|
+
mimeType: 'application/json'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
uri: `aioson://squad/${slug}/bus`,
|
|
97
|
+
name: `Squad ${slug} — Bus`,
|
|
98
|
+
description: `Recent intra-squad bus messages for "${slug}"`,
|
|
99
|
+
mimeType: 'application/json'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
uri: `aioson://squad/${slug}/budget`,
|
|
103
|
+
name: `Squad ${slug} — Budget`,
|
|
104
|
+
description: `Token budget status for "${slug}"`,
|
|
105
|
+
mimeType: 'application/json'
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
uri: `aioson://squad/${slug}/waves`,
|
|
109
|
+
name: `Squad ${slug} — Waves`,
|
|
110
|
+
description: `Execution wave status for "${slug}"`,
|
|
111
|
+
mimeType: 'application/json'
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
uri: `aioson://squad/${slug}/dashboard`,
|
|
115
|
+
name: `Squad ${slug} — Dashboard`,
|
|
116
|
+
description: `Complete dashboard data for "${slug}"`,
|
|
117
|
+
mimeType: 'application/json'
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
} catch { /* no squads dir */ }
|
|
122
|
+
|
|
123
|
+
return resources;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
resolveResource,
|
|
128
|
+
listResources,
|
|
129
|
+
RESOURCE_PATTERNS
|
|
130
|
+
};
|
package/src/parser.js
CHANGED
|
@@ -16,8 +16,14 @@ function parseArgv(argv) {
|
|
|
16
16
|
continue;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// Boolean-only flags that never consume the next token
|
|
20
|
+
const boolOnly = new Set([
|
|
21
|
+
'all', 'force', 'dry-run', 'no-interactive', 'fix', 'json',
|
|
22
|
+
'help', 'version', 'no-launch', 'attach'
|
|
23
|
+
]);
|
|
24
|
+
|
|
19
25
|
const next = tokens[i + 1];
|
|
20
|
-
if (next && !next.startsWith('-')) {
|
|
26
|
+
if (next && !next.startsWith('-') && !boolOnly.has(k)) {
|
|
21
27
|
options[k] = next;
|
|
22
28
|
i += 1;
|
|
23
29
|
} else {
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* preflight-engine — shared deterministic utilities for preflight, gate:check, artifact:validate.
|
|
5
|
+
* No LLM calls. Pure file parsing + logic.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('node:fs/promises');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
|
|
11
|
+
// ─── Frontmatter parser ───────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
function parseFrontmatter(content) {
|
|
14
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
15
|
+
if (!match) return {};
|
|
16
|
+
const result = {};
|
|
17
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
18
|
+
const colonIdx = line.indexOf(':');
|
|
19
|
+
if (colonIdx === -1) continue;
|
|
20
|
+
const key = line.slice(0, colonIdx).trim();
|
|
21
|
+
const value = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, '');
|
|
22
|
+
if (key) result[key] = value;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function readFileSafe(filePath) {
|
|
28
|
+
try {
|
|
29
|
+
return await fs.readFile(filePath, 'utf8');
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function fileExists(filePath) {
|
|
36
|
+
try {
|
|
37
|
+
await fs.access(filePath);
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function fileStat(filePath) {
|
|
45
|
+
try {
|
|
46
|
+
return await fs.stat(filePath);
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── Framework detection ──────────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
const FRAMEWORK_INDICATORS = [
|
|
55
|
+
{ file: 'composer.json', key: 'laravel/framework', name: 'Laravel' },
|
|
56
|
+
{ file: 'composer.json', key: 'symfony/framework-bundle', name: 'Symfony' },
|
|
57
|
+
{ file: 'package.json', key: '"next"', name: 'Next.js' },
|
|
58
|
+
{ file: 'package.json', key: '"nuxt"', name: 'Nuxt.js' },
|
|
59
|
+
{ file: 'package.json', key: '"react"', name: 'React' },
|
|
60
|
+
{ file: 'package.json', key: '"vue"', name: 'Vue' },
|
|
61
|
+
{ file: 'package.json', key: '"svelte"', name: 'Svelte' },
|
|
62
|
+
{ file: 'package.json', key: '"express"', name: 'Express' },
|
|
63
|
+
{ file: 'Gemfile', key: 'rails', name: 'Rails' },
|
|
64
|
+
{ file: 'requirements.txt', key: 'django', name: 'Django' },
|
|
65
|
+
{ file: 'requirements.txt', key: 'fastapi', name: 'FastAPI' },
|
|
66
|
+
{ file: 'requirements.txt', key: 'flask', name: 'Flask' },
|
|
67
|
+
{ file: 'go.mod', key: 'gin-gonic', name: 'Gin' },
|
|
68
|
+
{ file: 'go.mod', key: 'echo', name: 'Echo' },
|
|
69
|
+
{ file: 'Cargo.toml', key: 'actix-web', name: 'Actix' },
|
|
70
|
+
{ file: 'foundry.toml', key: null, name: 'Foundry (Solidity)' }
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
async function detectFramework(targetDir) {
|
|
74
|
+
for (const { file, key, name } of FRAMEWORK_INDICATORS) {
|
|
75
|
+
const filePath = path.join(targetDir, file);
|
|
76
|
+
const content = await readFileSafe(filePath);
|
|
77
|
+
if (!content) continue;
|
|
78
|
+
if (!key || content.toLowerCase().includes(key.toLowerCase())) {
|
|
79
|
+
return name;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── Test runner detection ────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
const TEST_RUNNER_INDICATORS = [
|
|
88
|
+
{ file: 'phpunit.xml', name: 'Pest/PHPUnit', command: 'php artisan test' },
|
|
89
|
+
{ file: 'phpunit.xml.dist', name: 'PHPUnit', command: './vendor/bin/phpunit' },
|
|
90
|
+
{ file: 'jest.config.js', name: 'Jest', command: 'npx jest' },
|
|
91
|
+
{ file: 'jest.config.ts', name: 'Jest', command: 'npx jest' },
|
|
92
|
+
{ file: 'jest.config.mjs', name: 'Jest', command: 'npx jest' },
|
|
93
|
+
{ file: 'vitest.config.js', name: 'Vitest', command: 'npx vitest' },
|
|
94
|
+
{ file: 'vitest.config.ts', name: 'Vitest', command: 'npx vitest' },
|
|
95
|
+
{ file: 'vitest.config.mjs', name: 'Vitest', command: 'npx vitest' },
|
|
96
|
+
{ file: 'pytest.ini', name: 'Pytest', command: 'pytest' },
|
|
97
|
+
{ file: 'setup.cfg', name: 'Pytest', command: 'pytest', key: '[tool:pytest]' },
|
|
98
|
+
{ file: 'pyproject.toml', name: 'Pytest', command: 'pytest', key: '[tool.pytest' },
|
|
99
|
+
{ file: '.rspec', name: 'RSpec', command: 'bundle exec rspec' },
|
|
100
|
+
{ file: 'foundry.toml', name: 'Forge', command: 'forge test' },
|
|
101
|
+
{ file: 'Makefile', name: 'Make', command: 'make test', key: 'test:' }
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
async function detectTestRunner(targetDir) {
|
|
105
|
+
for (const { file, name, command, key } of TEST_RUNNER_INDICATORS) {
|
|
106
|
+
const filePath = path.join(targetDir, file);
|
|
107
|
+
const content = await readFileSafe(filePath);
|
|
108
|
+
if (!content) continue;
|
|
109
|
+
if (key && !content.includes(key)) continue;
|
|
110
|
+
return { name, command, configFile: file };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check package.json scripts (test, test:unit, test:e2e, etc.)
|
|
114
|
+
const pkgContent = await readFileSafe(path.join(targetDir, 'package.json'));
|
|
115
|
+
if (pkgContent) {
|
|
116
|
+
try {
|
|
117
|
+
const pkg = JSON.parse(pkgContent);
|
|
118
|
+
if (pkg.scripts) {
|
|
119
|
+
// Check all test-related script keys, prioritize "test" then "test:*"
|
|
120
|
+
const testKeys = Object.keys(pkg.scripts).filter((k) => k === 'test' || k.startsWith('test:'));
|
|
121
|
+
for (const key of testKeys) {
|
|
122
|
+
const script = pkg.scripts[key];
|
|
123
|
+
if (script.includes('jest')) return { name: 'Jest', command: `npm run ${key}`, configFile: 'package.json' };
|
|
124
|
+
if (script.includes('vitest')) return { name: 'Vitest', command: `npm run ${key}`, configFile: 'package.json' };
|
|
125
|
+
if (script.includes('mocha')) return { name: 'Mocha', command: `npm run ${key}`, configFile: 'package.json' };
|
|
126
|
+
if (script.includes('node --test') || script.includes('node:test')) return { name: 'node:test', command: `npm run ${key}`, configFile: 'package.json' };
|
|
127
|
+
}
|
|
128
|
+
if (pkg.scripts.test) {
|
|
129
|
+
return { name: 'npm test', command: 'npm test', configFile: 'package.json' };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch { /* ignore malformed package.json */ }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ─── Context file paths ───────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
function contextDir(targetDir) {
|
|
141
|
+
return path.join(targetDir, '.aioson', 'context');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function rulesDir(targetDir) {
|
|
145
|
+
return path.join(targetDir, '.aioson', 'rules');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function artifactPath(targetDir, name, slug) {
|
|
149
|
+
const dir = contextDir(targetDir);
|
|
150
|
+
if (slug) return path.join(dir, `${name}-${slug}.md`);
|
|
151
|
+
return path.join(dir, `${name}.md`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Project context reader ───────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
async function loadProjectContext(targetDir) {
|
|
157
|
+
const filePath = path.join(contextDir(targetDir), 'project.context.md');
|
|
158
|
+
const content = await readFileSafe(filePath);
|
|
159
|
+
if (!content) return { exists: false, data: {} };
|
|
160
|
+
const data = parseFrontmatter(content);
|
|
161
|
+
return { exists: true, data, content };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ─── Artifact scanner ─────────────────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
async function scanArtifacts(targetDir, slug) {
|
|
167
|
+
const dir = contextDir(targetDir);
|
|
168
|
+
|
|
169
|
+
async function check(name, filePath) {
|
|
170
|
+
const stat = await fileStat(filePath);
|
|
171
|
+
if (!stat) return { exists: false };
|
|
172
|
+
|
|
173
|
+
const content = await readFileSafe(filePath);
|
|
174
|
+
const fm = content ? parseFrontmatter(content) : {};
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
exists: true,
|
|
178
|
+
path: path.relative(targetDir, filePath),
|
|
179
|
+
size: stat.size,
|
|
180
|
+
frontmatter: fm,
|
|
181
|
+
content
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const results = {
|
|
186
|
+
project_context: await check('project.context', path.join(dir, 'project.context.md')),
|
|
187
|
+
prd: slug ? await check('prd', path.join(dir, `prd-${slug}.md`)) : { exists: false },
|
|
188
|
+
sheldon_enrichment: slug ? await check('sheldon', path.join(dir, `sheldon-enrichment-${slug}.md`)) : { exists: false },
|
|
189
|
+
requirements: slug ? await check('requirements', path.join(dir, `requirements-${slug}.md`)) : { exists: false },
|
|
190
|
+
spec: slug ? await check('spec', path.join(dir, `spec-${slug}.md`)) : await check('spec', path.join(dir, 'spec.md')),
|
|
191
|
+
architecture: await check('architecture', path.join(dir, 'architecture.md')),
|
|
192
|
+
implementation_plan: slug ? await check('impl-plan', path.join(dir, `implementation-plan-${slug}.md`)) : { exists: false },
|
|
193
|
+
conformance: slug ? await check('conformance', path.join(dir, `conformance-${slug}.yaml`)) : { exists: false },
|
|
194
|
+
dev_state: await check('dev-state', path.join(dir, 'dev-state.md')),
|
|
195
|
+
features: await check('features', path.join(dir, 'features.md'))
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return results;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ─── Gate reader ─────────────────────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
const GATE_NAMES = {
|
|
204
|
+
A: 'requirements',
|
|
205
|
+
B: 'design',
|
|
206
|
+
C: 'plan',
|
|
207
|
+
D: 'execution'
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const GATE_ALIASES = {
|
|
211
|
+
requirements: 'A',
|
|
212
|
+
design: 'B',
|
|
213
|
+
plan: 'C',
|
|
214
|
+
execution: 'D'
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
function parseGatesFromSpec(content) {
|
|
218
|
+
if (!content) return {};
|
|
219
|
+
const fm = parseFrontmatter(content);
|
|
220
|
+
const gates = {};
|
|
221
|
+
|
|
222
|
+
// Try explicit gate fields: gate_requirements, gate_design, gate_plan, gate_execution
|
|
223
|
+
for (const [letter, name] of Object.entries(GATE_NAMES)) {
|
|
224
|
+
const val = fm[`gate_${name}`] || fm[`gate${letter}`] || fm[`gate_${letter}`];
|
|
225
|
+
if (val) gates[name] = val.toLowerCase();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Try phase_gates JSON field
|
|
229
|
+
if (fm.phase_gates) {
|
|
230
|
+
try {
|
|
231
|
+
const parsed = JSON.parse(fm.phase_gates.replace(/'/g, '"'));
|
|
232
|
+
Object.assign(gates, parsed);
|
|
233
|
+
} catch {
|
|
234
|
+
// phase_gates field exists but is not valid JSON — gate data from this field is lost
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Try scanning content for gate approval lines
|
|
239
|
+
const gateLineRe = /gate\s+([A-D])[^:]*:\s*(approved|pending|rejected)/gi;
|
|
240
|
+
let m;
|
|
241
|
+
while ((m = gateLineRe.exec(content)) !== null) {
|
|
242
|
+
const letter = m[1].toUpperCase();
|
|
243
|
+
const name = GATE_NAMES[letter];
|
|
244
|
+
if (name && !gates[name]) gates[name] = m[2].toLowerCase();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return gates;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function readPhaseGates(targetDir, slug) {
|
|
251
|
+
const specFile = slug
|
|
252
|
+
? path.join(contextDir(targetDir), `spec-${slug}.md`)
|
|
253
|
+
: path.join(contextDir(targetDir), 'spec.md');
|
|
254
|
+
|
|
255
|
+
const content = await readFileSafe(specFile);
|
|
256
|
+
if (!content) return {};
|
|
257
|
+
return parseGatesFromSpec(content);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ─── Dev state reader ─────────────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
async function readDevState(targetDir) {
|
|
263
|
+
const filePath = path.join(contextDir(targetDir), 'dev-state.md');
|
|
264
|
+
const content = await readFileSafe(filePath);
|
|
265
|
+
if (!content) return { exists: false };
|
|
266
|
+
const fm = parseFrontmatter(content);
|
|
267
|
+
return { exists: true, ...fm, content };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ─── Project pulse reader ────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
async function readProjectPulse(targetDir) {
|
|
273
|
+
const filePath = path.join(contextDir(targetDir), 'project-pulse.md');
|
|
274
|
+
const content = await readFileSafe(filePath);
|
|
275
|
+
if (!content) return { exists: false };
|
|
276
|
+
const fm = parseFrontmatter(content);
|
|
277
|
+
return { exists: true, ...fm, content };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ─── Classification reader ────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
async function detectClassification(targetDir, slug) {
|
|
283
|
+
// 1. Try project context
|
|
284
|
+
const ctx = await loadProjectContext(targetDir);
|
|
285
|
+
if (ctx.data.classification) return ctx.data.classification.toUpperCase();
|
|
286
|
+
|
|
287
|
+
// 2. Try spec frontmatter
|
|
288
|
+
if (slug) {
|
|
289
|
+
const specContent = await readFileSafe(path.join(contextDir(targetDir), `spec-${slug}.md`));
|
|
290
|
+
if (specContent) {
|
|
291
|
+
const fm = parseFrontmatter(specContent);
|
|
292
|
+
if (fm.classification) return fm.classification.toUpperCase();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 3. Try PRD frontmatter
|
|
296
|
+
const prdContent = await readFileSafe(path.join(contextDir(targetDir), `prd-${slug}.md`));
|
|
297
|
+
if (prdContent) {
|
|
298
|
+
const fm = parseFrontmatter(prdContent);
|
|
299
|
+
if (fm.classification) return fm.classification.toUpperCase();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ─── Rules discovery ──────────────────────────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
async function discoverRules(targetDir, agent) {
|
|
309
|
+
const dir = rulesDir(targetDir);
|
|
310
|
+
const rules = [];
|
|
311
|
+
|
|
312
|
+
let entries;
|
|
313
|
+
try {
|
|
314
|
+
entries = await fs.readdir(dir);
|
|
315
|
+
} catch {
|
|
316
|
+
return rules;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (const entry of entries) {
|
|
320
|
+
if (!entry.endsWith('.md')) continue;
|
|
321
|
+
const content = await readFileSafe(path.join(dir, entry));
|
|
322
|
+
if (!content) continue;
|
|
323
|
+
|
|
324
|
+
// Check applicability: universal rules or agent-specific
|
|
325
|
+
const fm = parseFrontmatter(content);
|
|
326
|
+
const applies = !fm.agents || fm.agents.includes('all') || fm.agents.includes(agent);
|
|
327
|
+
if (applies) rules.push(entry);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return rules;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ─── Context package builder ──────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
function buildContextPackage(agent, slug, classification, artifacts, devState) {
|
|
336
|
+
const pkg = [];
|
|
337
|
+
|
|
338
|
+
if (artifacts.project_context.exists) pkg.push(artifacts.project_context.path);
|
|
339
|
+
|
|
340
|
+
if (slug) {
|
|
341
|
+
// Feature-specific context
|
|
342
|
+
if (artifacts.spec.exists) pkg.push(artifacts.spec.path);
|
|
343
|
+
if (artifacts.implementation_plan.exists) pkg.push(artifacts.implementation_plan.path);
|
|
344
|
+
if (artifacts.requirements.exists && ['analyst', 'architect', 'dev'].includes(agent)) {
|
|
345
|
+
pkg.push(artifacts.requirements.path);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Agent-specific additions
|
|
350
|
+
if (agent === 'dev' && artifacts.dev_state.exists) pkg.push('dev-state.md (check for active state)');
|
|
351
|
+
if (agent === 'qa' && artifacts.spec.exists) pkg.push(artifacts.spec.path);
|
|
352
|
+
if (agent === 'architect' && artifacts.architecture.exists) pkg.push(artifacts.architecture.path);
|
|
353
|
+
|
|
354
|
+
return [...new Set(pkg)];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ─── Readiness evaluator ─────────────────────────────────────────────────────
|
|
358
|
+
|
|
359
|
+
function evaluateReadiness(artifacts, phaseGates, classification, agent) {
|
|
360
|
+
const blockers = [];
|
|
361
|
+
|
|
362
|
+
if (!artifacts.project_context.exists) blockers.push('project.context.md missing');
|
|
363
|
+
|
|
364
|
+
if (agent === 'dev') {
|
|
365
|
+
if (!artifacts.spec.exists) blockers.push('spec file missing');
|
|
366
|
+
if (phaseGates.plan && phaseGates.plan !== 'approved') {
|
|
367
|
+
blockers.push(`Gate C (plan) not approved: ${phaseGates.plan || 'pending'}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (agent === 'qa') {
|
|
372
|
+
if (!artifacts.spec.exists) blockers.push('spec file missing');
|
|
373
|
+
if (classification && classification !== 'MICRO') {
|
|
374
|
+
if (phaseGates.plan && phaseGates.plan !== 'approved') {
|
|
375
|
+
blockers.push(`Gate C (plan) not approved: ${phaseGates.plan || 'pending'}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (agent === 'analyst') {
|
|
381
|
+
if (!artifacts.prd.exists) blockers.push('prd file missing');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (agent === 'architect') {
|
|
385
|
+
if (!artifacts.requirements.exists) blockers.push('requirements file missing');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return blockers.length === 0
|
|
389
|
+
? { status: 'READY', blockers: [] }
|
|
390
|
+
: { status: 'BLOCKED', blockers };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ─── Spec version extractor ───────────────────────────────────────────────────
|
|
394
|
+
|
|
395
|
+
function extractSpecVersion(artifact) {
|
|
396
|
+
if (!artifact.exists) return null;
|
|
397
|
+
return artifact.frontmatter.version || null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function extractLastCheckpoint(artifact) {
|
|
401
|
+
if (!artifact.exists) return null;
|
|
402
|
+
const fm = artifact.frontmatter;
|
|
403
|
+
if (fm.last_checkpoint) return fm.last_checkpoint;
|
|
404
|
+
|
|
405
|
+
// Scan content for checkpoint patterns — use last occurrence (most recent)
|
|
406
|
+
if (artifact.content) {
|
|
407
|
+
const matches = artifact.content.match(/last_checkpoint:\s*(.+)/g);
|
|
408
|
+
if (matches && matches.length > 0) {
|
|
409
|
+
const last = matches[matches.length - 1];
|
|
410
|
+
const val = last.replace(/^last_checkpoint:\s*/, '').trim().replace(/^["']|["']$/g, '');
|
|
411
|
+
return val;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
module.exports = {
|
|
420
|
+
parseFrontmatter,
|
|
421
|
+
readFileSafe,
|
|
422
|
+
fileExists,
|
|
423
|
+
fileStat,
|
|
424
|
+
detectFramework,
|
|
425
|
+
detectTestRunner,
|
|
426
|
+
contextDir,
|
|
427
|
+
rulesDir,
|
|
428
|
+
artifactPath,
|
|
429
|
+
loadProjectContext,
|
|
430
|
+
scanArtifacts,
|
|
431
|
+
parseGatesFromSpec,
|
|
432
|
+
readPhaseGates,
|
|
433
|
+
readDevState,
|
|
434
|
+
readProjectPulse,
|
|
435
|
+
detectClassification,
|
|
436
|
+
discoverRules,
|
|
437
|
+
buildContextPackage,
|
|
438
|
+
evaluateReadiness,
|
|
439
|
+
extractSpecVersion,
|
|
440
|
+
extractLastCheckpoint,
|
|
441
|
+
GATE_NAMES,
|
|
442
|
+
GATE_ALIASES
|
|
443
|
+
};
|