@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,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson brief:validate — Validate brief completeness before spawning
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* aioson brief:validate . --brief=briefs/phase-1.md
|
|
8
|
+
* aioson brief:validate . --brief=briefs/phase-1.md --auto-fix
|
|
9
|
+
* aioson brief:validate . --brief=briefs/phase-1.md --json
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
const { validateBrief, autoFixBrief } = require('../squad/brief-validator');
|
|
14
|
+
|
|
15
|
+
async function runBriefValidate({ args, options = {}, logger }) {
|
|
16
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
17
|
+
const briefPath = String(options.brief || options.b || '').trim();
|
|
18
|
+
|
|
19
|
+
if (!briefPath) {
|
|
20
|
+
logger.error('Error: --brief=<path> is required');
|
|
21
|
+
return { ok: false, error: 'missing_brief' };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const result = await validateBrief(briefPath, targetDir);
|
|
25
|
+
|
|
26
|
+
// Auto-fix if requested and possible
|
|
27
|
+
if (!result.ready && options['auto-fix'] && result.autoFixable) {
|
|
28
|
+
const fixResult = await autoFixBrief(briefPath, targetDir);
|
|
29
|
+
if (fixResult.fixed) {
|
|
30
|
+
logger.log(`Auto-fixed: ${fixResult.fieldsFixed.join(', ')}`);
|
|
31
|
+
// Re-validate after fix
|
|
32
|
+
const recheck = await validateBrief(briefPath, targetDir);
|
|
33
|
+
if (options.json) {
|
|
34
|
+
return { ...recheck, autoFixed: fixResult.fieldsFixed };
|
|
35
|
+
}
|
|
36
|
+
printResult(recheck, logger, fixResult.fieldsFixed);
|
|
37
|
+
return { ok: recheck.ready, ...recheck, autoFixed: fixResult.fieldsFixed };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (options.json) return result;
|
|
42
|
+
|
|
43
|
+
printResult(result, logger);
|
|
44
|
+
return { ok: result.ready, ...result };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function printResult(result, logger, autoFixed) {
|
|
48
|
+
const status = result.ready ? 'READY' : `NOT READY (${result.issues.length} blocking issue${result.issues.length > 1 ? 's' : ''})`;
|
|
49
|
+
logger.log(`Score: ${result.score}/${result.total} — ${status}`);
|
|
50
|
+
|
|
51
|
+
if (result.issues.length > 0) {
|
|
52
|
+
logger.log('');
|
|
53
|
+
for (const issue of result.issues) {
|
|
54
|
+
const fix = issue.autoFixable ? ' [auto-fixable]' : '';
|
|
55
|
+
logger.log(` ✗ ${issue.field}: ${issue.message}${fix}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (autoFixed && autoFixed.length > 0) {
|
|
60
|
+
logger.log('');
|
|
61
|
+
logger.log(`Auto-fixed fields: ${autoFixed.join(', ')}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { runBriefValidate };
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson classify — deterministic classification scoring (MICRO/SMALL/MEDIUM).
|
|
5
|
+
*
|
|
6
|
+
* Reads prd-{slug}.md or requirements-{slug}.md and counts complexity indicators.
|
|
7
|
+
* Falls back to --interactive mode if insufficient data.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* aioson classify . --feature=checkout
|
|
11
|
+
* aioson classify . --feature=checkout --json
|
|
12
|
+
* aioson classify . --interactive
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
const readline = require('node:readline');
|
|
17
|
+
const { readFileSafe, contextDir } = require('../preflight-engine');
|
|
18
|
+
|
|
19
|
+
const BAR = '━'.repeat(30);
|
|
20
|
+
|
|
21
|
+
// Scoring thresholds
|
|
22
|
+
// user_types: 1→0, 2→1, 3+→2
|
|
23
|
+
// external_integrations: 0→0, 1-2→1, 3+→2
|
|
24
|
+
// rule_complexity: none→0, some→1, complex→2
|
|
25
|
+
// Score: 0-1=MICRO, 2-3=SMALL, 4-6=MEDIUM
|
|
26
|
+
|
|
27
|
+
function scoreUserTypes(count) {
|
|
28
|
+
if (count >= 3) return 2;
|
|
29
|
+
if (count >= 2) return 1;
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function scoreIntegrations(count) {
|
|
34
|
+
if (count >= 3) return 2;
|
|
35
|
+
if (count >= 1) return 1;
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function scoreComplexity(level) {
|
|
40
|
+
if (level === 'complex') return 2;
|
|
41
|
+
if (level === 'some') return 1;
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function scoreToClassification(score) {
|
|
46
|
+
if (score <= 1) return 'MICRO';
|
|
47
|
+
if (score <= 3) return 'SMALL';
|
|
48
|
+
return 'MEDIUM';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function classificationToPhaseDepth(classification) {
|
|
52
|
+
if (classification === 'MICRO') {
|
|
53
|
+
return {
|
|
54
|
+
specify: 'brief note or inline',
|
|
55
|
+
research: 'not needed',
|
|
56
|
+
requirements: 'optional',
|
|
57
|
+
design: 'skip',
|
|
58
|
+
plan: 'optional',
|
|
59
|
+
execute: 'direct from task description'
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (classification === 'SMALL') {
|
|
63
|
+
return {
|
|
64
|
+
specify: 'full PRD',
|
|
65
|
+
research: 'recommended (@sheldon)',
|
|
66
|
+
requirements: 'required (requirements-{slug}.md)',
|
|
67
|
+
design: 'selective (only if new pattern)',
|
|
68
|
+
plan: 'recommended',
|
|
69
|
+
execute: 'from requirements + spec'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
specify: 'full PRD + stakeholder review',
|
|
74
|
+
research: 'required (@sheldon)',
|
|
75
|
+
requirements: 'required + conformance YAML',
|
|
76
|
+
design: 'required (@ux-ui + @architect)',
|
|
77
|
+
plan: 'required + @pm backlog',
|
|
78
|
+
execute: 'from approved plan, phased delivery'
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Pattern-based auto-detection from markdown content
|
|
83
|
+
|
|
84
|
+
const USER_TYPE_PATTERNS = [
|
|
85
|
+
/\b(admin|administrator)\b/gi,
|
|
86
|
+
/\b(user|customer|client|buyer|seller|vendor|manager|operator|guest|visitor|member|owner|reviewer|moderator)\b/gi,
|
|
87
|
+
/\bAs an? ([a-z]+)/gi,
|
|
88
|
+
/\brole[s]?\b.*?:\s*([^,\n]+)/gi
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const INTEGRATION_PATTERNS = [
|
|
92
|
+
/\b(stripe|paypal|braintree|square)\b/gi,
|
|
93
|
+
/\b(sendgrid|mailchimp|ses|postmark|smtp)\b/gi,
|
|
94
|
+
/\b(twilio|vonage|nexmo)\b/gi,
|
|
95
|
+
/\b(s3|cloudinary|gcs|azure blob)\b/gi,
|
|
96
|
+
/\b(oauth|jwt|saml|sso|auth0|firebase auth)\b/gi,
|
|
97
|
+
/\b(redis|memcached|elasticsearch|algolia)\b/gi,
|
|
98
|
+
/\bAPI\s+(integration|endpoint|call)\b/gi,
|
|
99
|
+
/\bthird.party\b/gi,
|
|
100
|
+
/\bwebhook[s]?\b/gi,
|
|
101
|
+
/\bexternal\s+service\b/gi
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const COMPLEXITY_HIGH_PATTERNS = [
|
|
105
|
+
/\b(multi.step|multi-phase|pipeline|workflow)\b/gi,
|
|
106
|
+
/\b(state machine|finite state|transition)\b/gi,
|
|
107
|
+
/\b(calculation|formula|algorithm|score|pricing engine)\b/gi,
|
|
108
|
+
/\b(complex|intricate|elaborate)\s+(logic|rule|condition)/gi,
|
|
109
|
+
/\b(concurrent|parallel|async|queue)\b/gi,
|
|
110
|
+
/\b(if.+then.+else|conditional|depends on)\b/gi
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const COMPLEXITY_SOME_PATTERNS = [
|
|
114
|
+
/\b(validation|constraint|rule)\b/gi,
|
|
115
|
+
/\b(permission|role.based|access control)\b/gi,
|
|
116
|
+
/\b(notification|trigger|event)\b/gi
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
function analyzeContent(content) {
|
|
120
|
+
// Count unique user types
|
|
121
|
+
const userTypeSet = new Set();
|
|
122
|
+
for (const pattern of USER_TYPE_PATTERNS) {
|
|
123
|
+
let m;
|
|
124
|
+
while ((m = pattern.exec(content)) !== null) {
|
|
125
|
+
userTypeSet.add(m[1] ? m[1].toLowerCase() : m[0].toLowerCase());
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const userTypeCount = Math.min(userTypeSet.size, 5);
|
|
129
|
+
|
|
130
|
+
// Count integrations
|
|
131
|
+
const integrationSet = new Set();
|
|
132
|
+
for (const pattern of INTEGRATION_PATTERNS) {
|
|
133
|
+
let m;
|
|
134
|
+
while ((m = pattern.exec(content)) !== null) {
|
|
135
|
+
integrationSet.add(m[0].toLowerCase());
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const integrationCount = integrationSet.size;
|
|
139
|
+
|
|
140
|
+
// Complexity level
|
|
141
|
+
let complexityLevel = 'none';
|
|
142
|
+
const highMatches = COMPLEXITY_HIGH_PATTERNS.some((p) => p.test(content));
|
|
143
|
+
if (highMatches) {
|
|
144
|
+
complexityLevel = 'complex';
|
|
145
|
+
} else {
|
|
146
|
+
const someMatches = COMPLEXITY_SOME_PATTERNS.some((p) => p.test(content));
|
|
147
|
+
if (someMatches) complexityLevel = 'some';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { userTypeCount, integrationCount, complexityLevel };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function runInteractive(logger) {
|
|
154
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
155
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
logger.log('');
|
|
159
|
+
logger.log('Classification — Interactive Mode');
|
|
160
|
+
logger.log(BAR);
|
|
161
|
+
|
|
162
|
+
const utRaw = await ask('User types (1 / 2 / 3+): ');
|
|
163
|
+
const userTypeCount = parseInt(utRaw) || 1;
|
|
164
|
+
|
|
165
|
+
const intRaw = await ask('External integrations (0 / 1-2 / 3+): ');
|
|
166
|
+
const integrationCount = parseInt(intRaw) || 0;
|
|
167
|
+
|
|
168
|
+
const cxRaw = await ask('Business rule complexity (none / some / complex): ');
|
|
169
|
+
const complexityLevel = ['none', 'some', 'complex'].includes(cxRaw.trim().toLowerCase())
|
|
170
|
+
? cxRaw.trim().toLowerCase()
|
|
171
|
+
: 'none';
|
|
172
|
+
|
|
173
|
+
rl.close();
|
|
174
|
+
return { userTypeCount, integrationCount, complexityLevel };
|
|
175
|
+
} catch {
|
|
176
|
+
rl.close();
|
|
177
|
+
return { userTypeCount: 1, integrationCount: 0, complexityLevel: 'none' };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function runClassify({ args, options = {}, logger }) {
|
|
182
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
183
|
+
const slug = options.feature ? String(options.feature) : null;
|
|
184
|
+
const interactive = Boolean(options.interactive);
|
|
185
|
+
|
|
186
|
+
let userTypeCount, integrationCount, complexityLevel;
|
|
187
|
+
let sourceFile = null;
|
|
188
|
+
|
|
189
|
+
if (interactive) {
|
|
190
|
+
({ userTypeCount, integrationCount, complexityLevel } = await runInteractive(logger));
|
|
191
|
+
} else {
|
|
192
|
+
// Auto-detect from PRD or requirements file
|
|
193
|
+
const dir = contextDir(targetDir);
|
|
194
|
+
const candidates = slug
|
|
195
|
+
? [
|
|
196
|
+
path.join(dir, `requirements-${slug}.md`),
|
|
197
|
+
path.join(dir, `prd-${slug}.md`),
|
|
198
|
+
path.join(dir, `sheldon-enrichment-${slug}.md`)
|
|
199
|
+
]
|
|
200
|
+
: [path.join(dir, 'requirements.md'), path.join(dir, 'prd.md')];
|
|
201
|
+
|
|
202
|
+
let content = null;
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
content = await readFileSafe(candidate);
|
|
205
|
+
if (content) { sourceFile = path.relative(targetDir, candidate); break; }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!content) {
|
|
209
|
+
if (options.json) return { ok: false, reason: 'no_source', message: 'No prd or requirements file found. Use --interactive or --feature=<slug>.' };
|
|
210
|
+
logger.log('No source file found. Use --interactive or provide --feature=<slug>.');
|
|
211
|
+
return { ok: false };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
({ userTypeCount, integrationCount, complexityLevel } = analyzeContent(content));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const utScore = scoreUserTypes(userTypeCount);
|
|
218
|
+
const intScore = scoreIntegrations(integrationCount);
|
|
219
|
+
const cxScore = scoreComplexity(complexityLevel);
|
|
220
|
+
const totalScore = utScore + intScore + cxScore;
|
|
221
|
+
const classification = scoreToClassification(totalScore);
|
|
222
|
+
const phaseDepth = classificationToPhaseDepth(classification);
|
|
223
|
+
|
|
224
|
+
const result = {
|
|
225
|
+
ok: true,
|
|
226
|
+
feature_slug: slug,
|
|
227
|
+
source_file: sourceFile,
|
|
228
|
+
inputs: { user_types: userTypeCount, external_integrations: integrationCount, rule_complexity: complexityLevel },
|
|
229
|
+
scores: { user_types: utScore, integrations: intScore, complexity: cxScore, total: totalScore },
|
|
230
|
+
classification,
|
|
231
|
+
phase_depth: phaseDepth
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
if (options.json) return result;
|
|
235
|
+
|
|
236
|
+
const header = slug ? `Classification — ${slug}` : 'Classification';
|
|
237
|
+
logger.log('');
|
|
238
|
+
logger.log(header);
|
|
239
|
+
logger.log(BAR);
|
|
240
|
+
if (sourceFile) logger.log(`Source: ${sourceFile}`);
|
|
241
|
+
logger.log(`User types: ${userTypeCount} → +${utScore}`);
|
|
242
|
+
logger.log(`External integrations: ${integrationCount} → +${intScore}`);
|
|
243
|
+
logger.log(`Business rule complexity: ${complexityLevel} → +${cxScore}`);
|
|
244
|
+
logger.log(BAR);
|
|
245
|
+
logger.log(`Score: ${totalScore} → ${classification}`);
|
|
246
|
+
logger.log('');
|
|
247
|
+
logger.log('Phase depth:');
|
|
248
|
+
for (const [phase, desc] of Object.entries(phaseDepth)) {
|
|
249
|
+
logger.log(` ${phase.padEnd(14)}: ${desc}`);
|
|
250
|
+
}
|
|
251
|
+
logger.log('');
|
|
252
|
+
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
module.exports = { runClassify };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { saveContextShadow, listSessions, restoreContext, cleanup } = require('../context-cache');
|
|
5
|
+
|
|
6
|
+
async function runContextCacheList({ args, options, logger }) {
|
|
7
|
+
const cacheDir = options.cacheDir || undefined;
|
|
8
|
+
const sessions = await listSessions({ cacheDir });
|
|
9
|
+
|
|
10
|
+
if (options.json) {
|
|
11
|
+
return { ok: true, sessions };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (sessions.length === 0) {
|
|
15
|
+
logger.log('No cached sessions found.');
|
|
16
|
+
return { ok: true, sessions: [] };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
logger.log('\n Cached Context Sessions\n');
|
|
20
|
+
for (const s of sessions) {
|
|
21
|
+
const kb = Math.round(s.size / 1024);
|
|
22
|
+
const goal = s.metadata.goal ? ` — ${s.metadata.goal.slice(0, 50)}` : '';
|
|
23
|
+
logger.log(` ${s.sessionId} ${s.createdAt.slice(0, 16)} ${kb}KB${goal}`);
|
|
24
|
+
}
|
|
25
|
+
logger.log('');
|
|
26
|
+
|
|
27
|
+
return { ok: true, sessions };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function runContextCacheSave({ args, options, logger }) {
|
|
31
|
+
const cwd = path.resolve(process.cwd(), args[0] || '.');
|
|
32
|
+
const content = options.content || '';
|
|
33
|
+
|
|
34
|
+
if (!content) {
|
|
35
|
+
logger.error('--content is required. Example: aioson context:cache:save . --content="..." --goal="..."');
|
|
36
|
+
return { ok: false, error: 'missing_content' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = await saveContextShadow(content, {
|
|
40
|
+
goal: options.goal || '',
|
|
41
|
+
agent: options.agent || '',
|
|
42
|
+
projectDir: cwd
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (options.json) return result;
|
|
46
|
+
|
|
47
|
+
logger.log(`Context saved. Session ID: ${result.sessionId}`);
|
|
48
|
+
logger.log(`Path: ${result.path}`);
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function runContextCacheRestore({ args, options, logger }) {
|
|
53
|
+
const sessionId = options.session || args[0] || '';
|
|
54
|
+
const cacheDir = options.cacheDir || undefined;
|
|
55
|
+
|
|
56
|
+
if (!sessionId) {
|
|
57
|
+
logger.error('--session=<id> is required.');
|
|
58
|
+
return { ok: false, error: 'missing_session' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result = await restoreContext(sessionId, {
|
|
62
|
+
cacheDir,
|
|
63
|
+
query: options.query || undefined
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!result.ok) {
|
|
67
|
+
logger.error(`Restore failed: ${result.error}`);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (options.json) return result;
|
|
72
|
+
|
|
73
|
+
logger.log(result.content);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function runContextCacheCleanup({ args, options, logger }) {
|
|
78
|
+
const cacheDir = options.cacheDir || undefined;
|
|
79
|
+
const maxAgeHours = Number(options['max-age']) || 24;
|
|
80
|
+
const maxAge = maxAgeHours * 60 * 60 * 1000;
|
|
81
|
+
|
|
82
|
+
const result = await cleanup({ cacheDir, maxAge });
|
|
83
|
+
|
|
84
|
+
if (options.json) return { ok: true, ...result };
|
|
85
|
+
|
|
86
|
+
logger.log(`Removed ${result.removed} expired session(s).`);
|
|
87
|
+
return { ok: true, ...result };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { runContextCacheList, runContextCacheSave, runContextCacheRestore, runContextCacheCleanup };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* aioson context:compact — Standalone context compaction
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* aioson context:compact . --agent=dev --input=devlog.md
|
|
8
|
+
* aioson context:compact . --agent=orache --input=notes.md --session=abc-123
|
|
9
|
+
* aioson context:compact . --agent=dev --json
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
const { compactContext } = require('../squad/context-compactor');
|
|
14
|
+
|
|
15
|
+
async function runContextCompact({ args, options = {}, logger }) {
|
|
16
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
17
|
+
const agent = String(options.agent || options.a || 'dev').trim();
|
|
18
|
+
const input = options.input ? String(options.input).trim() : undefined;
|
|
19
|
+
const session = options.session ? String(options.session).trim() : undefined;
|
|
20
|
+
|
|
21
|
+
if (!input) {
|
|
22
|
+
logger.error('Error: --input=<path> is required (path to devlog, notes, or output)');
|
|
23
|
+
return { ok: false, error: 'missing_input' };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result = await compactContext(targetDir, { agent, input, session });
|
|
27
|
+
|
|
28
|
+
if (!result.ok) {
|
|
29
|
+
logger.error(`Error: ${result.error}`);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (options.json) return result.summary;
|
|
34
|
+
|
|
35
|
+
const s = result.summary.summary;
|
|
36
|
+
logger.log(`Context compacted for @${agent}`);
|
|
37
|
+
logger.log(` Session: ${result.summary.session_id}`);
|
|
38
|
+
logger.log(` Tools: ${s.tools_used.length} detected`);
|
|
39
|
+
logger.log(` Requests: ${s.recent_requests.length} extracted`);
|
|
40
|
+
logger.log(` Pending: ${s.pending_work.length} items`);
|
|
41
|
+
logger.log(` Files: ${s.key_files.length} referenced`);
|
|
42
|
+
logger.log(` Timeline: ${s.timeline.length} events`);
|
|
43
|
+
logger.log('');
|
|
44
|
+
logger.log(`Output: ${path.relative(targetDir, result.path)}`);
|
|
45
|
+
|
|
46
|
+
return { ok: true, ...result.summary };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { runContextCompact };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { openRuntimeDb } = require('../runtime-store');
|
|
6
|
+
|
|
7
|
+
const CHARS_PER_TOKEN = 4;
|
|
8
|
+
const HEAVY_TOKEN_THRESHOLD = 5000; // ~20KB
|
|
9
|
+
const CRITICAL_TOKEN_THRESHOLD = 12500; // ~50KB
|
|
10
|
+
|
|
11
|
+
function estimateTokens(content) {
|
|
12
|
+
return Math.ceil(content.length / CHARS_PER_TOKEN);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatBytes(bytes) {
|
|
16
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
17
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatTokens(n) {
|
|
21
|
+
return `~${n.toLocaleString()}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function loadFeatureStatuses(contextDir) {
|
|
25
|
+
const featuresPath = path.join(contextDir, 'features.md');
|
|
26
|
+
try {
|
|
27
|
+
const content = await fs.readFile(featuresPath, 'utf8');
|
|
28
|
+
const done = new Set();
|
|
29
|
+
for (const line of content.split(/\r?\n/)) {
|
|
30
|
+
// Match lines like: - auth: done or | auth | done |
|
|
31
|
+
const m = line.match(/[-|]\s*([a-z0-9_-]+)\s*[:|]\s*done/i);
|
|
32
|
+
if (m) done.add(m[1].toLowerCase());
|
|
33
|
+
}
|
|
34
|
+
return done;
|
|
35
|
+
} catch {
|
|
36
|
+
return new Set();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function getCacheHitRate(db) {
|
|
41
|
+
if (!db) return null;
|
|
42
|
+
try {
|
|
43
|
+
const rows = db.prepare(`
|
|
44
|
+
SELECT COUNT(*) as total,
|
|
45
|
+
SUM(CASE WHEN event_type = 'cache_hit' THEN 1 ELSE 0 END) as hits
|
|
46
|
+
FROM execution_events
|
|
47
|
+
WHERE created_at >= datetime('now', '-7 days')
|
|
48
|
+
`).get();
|
|
49
|
+
if (!rows || rows.total === 0) return null;
|
|
50
|
+
return Math.round((rows.hits / rows.total) * 100);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function runContextHealth({ args, options = {}, logger }) {
|
|
57
|
+
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
58
|
+
const contextDir = path.join(targetDir, '.aioson', 'context');
|
|
59
|
+
|
|
60
|
+
let entries;
|
|
61
|
+
try {
|
|
62
|
+
entries = await fs.readdir(contextDir);
|
|
63
|
+
} catch {
|
|
64
|
+
if (!options.json) logger.log('No .aioson/context/ directory found.');
|
|
65
|
+
return { ok: false, reason: 'no_context_dir' };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const mdFiles = entries.filter((f) => f.endsWith('.md'));
|
|
69
|
+
const report = [];
|
|
70
|
+
let totalTokens = 0;
|
|
71
|
+
|
|
72
|
+
for (const file of mdFiles) {
|
|
73
|
+
try {
|
|
74
|
+
const content = await fs.readFile(path.join(contextDir, file), 'utf8');
|
|
75
|
+
const tokens = estimateTokens(content);
|
|
76
|
+
totalTokens += tokens;
|
|
77
|
+
report.push({
|
|
78
|
+
file,
|
|
79
|
+
sizeBytes: content.length,
|
|
80
|
+
tokens,
|
|
81
|
+
heavy: tokens > HEAVY_TOKEN_THRESHOLD,
|
|
82
|
+
critical: tokens > CRITICAL_TOKEN_THRESHOLD
|
|
83
|
+
});
|
|
84
|
+
} catch { /* skip unreadable files */ }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
report.sort((a, b) => b.tokens - a.tokens);
|
|
88
|
+
|
|
89
|
+
const doneFeatures = await loadFeatureStatuses(contextDir);
|
|
90
|
+
const staleSpecs = report.filter((r) => {
|
|
91
|
+
if (!r.file.startsWith('spec-')) return false;
|
|
92
|
+
const slug = r.file.replace(/^spec-/, '').replace(/\.md$/, '');
|
|
93
|
+
return doneFeatures.has(slug);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const { db, dbPath } = await openRuntimeDb(targetDir, { mustExist: true }).catch(() => ({ db: null, dbPath: null }));
|
|
97
|
+
const cacheHitRate = await getCacheHitRate(db);
|
|
98
|
+
if (db) db.close();
|
|
99
|
+
|
|
100
|
+
const skeletonPresent = entries.includes('skeleton-system.md') || entries.includes('skeleton.md');
|
|
101
|
+
|
|
102
|
+
if (options.json) {
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
totalTokens,
|
|
106
|
+
files: report,
|
|
107
|
+
staleSpecs: staleSpecs.map((s) => s.file),
|
|
108
|
+
cacheHitRate,
|
|
109
|
+
skeletonPresent,
|
|
110
|
+
dbPath
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const COL_FILE = 28;
|
|
115
|
+
const COL_SIZE = 10;
|
|
116
|
+
const COL_TOKENS = 16;
|
|
117
|
+
|
|
118
|
+
logger.log(`Context Health Report — ${path.basename(targetDir)}`);
|
|
119
|
+
logger.log('─'.repeat(56));
|
|
120
|
+
logger.log('Files'.padEnd(COL_FILE) + 'Size'.padEnd(COL_SIZE) + 'Tokens (est.)');
|
|
121
|
+
logger.log('─'.repeat(56));
|
|
122
|
+
|
|
123
|
+
for (const r of report) {
|
|
124
|
+
const flag = r.critical ? ' !! CRITICAL' : r.heavy ? ' ⚠ HEAVY' : '';
|
|
125
|
+
logger.log(
|
|
126
|
+
r.file.padEnd(COL_FILE) +
|
|
127
|
+
formatBytes(r.sizeBytes).padEnd(COL_SIZE) +
|
|
128
|
+
formatTokens(r.tokens) + flag
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
logger.log('─'.repeat(56));
|
|
133
|
+
logger.log(`Total context load:`.padEnd(COL_FILE + COL_SIZE) + formatTokens(totalTokens) + ' tokens');
|
|
134
|
+
logger.log('');
|
|
135
|
+
|
|
136
|
+
const heavyFiles = report.filter((r) => r.heavy);
|
|
137
|
+
if (heavyFiles.length > 0) {
|
|
138
|
+
for (const r of heavyFiles) {
|
|
139
|
+
const label = r.critical ? 'CRITICAL' : 'heavy';
|
|
140
|
+
logger.log(`⚠ ${r.file} is ${label} (${formatBytes(r.sizeBytes)}). Consider:`);
|
|
141
|
+
logger.log(` → Run: aioson context:pack . --scope=<feature>`);
|
|
142
|
+
logger.log(` Creates a scoped context for a specific feature`);
|
|
143
|
+
}
|
|
144
|
+
logger.log('');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (staleSpecs.length > 0) {
|
|
148
|
+
logger.log(`⚠ ${staleSpecs.length} stale spec file(s) (features: done):`);
|
|
149
|
+
for (const s of staleSpecs) {
|
|
150
|
+
const slug = s.file.replace(/^spec-/, '').replace(/\.md$/, '');
|
|
151
|
+
logger.log(` → ${s.file} (feature: ${slug} is done)`);
|
|
152
|
+
}
|
|
153
|
+
logger.log(` Run: aioson context:trim . to archive them`);
|
|
154
|
+
logger.log('');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (cacheHitRate !== null) {
|
|
158
|
+
logger.log(`✓ Cache hit rate: ${cacheHitRate}% (last 7 days)`);
|
|
159
|
+
}
|
|
160
|
+
if (skeletonPresent) {
|
|
161
|
+
logger.log(`✓ skeleton-system.md present — agents can use as lightweight index`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
ok: true,
|
|
166
|
+
totalTokens,
|
|
167
|
+
files: report,
|
|
168
|
+
staleSpecs: staleSpecs.map((s) => s.file),
|
|
169
|
+
cacheHitRate,
|
|
170
|
+
skeletonPresent,
|
|
171
|
+
dbPath
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = { runContextHealth };
|