@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
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('node:fs/promises');
|
|
4
4
|
const path = require('node:path');
|
|
5
|
+
const stateManager = require('./state-manager');
|
|
5
6
|
|
|
6
7
|
const SQUADS_DIR = path.join('.aioson', 'squads');
|
|
8
|
+
const MAX_HISTORY_ENTRIES = 5;
|
|
9
|
+
const MAX_TOKENS = 2000;
|
|
10
|
+
const HISTORY_TOKEN_BUDGET = 600; // tokens reserved for history section
|
|
7
11
|
|
|
8
12
|
// Events that trigger an automatic refresh of the recovery context
|
|
9
13
|
const REFRESH_EVENTS = new Set(['task_completed', 'decision_made', 'handoff']);
|
|
10
14
|
|
|
11
|
-
// Approximate token count (chars / 4)
|
|
15
|
+
// Approximate token count (chars / 4) + 1
|
|
12
16
|
function estimateTokens(str) {
|
|
13
|
-
return Math.ceil(str.length / 4);
|
|
17
|
+
return Math.ceil(str.length / 4) + 1;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
20
|
+
// ─── Data loaders ─────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
19
22
|
async function readManifest(projectDir, squadSlug) {
|
|
20
23
|
const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'squad.manifest.json');
|
|
21
24
|
try {
|
|
@@ -26,27 +29,56 @@ async function readManifest(projectDir, squadSlug) {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
|
-
* Read recent events from
|
|
32
|
+
* Read recent bus events from the bus/ directory (JSONL files).
|
|
33
|
+
* Falls back gracefully if no sessions exist yet.
|
|
30
34
|
*/
|
|
31
35
|
async function readRecentEvents(projectDir, squadSlug, limit = 10) {
|
|
32
|
-
const
|
|
36
|
+
const busDir = path.join(projectDir, SQUADS_DIR, squadSlug, 'bus');
|
|
33
37
|
try {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
38
|
+
const entries = await fs.readdir(busDir, { withFileTypes: true });
|
|
39
|
+
const jsonlFiles = entries
|
|
40
|
+
.filter((e) => e.isFile() && e.name.endsWith('.jsonl'))
|
|
41
|
+
.map((e) => path.join(busDir, e.name));
|
|
42
|
+
|
|
43
|
+
if (jsonlFiles.length === 0) return [];
|
|
44
|
+
|
|
45
|
+
// Read the most recent file (sorted by name — sessions use UUIDs/timestamps)
|
|
46
|
+
const sorted = jsonlFiles.sort();
|
|
47
|
+
const recentFile = sorted[sorted.length - 1];
|
|
48
|
+
const content = await fs.readFile(recentFile, 'utf8');
|
|
49
|
+
|
|
50
|
+
const events = content
|
|
51
|
+
.split('\n')
|
|
52
|
+
.filter((line) => line.trim())
|
|
53
|
+
.map((line) => { try { return JSON.parse(line); } catch { return null; } })
|
|
54
|
+
.filter(Boolean);
|
|
55
|
+
|
|
56
|
+
return events.slice(-limit);
|
|
37
57
|
} catch {
|
|
38
58
|
return [];
|
|
39
59
|
}
|
|
40
60
|
}
|
|
41
61
|
|
|
42
62
|
/**
|
|
43
|
-
* Read recent tasks from
|
|
63
|
+
* Read recent tasks from the most recent session plan.json.
|
|
64
|
+
* Falls back gracefully if no sessions exist yet.
|
|
44
65
|
*/
|
|
45
66
|
async function readRecentTasks(projectDir, squadSlug, limit = 5) {
|
|
46
|
-
const
|
|
67
|
+
const sessionsDir = path.join(projectDir, SQUADS_DIR, squadSlug, 'sessions');
|
|
47
68
|
try {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
69
|
+
const sessionEntries = await fs.readdir(sessionsDir, { withFileTypes: true });
|
|
70
|
+
const sessionDirs = sessionEntries
|
|
71
|
+
.filter((e) => e.isDirectory())
|
|
72
|
+
.map((e) => e.name)
|
|
73
|
+
.sort(); // lexicographic — UUID/timestamp sorts correctly
|
|
74
|
+
|
|
75
|
+
if (sessionDirs.length === 0) return [];
|
|
76
|
+
|
|
77
|
+
// Use most recent session
|
|
78
|
+
const latestSession = sessionDirs[sessionDirs.length - 1];
|
|
79
|
+
const planPath = path.join(sessionsDir, latestSession, 'plan.json');
|
|
80
|
+
const plan = JSON.parse(await fs.readFile(planPath, 'utf8'));
|
|
81
|
+
const tasks = Array.isArray(plan.tasks) ? plan.tasks : [];
|
|
50
82
|
return tasks.slice(-limit);
|
|
51
83
|
} catch {
|
|
52
84
|
return [];
|
|
@@ -54,132 +86,266 @@ async function readRecentTasks(projectDir, squadSlug, limit = 5) {
|
|
|
54
86
|
}
|
|
55
87
|
|
|
56
88
|
/**
|
|
57
|
-
* Read context
|
|
89
|
+
* Read context snapshot from STATE.md via state-manager.
|
|
90
|
+
* Returns a simplified snapshot compatible with buildCurrentState().
|
|
58
91
|
*/
|
|
59
|
-
async function readContextSnapshot(projectDir, squadSlug
|
|
60
|
-
const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'context-monitor.json');
|
|
92
|
+
async function readContextSnapshot(projectDir, squadSlug) {
|
|
61
93
|
try {
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
94
|
+
const state = await stateManager.readState(projectDir, squadSlug);
|
|
95
|
+
if (!state) return null;
|
|
96
|
+
return {
|
|
97
|
+
currentPhase: state.meta.current_session || '',
|
|
98
|
+
recentDecisions: (state.decisions || []).slice(-5),
|
|
99
|
+
activeBlockers: state.blockers || [],
|
|
100
|
+
sessionsCompleted: state.meta.sessions_completed || 0
|
|
101
|
+
};
|
|
67
102
|
} catch {
|
|
68
103
|
return null;
|
|
69
104
|
}
|
|
70
105
|
}
|
|
71
106
|
|
|
107
|
+
// ─── Incremental compaction history ──────────────────────────────────────────
|
|
108
|
+
|
|
72
109
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
110
|
+
* Extract the "## Compaction History" section from an existing recovery-context.md.
|
|
111
|
+
* Returns an array of raw entry strings (trimmed), oldest first.
|
|
75
112
|
*/
|
|
76
|
-
function
|
|
77
|
-
|
|
113
|
+
function extractCompactionHistory(existingContent) {
|
|
114
|
+
if (!existingContent) return [];
|
|
115
|
+
|
|
116
|
+
const match = existingContent.match(/## Compaction History\n([\s\S]*?)(?=\n---|\n## [^C]|$)/);
|
|
117
|
+
if (!match) return [];
|
|
78
118
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
119
|
+
// Split on "### Snapshot" entries
|
|
120
|
+
const raw = match[1].trim();
|
|
121
|
+
if (!raw) return [];
|
|
122
|
+
|
|
123
|
+
const entries = raw.split(/(?=### Snapshot)/).map((e) => e.trim()).filter(Boolean);
|
|
124
|
+
return entries;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extract the "## Current State" section from an existing recovery-context.md.
|
|
129
|
+
* This becomes a history entry when a new snapshot is generated.
|
|
130
|
+
*/
|
|
131
|
+
function extractCurrentState(existingContent) {
|
|
132
|
+
if (!existingContent) return null;
|
|
133
|
+
|
|
134
|
+
const match = existingContent.match(/## Current State\n([\s\S]*?)(?=\n## |$)/);
|
|
135
|
+
if (!match) return null;
|
|
136
|
+
|
|
137
|
+
const body = match[1].trim();
|
|
138
|
+
if (!body) return null;
|
|
139
|
+
|
|
140
|
+
// Wrap as a Snapshot history entry
|
|
141
|
+
const ts = existingContent.match(/> Last Updated: (.+)/)?.[1] || new Date().toISOString();
|
|
142
|
+
return `### Snapshot — ${ts}\n${body}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─── Snapshot builder ─────────────────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Build the "Current State" section content from live data.
|
|
149
|
+
* This is the fresh snapshot injected on every recovery update.
|
|
150
|
+
*/
|
|
151
|
+
function buildCurrentState(squadSlug, agentSlug, manifest, tasks, events, ctxSnapshot) {
|
|
152
|
+
const lines = [];
|
|
82
153
|
|
|
83
154
|
// Squad goal
|
|
84
155
|
if (manifest.goal) {
|
|
85
|
-
lines.push(
|
|
86
|
-
lines.push(manifest.goal);
|
|
156
|
+
lines.push(`**Squad goal:** ${manifest.goal}`);
|
|
87
157
|
lines.push('');
|
|
88
158
|
}
|
|
89
159
|
|
|
90
|
-
// Agent role
|
|
91
|
-
const executor = (manifest.executors || []).find(e => e.slug === agentSlug);
|
|
160
|
+
// Agent role
|
|
161
|
+
const executor = (manifest.executors || []).find((e) => e.slug === agentSlug);
|
|
92
162
|
if (executor) {
|
|
93
|
-
lines.push(
|
|
94
|
-
lines.push(`**${executor.title || agentSlug}**: ${executor.role || ''}`);
|
|
163
|
+
lines.push(`**Your role:** ${executor.title || agentSlug} — ${executor.role || ''}`);
|
|
95
164
|
lines.push('');
|
|
96
165
|
}
|
|
97
166
|
|
|
98
|
-
// Recent tasks
|
|
167
|
+
// Recent tasks (most recent first, capped)
|
|
99
168
|
if (tasks.length > 0) {
|
|
100
|
-
lines.push('
|
|
169
|
+
lines.push('**Recent tasks:**');
|
|
101
170
|
for (const t of tasks) {
|
|
102
171
|
const status = t.status || 'unknown';
|
|
103
172
|
const title = t.title || t.slug || t.id || '(untitled)';
|
|
104
173
|
lines.push(`- [${status}] ${title}`);
|
|
105
174
|
if (t.output && typeof t.output === 'string') {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
lines.push(` Output: ${out}`);
|
|
175
|
+
const out = t.output.length > 150 ? t.output.slice(0, 150) + '…' : t.output;
|
|
176
|
+
lines.push(` → ${out}`);
|
|
109
177
|
}
|
|
110
178
|
}
|
|
111
179
|
lines.push('');
|
|
112
180
|
}
|
|
113
181
|
|
|
114
|
-
// Recent events
|
|
182
|
+
// Recent events (brief)
|
|
115
183
|
if (events.length > 0) {
|
|
116
|
-
lines.push('
|
|
117
|
-
for (const ev of events) {
|
|
118
|
-
const ts = ev.created_at || ev.timestamp || '';
|
|
184
|
+
lines.push('**Recent events:**');
|
|
185
|
+
for (const ev of events.slice(-5)) {
|
|
119
186
|
const type = ev.event_type || ev.type || 'event';
|
|
120
187
|
const msg = ev.message || ev.summary || '';
|
|
121
|
-
lines.push(`-
|
|
188
|
+
lines.push(`- ${type}: ${msg}`);
|
|
122
189
|
}
|
|
123
190
|
lines.push('');
|
|
124
191
|
}
|
|
125
192
|
|
|
126
|
-
//
|
|
193
|
+
// State snapshot (from STATE.md via state-manager)
|
|
127
194
|
if (ctxSnapshot) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
195
|
+
if (ctxSnapshot.sessionsCompleted) {
|
|
196
|
+
lines.push(`**Sessions completed:** ${ctxSnapshot.sessionsCompleted}`);
|
|
197
|
+
}
|
|
198
|
+
if (ctxSnapshot.recentDecisions && ctxSnapshot.recentDecisions.length > 0) {
|
|
199
|
+
lines.push('**Recent decisions:**');
|
|
200
|
+
for (const d of ctxSnapshot.recentDecisions.slice(-3)) {
|
|
201
|
+
const text = typeof d === 'string' ? d : (d.text || JSON.stringify(d));
|
|
202
|
+
lines.push(`- ${text}`);
|
|
203
|
+
}
|
|
204
|
+
lines.push('');
|
|
205
|
+
}
|
|
206
|
+
if (ctxSnapshot.activeBlockers && ctxSnapshot.activeBlockers.length > 0) {
|
|
207
|
+
lines.push('**Active blockers:**');
|
|
208
|
+
for (const b of ctxSnapshot.activeBlockers) {
|
|
209
|
+
const text = typeof b === 'string' ? b : (b.text || JSON.stringify(b));
|
|
210
|
+
lines.push(`- ${text}`);
|
|
211
|
+
}
|
|
212
|
+
lines.push('');
|
|
213
|
+
}
|
|
134
214
|
}
|
|
135
215
|
|
|
136
|
-
lines.
|
|
137
|
-
|
|
216
|
+
return lines.join('\n');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── Full incremental markdown builder ───────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Build the incremental recovery-context.md content.
|
|
223
|
+
*
|
|
224
|
+
* Strategy:
|
|
225
|
+
* 1. Extract current "Current State" from existing file → becomes history entry
|
|
226
|
+
* 2. Build fresh "Current State" from live data
|
|
227
|
+
* 3. Assemble: header + current state + history (oldest → newest, capped)
|
|
228
|
+
* 4. Enforce MAX_TOKENS budget by trimming history first
|
|
229
|
+
*/
|
|
230
|
+
function buildIncrementalRecovery(
|
|
231
|
+
squadSlug, agentSlug,
|
|
232
|
+
manifest, tasks, events, ctxSnapshot,
|
|
233
|
+
existingContent
|
|
234
|
+
) {
|
|
235
|
+
const now = new Date().toISOString();
|
|
236
|
+
|
|
237
|
+
// Build fresh current state
|
|
238
|
+
const currentState = buildCurrentState(squadSlug, agentSlug, manifest, tasks, events, ctxSnapshot);
|
|
239
|
+
|
|
240
|
+
// Gather history: existing history entries + previous current state (if any)
|
|
241
|
+
const prevState = extractCurrentState(existingContent);
|
|
242
|
+
const existingHistory = extractCompactionHistory(existingContent);
|
|
243
|
+
|
|
244
|
+
let historyEntries = [...existingHistory];
|
|
245
|
+
if (prevState) historyEntries.push(prevState);
|
|
246
|
+
// Keep only the most recent MAX_HISTORY_ENTRIES - 1 entries (we add current above)
|
|
247
|
+
historyEntries = historyEntries.slice(-(MAX_HISTORY_ENTRIES - 1));
|
|
248
|
+
|
|
249
|
+
// Assemble full content
|
|
250
|
+
const headerLines = [
|
|
251
|
+
`# Recovery Context — ${squadSlug} / ${agentSlug}`,
|
|
252
|
+
`> Last Updated: ${now}`,
|
|
253
|
+
'',
|
|
254
|
+
'## Current State',
|
|
255
|
+
currentState,
|
|
256
|
+
''
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
const historyLines = historyEntries.length > 0
|
|
260
|
+
? ['## Compaction History', '*Previous snapshots — oldest → newest:*', '', ...historyEntries.map((e) => e + '\n'), '']
|
|
261
|
+
: [];
|
|
138
262
|
|
|
139
|
-
const
|
|
263
|
+
const footerLines = [
|
|
264
|
+
'---',
|
|
265
|
+
'*Inject at top of session to restore context after compact.*'
|
|
266
|
+
];
|
|
140
267
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
268
|
+
const allLines = [...headerLines, ...historyLines, ...footerLines];
|
|
269
|
+
let content = allLines.join('\n');
|
|
270
|
+
|
|
271
|
+
// Enforce token budget: trim history entries if needed
|
|
272
|
+
while (estimateTokens(content) > MAX_TOKENS && historyEntries.length > 0) {
|
|
273
|
+
historyEntries.shift(); // remove oldest entry
|
|
274
|
+
const trimmedHistoryLines = historyEntries.length > 0
|
|
275
|
+
? ['## Compaction History', '*Previous snapshots — oldest → newest:*', '', ...historyEntries.map((e) => e + '\n'), '']
|
|
276
|
+
: [];
|
|
277
|
+
content = [...headerLines, ...trimmedHistoryLines, ...footerLines].join('\n');
|
|
145
278
|
}
|
|
146
279
|
|
|
147
280
|
return content;
|
|
148
281
|
}
|
|
149
282
|
|
|
283
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
284
|
+
|
|
150
285
|
/**
|
|
151
|
-
* Generate and write recovery-context.md for
|
|
286
|
+
* Generate and write recovery-context.md for a squad agent.
|
|
287
|
+
* Uses incremental merging: preserves compaction history across cycles.
|
|
288
|
+
*
|
|
152
289
|
* @param {string} projectDir
|
|
153
290
|
* @param {string} squadSlug
|
|
154
291
|
* @param {string} agentSlug
|
|
155
|
-
* @returns {{ ok: boolean, path: string, tokens: number }}
|
|
292
|
+
* @returns {{ ok: boolean, path: string, tokens: number, incremental: boolean }}
|
|
156
293
|
*/
|
|
157
294
|
async function generateRecovery(projectDir, squadSlug, agentSlug) {
|
|
158
|
-
const
|
|
295
|
+
const outDir = path.join(projectDir, SQUADS_DIR, squadSlug);
|
|
296
|
+
const outPath = path.join(outDir, 'recovery-context.md');
|
|
297
|
+
|
|
298
|
+
const [manifest, tasks, events, ctxSnapshot, existingContent] = await Promise.all([
|
|
159
299
|
readManifest(projectDir, squadSlug),
|
|
160
300
|
readRecentTasks(projectDir, squadSlug),
|
|
161
301
|
readRecentEvents(projectDir, squadSlug),
|
|
162
|
-
readContextSnapshot(projectDir, squadSlug,
|
|
302
|
+
readContextSnapshot(projectDir, squadSlug),
|
|
303
|
+
fs.readFile(outPath, 'utf8').catch(() => null)
|
|
163
304
|
]);
|
|
164
305
|
|
|
165
|
-
const content =
|
|
306
|
+
const content = buildIncrementalRecovery(
|
|
307
|
+
squadSlug, agentSlug,
|
|
308
|
+
manifest, tasks, events, ctxSnapshot,
|
|
309
|
+
existingContent
|
|
310
|
+
);
|
|
311
|
+
|
|
166
312
|
const tokens = estimateTokens(content);
|
|
313
|
+
const incremental = Boolean(existingContent);
|
|
167
314
|
|
|
168
|
-
|
|
169
|
-
const
|
|
315
|
+
// Build structured handoff JSON (consumed by MCP resources, Agent Teams adapter, etc.)
|
|
316
|
+
const completedTasks = tasks.filter((t) => t.status === 'completed').map((t) => t.title || t.id || '(untitled)');
|
|
317
|
+
const pendingTasks = tasks.filter((t) => t.status !== 'completed').map((t) => t.title || t.id || '(untitled)');
|
|
318
|
+
const handoffJson = {
|
|
319
|
+
agent: agentSlug,
|
|
320
|
+
squad: squadSlug,
|
|
321
|
+
session_id: null,
|
|
322
|
+
compacted_at: new Date().toISOString(),
|
|
323
|
+
summary: {
|
|
324
|
+
tasks_completed: completedTasks,
|
|
325
|
+
pending_work: pendingTasks,
|
|
326
|
+
key_files: [],
|
|
327
|
+
decisions: []
|
|
328
|
+
},
|
|
329
|
+
resume_instruction: 'Continue from this checkpoint. Do not acknowledge this summary.'
|
|
330
|
+
};
|
|
170
331
|
|
|
171
332
|
try {
|
|
172
333
|
await fs.mkdir(outDir, { recursive: true });
|
|
173
334
|
await fs.writeFile(outPath, content, 'utf8');
|
|
335
|
+
await fs.writeFile(
|
|
336
|
+
path.join(outDir, 'last-handoff.json'),
|
|
337
|
+
JSON.stringify(handoffJson, null, 2),
|
|
338
|
+
'utf8'
|
|
339
|
+
);
|
|
174
340
|
} catch (err) {
|
|
175
|
-
return { ok: false, error: err.message, path: outPath, tokens };
|
|
341
|
+
return { ok: false, error: err.message, path: outPath, tokens, incremental };
|
|
176
342
|
}
|
|
177
343
|
|
|
178
|
-
return { ok: true, path: outPath, tokens, squadSlug, agentSlug };
|
|
344
|
+
return { ok: true, path: outPath, tokens, squadSlug, agentSlug, incremental };
|
|
179
345
|
}
|
|
180
346
|
|
|
181
347
|
/**
|
|
182
|
-
* Read the current recovery-context.md for
|
|
348
|
+
* Read the current recovery-context.md for a squad (returns null if missing).
|
|
183
349
|
*/
|
|
184
350
|
async function readRecovery(projectDir, squadSlug) {
|
|
185
351
|
const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'recovery-context.md');
|
|
@@ -192,10 +358,15 @@ async function readRecovery(projectDir, squadSlug) {
|
|
|
192
358
|
|
|
193
359
|
/**
|
|
194
360
|
* Check if a runtime event should trigger a recovery refresh.
|
|
195
|
-
* @param {string} eventType
|
|
196
361
|
*/
|
|
197
362
|
function shouldRefreshOnEvent(eventType) {
|
|
198
363
|
return REFRESH_EVENTS.has(eventType);
|
|
199
364
|
}
|
|
200
365
|
|
|
201
|
-
module.exports = {
|
|
366
|
+
module.exports = {
|
|
367
|
+
generateRecovery,
|
|
368
|
+
readRecovery,
|
|
369
|
+
shouldRefreshOnEvent,
|
|
370
|
+
REFRESH_EVENTS,
|
|
371
|
+
estimateTokens
|
|
372
|
+
};
|