bmad-overlay 0.1.1
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 +24 -0
- package/LICENSE +21 -0
- package/PUBLISH.md +82 -0
- package/QUICKSTART.md +69 -0
- package/README.md +84 -0
- package/agents/orchestrator.md +94 -0
- package/config/agent-map.yaml +40 -0
- package/customizations/bmm-dev.customize.yaml +25 -0
- package/customizations/bmm-pm.customize.yaml +24 -0
- package/customizations/bmm-qa.customize.yaml +23 -0
- package/install/INSTALL.md +151 -0
- package/install/check-package.mjs +21 -0
- package/install/install-overlay.mjs +279 -0
- package/install/install-overlay.sh +14 -0
- package/install/merge-bmad-customizations.mjs +179 -0
- package/package.json +32 -0
- package/policies/routing-policy.md +60 -0
- package/runtime/README.md +112 -0
- package/runtime/STAGE_RUNNER.md +53 -0
- package/runtime/codex-orchestrator.js +922 -0
- package/runtime/codex-stage-runner.js +747 -0
- package/skills/README.md +38 -0
- package/skills/orchestrator-advance-stage/SKILL.md +55 -0
- package/skills/orchestrator-advance-stage/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-advance-stage/workflow.md +47 -0
- package/skills/orchestrator-block-stage/SKILL.md +44 -0
- package/skills/orchestrator-block-stage/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-block-stage/workflow.md +44 -0
- package/skills/orchestrator-close-run/SKILL.md +59 -0
- package/skills/orchestrator-close-run/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-close-run/workflow.md +47 -0
- package/skills/orchestrator-execute-active-stage/SKILL.md +50 -0
- package/skills/orchestrator-execute-active-stage/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-execute-active-stage/workflow.md +42 -0
- package/skills/orchestrator-launch-council/SKILL.md +53 -0
- package/skills/orchestrator-launch-council/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-launch-council/workflow.md +43 -0
- package/skills/orchestrator-resume-stage/SKILL.md +47 -0
- package/skills/orchestrator-resume-stage/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-resume-stage/workflow.md +45 -0
- package/skills/orchestrator-start-run/SKILL.md +62 -0
- package/skills/orchestrator-start-run/bmad-skill-manifest.yaml +1 -0
- package/skills/orchestrator-start-run/workflow.md +47 -0
- package/templates/CLAUDE.md +51 -0
- package/templates/analysis-synthesis.md +25 -0
- package/templates/analyst-seat-output.yaml +7 -0
- package/templates/architecture-handoff.yaml +8 -0
- package/templates/architecture-seat-output.yaml +7 -0
- package/templates/architecture-synthesis.md +23 -0
- package/templates/current-stage.yaml +26 -0
- package/templates/delivery-seat-output.yaml +7 -0
- package/templates/delivery-synthesis.md +27 -0
- package/templates/docs-index.md +19 -0
- package/templates/docs-studio-layout.md +88 -0
- package/templates/feature-index.md +21 -0
- package/templates/intake-request.yaml +12 -0
- package/templates/pm-handoff.yaml +11 -0
- package/templates/pm-seat-output.yaml +7 -0
- package/templates/pm-synthesis.md +25 -0
- package/templates/qa-review-findings.yaml +7 -0
- package/templates/qa-review-handoff.yaml +10 -0
- package/templates/qa-review-seat-output.yaml +7 -0
- package/templates/qa-review-summary.md +21 -0
- package/templates/run-manifest.md +46 -0
- package/templates/stage-handoff.yaml +8 -0
- package/workflows/councils/analyst-council.md +229 -0
- package/workflows/councils/architecture-council.md +240 -0
- package/workflows/councils/delivery-council.md +379 -0
- package/workflows/councils/orchestrator-governance.md +474 -0
- package/workflows/councils/pm-council.md +241 -0
- package/workflows/councils/qa-review-council.md +241 -0
- package/workflows/new-saas.md +51 -0
- package/workflows/orchestrated-saas-studio.md +155 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import process from "process";
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
|
+
|
|
8
|
+
const SCRIPT_DIR = path.dirname(new URL(import.meta.url).pathname);
|
|
9
|
+
const OVERLAY_ROOT = path.resolve(SCRIPT_DIR, "..");
|
|
10
|
+
const VALID_MODES = new Set(["codex", "claude-code", "bmad", "full"]);
|
|
11
|
+
|
|
12
|
+
main();
|
|
13
|
+
|
|
14
|
+
function main() {
|
|
15
|
+
const args = parseArgs(process.argv.slice(2));
|
|
16
|
+
const mode = args.mode || "full";
|
|
17
|
+
const target = args.target || process.cwd();
|
|
18
|
+
const mergeBmad = args["merge-bmad"] === true || args["merge-bmad"] === "true";
|
|
19
|
+
|
|
20
|
+
if (!VALID_MODES.has(mode)) {
|
|
21
|
+
fail(`Unknown mode '${mode}'. Valid modes: codex, claude-code, bmad, full`);
|
|
22
|
+
}
|
|
23
|
+
if (!fs.existsSync(target) || !fs.statSync(target).isDirectory()) {
|
|
24
|
+
fail(`Target project does not exist: ${target}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const targetOverlayDir = path.join(target, "overlay");
|
|
28
|
+
copyDir(OVERLAY_ROOT, targetOverlayDir, new Set([".DS_Store", "node_modules"]));
|
|
29
|
+
|
|
30
|
+
ensureDir(path.join(target, "docs"));
|
|
31
|
+
ensureDir(path.join(target, "docs", "features"));
|
|
32
|
+
ensureDir(path.join(target, "docs", "studio", "runs"));
|
|
33
|
+
ensureDir(path.join(target, ".overlay"));
|
|
34
|
+
ensureDir(path.join(target, "scripts"));
|
|
35
|
+
|
|
36
|
+
installDocs(target);
|
|
37
|
+
installRuntimeWrappers(target);
|
|
38
|
+
installMetadata(target, mode);
|
|
39
|
+
|
|
40
|
+
if (mode === "claude-code" || mode === "full") {
|
|
41
|
+
installClaudeFiles(target);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (mode === "codex" || mode === "full") {
|
|
45
|
+
installCodexFiles(target);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (mode === "bmad" || mode === "full") {
|
|
49
|
+
installBmadFiles(target);
|
|
50
|
+
if (mergeBmad) {
|
|
51
|
+
applyBmadMerge(target);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
printSummary(target, mode, mergeBmad);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseArgs(argv) {
|
|
59
|
+
const args = {};
|
|
60
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
61
|
+
const token = argv[i];
|
|
62
|
+
if (!token.startsWith("--")) continue;
|
|
63
|
+
const key = token.slice(2);
|
|
64
|
+
const next = argv[i + 1];
|
|
65
|
+
if (!next || next.startsWith("--")) {
|
|
66
|
+
args[key] = true;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
args[key] = next;
|
|
70
|
+
i += 1;
|
|
71
|
+
}
|
|
72
|
+
return args;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function installDocs(target) {
|
|
76
|
+
copyIfMissing(
|
|
77
|
+
path.join(OVERLAY_ROOT, "templates", "docs-index.md"),
|
|
78
|
+
path.join(target, "docs", "index.md")
|
|
79
|
+
);
|
|
80
|
+
copyIfMissing(
|
|
81
|
+
path.join(OVERLAY_ROOT, "templates", "feature-index.md"),
|
|
82
|
+
path.join(target, "docs", "features", "_template.index.md")
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function installClaudeFiles(target) {
|
|
87
|
+
copyIfMissing(
|
|
88
|
+
path.join(OVERLAY_ROOT, "templates", "CLAUDE.md"),
|
|
89
|
+
path.join(target, "CLAUDE.md")
|
|
90
|
+
);
|
|
91
|
+
ensureDir(path.join(target, ".claude"));
|
|
92
|
+
writeText(
|
|
93
|
+
path.join(target, ".claude", "overlay-mode.md"),
|
|
94
|
+
[
|
|
95
|
+
"# Claude Code Overlay Mode",
|
|
96
|
+
"",
|
|
97
|
+
"This project has the BMAD overlay installed for Claude Code.",
|
|
98
|
+
"",
|
|
99
|
+
"Primary runtime entry points:",
|
|
100
|
+
"- `scripts/overlay-run-claude.sh`",
|
|
101
|
+
"- `scripts/overlay-execute-claude.sh`",
|
|
102
|
+
"",
|
|
103
|
+
"Primary state files:",
|
|
104
|
+
"- `docs/studio/current-stage.yaml`",
|
|
105
|
+
"- `docs/studio/run-manifest.md`",
|
|
106
|
+
].join("\n")
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function installCodexFiles(target) {
|
|
111
|
+
ensureDir(path.join(target, ".codex"));
|
|
112
|
+
writeText(
|
|
113
|
+
path.join(target, ".codex", "overlay-mode.md"),
|
|
114
|
+
[
|
|
115
|
+
"# Codex Overlay Mode",
|
|
116
|
+
"",
|
|
117
|
+
"This project has the BMAD overlay installed for Codex.",
|
|
118
|
+
"",
|
|
119
|
+
"Primary runtime entry points:",
|
|
120
|
+
"- `scripts/overlay-run-codex.sh`",
|
|
121
|
+
"- `scripts/overlay-execute-codex.sh`",
|
|
122
|
+
"",
|
|
123
|
+
"Primary state files:",
|
|
124
|
+
"- `docs/studio/current-stage.yaml`",
|
|
125
|
+
"- `docs/studio/run-manifest.md`",
|
|
126
|
+
].join("\n")
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function installBmadFiles(target) {
|
|
131
|
+
const appliedDir = path.join(target, "overlay-applied");
|
|
132
|
+
ensureDir(appliedDir);
|
|
133
|
+
ensureDir(path.join(appliedDir, "customizations"));
|
|
134
|
+
copyDir(path.join(OVERLAY_ROOT, "customizations"), path.join(appliedDir, "customizations"));
|
|
135
|
+
copyFile(
|
|
136
|
+
path.join(OVERLAY_ROOT, "config", "agent-map.yaml"),
|
|
137
|
+
path.join(appliedDir, "agent-map.yaml")
|
|
138
|
+
);
|
|
139
|
+
writeText(
|
|
140
|
+
path.join(appliedDir, "README.md"),
|
|
141
|
+
[
|
|
142
|
+
"# Overlay Applied",
|
|
143
|
+
"",
|
|
144
|
+
"This folder contains BMAD integration artifacts to merge into the target project.",
|
|
145
|
+
"",
|
|
146
|
+
"Files:",
|
|
147
|
+
"- `customizations/` for `_bmad/_config/agents/*.customize.yaml`",
|
|
148
|
+
"- `agent-map.yaml` for overlay council-to-BMAD role mapping",
|
|
149
|
+
"",
|
|
150
|
+
"Next step:",
|
|
151
|
+
"- merge the customization snippets into `_bmad/_config/agents/`",
|
|
152
|
+
"- rerun `npx bmad-method install` in the target project",
|
|
153
|
+
].join("\n")
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function applyBmadMerge(target) {
|
|
158
|
+
const mergeScript = path.join(OVERLAY_ROOT, "install", "merge-bmad-customizations.mjs");
|
|
159
|
+
execFileSync("node", [mergeScript, "--target", target], {
|
|
160
|
+
cwd: OVERLAY_ROOT,
|
|
161
|
+
stdio: "inherit",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function installRuntimeWrappers(target) {
|
|
166
|
+
const scripts = {
|
|
167
|
+
"overlay-run-codex.sh": `#!/usr/bin/env bash
|
|
168
|
+
set -euo pipefail
|
|
169
|
+
node overlay/runtime/codex-stage-runner.js run-to-close --studio-dir docs/studio --executor codex-cli --request "\${*:-Create a SaaS project}"`,
|
|
170
|
+
"overlay-run-claude.sh": `#!/usr/bin/env bash
|
|
171
|
+
set -euo pipefail
|
|
172
|
+
node overlay/runtime/codex-stage-runner.js run-to-close --studio-dir docs/studio --executor claude-code --request "\${*:-Create a SaaS project}"`,
|
|
173
|
+
"overlay-run-auto.sh": `#!/usr/bin/env bash
|
|
174
|
+
set -euo pipefail
|
|
175
|
+
node overlay/runtime/codex-stage-runner.js run-to-close --studio-dir docs/studio --executor heuristic --request "\${*:-Create a SaaS project}"`,
|
|
176
|
+
"overlay-execute-codex.sh": `#!/usr/bin/env bash
|
|
177
|
+
set -euo pipefail
|
|
178
|
+
node overlay/runtime/codex-stage-runner.js execute-stage --studio-dir docs/studio --advance --executor codex-cli`,
|
|
179
|
+
"overlay-execute-claude.sh": `#!/usr/bin/env bash
|
|
180
|
+
set -euo pipefail
|
|
181
|
+
node overlay/runtime/codex-stage-runner.js execute-stage --studio-dir docs/studio --advance --executor claude-code`,
|
|
182
|
+
"overlay-status.sh": `#!/usr/bin/env bash
|
|
183
|
+
set -euo pipefail
|
|
184
|
+
node overlay/runtime/codex-orchestrator.js status --studio-dir docs/studio`,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
for (const [name, content] of Object.entries(scripts)) {
|
|
188
|
+
const scriptPath = path.join(target, "scripts", name);
|
|
189
|
+
writeText(scriptPath, content);
|
|
190
|
+
fs.chmodSync(scriptPath, 0o755);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function installMetadata(target, mode) {
|
|
195
|
+
const metadata = {
|
|
196
|
+
installed_at: new Date().toISOString(),
|
|
197
|
+
mode,
|
|
198
|
+
overlay_dir: "overlay",
|
|
199
|
+
runtime_entrypoints: {
|
|
200
|
+
run_codex: "scripts/overlay-run-codex.sh",
|
|
201
|
+
run_claude: "scripts/overlay-run-claude.sh",
|
|
202
|
+
run_auto: "scripts/overlay-run-auto.sh",
|
|
203
|
+
execute_codex: "scripts/overlay-execute-codex.sh",
|
|
204
|
+
execute_claude: "scripts/overlay-execute-claude.sh",
|
|
205
|
+
status: "scripts/overlay-status.sh",
|
|
206
|
+
},
|
|
207
|
+
docs_dir: "docs/studio",
|
|
208
|
+
};
|
|
209
|
+
writeText(path.join(target, ".overlay", "setup.json"), `${JSON.stringify(metadata, null, 2)}\n`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function printSummary(target, mode, mergeBmad) {
|
|
213
|
+
const lines = [
|
|
214
|
+
`Overlay installed in: ${target}`,
|
|
215
|
+
`Mode: ${mode}`,
|
|
216
|
+
"Installed assets:",
|
|
217
|
+
"- overlay/",
|
|
218
|
+
"- docs/index.md and docs/features/_template.index.md when missing",
|
|
219
|
+
"- docs/studio/ runtime directories",
|
|
220
|
+
"- scripts/overlay-*.sh wrappers",
|
|
221
|
+
"- .overlay/setup.json",
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
if (mode === "claude-code" || mode === "full") {
|
|
225
|
+
lines.push("- CLAUDE.md when missing");
|
|
226
|
+
lines.push("- .claude/overlay-mode.md");
|
|
227
|
+
}
|
|
228
|
+
if (mode === "codex" || mode === "full") {
|
|
229
|
+
lines.push("- .codex/overlay-mode.md");
|
|
230
|
+
}
|
|
231
|
+
if (mode === "bmad" || mode === "full") {
|
|
232
|
+
lines.push("- overlay-applied/customizations/");
|
|
233
|
+
lines.push("- overlay-applied/agent-map.yaml");
|
|
234
|
+
lines.push(mergeBmad
|
|
235
|
+
? "- BMAD customizations merged into _bmad/_config/agents/"
|
|
236
|
+
: "Next BMAD step: merge snippets into _bmad/_config/agents/ and run `npx bmad-method install`.");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log(lines.join("\n"));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function copyIfMissing(source, target) {
|
|
243
|
+
if (!fs.existsSync(target)) {
|
|
244
|
+
copyFile(source, target);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function copyFile(source, target) {
|
|
249
|
+
ensureDir(path.dirname(target));
|
|
250
|
+
fs.copyFileSync(source, target);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function copyDir(source, target, exclude = new Set()) {
|
|
254
|
+
ensureDir(target);
|
|
255
|
+
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
256
|
+
if (exclude.has(entry.name)) continue;
|
|
257
|
+
const sourcePath = path.join(source, entry.name);
|
|
258
|
+
const targetPath = path.join(target, entry.name);
|
|
259
|
+
if (entry.isDirectory()) {
|
|
260
|
+
copyDir(sourcePath, targetPath, exclude);
|
|
261
|
+
} else {
|
|
262
|
+
copyFile(sourcePath, targetPath);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function ensureDir(dir) {
|
|
268
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function writeText(file, content) {
|
|
272
|
+
ensureDir(path.dirname(file));
|
|
273
|
+
fs.writeFileSync(file, content, "utf8");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function fail(message) {
|
|
277
|
+
console.error(message);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [ "${1:-}" = "" ]; then
|
|
5
|
+
echo "Usage: $0 /path/to/target-project [mode] [merge-bmad:true|false]"
|
|
6
|
+
exit 1
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
SOURCE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
TARGET_DIR="$1"
|
|
11
|
+
MODE="${2:-full}"
|
|
12
|
+
MERGE_BMAD="${3:-false}"
|
|
13
|
+
|
|
14
|
+
node "$SOURCE_DIR/install/install-overlay.mjs" --target "$TARGET_DIR" --mode "$MODE" --merge-bmad "$MERGE_BMAD"
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import process from "process";
|
|
6
|
+
|
|
7
|
+
const SCRIPT_DIR = path.dirname(new URL(import.meta.url).pathname);
|
|
8
|
+
const OVERLAY_ROOT = path.resolve(SCRIPT_DIR, "..");
|
|
9
|
+
const SECTION_NAMES = ["critical_actions", "memories", "menu", "prompts"];
|
|
10
|
+
const FILES = ["bmm-pm.customize.yaml", "bmm-dev.customize.yaml", "bmm-qa.customize.yaml"];
|
|
11
|
+
|
|
12
|
+
main();
|
|
13
|
+
|
|
14
|
+
function main() {
|
|
15
|
+
const args = parseArgs(process.argv.slice(2));
|
|
16
|
+
const target = args.target || process.cwd();
|
|
17
|
+
const targetAgentsDir = path.join(target, "_bmad", "_config", "agents");
|
|
18
|
+
|
|
19
|
+
if (!fs.existsSync(targetAgentsDir)) {
|
|
20
|
+
fail(`BMAD agent customization directory not found: ${targetAgentsDir}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const fileName of FILES) {
|
|
24
|
+
const sourcePath = path.join(OVERLAY_ROOT, "customizations", fileName);
|
|
25
|
+
const targetPath = path.join(targetAgentsDir, fileName);
|
|
26
|
+
if (!fs.existsSync(targetPath)) {
|
|
27
|
+
fail(`Target BMAD customization file not found: ${targetPath}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sourceSections = parseSections(readText(sourcePath));
|
|
31
|
+
const merged = mergeIntoTarget(readText(targetPath), sourceSections);
|
|
32
|
+
fs.writeFileSync(targetPath, merged, "utf8");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Merged overlay customizations into: ${targetAgentsDir}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseArgs(argv) {
|
|
39
|
+
const args = {};
|
|
40
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
41
|
+
const token = argv[i];
|
|
42
|
+
if (!token.startsWith("--")) continue;
|
|
43
|
+
const key = token.slice(2);
|
|
44
|
+
const next = argv[i + 1];
|
|
45
|
+
if (!next || next.startsWith("--")) {
|
|
46
|
+
args[key] = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
args[key] = next;
|
|
50
|
+
i += 1;
|
|
51
|
+
}
|
|
52
|
+
return args;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseSections(text) {
|
|
56
|
+
const lines = text.split("\n");
|
|
57
|
+
const result = {};
|
|
58
|
+
let current = null;
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
61
|
+
const line = lines[i];
|
|
62
|
+
const sectionMatch = line.match(/^([a-z_]+):\s*(.*)$/);
|
|
63
|
+
if (sectionMatch && SECTION_NAMES.includes(sectionMatch[1])) {
|
|
64
|
+
current = sectionMatch[1];
|
|
65
|
+
result[current] = [];
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (!current) continue;
|
|
69
|
+
if (/^[A-Za-z_]+:/.test(line)) {
|
|
70
|
+
current = null;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!line.startsWith(" - ")) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const itemLines = [line];
|
|
78
|
+
while (i + 1 < lines.length) {
|
|
79
|
+
const next = lines[i + 1];
|
|
80
|
+
if (/^[A-Za-z_]+:/.test(next) || next.startsWith(" - ")) break;
|
|
81
|
+
if (next.startsWith(" ") || next.trim() === "") {
|
|
82
|
+
itemLines.push(next);
|
|
83
|
+
i += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
result[current].push(itemLines.join("\n").trimEnd());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function mergeIntoTarget(targetText, sourceSections) {
|
|
95
|
+
const lines = targetText.split("\n");
|
|
96
|
+
const sectionRanges = findSectionRanges(lines);
|
|
97
|
+
|
|
98
|
+
for (const sectionName of SECTION_NAMES) {
|
|
99
|
+
const additions = sourceSections[sectionName] || [];
|
|
100
|
+
if (additions.length === 0 || !sectionRanges[sectionName]) continue;
|
|
101
|
+
|
|
102
|
+
const { start, end } = sectionRanges[sectionName];
|
|
103
|
+
const blockLines = lines.slice(start, end);
|
|
104
|
+
const existingItems = parseItemsFromBlock(blockLines);
|
|
105
|
+
const seen = new Set(existingItems.map(normalizeItem));
|
|
106
|
+
const uniqueAdditions = additions.filter((item) => !seen.has(normalizeItem(item)));
|
|
107
|
+
if (uniqueAdditions.length === 0) continue;
|
|
108
|
+
|
|
109
|
+
const sectionHeader = lines[start];
|
|
110
|
+
const replacement = buildMergedSection(sectionHeader, existingItems, uniqueAdditions);
|
|
111
|
+
lines.splice(start, end - start, ...replacement);
|
|
112
|
+
|
|
113
|
+
const delta = replacement.length - (end - start);
|
|
114
|
+
for (const key of Object.keys(sectionRanges)) {
|
|
115
|
+
if (sectionRanges[key].start > start) {
|
|
116
|
+
sectionRanges[key].start += delta;
|
|
117
|
+
sectionRanges[key].end += delta;
|
|
118
|
+
} else if (sectionRanges[key].start === start) {
|
|
119
|
+
sectionRanges[key].end = sectionRanges[key].start + replacement.length;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return `${lines.join("\n").replace(/\n+$/, "\n")}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function findSectionRanges(lines) {
|
|
128
|
+
const ranges = {};
|
|
129
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
130
|
+
const match = lines[i].match(/^([a-z_]+):\s*(.*)$/);
|
|
131
|
+
if (!match || !SECTION_NAMES.includes(match[1])) continue;
|
|
132
|
+
const name = match[1];
|
|
133
|
+
let end = i + 1;
|
|
134
|
+
while (end < lines.length && !/^[A-Za-z_]+:/.test(lines[end])) {
|
|
135
|
+
end += 1;
|
|
136
|
+
}
|
|
137
|
+
ranges[name] = { start: i, end };
|
|
138
|
+
}
|
|
139
|
+
return ranges;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parseItemsFromBlock(blockLines) {
|
|
143
|
+
if (blockLines.length === 0) return [];
|
|
144
|
+
const items = [];
|
|
145
|
+
for (let i = 1; i < blockLines.length; i += 1) {
|
|
146
|
+
const line = blockLines[i];
|
|
147
|
+
if (!line.startsWith(" - ")) continue;
|
|
148
|
+
const itemLines = [line];
|
|
149
|
+
while (i + 1 < blockLines.length) {
|
|
150
|
+
const next = blockLines[i + 1];
|
|
151
|
+
if (next.startsWith(" - ") || /^[A-Za-z_]+:/.test(next)) break;
|
|
152
|
+
itemLines.push(next);
|
|
153
|
+
i += 1;
|
|
154
|
+
}
|
|
155
|
+
items.push(itemLines.join("\n").trimEnd());
|
|
156
|
+
}
|
|
157
|
+
return items;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function buildMergedSection(header, existingItems, uniqueAdditions) {
|
|
161
|
+
const mergedItems = [...existingItems, ...uniqueAdditions];
|
|
162
|
+
if (mergedItems.length === 0) {
|
|
163
|
+
return [`${header.replace(/:\s*.*/, ": []")}`];
|
|
164
|
+
}
|
|
165
|
+
return [header.replace(/:\s*.*/, ":"), ...mergedItems];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function normalizeItem(item) {
|
|
169
|
+
return item.replace(/\s+$/gm, "").trim();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function readText(filePath) {
|
|
173
|
+
return fs.readFileSync(filePath, "utf8");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function fail(message) {
|
|
177
|
+
console.error(message);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bmad-overlay",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Portable BMAD overlay kit for Codex, Claude Code, and BMAD projects",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"LICENSE",
|
|
8
|
+
"CHANGELOG.md",
|
|
9
|
+
"README.md",
|
|
10
|
+
"QUICKSTART.md",
|
|
11
|
+
"PUBLISH.md",
|
|
12
|
+
"agents/",
|
|
13
|
+
"config/",
|
|
14
|
+
"customizations/",
|
|
15
|
+
"install/",
|
|
16
|
+
"policies/",
|
|
17
|
+
"runtime/",
|
|
18
|
+
"skills/",
|
|
19
|
+
"templates/",
|
|
20
|
+
"workflows/"
|
|
21
|
+
],
|
|
22
|
+
"bin": {
|
|
23
|
+
"bmad-overlay": "install/install-overlay.mjs",
|
|
24
|
+
"bmad-overlay-merge-bmad": "install/merge-bmad-customizations.mjs"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"pack:check": "node install/check-package.mjs"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Routing Policy
|
|
2
|
+
|
|
3
|
+
## Objectif
|
|
4
|
+
|
|
5
|
+
Orienter chaque demande vers le bon niveau de ceremonie.
|
|
6
|
+
|
|
7
|
+
## Entree
|
|
8
|
+
|
|
9
|
+
La demande doit respecter le contrat:
|
|
10
|
+
|
|
11
|
+
- nom
|
|
12
|
+
- objectif
|
|
13
|
+
- domaine
|
|
14
|
+
- stack envisagee
|
|
15
|
+
- contraintes
|
|
16
|
+
- risques
|
|
17
|
+
- definition minimale de succes
|
|
18
|
+
|
|
19
|
+
## Modes
|
|
20
|
+
|
|
21
|
+
### `quick`
|
|
22
|
+
|
|
23
|
+
Choisir `quick` si:
|
|
24
|
+
|
|
25
|
+
- une seule capacite principale
|
|
26
|
+
- stack connue
|
|
27
|
+
- peu d'integration externe
|
|
28
|
+
- pas de contrainte forte de conformite
|
|
29
|
+
- pas de complexite multi-tenant ou billing avance
|
|
30
|
+
|
|
31
|
+
Pipeline:
|
|
32
|
+
|
|
33
|
+
`product-brief -> quick-spec -> quick-flow-solo-dev -> code-review`
|
|
34
|
+
|
|
35
|
+
### `structured`
|
|
36
|
+
|
|
37
|
+
Choisir `structured` si au moins un point est vrai:
|
|
38
|
+
|
|
39
|
+
- auth complexe
|
|
40
|
+
- billing
|
|
41
|
+
- multi-tenant
|
|
42
|
+
- workflows metier multiples
|
|
43
|
+
- integrateurs externes nombreux
|
|
44
|
+
- exigences de fiabilite ou audit
|
|
45
|
+
- equipe multi-contributeurs
|
|
46
|
+
|
|
47
|
+
Pipeline:
|
|
48
|
+
|
|
49
|
+
`analyst -> pm/prd -> ux -> architect -> create-epics-and-stories -> dev -> qa`
|
|
50
|
+
|
|
51
|
+
## Rule of escalation
|
|
52
|
+
|
|
53
|
+
Si une demande commence en `quick` mais expose ensuite:
|
|
54
|
+
|
|
55
|
+
- un second domaine produit
|
|
56
|
+
- une dette de cadrage
|
|
57
|
+
- plusieurs inconnues techniques
|
|
58
|
+
- des regressions repetitives
|
|
59
|
+
|
|
60
|
+
alors elle doit basculer en `structured`.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Overlay Runtime
|
|
2
|
+
|
|
3
|
+
This folder contains the first Codex-first runtime controller for the overlay.
|
|
4
|
+
|
|
5
|
+
Companion runner:
|
|
6
|
+
|
|
7
|
+
- `codex-stage-runner.js` executes the active stage from generated task files
|
|
8
|
+
- supports `codex-cli`, `claude-code`, and `heuristic` executors
|
|
9
|
+
|
|
10
|
+
## Current Scope
|
|
11
|
+
|
|
12
|
+
The runtime controls the file-backed orchestration loop:
|
|
13
|
+
|
|
14
|
+
- `start-run`
|
|
15
|
+
- `launch-council`
|
|
16
|
+
- `advance-stage`
|
|
17
|
+
- `block-stage`
|
|
18
|
+
- `resume-stage`
|
|
19
|
+
- `close-run`
|
|
20
|
+
- `status`
|
|
21
|
+
|
|
22
|
+
It manages:
|
|
23
|
+
|
|
24
|
+
- `current-stage.yaml`
|
|
25
|
+
- `run-manifest.md`
|
|
26
|
+
- run directories under `docs/studio/runs/`
|
|
27
|
+
- council seat output targets
|
|
28
|
+
- council seat and lead task files
|
|
29
|
+
- output templates for seats, synthesis, and handoff
|
|
30
|
+
|
|
31
|
+
It does not yet launch real LLM workers by itself.
|
|
32
|
+
It is the autonomous control plane for the overlay state machine.
|
|
33
|
+
|
|
34
|
+
The intended execution bridge is:
|
|
35
|
+
|
|
36
|
+
- runtime launches a stage
|
|
37
|
+
- runtime writes task files and templates
|
|
38
|
+
- Codex uses `orchestrator-execute-active-stage`
|
|
39
|
+
- runtime advances or blocks based on the produced artifacts
|
|
40
|
+
|
|
41
|
+
## Command Examples
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node overlay/runtime/codex-orchestrator.js start-run --request "Create a SaaS website for appointment booking"
|
|
45
|
+
node overlay/runtime/codex-orchestrator.js launch-council
|
|
46
|
+
node overlay/runtime/codex-orchestrator.js status
|
|
47
|
+
node overlay/runtime/codex-orchestrator.js block-stage --reason "Missing synthesis" --missing docs/studio/runs/<run_id>/analysis-synthesis.md
|
|
48
|
+
node overlay/runtime/codex-orchestrator.js resume-stage
|
|
49
|
+
node overlay/runtime/codex-orchestrator.js close-run --reason "QA passed"
|
|
50
|
+
node overlay/runtime/codex-stage-runner.js execute-stage --studio-dir docs/studio-runtime-test --advance
|
|
51
|
+
node overlay/runtime/codex-stage-runner.js run-to-close --studio-dir docs/studio-runtime-test --request "Create a SaaS website"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Safer Test Mode
|
|
55
|
+
|
|
56
|
+
To avoid touching the main studio state, use a dedicated studio dir:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node overlay/runtime/codex-orchestrator.js start-run --studio-dir docs/studio-runtime-test --request "Create a SaaS website"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Recommended Test Order
|
|
63
|
+
|
|
64
|
+
1. `start-run`
|
|
65
|
+
2. `launch-council`
|
|
66
|
+
3. create seat outputs, synthesis, and handoff
|
|
67
|
+
4. `advance-stage`
|
|
68
|
+
5. `status`
|
|
69
|
+
|
|
70
|
+
## Current Validation Status
|
|
71
|
+
|
|
72
|
+
Smoke-tested on `docs/studio-runtime-test/` for:
|
|
73
|
+
|
|
74
|
+
- `start-run`
|
|
75
|
+
- `launch-council`
|
|
76
|
+
- `advance-stage`
|
|
77
|
+
- `block-stage`
|
|
78
|
+
- `resume-stage`
|
|
79
|
+
- `close-run`
|
|
80
|
+
|
|
81
|
+
Multi-stage runtime proof also completed on `docs/studio-runtime-test-4/`:
|
|
82
|
+
|
|
83
|
+
- real `analysis` artifact production
|
|
84
|
+
- real `pm` artifact production
|
|
85
|
+
- runtime advancement up to `architecture`
|
|
86
|
+
|
|
87
|
+
End-to-end runtime proof completed on `docs/studio-runtime-test-5/`:
|
|
88
|
+
|
|
89
|
+
- `analysis`
|
|
90
|
+
- `pm`
|
|
91
|
+
- `architecture`
|
|
92
|
+
- `delivery`
|
|
93
|
+
- `qa-review`
|
|
94
|
+
- `closed`
|
|
95
|
+
|
|
96
|
+
Fully autonomous runner proof completed on `docs/studio-runtime-test-6/`:
|
|
97
|
+
|
|
98
|
+
- one `run-to-close` command
|
|
99
|
+
- full planning pipeline execution
|
|
100
|
+
- clean final `closed` state
|
|
101
|
+
|
|
102
|
+
Executor fallback proof completed on `docs/studio-runtime-test-9/`:
|
|
103
|
+
|
|
104
|
+
- `codex-cli` attempted first
|
|
105
|
+
- single failure disables further `codex-cli` attempts for the run
|
|
106
|
+
- heuristic fallback still closes the pipeline
|
|
107
|
+
|
|
108
|
+
Claude Code fallback proof completed on `docs/studio-runtime-test-10/`:
|
|
109
|
+
|
|
110
|
+
- `claude-code` attempted first
|
|
111
|
+
- single timeout disables further `claude-code` attempts for the run
|
|
112
|
+
- heuristic fallback still closes the pipeline
|