@jaimevalasek/aioson 1.6.0 → 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 -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 +9 -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/design-hybrid-forge.md +14 -0
- package/template/.aioson/agents/dev.md +242 -24
- package/template/.aioson/agents/deyvin.md +66 -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 +78 -1
- 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 +172 -21
- package/template/.aioson/agents/setup.md +79 -9
- package/template/.aioson/agents/sheldon.md +131 -6
- package/template/.aioson/agents/site-forge.md +1753 -0
- package/template/.aioson/agents/squad.md +162 -0
- package/template/.aioson/agents/tester.md +53 -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 +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/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/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 +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/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 +55 -3
- package/template/CLAUDE.md +30 -0
- package/template/OPENCODE.md +4 -0
- package/template/researchs/.gitkeep +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Brief Validator — Plan 80, Script 1
|
|
5
|
+
*
|
|
6
|
+
* Validates completeness of a squad brief/spec before spawning workers.
|
|
7
|
+
* Prevents retries caused by incomplete briefs by catching issues early.
|
|
8
|
+
*
|
|
9
|
+
* Validated fields (6):
|
|
10
|
+
* 1. Phase name and objective (non-empty, ≤ 2 sentences)
|
|
11
|
+
* 2. Files to read (at least 1 existing path)
|
|
12
|
+
* 3. Files to write (at least 1 path with action)
|
|
13
|
+
* 4. Constraints (at least 1)
|
|
14
|
+
* 5. Out of scope (at least 1 item — BLOCKING)
|
|
15
|
+
* 6. Done criteria (must contain DONE | DONE_WITH_CONCERNS | BLOCKED)
|
|
16
|
+
*
|
|
17
|
+
* Output:
|
|
18
|
+
* Score: N/6 — READY | NOT READY (N blocking issues)
|
|
19
|
+
*
|
|
20
|
+
* Evaluator-Optimizer improvement: auto-fix for simple missing fields
|
|
21
|
+
* (out_of_scope, done_criteria template).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const fs = require('node:fs/promises');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
|
|
27
|
+
// ─── Section matchers ────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const SECTION_PATTERNS = {
|
|
30
|
+
phase_objective: /^#+\s*(phase|objective|goal|task)\b/i,
|
|
31
|
+
files_to_read: /^#+\s*(files?\s+to\s+read|read\s+first|input\s+files?)\b/i,
|
|
32
|
+
files_to_write: /^#+\s*(files?\s+to\s+(write|create)|output\s+files?)\b/i,
|
|
33
|
+
constraints: /^#+\s*(constraints?|hard\s+constraints?|rules?|guardrails?)\b/i,
|
|
34
|
+
out_of_scope: /^#+\s*(out\s+of\s+scope|exclusions?|not\s+in\s+scope)\b/i,
|
|
35
|
+
done_criteria: /^#+\s*(done\s+criteria|acceptance\s+criteria|definition\s+of\s+done|completion\s+criteria)\b/i
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const DONE_VERDICTS = ['DONE', 'DONE_WITH_CONCERNS', 'BLOCKED'];
|
|
39
|
+
const FILE_ACTIONS = /\b(create|extend|modify|update|replace|overwrite|append|write)\b/i;
|
|
40
|
+
|
|
41
|
+
// ─── Brief parser ────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse a brief markdown into structured sections.
|
|
45
|
+
* Returns { sections: { name: string[] }, raw: string }
|
|
46
|
+
*/
|
|
47
|
+
function parseBrief(content) {
|
|
48
|
+
const lines = content.split(/\r?\n/);
|
|
49
|
+
const sections = {};
|
|
50
|
+
let currentSection = null;
|
|
51
|
+
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
// Check if this line starts a recognized section
|
|
54
|
+
let matched = false;
|
|
55
|
+
for (const [name, pattern] of Object.entries(SECTION_PATTERNS)) {
|
|
56
|
+
if (pattern.test(line)) {
|
|
57
|
+
currentSection = name;
|
|
58
|
+
if (!sections[currentSection]) sections[currentSection] = [];
|
|
59
|
+
matched = true;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Also detect generic headings as potential section enders
|
|
65
|
+
if (!matched && /^#+\s/.test(line)) {
|
|
66
|
+
currentSection = null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (currentSection && !matched) {
|
|
70
|
+
sections[currentSection].push(line);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { sections, raw: content };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Extract meaningful list items from section lines.
|
|
79
|
+
*/
|
|
80
|
+
function extractItems(lines) {
|
|
81
|
+
return lines
|
|
82
|
+
.filter((l) => l.match(/^\s*[-*]\s+/) || l.match(/^\s*\d+\.\s+/) || l.match(/^\s*\[.\]\s+/))
|
|
83
|
+
.map((l) => l.replace(/^\s*[-*]\s+/, '').replace(/^\s*\d+\.\s+/, '').replace(/^\s*\[.\]\s+/, '').trim())
|
|
84
|
+
.filter(Boolean);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Extract non-empty text content from section lines.
|
|
89
|
+
*/
|
|
90
|
+
function extractText(lines) {
|
|
91
|
+
return lines
|
|
92
|
+
.map((l) => l.trim())
|
|
93
|
+
.filter((l) => l && !l.startsWith('```'))
|
|
94
|
+
.join(' ')
|
|
95
|
+
.trim();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Validators ──────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
function validatePhaseObjective(sections) {
|
|
101
|
+
const lines = sections.phase_objective || [];
|
|
102
|
+
const text = extractText(lines);
|
|
103
|
+
|
|
104
|
+
if (!text) {
|
|
105
|
+
return { valid: false, field: 'phase_objective', message: 'Phase name/objective is missing' };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check ≤ 2 sentences (heuristic: count periods/exclamation/question marks)
|
|
109
|
+
const sentenceCount = (text.match(/[.!?]+/g) || []).length;
|
|
110
|
+
if (sentenceCount > 3) {
|
|
111
|
+
return { valid: false, field: 'phase_objective', message: `Objective has ${sentenceCount} sentences — keep it ≤ 2 for clarity` };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { valid: true, field: 'phase_objective' };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateFilesToRead(sections, projectDir) {
|
|
118
|
+
const lines = sections.files_to_read || [];
|
|
119
|
+
const items = extractItems(lines);
|
|
120
|
+
|
|
121
|
+
if (items.length === 0) {
|
|
122
|
+
return { valid: false, field: 'files_to_read', message: 'No files to read specified' };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Extract file paths from items (first path-like token)
|
|
126
|
+
const paths = items
|
|
127
|
+
.map((item) => {
|
|
128
|
+
const match = item.match(/[\w./\\-]+\.\w+|[\w./\\-]+\//);
|
|
129
|
+
return match ? match[0] : null;
|
|
130
|
+
})
|
|
131
|
+
.filter(Boolean);
|
|
132
|
+
|
|
133
|
+
if (paths.length === 0) {
|
|
134
|
+
return { valid: false, field: 'files_to_read', message: 'No recognizable file paths in "Files to read"' };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { valid: true, field: 'files_to_read', paths };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function validateFilesToWrite(sections) {
|
|
141
|
+
const lines = sections.files_to_write || [];
|
|
142
|
+
const items = extractItems(lines);
|
|
143
|
+
|
|
144
|
+
if (items.length === 0) {
|
|
145
|
+
return { valid: false, field: 'files_to_write', message: 'No files to write specified' };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check at least one item has an action keyword
|
|
149
|
+
const hasAction = items.some((item) => FILE_ACTIONS.test(item));
|
|
150
|
+
if (!hasAction) {
|
|
151
|
+
return {
|
|
152
|
+
valid: false,
|
|
153
|
+
field: 'files_to_write',
|
|
154
|
+
message: 'Files to write must include at least one action (create|extend|modify)'
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return { valid: true, field: 'files_to_write' };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function validateConstraints(sections) {
|
|
162
|
+
const lines = sections.constraints || [];
|
|
163
|
+
const items = extractItems(lines);
|
|
164
|
+
|
|
165
|
+
if (items.length === 0) {
|
|
166
|
+
return { valid: false, field: 'constraints', message: 'No constraints specified' };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return { valid: true, field: 'constraints' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function validateOutOfScope(sections) {
|
|
173
|
+
const lines = sections.out_of_scope || [];
|
|
174
|
+
const items = extractItems(lines);
|
|
175
|
+
|
|
176
|
+
if (items.length === 0) {
|
|
177
|
+
return {
|
|
178
|
+
valid: false,
|
|
179
|
+
field: 'out_of_scope',
|
|
180
|
+
message: 'Out of scope section is missing or empty — BLOCKING',
|
|
181
|
+
blocking: true,
|
|
182
|
+
autoFixable: true
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { valid: true, field: 'out_of_scope' };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function validateDoneCriteria(sections) {
|
|
190
|
+
const lines = sections.done_criteria || [];
|
|
191
|
+
const text = lines.join('\n');
|
|
192
|
+
|
|
193
|
+
if (!text.trim()) {
|
|
194
|
+
return {
|
|
195
|
+
valid: false,
|
|
196
|
+
field: 'done_criteria',
|
|
197
|
+
message: 'Done criteria section is missing',
|
|
198
|
+
autoFixable: true
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const hasVerdict = DONE_VERDICTS.some((v) => text.includes(v));
|
|
203
|
+
if (!hasVerdict) {
|
|
204
|
+
return {
|
|
205
|
+
valid: false,
|
|
206
|
+
field: 'done_criteria',
|
|
207
|
+
message: 'Done criteria must contain DONE | DONE_WITH_CONCERNS | BLOCKED verdicts',
|
|
208
|
+
autoFixable: true
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { valid: true, field: 'done_criteria' };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ─── Auto-fix generators ─────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Generate a default "Out of scope" section based on brief context.
|
|
219
|
+
*/
|
|
220
|
+
function generateOutOfScope(sections) {
|
|
221
|
+
const items = ['Refactoring code outside the listed files'];
|
|
222
|
+
|
|
223
|
+
const writeItems = extractItems(sections.files_to_write || []);
|
|
224
|
+
if (writeItems.length > 0) {
|
|
225
|
+
items.push('Creating files not listed in "Files to write"');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
items.push('Performance optimization beyond stated requirements');
|
|
229
|
+
items.push('Documentation updates outside this brief\'s scope');
|
|
230
|
+
|
|
231
|
+
return `\n## Out of scope\n${items.map((i) => `- ${i}`).join('\n')}\n`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Generate a default "Done criteria" section template.
|
|
236
|
+
*/
|
|
237
|
+
function generateDoneCriteria(sections) {
|
|
238
|
+
const items = ['All files listed in "Files to write" are created/modified'];
|
|
239
|
+
|
|
240
|
+
const constraintItems = extractItems(sections.constraints || []);
|
|
241
|
+
if (constraintItems.length > 0) {
|
|
242
|
+
items.push('All constraints are respected');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return `\n## Done criteria\n${items.map((i) => `- [ ] ${i}`).join('\n')}\n\nVerdict: DONE | DONE_WITH_CONCERNS | BLOCKED\n`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Validate a brief file for completeness.
|
|
252
|
+
*
|
|
253
|
+
* @param {string} briefPath — Absolute or relative path to the brief markdown
|
|
254
|
+
* @param {string} [projectDir] — Project root for path resolution
|
|
255
|
+
* @returns {Promise<object>} — { ready, score, total, issues, autoFixable }
|
|
256
|
+
*/
|
|
257
|
+
async function validateBrief(briefPath, projectDir) {
|
|
258
|
+
const resolvedPath = projectDir
|
|
259
|
+
? path.resolve(projectDir, briefPath)
|
|
260
|
+
: path.resolve(briefPath);
|
|
261
|
+
|
|
262
|
+
let content;
|
|
263
|
+
try {
|
|
264
|
+
content = await fs.readFile(resolvedPath, 'utf8');
|
|
265
|
+
} catch (err) {
|
|
266
|
+
return {
|
|
267
|
+
ready: false,
|
|
268
|
+
score: 0,
|
|
269
|
+
total: 6,
|
|
270
|
+
issues: [{ field: 'file', message: `Cannot read brief: ${err.message}` }],
|
|
271
|
+
autoFixable: false
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const { sections } = parseBrief(content);
|
|
276
|
+
|
|
277
|
+
const validators = [
|
|
278
|
+
validatePhaseObjective,
|
|
279
|
+
validateFilesToRead,
|
|
280
|
+
validateFilesToWrite,
|
|
281
|
+
validateConstraints,
|
|
282
|
+
validateOutOfScope,
|
|
283
|
+
validateDoneCriteria
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const results = validators.map((fn) => fn(sections, projectDir));
|
|
287
|
+
const passed = results.filter((r) => r.valid).length;
|
|
288
|
+
const issues = results.filter((r) => !r.valid);
|
|
289
|
+
const autoFixable = issues.some((i) => i.autoFixable);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
ready: issues.length === 0,
|
|
293
|
+
score: passed,
|
|
294
|
+
total: 6,
|
|
295
|
+
issues,
|
|
296
|
+
autoFixable
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Auto-fix simple missing fields in a brief.
|
|
302
|
+
* Currently supports: out_of_scope, done_criteria.
|
|
303
|
+
*
|
|
304
|
+
* @param {string} briefPath — Path to the brief
|
|
305
|
+
* @param {string} [projectDir] — Project root
|
|
306
|
+
* @returns {Promise<object>} — { fixed, fieldsFixed, newContent }
|
|
307
|
+
*/
|
|
308
|
+
async function autoFixBrief(briefPath, projectDir) {
|
|
309
|
+
const resolvedPath = projectDir
|
|
310
|
+
? path.resolve(projectDir, briefPath)
|
|
311
|
+
: path.resolve(briefPath);
|
|
312
|
+
|
|
313
|
+
let content;
|
|
314
|
+
try {
|
|
315
|
+
content = await fs.readFile(resolvedPath, 'utf8');
|
|
316
|
+
} catch (err) {
|
|
317
|
+
return { fixed: false, error: err.message };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const { sections } = parseBrief(content);
|
|
321
|
+
const fieldsFixed = [];
|
|
322
|
+
let newContent = content;
|
|
323
|
+
|
|
324
|
+
// Fix out_of_scope
|
|
325
|
+
const oosResult = validateOutOfScope(sections);
|
|
326
|
+
if (!oosResult.valid && oosResult.autoFixable) {
|
|
327
|
+
newContent += generateOutOfScope(sections);
|
|
328
|
+
fieldsFixed.push('out_of_scope');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Fix done_criteria
|
|
332
|
+
const dcResult = validateDoneCriteria(sections);
|
|
333
|
+
if (!dcResult.valid && dcResult.autoFixable) {
|
|
334
|
+
newContent += generateDoneCriteria(sections);
|
|
335
|
+
fieldsFixed.push('done_criteria');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (fieldsFixed.length === 0) {
|
|
339
|
+
return { fixed: false, fieldsFixed: [] };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
await fs.writeFile(resolvedPath, newContent, 'utf8');
|
|
343
|
+
return { fixed: true, fieldsFixed, newContent };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
module.exports = {
|
|
347
|
+
validateBrief,
|
|
348
|
+
autoFixBrief,
|
|
349
|
+
parseBrief
|
|
350
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bus Bridge — Plan 81, Phase 1.3
|
|
5
|
+
*
|
|
6
|
+
* Bidirectional bridge between Agent Teams mailbox and intra-bus JSONL.
|
|
7
|
+
* Ensures that when squads use Agent Teams engine, all communication
|
|
8
|
+
* is still persisted in the intra-bus for:
|
|
9
|
+
* - Historical analysis by learning-extractor
|
|
10
|
+
* - Cross-session context via recovery-context.js
|
|
11
|
+
* - Pattern detection by pattern-detector.js
|
|
12
|
+
*
|
|
13
|
+
* Directions:
|
|
14
|
+
* mailbox → bus: intercepts SendMessage via PostToolUse hook
|
|
15
|
+
* bus → mailbox: coordinator reads bus and relays to team-lead
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const bus = require('./intra-bus');
|
|
19
|
+
|
|
20
|
+
// ─── Mailbox → Bus ──────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Bridge a SendMessage tool call from Agent Teams mailbox to intra-bus.
|
|
24
|
+
* Called via PostToolUse hook when matcher='SendMessage'.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} projectDir
|
|
27
|
+
* @param {string} squadSlug
|
|
28
|
+
* @param {string} sessionId
|
|
29
|
+
* @param {string} toolInput — JSON string from TOOL_INPUT env var
|
|
30
|
+
*/
|
|
31
|
+
async function bridgeMailboxToBus(projectDir, squadSlug, sessionId, toolInput) {
|
|
32
|
+
let parsed;
|
|
33
|
+
try {
|
|
34
|
+
parsed = typeof toolInput === 'string' ? JSON.parse(toolInput) : toolInput;
|
|
35
|
+
} catch {
|
|
36
|
+
return { ok: false, error: 'invalid_tool_input' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { to, message, from } = parsed;
|
|
40
|
+
if (!message) return { ok: false, error: 'no_message' };
|
|
41
|
+
|
|
42
|
+
// Map mailbox message to bus message type
|
|
43
|
+
const type = inferMessageType(message);
|
|
44
|
+
|
|
45
|
+
const posted = await bus.post(projectDir, squadSlug, sessionId, {
|
|
46
|
+
from: from || 'agent-teams',
|
|
47
|
+
to: to || '*',
|
|
48
|
+
type,
|
|
49
|
+
content: message,
|
|
50
|
+
metadata: {
|
|
51
|
+
source: 'mailbox-bridge',
|
|
52
|
+
original_to: to
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return { ok: true, busMessageId: posted.id };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Infer a bus message type from mailbox message content.
|
|
61
|
+
*/
|
|
62
|
+
function inferMessageType(message) {
|
|
63
|
+
const lower = (message || '').toLowerCase();
|
|
64
|
+
|
|
65
|
+
if (/\b(block|stuck|cannot|unable|help)\b/i.test(lower)) return 'block';
|
|
66
|
+
if (/\b(done|completed|finished|result)\b/i.test(lower)) return 'result';
|
|
67
|
+
if (/\b(found|discovered|noticed)\b/i.test(lower)) return 'finding';
|
|
68
|
+
if (/\b(question|how|what|why|should)\b/i.test(lower)) return 'question';
|
|
69
|
+
if (/\b(review|feedback|suggest)\b/i.test(lower)) return 'feedback';
|
|
70
|
+
|
|
71
|
+
return 'status';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── Bus → Mailbox ──────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Read recent bus messages and format them for Agent Teams team-lead consumption.
|
|
78
|
+
* Used by coordinator to relay bus intelligence to the team.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} projectDir
|
|
81
|
+
* @param {string} squadSlug
|
|
82
|
+
* @param {string} sessionId
|
|
83
|
+
* @param {object} [filters] — { since, types }
|
|
84
|
+
* @returns {Promise<object[]>} — formatted messages for mailbox relay
|
|
85
|
+
*/
|
|
86
|
+
async function readBusForMailbox(projectDir, squadSlug, sessionId, filters = {}) {
|
|
87
|
+
const { since, types } = filters;
|
|
88
|
+
const messages = await bus.read(projectDir, squadSlug, sessionId, {
|
|
89
|
+
since,
|
|
90
|
+
type: types
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Filter out messages already from mailbox bridge (prevent loops)
|
|
94
|
+
const filtered = messages.filter(
|
|
95
|
+
(m) => !m.metadata || m.metadata.source !== 'mailbox-bridge'
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return filtered.map((m) => ({
|
|
99
|
+
from: m.from,
|
|
100
|
+
to: m.to,
|
|
101
|
+
type: m.type,
|
|
102
|
+
content: m.content,
|
|
103
|
+
ts: m.ts,
|
|
104
|
+
mailboxFormat: `[${m.type}] ${m.from}: ${m.content}`
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get a summary of bus state for team-lead injection.
|
|
110
|
+
* Returns a concise text block suitable for Agent Teams context.
|
|
111
|
+
*/
|
|
112
|
+
async function busSummaryForTeam(projectDir, squadSlug, sessionId) {
|
|
113
|
+
const busSummary = await bus.summary(projectDir, squadSlug, sessionId);
|
|
114
|
+
|
|
115
|
+
if (!busSummary || busSummary.total === 0) {
|
|
116
|
+
return 'Bus: empty (no inter-executor messages yet)';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const lines = [
|
|
120
|
+
`Bus: ${busSummary.total} messages`,
|
|
121
|
+
` Types: ${Object.entries(busSummary.by_type).map(([t, c]) => `${t}(${c})`).join(', ')}`,
|
|
122
|
+
` Executors: ${Object.entries(busSummary.by_executor).map(([e, c]) => `${e}(${c})`).join(', ')}`
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
if (busSummary.blocks.length > 0) {
|
|
126
|
+
lines.push(` ⚠ Unresolved blocks: ${busSummary.blocks.length}`);
|
|
127
|
+
for (const b of busSummary.blocks.slice(0, 3)) {
|
|
128
|
+
lines.push(` - ${b.from}: ${b.content.slice(0, 80)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
bridgeMailboxToBus,
|
|
137
|
+
readBusForMailbox,
|
|
138
|
+
busSummaryForTeam,
|
|
139
|
+
inferMessageType
|
|
140
|
+
};
|