@jaimevalasek/aioson 1.6.0 → 1.7.2
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 +74 -0
- package/README.md +729 -232
- package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
- 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 +3 -0
- package/docs/pt/agentes.md +1 -0
- package/docs/pt/comandos-cli.md +888 -2
- package/docs/pt/design-hybrid-forge.md +255 -6
- 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/monitor-de-contexto.md +59 -5
- package/docs/pt/sdd-automation-scripts.md +557 -0
- package/docs/pt/site-forge.md +309 -0
- 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/cli.js +235 -1
- package/src/commands/agent-audit.js +397 -0
- package/src/commands/agent-export-skill.js +229 -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-compact.js +49 -0
- package/src/commands/context-health.js +175 -0
- package/src/commands/context-monitor.js +71 -0
- package/src/commands/context-trim.js +177 -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/hooks-emit.js +253 -0
- package/src/commands/hooks-install.js +347 -0
- package/src/commands/learning-auto-promote.js +195 -0
- package/src/commands/learning-evolve.js +18 -9
- package/src/commands/learning-export.js +103 -0
- package/src/commands/learning-rollback.js +164 -0
- package/src/commands/live.js +25 -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/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/self-implement-loop.js +256 -0
- package/src/commands/session-guard.js +218 -0
- package/src/commands/sizing.js +165 -0
- package/src/commands/skill.js +65 -0
- 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/update.js +2 -0
- package/src/commands/verify-gate.js +572 -0
- package/src/commands/workflow-execute.js +241 -0
- package/src/constants.js +22 -0
- package/src/install-profile.js +2 -2
- package/src/install-wizard.js +3 -2
- package/src/installer.js +6 -0
- 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/preflight-engine.js +443 -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 +61 -3
- 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/updater.js +4 -5
- package/src/worker-runner.js +186 -1
- package/template/.aioson/agents/analyst.md +62 -1
- package/template/.aioson/agents/architect.md +61 -1
- package/template/.aioson/agents/copywriter.md +463 -0
- package/template/.aioson/agents/design-hybrid-forge.md +14 -0
- package/template/.aioson/agents/dev.md +271 -25
- package/template/.aioson/agents/deyvin.md +67 -8
- package/template/.aioson/agents/discovery-design-doc.md +44 -0
- package/template/.aioson/agents/genome.md +14 -0
- package/template/.aioson/agents/neo.md +83 -2
- package/template/.aioson/agents/orache.md +50 -4
- package/template/.aioson/agents/orchestrator.md +197 -1
- package/template/.aioson/agents/pm.md +35 -0
- package/template/.aioson/agents/product.md +50 -5
- 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 +273 -21
- package/template/.aioson/agents/setup.md +96 -10
- package/template/.aioson/agents/sheldon.md +131 -6
- package/template/.aioson/agents/site-forge.md +1753 -0
- package/template/.aioson/agents/squad.md +352 -0
- package/template/.aioson/agents/tester.md +53 -0
- package/template/.aioson/agents/ux-ui.md +203 -4
- 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 +143 -13
- package/template/.aioson/constitution.md +33 -0
- package/template/.aioson/context/project-pulse.md +34 -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/genomes/copywriting.md +204 -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/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 +2 -0
- 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 +2 -0
- 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 +101 -18
- 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/cognitive-core-ui/references/motion.md +2 -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/marketing/references/anti-patterns.md +254 -0
- package/template/.aioson/skills/marketing/references/fascinations.md +192 -0
- package/template/.aioson/skills/marketing/references/five-acts.md +248 -0
- package/template/.aioson/skills/marketing/references/market-intelligence.md +198 -0
- package/template/.aioson/skills/marketing/references/offer-structure.md +203 -0
- package/template/.aioson/skills/marketing/references/one-belief.md +149 -0
- package/template/.aioson/skills/marketing/references/patterns.md +218 -0
- package/template/.aioson/skills/marketing/references/pms-research.md +193 -0
- package/template/.aioson/skills/marketing/vsl-craft.md +385 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +1 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -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/maintenance-and-state.md +35 -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/design-hybrid-forge/SKILL.md +4 -1
- package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +15 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +32 -0
- package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +20 -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/landing-page-deploy.md +192 -0
- package/template/.aioson/skills/static/landing-page-forge.md +730 -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/ui-ux-modern.md +1 -0
- package/template/.aioson/skills/static/web-research-cache.md +112 -0
- package/template/.aioson/tasks/implementation-plan.md +21 -1
- package/template/.aioson/tasks/squad-create.md +22 -0
- package/template/.aioson/tasks/squad-design.md +30 -0
- package/template/.aioson/templates/squads/digital-marketing-agency/template.json +96 -0
- 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 +55 -3
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +4 -0
- package/template/researchs/.gitkeep +0 -0
- package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Squad 4-tier verification gate
|
|
5
|
+
*
|
|
6
|
+
* Verifies task deliverables beyond pass/fail:
|
|
7
|
+
*
|
|
8
|
+
* Tier 1 — Exists: File/path exists on disk
|
|
9
|
+
* Tier 2 — Substantive: Not a stub/placeholder (min lines + anti-pattern scan)
|
|
10
|
+
* Tier 3 — Wired: Referenced/imported in expected target location
|
|
11
|
+
* Tier 4 — Functional: Smoke command returns expected output (opt-in, expensive)
|
|
12
|
+
*
|
|
13
|
+
* Used by reflection.js when a task has a `must_haves` contract.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('node:fs/promises');
|
|
17
|
+
const { execFile } = require('node:child_process');
|
|
18
|
+
const { promisify } = require('node:util');
|
|
19
|
+
|
|
20
|
+
const execFileAsync = promisify(execFile);
|
|
21
|
+
|
|
22
|
+
// Patterns that indicate a file is a stub, not real implementation
|
|
23
|
+
const ANTI_PATTERNS = [
|
|
24
|
+
{ pattern: /\bTODO\b/, label: 'TODO marker' },
|
|
25
|
+
{ pattern: /\bFIXME\b/, label: 'FIXME marker' },
|
|
26
|
+
{ pattern: /\bplaceholder\b/i, label: 'placeholder text' },
|
|
27
|
+
{ pattern: /not\s+implemented/i, label: '"not implemented"' },
|
|
28
|
+
{ pattern: /return\s+null;\s*\n?\s*\}/m, label: 'empty return null' },
|
|
29
|
+
{ pattern: /throw\s+new\s+Error\s*\(['"]not\s+impl/i, label: 'NotImplemented throw' },
|
|
30
|
+
{ pattern: /^\s*pass\s*$/m, label: 'Python stub (pass)' },
|
|
31
|
+
{ pattern: /^\s*\.\.\.\s*$/m, label: 'ellipsis stub' }
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// ─── Artifact string parser ───────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse an artifact descriptor string into structured fields.
|
|
38
|
+
*
|
|
39
|
+
* Examples:
|
|
40
|
+
* "src/routes/auth.ts"
|
|
41
|
+
* "src/routes/auth.ts (>50 lines)"
|
|
42
|
+
* "src/routes/auth.ts (>50 lines, exports router)"
|
|
43
|
+
*
|
|
44
|
+
* Returns: { filePath, minLines, wiredPattern }
|
|
45
|
+
*/
|
|
46
|
+
function parseArtifact(str, projectDir) {
|
|
47
|
+
const s = String(str).trim();
|
|
48
|
+
|
|
49
|
+
// Extract optional annotations in parentheses
|
|
50
|
+
const parenMatch = s.match(/^(.+?)\s*\(([^)]*)\)\s*$/);
|
|
51
|
+
const rawPath = parenMatch ? parenMatch[1].trim() : s;
|
|
52
|
+
const annotations = parenMatch ? parenMatch[2] : '';
|
|
53
|
+
|
|
54
|
+
// Build absolute path (relative to projectDir if given)
|
|
55
|
+
const filePath = projectDir
|
|
56
|
+
? require('node:path').resolve(projectDir, rawPath)
|
|
57
|
+
: rawPath;
|
|
58
|
+
|
|
59
|
+
// Parse min lines: ">50 lines" or "50 lines"
|
|
60
|
+
const linesMatch = annotations.match(/>?\s*(\d+)\s+lines?/i);
|
|
61
|
+
const minLines = linesMatch ? parseInt(linesMatch[1], 10) : 5;
|
|
62
|
+
|
|
63
|
+
// Parse wired pattern: any text after "exports" or "imports" or "registers"
|
|
64
|
+
const wiredMatch = annotations.match(/(?:exports?|imports?|registers?)\s+(.+)/i);
|
|
65
|
+
const wiredPattern = wiredMatch ? wiredMatch[1].trim() : null;
|
|
66
|
+
|
|
67
|
+
return { filePath, rawPath, minLines, wiredPattern };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse a key_link descriptor string.
|
|
72
|
+
*
|
|
73
|
+
* Example: "auth router registered in src/app.ts"
|
|
74
|
+
* Returns: { pattern, inFile }
|
|
75
|
+
*
|
|
76
|
+
* Pattern is extracted heuristically from the string.
|
|
77
|
+
*/
|
|
78
|
+
function parseKeyLink(str, projectDir) {
|
|
79
|
+
const path = require('node:path');
|
|
80
|
+
const s = String(str).trim();
|
|
81
|
+
|
|
82
|
+
// Pattern: "<thing> in <filepath>" or "<thing> registered/imported/used in <filepath>"
|
|
83
|
+
const inMatch = s.match(/^(.+?)\s+(?:in|from|inside)\s+(\S+\.(?:ts|js|tsx|jsx|py|rb|go|java|php|vue|svelte))\s*$/i);
|
|
84
|
+
if (inMatch) {
|
|
85
|
+
const rawFile = inMatch[2].trim();
|
|
86
|
+
const inFile = projectDir ? path.resolve(projectDir, rawFile) : rawFile;
|
|
87
|
+
|
|
88
|
+
// Extract meaningful keywords from the pattern part (first 3 significant words)
|
|
89
|
+
const patternWords = inMatch[1]
|
|
90
|
+
.replace(/\b(?:registered|imported|used|exported|referenced|wired|connected|added)\b/gi, '')
|
|
91
|
+
.trim()
|
|
92
|
+
.split(/\s+/)
|
|
93
|
+
.filter((w) => w.length > 2)
|
|
94
|
+
.slice(0, 3);
|
|
95
|
+
|
|
96
|
+
return { pattern: patternWords.join('|'), inFile, rawFile };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── Tier implementations ─────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/** Tier 1: file exists on disk */
|
|
105
|
+
async function verifyExists(filePath) {
|
|
106
|
+
try {
|
|
107
|
+
await fs.access(filePath);
|
|
108
|
+
return { passed: true, tier: 1, file: filePath };
|
|
109
|
+
} catch {
|
|
110
|
+
return { passed: false, tier: 1, file: filePath, reason: 'file does not exist' };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Tier 2: file is substantive (not a stub) */
|
|
115
|
+
async function verifySubstantive(filePath, minLines = 5) {
|
|
116
|
+
let content;
|
|
117
|
+
try {
|
|
118
|
+
content = await fs.readFile(filePath, 'utf8');
|
|
119
|
+
} catch {
|
|
120
|
+
return { passed: false, tier: 2, file: filePath, reason: 'cannot read file' };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const nonEmptyLines = content.split('\n').filter((l) => l.trim().length > 0).length;
|
|
124
|
+
if (nonEmptyLines < minLines) {
|
|
125
|
+
return {
|
|
126
|
+
passed: false, tier: 2, file: filePath,
|
|
127
|
+
reason: `only ${nonEmptyLines} non-empty lines (min: ${minLines})`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (const { pattern, label } of ANTI_PATTERNS) {
|
|
132
|
+
if (pattern.test(content)) {
|
|
133
|
+
return { passed: false, tier: 2, file: filePath, reason: `contains ${label}` };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { passed: true, tier: 2, file: filePath, lines: nonEmptyLines };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Tier 3: pattern found in a target file (wired/imported/registered) */
|
|
141
|
+
async function verifyWired(pattern, inFile) {
|
|
142
|
+
if (!pattern || !inFile) {
|
|
143
|
+
return { passed: true, tier: 3, skipped: true, reason: 'no wired constraint to check' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let content;
|
|
147
|
+
try {
|
|
148
|
+
content = await fs.readFile(inFile, 'utf8');
|
|
149
|
+
} catch {
|
|
150
|
+
return { passed: false, tier: 3, file: inFile, reason: `cannot read target file: ${inFile}` };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const regex = typeof pattern === 'string'
|
|
154
|
+
? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')
|
|
155
|
+
: pattern;
|
|
156
|
+
|
|
157
|
+
if (regex.test(content)) {
|
|
158
|
+
return { passed: true, tier: 3, pattern, inFile };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
passed: false, tier: 3, pattern, inFile,
|
|
163
|
+
reason: `pattern "${pattern}" not found in ${inFile}`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Tier 4: smoke command returns expected output (optional, expensive) */
|
|
168
|
+
async function verifyFunctional(command, args = [], expectedPattern = null, timeoutMs = 10_000) {
|
|
169
|
+
try {
|
|
170
|
+
const { stdout, stderr } = await execFileAsync(command, args, {
|
|
171
|
+
timeout: timeoutMs,
|
|
172
|
+
shell: false
|
|
173
|
+
});
|
|
174
|
+
const output = stdout + stderr;
|
|
175
|
+
|
|
176
|
+
if (expectedPattern) {
|
|
177
|
+
const regex = typeof expectedPattern === 'string'
|
|
178
|
+
? new RegExp(expectedPattern, 'i')
|
|
179
|
+
: expectedPattern;
|
|
180
|
+
if (!regex.test(output)) {
|
|
181
|
+
return {
|
|
182
|
+
passed: false, tier: 4, command,
|
|
183
|
+
reason: `expected pattern "${expectedPattern}" not found in command output`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { passed: true, tier: 4, command, output: output.slice(0, 300) };
|
|
189
|
+
} catch (err) {
|
|
190
|
+
return { passed: false, tier: 4, command, reason: err.message.slice(0, 200) };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ─── must_haves checker ───────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Run must_haves verification against a task's contract.
|
|
198
|
+
*
|
|
199
|
+
* @param {object} mustHaves — { truths?, artifacts?, key_links? }
|
|
200
|
+
* @param {string} output — Task output text (used for truths checks)
|
|
201
|
+
* @param {string} projectDir
|
|
202
|
+
* @returns {Promise<MustHavesResult>}
|
|
203
|
+
*
|
|
204
|
+
* MustHavesResult:
|
|
205
|
+
* {
|
|
206
|
+
* passed: boolean,
|
|
207
|
+
* failures: string[],
|
|
208
|
+
* warnings: string[],
|
|
209
|
+
* details: object[]
|
|
210
|
+
* }
|
|
211
|
+
*/
|
|
212
|
+
async function checkMustHaves(mustHaves, output, projectDir) {
|
|
213
|
+
if (!mustHaves) return { passed: true, failures: [], warnings: [], details: [] };
|
|
214
|
+
|
|
215
|
+
const failures = [];
|
|
216
|
+
const warnings = [];
|
|
217
|
+
const details = [];
|
|
218
|
+
const outputText = String(output || '').toLowerCase();
|
|
219
|
+
|
|
220
|
+
// ── Truths: heuristic — check if output mentions the expected state ──────
|
|
221
|
+
for (const truth of (mustHaves.truths || [])) {
|
|
222
|
+
// Extract key nouns/verbs from truth statement and check they appear in output
|
|
223
|
+
const keywords = String(truth)
|
|
224
|
+
.replace(/\b(?:the|a|an|is|are|can|will|should|must|to|of|in|and|or|that)\b/gi, '')
|
|
225
|
+
.split(/\s+/)
|
|
226
|
+
.filter((w) => w.length > 3)
|
|
227
|
+
.slice(0, 4);
|
|
228
|
+
|
|
229
|
+
const found = keywords.some((kw) => outputText.includes(kw.toLowerCase()));
|
|
230
|
+
const result = { type: 'truth', statement: truth, passed: found };
|
|
231
|
+
details.push(result);
|
|
232
|
+
|
|
233
|
+
if (!found) {
|
|
234
|
+
warnings.push(`Truth not evident in output: "${truth.slice(0, 80)}"`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Artifacts: Tier 1 + Tier 2 checks ────────────────────────────────────
|
|
239
|
+
for (const artifactStr of (mustHaves.artifacts || [])) {
|
|
240
|
+
const { filePath, rawPath, minLines, wiredPattern } = parseArtifact(artifactStr, projectDir);
|
|
241
|
+
|
|
242
|
+
const t1 = await verifyExists(filePath);
|
|
243
|
+
details.push({ type: 'artifact', descriptor: artifactStr, ...t1 });
|
|
244
|
+
|
|
245
|
+
if (!t1.passed) {
|
|
246
|
+
failures.push(`Artifact missing: ${rawPath}`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const t2 = await verifySubstantive(filePath, minLines);
|
|
251
|
+
details.push({ type: 'artifact_substantive', descriptor: artifactStr, ...t2 });
|
|
252
|
+
|
|
253
|
+
if (!t2.passed) {
|
|
254
|
+
failures.push(`Artifact is a stub: ${rawPath} — ${t2.reason}`);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// If wired pattern specified, verify it exists in the same file
|
|
259
|
+
if (wiredPattern) {
|
|
260
|
+
const t3 = await verifyWired(wiredPattern, filePath);
|
|
261
|
+
details.push({ type: 'artifact_wired', descriptor: artifactStr, ...t3 });
|
|
262
|
+
if (!t3.passed) {
|
|
263
|
+
warnings.push(`Artifact wiring issue: ${rawPath} — ${t3.reason}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── Key links: Tier 3 checks ──────────────────────────────────────────────
|
|
269
|
+
for (const keyLink of (mustHaves.key_links || [])) {
|
|
270
|
+
const parsed = parseKeyLink(keyLink, projectDir);
|
|
271
|
+
|
|
272
|
+
if (!parsed) {
|
|
273
|
+
// Cannot parse — skip silently (heuristic limitation)
|
|
274
|
+
details.push({ type: 'key_link', descriptor: keyLink, passed: true, skipped: true });
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const t3 = await verifyWired(parsed.pattern, parsed.inFile);
|
|
279
|
+
details.push({ type: 'key_link', descriptor: keyLink, ...t3 });
|
|
280
|
+
|
|
281
|
+
if (!t3.passed) {
|
|
282
|
+
warnings.push(`Key link not wired: "${keyLink.slice(0, 80)}" — ${t3.reason}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
passed: failures.length === 0,
|
|
288
|
+
failures,
|
|
289
|
+
warnings,
|
|
290
|
+
details
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = {
|
|
295
|
+
verifyExists,
|
|
296
|
+
verifySubstantive,
|
|
297
|
+
verifyWired,
|
|
298
|
+
verifyFunctional,
|
|
299
|
+
checkMustHaves,
|
|
300
|
+
parseArtifact,
|
|
301
|
+
parseKeyLink,
|
|
302
|
+
ANTI_PATTERNS
|
|
303
|
+
};
|
package/src/updater.js
CHANGED
|
@@ -15,17 +15,16 @@ async function updateInstallation(targetDir, options = {}) {
|
|
|
15
15
|
|
|
16
16
|
const savedProfile = await readInstallProfile(targetDir);
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// This ensures new framework files from the new version are always installed.
|
|
21
|
-
// Profile-based filtering only applies to init/install.
|
|
18
|
+
// Default: only update files already present in the target (selective update).
|
|
19
|
+
// With --all: install every file from the template, including new ones not yet installed.
|
|
22
20
|
const result = await installTemplate(targetDir, {
|
|
23
21
|
overwrite: true,
|
|
24
22
|
dryRun: Boolean(options.dryRun),
|
|
25
23
|
mode: 'update',
|
|
26
24
|
backupOnOverwrite: true,
|
|
27
25
|
frameworkDetection: options.frameworkDetection || null,
|
|
28
|
-
installProfile: null
|
|
26
|
+
installProfile: null,
|
|
27
|
+
selectiveUpdate: !options.all
|
|
29
28
|
});
|
|
30
29
|
|
|
31
30
|
return {
|
package/src/worker-runner.js
CHANGED
|
@@ -123,12 +123,188 @@ function sleep(ms) {
|
|
|
123
123
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// ─── Research Worker ──────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Handle a `type: 'research'` worker.
|
|
130
|
+
*
|
|
131
|
+
* Checks the researchs/ cache first (7-day default TTL), falls back to
|
|
132
|
+
* scraping declared URLs or the topic keyword via web.js fetchPage.
|
|
133
|
+
*
|
|
134
|
+
* Cache location: researchs/{topic}/summary.md
|
|
135
|
+
*/
|
|
136
|
+
async function runResearchWorker(projectDir, config, inputPayload) {
|
|
137
|
+
const { fetchPage } = require('./web');
|
|
138
|
+
const research = config.research || {};
|
|
139
|
+
const topic = String(research.topic || inputPayload?.topic || 'general').replace(/[^a-z0-9-]/gi, '-').toLowerCase();
|
|
140
|
+
const cacheHours = Number(research.cache_hours || 168); // 7 days default
|
|
141
|
+
const cacheDir = path.join(projectDir, research.cache_dir || 'researchs', topic);
|
|
142
|
+
const summaryPath = path.join(cacheDir, 'summary.md');
|
|
143
|
+
|
|
144
|
+
// ── Cache check ────────────────────────────────────────────────────────────
|
|
145
|
+
try {
|
|
146
|
+
const stat = await fs.stat(summaryPath);
|
|
147
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
148
|
+
const ageHours = ageMs / (1000 * 60 * 60);
|
|
149
|
+
if (ageHours < cacheHours) {
|
|
150
|
+
const cached = await fs.readFile(summaryPath, 'utf8');
|
|
151
|
+
return {
|
|
152
|
+
ok: true,
|
|
153
|
+
output: { topic, summary: cached, cached: true, cache_age_hours: Math.round(ageHours) },
|
|
154
|
+
attempt: 1
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
} catch { /* no cache yet */ }
|
|
158
|
+
|
|
159
|
+
// ── Scrape sources ─────────────────────────────────────────────────────────
|
|
160
|
+
const urls = research.urls || inputPayload?.urls || [];
|
|
161
|
+
const maxSources = Number(research.max_sources || 5);
|
|
162
|
+
const pages = [];
|
|
163
|
+
|
|
164
|
+
for (const url of urls.slice(0, maxSources)) {
|
|
165
|
+
try {
|
|
166
|
+
const result = await fetchPage(url, { timeoutMs: 15000, extractLinks: false });
|
|
167
|
+
if (result.ok && result.text) {
|
|
168
|
+
pages.push({ url, content: result.text.slice(0, 3000) });
|
|
169
|
+
}
|
|
170
|
+
} catch { /* skip unreachable sources */ }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (pages.length === 0) {
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
error: `Research worker "${topic}": no URLs declared and no cached summary. Add "research.urls" to worker.json or provide ?topic= with cached data.`,
|
|
177
|
+
attempts: 1
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── Build summary ──────────────────────────────────────────────────────────
|
|
182
|
+
const ts = new Date().toISOString();
|
|
183
|
+
const summary = [
|
|
184
|
+
`# Research: ${topic}`,
|
|
185
|
+
`_Generated: ${ts} · Sources: ${pages.length}_`,
|
|
186
|
+
'',
|
|
187
|
+
...pages.map((p, i) => [
|
|
188
|
+
`## Source ${i + 1}: ${p.url}`,
|
|
189
|
+
'',
|
|
190
|
+
p.content.slice(0, 2000),
|
|
191
|
+
''
|
|
192
|
+
].join('\n'))
|
|
193
|
+
].join('\n');
|
|
194
|
+
|
|
195
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
196
|
+
await fs.writeFile(summaryPath, summary, 'utf8');
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
output: { topic, summary, cached: false, sources: pages.length, generated_at: ts },
|
|
201
|
+
attempt: 1
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ─── Skill Worker (Plan 81 §2.2) ─────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Handle a `type: 'skill'` worker.
|
|
209
|
+
* Resolves an external Agent Skills Standard skill and executes it.
|
|
210
|
+
*
|
|
211
|
+
* Skill sources:
|
|
212
|
+
* - Local path: ./skills/my-skill/ or .claude/skills/my-skill/
|
|
213
|
+
* - NPM package: npm:@org/skill-name (resolved from node_modules)
|
|
214
|
+
*/
|
|
215
|
+
async function runSkillWorker(projectDir, config, inputPayload) {
|
|
216
|
+
const skillRef = config.skill || config.source || '';
|
|
217
|
+
let skillDir;
|
|
218
|
+
|
|
219
|
+
if (skillRef.startsWith('npm:')) {
|
|
220
|
+
// Resolve from node_modules
|
|
221
|
+
const pkgName = skillRef.slice(4);
|
|
222
|
+
skillDir = path.join(projectDir, 'node_modules', pkgName);
|
|
223
|
+
} else {
|
|
224
|
+
// Local path
|
|
225
|
+
skillDir = path.resolve(projectDir, skillRef);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check SKILL.md exists
|
|
229
|
+
const skillMdPath = path.join(skillDir, 'SKILL.md');
|
|
230
|
+
if (!(await pathExists(skillMdPath))) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
error: `Skill not found: ${skillRef} (expected SKILL.md at ${skillMdPath})`,
|
|
234
|
+
attempts: 0
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Check for executable scripts
|
|
239
|
+
const scriptsDir = path.join(skillDir, 'scripts');
|
|
240
|
+
const runScript = path.join(scriptsDir, 'run.js');
|
|
241
|
+
const runPyScript = path.join(scriptsDir, 'run.py');
|
|
242
|
+
|
|
243
|
+
if (await pathExists(runScript)) {
|
|
244
|
+
return spawnWorker(runScript, inputPayload || {}, {}, config.timeout_ms || DEFAULT_TIMEOUT);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (await pathExists(runPyScript)) {
|
|
248
|
+
return spawnWorker(runPyScript, inputPayload || {}, {}, config.timeout_ms || DEFAULT_TIMEOUT);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// No executable script — return skill content for LLM-based execution
|
|
252
|
+
const skillContent = await fs.readFile(skillMdPath, 'utf8');
|
|
253
|
+
return {
|
|
254
|
+
ok: true,
|
|
255
|
+
output: {
|
|
256
|
+
type: 'skill-prompt',
|
|
257
|
+
skillPath: skillMdPath,
|
|
258
|
+
content: skillContent.slice(0, 4000),
|
|
259
|
+
message: `Skill "${skillRef}" loaded. No run script found — use skill content as agent instructions.`
|
|
260
|
+
},
|
|
261
|
+
attempt: 1
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ─── Agent Memory Loader (Plan 81 §Sprint 4) ────────────────────────────────
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Load per-agent persistent memory if it exists.
|
|
269
|
+
* Returns memory content or null.
|
|
270
|
+
*/
|
|
271
|
+
async function loadAgentMemory(projectDir, squadSlug, executorSlug) {
|
|
272
|
+
const memoryPath = path.join(
|
|
273
|
+
projectDir, SQUADS_DIR, squadSlug, 'agent-memory', `${executorSlug}.md`
|
|
274
|
+
);
|
|
275
|
+
try {
|
|
276
|
+
return await fs.readFile(memoryPath, 'utf8');
|
|
277
|
+
} catch {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
126
282
|
async function runWorker(projectDir, squadSlug, workerSlug, inputPayload, options = {}) {
|
|
127
283
|
const config = await loadWorkerConfig(projectDir, squadSlug, workerSlug);
|
|
128
284
|
if (!config) {
|
|
129
285
|
return { ok: false, error: `Worker config not found: ${workerSlug}`, attempts: 0 };
|
|
130
286
|
}
|
|
131
287
|
|
|
288
|
+
// Skill worker — external skill execution (Plan 81 §2.2)
|
|
289
|
+
if (config.type === 'skill') {
|
|
290
|
+
return runSkillWorker(projectDir, config, inputPayload || {});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Research worker — special handler (4.1)
|
|
294
|
+
if (config.type === 'research') {
|
|
295
|
+
return runResearchWorker(projectDir, config, inputPayload || {});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Load per-agent persistent memory (Plan 81 §Sprint 4)
|
|
299
|
+
const agentMemory = await loadAgentMemory(projectDir, squadSlug, workerSlug);
|
|
300
|
+
if (agentMemory && inputPayload) {
|
|
301
|
+
// Inject into _agent_memory field (readable by Node.js workers via process.argv[2])
|
|
302
|
+
inputPayload._agent_memory = agentMemory;
|
|
303
|
+
// Prefix into context so LLM-based workers receive it as part of their task context
|
|
304
|
+
const existingContext = inputPayload.context || '';
|
|
305
|
+
inputPayload.context = `## Your accumulated knowledge:\n${agentMemory}\n\n---\n\n${existingContext}`.trimEnd();
|
|
306
|
+
}
|
|
307
|
+
|
|
132
308
|
// Validate inputs
|
|
133
309
|
const validation = validateInputs(inputPayload || {}, config.inputs);
|
|
134
310
|
if (!validation.valid) {
|
|
@@ -149,6 +325,13 @@ async function runWorker(projectDir, squadSlug, workerSlug, inputPayload, option
|
|
|
149
325
|
// Resolve env vars
|
|
150
326
|
const env = resolveEnvVars(config.env);
|
|
151
327
|
|
|
328
|
+
// Expose agent memory path as env var so workers can read it directly
|
|
329
|
+
const memoryFilePath = path.join(projectDir, SQUADS_DIR, squadSlug, 'agent-memory', `${workerSlug}.md`);
|
|
330
|
+
try {
|
|
331
|
+
await fs.access(memoryFilePath);
|
|
332
|
+
env.AIOSON_AGENT_MEMORY_PATH = memoryFilePath;
|
|
333
|
+
} catch { /* no memory file yet — env var omitted */ }
|
|
334
|
+
|
|
152
335
|
// Resolve MCP env vars if worker declares uses_mcp
|
|
153
336
|
if (config.uses_mcp && config.uses_mcp.length > 0) {
|
|
154
337
|
try {
|
|
@@ -335,5 +518,7 @@ module.exports = {
|
|
|
335
518
|
generateRunJs,
|
|
336
519
|
generateWorkerReadme,
|
|
337
520
|
validateInputs,
|
|
338
|
-
resolveEnvVars
|
|
521
|
+
resolveEnvVars,
|
|
522
|
+
runSkillWorker,
|
|
523
|
+
loadAgentMemory
|
|
339
524
|
};
|
|
@@ -39,6 +39,15 @@ Check the following before doing anything else:
|
|
|
39
39
|
- `.aioson/context/prd-{slug}.md` (feature mode)
|
|
40
40
|
- `.aioson/context/design-doc.md` + `readiness.md` (if present)
|
|
41
41
|
- `.aioson/context/discovery.md` + `spec.md` (feature mode — project context, if present)
|
|
42
|
+
- `.aioson/plans/{slug}/manifest.md` (if present — Sheldon phased plans; check subdirectories of `.aioson/plans/`)
|
|
43
|
+
|
|
44
|
+
## Sheldon enrichment context (RDA-01)
|
|
45
|
+
|
|
46
|
+
If `.aioson/context/sheldon-enrichment.md` (or `sheldon-enrichment-{slug}.md`) exists at session start:
|
|
47
|
+
- Read it silently — do not display its contents to the user
|
|
48
|
+
- Use the gaps identified and pre-made decisions as additional context for discovery
|
|
49
|
+
- Do not re-ask questions that are already documented in the enrichment log
|
|
50
|
+
- If `plan_path` is set in the frontmatter: read the manifest at that path and scope discovery to Phase 1 first
|
|
42
51
|
|
|
43
52
|
## Context loading policy
|
|
44
53
|
|
|
@@ -102,6 +111,10 @@ Stop here only when neither `discovery.md` nor local scan artifacts exist. Do no
|
|
|
102
111
|
|
|
103
112
|
> **Rule:** whenever `discovery.md` is present, always read `spec.md` alongside it — never one without the other.
|
|
104
113
|
|
|
114
|
+
## Web research cache
|
|
115
|
+
|
|
116
|
+
Before running any web search, load `.aioson/skills/static/web-research-cache.md` and follow the protocol: check `researchs/{slug}/summary.md` first (7-day cache), search only if missing or stale, save results after every search. Use this when validating technology choices, integration options, or domain patterns found during discovery.
|
|
117
|
+
|
|
105
118
|
## Skills and docs on demand
|
|
106
119
|
|
|
107
120
|
Before deepening discovery:
|
|
@@ -111,7 +124,7 @@ Before deepening discovery:
|
|
|
111
124
|
- load only the docs that actually matter for this batch
|
|
112
125
|
- consult local skills only when they improve domain mapping or flow clarity
|
|
113
126
|
- check `.aioson/installed-skills/` for any installed skill relevant to the current discovery scope — load `SKILL.md` of matching skills, then load per-agent references only if they reduce ambiguity for the current phase
|
|
114
|
-
- if `aioson-spec-driven`
|
|
127
|
+
- if `aioson-spec-driven` exists in `.aioson/installed-skills/aioson-spec-driven/SKILL.md` OR in `.aioson/skills/process/aioson-spec-driven/SKILL.md`, load it when starting feature discovery or project discovery — then load `references/analyst.md` from that skill
|
|
115
128
|
|
|
116
129
|
Do not inflate context without need.
|
|
117
130
|
|
|
@@ -213,6 +226,7 @@ For each new or modified entity, produce field-level detail (same format as Phas
|
|
|
213
226
|
feature: {slug}
|
|
214
227
|
status: in_progress
|
|
215
228
|
started: {ISO-date}
|
|
229
|
+
spec_version: 1
|
|
216
230
|
phase_gates:
|
|
217
231
|
requirements: approved # approved | pending | needs_work
|
|
218
232
|
design: pending # approved | pending | skipped (MICRO/SMALL sem @architect)
|
|
@@ -255,6 +269,38 @@ AskUserQuestion:
|
|
|
255
269
|
- label: "Todos aprovados"
|
|
256
270
|
```
|
|
257
271
|
|
|
272
|
+
### Conformance contract (MEDIUM only)
|
|
273
|
+
|
|
274
|
+
If classification is MEDIUM, also generate `.aioson/context/conformance-{slug}.yaml` — a YAML file that structures each AC into machine-readable format:
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
# Conformance Contract — {feature}
|
|
278
|
+
# Generated by: @analyst
|
|
279
|
+
# Verified by: @qa
|
|
280
|
+
|
|
281
|
+
feature: {slug}
|
|
282
|
+
spec_version: 1
|
|
283
|
+
generated_at: {ISO-date}
|
|
284
|
+
|
|
285
|
+
acceptance_criteria:
|
|
286
|
+
- id: AC-{slug}-01
|
|
287
|
+
description: "..."
|
|
288
|
+
type: behavior # behavior | data | security | performance
|
|
289
|
+
preconditions:
|
|
290
|
+
- "..."
|
|
291
|
+
action: "..."
|
|
292
|
+
expected:
|
|
293
|
+
- "..."
|
|
294
|
+
negative_cases:
|
|
295
|
+
- input: "..."
|
|
296
|
+
expected: "..."
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Rules:
|
|
300
|
+
- Only for MEDIUM classification — do not generate for MICRO or SMALL
|
|
301
|
+
- @qa uses it as a structured verification checklist
|
|
302
|
+
- @dev uses it to understand exact expected behavior for test writing
|
|
303
|
+
|
|
258
304
|
Then tell the user: "Feature spec ready. Activate **@dev** to implement — it will read `prd-{slug}.md`, `requirements-{slug}.md`, and `spec-{slug}.md`."
|
|
259
305
|
|
|
260
306
|
## MICRO shortcut
|
|
@@ -298,6 +344,7 @@ Generate `.aioson/context/discovery.md` with the following sections:
|
|
|
298
344
|
- Do not finalize any output file with missing or assumed fields.
|
|
299
345
|
- In feature mode: never duplicate content already in `discovery.md` — only document what is new or changed.
|
|
300
346
|
- If `readiness.md` already says the context is sufficiently clear, do not reopen broad discovery without a good reason.
|
|
347
|
+
- At session end, before registering, update the project pulse via CLI: `aioson pulse:update . --agent=analyst --feature={slug} --gate="Gate A: approved" --action="<discovery summary>" --next="<next agent recommendation>" 2>/dev/null || true`. If `aioson` CLI is not available, update `.aioson/context/project-pulse.md` manually.
|
|
301
348
|
- At session end, after writing the discovery file, register the session: `aioson agent:done . --agent=analyst --summary="<one-line summary of discovery produced>" 2>/dev/null || true`
|
|
302
349
|
- If `aioson` CLI is not available, write a devlog at session end following the "Devlog" section in `.aioson/config.md`.
|
|
303
350
|
|
|
@@ -309,3 +356,17 @@ Ative: `/architect` ou `/dev`
|
|
|
309
356
|
|
|
310
357
|
Também disponível: `/sheldon` (enriquecimento adicional), `/qa` (revisão dos requisitos)
|
|
311
358
|
---
|
|
359
|
+
|
|
360
|
+
## Continuation Protocol
|
|
361
|
+
|
|
362
|
+
Before ending your response, always append:
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
## Next Up
|
|
366
|
+
- Domain modeled: [scope]
|
|
367
|
+
- Next step: `@architect` (technical decisions) or `@product` (PRD refinement) or `/sheldon` (enrichment)
|
|
368
|
+
- `/clear` → fresh context window before continuing
|
|
369
|
+
|
|
370
|
+
**Session artifacts written:**
|
|
371
|
+
- [ ] [list each file created or modified]
|
|
372
|
+
---
|