bmalph 2.7.5 → 2.7.6
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/README.md +20 -5
- package/dist/commands/doctor-runtime-checks.js +104 -86
- package/dist/commands/doctor-runtime-checks.js.map +1 -1
- package/dist/installer/bmad-assets.js +182 -0
- package/dist/installer/bmad-assets.js.map +1 -0
- package/dist/installer/commands.js +324 -0
- package/dist/installer/commands.js.map +1 -0
- package/dist/installer/install.js +42 -0
- package/dist/installer/install.js.map +1 -0
- package/dist/installer/metadata.js +56 -0
- package/dist/installer/metadata.js.map +1 -0
- package/dist/installer/project-files.js +169 -0
- package/dist/installer/project-files.js.map +1 -0
- package/dist/installer/ralph-assets.js +91 -0
- package/dist/installer/ralph-assets.js.map +1 -0
- package/dist/installer/template-files.js +168 -0
- package/dist/installer/template-files.js.map +1 -0
- package/dist/installer/types.js +2 -0
- package/dist/installer/types.js.map +1 -0
- package/dist/installer.js +5 -843
- package/dist/installer.js.map +1 -1
- package/dist/transition/artifact-loading.js +91 -0
- package/dist/transition/artifact-loading.js.map +1 -0
- package/dist/transition/context-output.js +85 -0
- package/dist/transition/context-output.js.map +1 -0
- package/dist/transition/fix-plan-sync.js +119 -0
- package/dist/transition/fix-plan-sync.js.map +1 -0
- package/dist/transition/orchestration.js +25 -362
- package/dist/transition/orchestration.js.map +1 -1
- package/dist/transition/specs-sync.js +78 -2
- package/dist/transition/specs-sync.js.map +1 -1
- package/dist/utils/ralph-runtime-state.js +222 -0
- package/dist/utils/ralph-runtime-state.js.map +1 -0
- package/dist/utils/state.js +17 -16
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/validate.js +16 -0
- package/dist/utils/validate.js.map +1 -1
- package/dist/watch/renderer.js +48 -6
- package/dist/watch/renderer.js.map +1 -1
- package/dist/watch/state-reader.js +79 -44
- package/dist/watch/state-reader.js.map +1 -1
- package/package.json +1 -1
- package/ralph/RALPH-REFERENCE.md +33 -13
- package/ralph/drivers/claude-code.sh +25 -0
- package/ralph/drivers/codex.sh +11 -0
- package/ralph/drivers/copilot.sh +11 -0
- package/ralph/drivers/cursor.sh +11 -0
- package/ralph/lib/circuit_breaker.sh +3 -3
- package/ralph/lib/date_utils.sh +28 -9
- package/ralph/lib/response_analyzer.sh +127 -7
- package/ralph/ralph_loop.sh +464 -121
- package/ralph/templates/PROMPT.md +5 -0
- package/ralph/templates/ralphrc.template +14 -4
package/README.md
CHANGED
|
@@ -394,7 +394,7 @@ The instructions file and command directory depend on the configured platform. S
|
|
|
394
394
|
|
|
395
395
|
Ralph is a bash loop that spawns fresh AI coding sessions using a **platform driver** matching the configured platform:
|
|
396
396
|
|
|
397
|
-
- **Claude Code driver** — invokes `claude` with `--output-format json`, `--allowedTools`, and explicit `--resume <session_id>`
|
|
397
|
+
- **Claude Code driver** — invokes `claude` with `--output-format json`, `--permission-mode auto`, `--allowedTools`, and explicit `--resume <session_id>`
|
|
398
398
|
- **Codex driver** — invokes `codex exec --json --sandbox workspace-write` with explicit `--resume <session_id>`
|
|
399
399
|
- **Copilot driver** _(experimental)_ — invokes `copilot --autopilot --yolo` with plain-text output
|
|
400
400
|
- **Cursor driver** _(experimental)_ — invokes `cursor-agent -p --force --output-format json`, persists `session_id` for `--resume`, and switches to `stream-json` only for live output
|
|
@@ -447,13 +447,28 @@ wsl --install
|
|
|
447
447
|
If you get permission errors:
|
|
448
448
|
|
|
449
449
|
```bash
|
|
450
|
-
#
|
|
451
|
-
|
|
450
|
+
# Claude Code only: broaden the tool allowlist in the managed config
|
|
451
|
+
# .ralph/.ralphrc
|
|
452
|
+
ALLOWED_TOOLS="Write,Read,Edit,MultiEdit,Glob,Grep,Task,TodoWrite,WebFetch,WebSearch,NotebookEdit,Bash"
|
|
452
453
|
|
|
453
|
-
#
|
|
454
|
-
|
|
454
|
+
# Keep interactive approval workflows out of unattended Claude loops
|
|
455
|
+
CLAUDE_PERMISSION_MODE="auto"
|
|
456
|
+
|
|
457
|
+
# Keep the loop unattended by continuing after detected denials
|
|
458
|
+
PERMISSION_DENIAL_MODE="continue"
|
|
459
|
+
|
|
460
|
+
# Reset stale session state and restart
|
|
461
|
+
bash .ralph/ralph_loop.sh --reset-session
|
|
462
|
+
bmalph run
|
|
455
463
|
```
|
|
456
464
|
|
|
465
|
+
Notes:
|
|
466
|
+
|
|
467
|
+
- `ALLOWED_TOOLS` only applies to the Claude Code driver and controls normal tool access.
|
|
468
|
+
- `CLAUDE_PERMISSION_MODE="auto"` prevents interactive approval modes like plan approval from blocking unattended Claude loops.
|
|
469
|
+
- Codex, Cursor, and Copilot use their native sandbox/approval settings instead.
|
|
470
|
+
- Fresh installs default to unattended mode and discourage in-loop user questions via `.ralph/PROMPT.md`.
|
|
471
|
+
|
|
457
472
|
### Common Issues
|
|
458
473
|
|
|
459
474
|
| Scenario | Solution |
|
|
@@ -1,83 +1,53 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { getBundledVersions } from "../installer.js";
|
|
4
2
|
import { checkUpstream, getSkipReason } from "../utils/github.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { SESSION_AGE_WARNING_MS, API_USAGE_WARNING_PERCENT
|
|
3
|
+
import { formatError } from "../utils/errors.js";
|
|
4
|
+
import { readRalphCircuitBreaker, readRalphRuntimeSession, readRalphRuntimeStatus, } from "../utils/ralph-runtime-state.js";
|
|
5
|
+
import { SESSION_AGE_WARNING_MS, API_USAGE_WARNING_PERCENT } from "../utils/constants.js";
|
|
8
6
|
export async function checkCircuitBreaker(projectDir) {
|
|
9
7
|
const label = "circuit breaker";
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (state.state === "CLOSED") {
|
|
16
|
-
const detail = `CLOSED (${state.consecutive_no_progress} loops without progress)`;
|
|
17
|
-
return { label, passed: true, detail };
|
|
18
|
-
}
|
|
19
|
-
if (state.state === "HALF_OPEN") {
|
|
20
|
-
return { label, passed: true, detail: `HALF_OPEN - monitoring` };
|
|
21
|
-
}
|
|
22
|
-
const detail = `OPEN - ${state.reason ?? "stagnation detected"}`;
|
|
8
|
+
const result = await readRalphCircuitBreaker(projectDir);
|
|
9
|
+
if (result.kind === "missing") {
|
|
10
|
+
return { label, passed: true, detail: "not running" };
|
|
11
|
+
}
|
|
12
|
+
if (result.kind === "invalid") {
|
|
23
13
|
return {
|
|
24
14
|
label,
|
|
25
15
|
passed: false,
|
|
26
|
-
detail,
|
|
27
|
-
hint: "
|
|
16
|
+
detail: "corrupt state file",
|
|
17
|
+
hint: "Delete .ralph/.circuit_breaker_state and restart Ralph",
|
|
28
18
|
};
|
|
29
19
|
}
|
|
30
|
-
|
|
31
|
-
if (isEnoent(err)) {
|
|
32
|
-
return { label, passed: true, detail: "not running" };
|
|
33
|
-
}
|
|
20
|
+
if (result.kind === "unreadable") {
|
|
34
21
|
return {
|
|
35
22
|
label,
|
|
36
23
|
passed: false,
|
|
37
|
-
detail: "
|
|
38
|
-
hint: "
|
|
24
|
+
detail: "unreadable state file",
|
|
25
|
+
hint: "Check file permissions or locks on .ralph/.circuit_breaker_state and restart Ralph",
|
|
39
26
|
};
|
|
40
27
|
}
|
|
28
|
+
const state = result.value;
|
|
29
|
+
if (state.state === "CLOSED") {
|
|
30
|
+
const detail = `CLOSED (${state.consecutiveNoProgress} loops without progress)`;
|
|
31
|
+
return { label, passed: true, detail };
|
|
32
|
+
}
|
|
33
|
+
if (state.state === "HALF_OPEN") {
|
|
34
|
+
return { label, passed: true, detail: "HALF_OPEN - monitoring" };
|
|
35
|
+
}
|
|
36
|
+
const detail = `OPEN - ${state.reason ?? "stagnation detected"}`;
|
|
37
|
+
return {
|
|
38
|
+
label,
|
|
39
|
+
passed: false,
|
|
40
|
+
detail,
|
|
41
|
+
hint: "Ralph detected stagnation. Review logs with: bmalph status",
|
|
42
|
+
};
|
|
41
43
|
}
|
|
42
44
|
export async function checkRalphSession(projectDir) {
|
|
43
45
|
const label = "Ralph session";
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const parsed = JSON.parse(content);
|
|
48
|
-
const session = validateRalphSession(parsed);
|
|
49
|
-
if (!session.session_id || session.session_id === "") {
|
|
50
|
-
return { label, passed: true, detail: "no active session" };
|
|
51
|
-
}
|
|
52
|
-
const createdAt = new Date(session.created_at);
|
|
53
|
-
const now = new Date();
|
|
54
|
-
const ageMs = now.getTime() - createdAt.getTime();
|
|
55
|
-
if (ageMs < 0) {
|
|
56
|
-
return {
|
|
57
|
-
label,
|
|
58
|
-
passed: false,
|
|
59
|
-
detail: "invalid timestamp (future)",
|
|
60
|
-
hint: "Delete .ralph/.ralph_session to reset",
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
const ageHours = Math.floor(ageMs / (1000 * 60 * 60));
|
|
64
|
-
const ageMinutes = Math.floor((ageMs % (1000 * 60 * 60)) / (1000 * 60));
|
|
65
|
-
const ageStr = ageHours > 0 ? `${ageHours}h${ageMinutes}m` : `${ageMinutes}m`;
|
|
66
|
-
const maxAgeHours = Math.floor(SESSION_AGE_WARNING_MS / (1000 * 60 * 60));
|
|
67
|
-
if (ageMs >= SESSION_AGE_WARNING_MS) {
|
|
68
|
-
return {
|
|
69
|
-
label,
|
|
70
|
-
passed: false,
|
|
71
|
-
detail: `${ageStr} old (max ${maxAgeHours}h)`,
|
|
72
|
-
hint: "Session is stale. Start a fresh Ralph session",
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return { label, passed: true, detail: ageStr };
|
|
46
|
+
const result = await readRalphRuntimeSession(projectDir);
|
|
47
|
+
if (result.kind === "missing") {
|
|
48
|
+
return { label, passed: true, detail: "no active session" };
|
|
76
49
|
}
|
|
77
|
-
|
|
78
|
-
if (isEnoent(err)) {
|
|
79
|
-
return { label, passed: true, detail: "no active session" };
|
|
80
|
-
}
|
|
50
|
+
if (result.kind === "invalid") {
|
|
81
51
|
return {
|
|
82
52
|
label,
|
|
83
53
|
passed: false,
|
|
@@ -85,34 +55,59 @@ export async function checkRalphSession(projectDir) {
|
|
|
85
55
|
hint: "Delete .ralph/.ralph_session to reset",
|
|
86
56
|
};
|
|
87
57
|
}
|
|
58
|
+
if (result.kind === "unreadable") {
|
|
59
|
+
return {
|
|
60
|
+
label,
|
|
61
|
+
passed: false,
|
|
62
|
+
detail: "unreadable session file",
|
|
63
|
+
hint: "Check file permissions or locks on .ralph/.ralph_session and retry",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const session = result.value;
|
|
67
|
+
if (session.kind === "inactive") {
|
|
68
|
+
return { label, passed: true, detail: "no active session" };
|
|
69
|
+
}
|
|
70
|
+
const createdAt = new Date(session.created_at);
|
|
71
|
+
const createdAtMs = createdAt.getTime();
|
|
72
|
+
if (Number.isNaN(createdAtMs)) {
|
|
73
|
+
return {
|
|
74
|
+
label,
|
|
75
|
+
passed: false,
|
|
76
|
+
detail: "invalid timestamp",
|
|
77
|
+
hint: "Delete .ralph/.ralph_session to reset",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const now = new Date();
|
|
81
|
+
const ageMs = now.getTime() - createdAtMs;
|
|
82
|
+
if (ageMs < 0) {
|
|
83
|
+
return {
|
|
84
|
+
label,
|
|
85
|
+
passed: false,
|
|
86
|
+
detail: "invalid timestamp (future)",
|
|
87
|
+
hint: "Delete .ralph/.ralph_session to reset",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const ageHours = Math.floor(ageMs / (1000 * 60 * 60));
|
|
91
|
+
const ageMinutes = Math.floor((ageMs % (1000 * 60 * 60)) / (1000 * 60));
|
|
92
|
+
const ageStr = ageHours > 0 ? `${ageHours}h${ageMinutes}m` : `${ageMinutes}m`;
|
|
93
|
+
const maxAgeHours = Math.floor(SESSION_AGE_WARNING_MS / (1000 * 60 * 60));
|
|
94
|
+
if (ageMs >= SESSION_AGE_WARNING_MS) {
|
|
95
|
+
return {
|
|
96
|
+
label,
|
|
97
|
+
passed: false,
|
|
98
|
+
detail: `${ageStr} old (max ${maxAgeHours}h)`,
|
|
99
|
+
hint: "Session is stale. Start a fresh Ralph session",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return { label, passed: true, detail: ageStr };
|
|
88
103
|
}
|
|
89
104
|
export async function checkApiCalls(projectDir) {
|
|
90
105
|
const label = "API calls this hour";
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const parsed = JSON.parse(content);
|
|
95
|
-
const status = validateRalphApiStatus(parsed);
|
|
96
|
-
const calls = status.calls_made_this_hour;
|
|
97
|
-
const max = status.max_calls_per_hour;
|
|
98
|
-
if (max <= 0) {
|
|
99
|
-
return { label, passed: true, detail: `${calls}/unlimited` };
|
|
100
|
-
}
|
|
101
|
-
const percentage = (calls / max) * 100;
|
|
102
|
-
if (percentage >= API_USAGE_WARNING_PERCENT) {
|
|
103
|
-
return {
|
|
104
|
-
label,
|
|
105
|
-
passed: false,
|
|
106
|
-
detail: `${calls}/${max} (approaching limit)`,
|
|
107
|
-
hint: "Wait for rate limit reset or increase API quota",
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
return { label, passed: true, detail: `${calls}/${max}` };
|
|
106
|
+
const result = await readRalphRuntimeStatus(projectDir);
|
|
107
|
+
if (result.kind === "missing") {
|
|
108
|
+
return { label, passed: true, detail: "not running" };
|
|
111
109
|
}
|
|
112
|
-
|
|
113
|
-
if (isEnoent(err)) {
|
|
114
|
-
return { label, passed: true, detail: "not running" };
|
|
115
|
-
}
|
|
110
|
+
if (result.kind === "invalid") {
|
|
116
111
|
return {
|
|
117
112
|
label,
|
|
118
113
|
passed: false,
|
|
@@ -120,6 +115,29 @@ export async function checkApiCalls(projectDir) {
|
|
|
120
115
|
hint: "Delete .ralph/status.json to reset",
|
|
121
116
|
};
|
|
122
117
|
}
|
|
118
|
+
if (result.kind === "unreadable") {
|
|
119
|
+
return {
|
|
120
|
+
label,
|
|
121
|
+
passed: false,
|
|
122
|
+
detail: "unreadable status file",
|
|
123
|
+
hint: "Check file permissions or locks on .ralph/status.json and retry",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const calls = result.value.callsMadeThisHour;
|
|
127
|
+
const max = result.value.maxCallsPerHour;
|
|
128
|
+
if (max <= 0) {
|
|
129
|
+
return { label, passed: true, detail: `${calls}/unlimited` };
|
|
130
|
+
}
|
|
131
|
+
const percentage = (calls / max) * 100;
|
|
132
|
+
if (percentage >= API_USAGE_WARNING_PERCENT) {
|
|
133
|
+
return {
|
|
134
|
+
label,
|
|
135
|
+
passed: false,
|
|
136
|
+
detail: `${calls}/${max} (approaching limit)`,
|
|
137
|
+
hint: "Wait for rate limit reset or increase API quota",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return { label, passed: true, detail: `${calls}/${max}` };
|
|
123
141
|
}
|
|
124
142
|
export async function checkUpstreamGitHubStatus(_projectDir) {
|
|
125
143
|
const label = "upstream status";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-runtime-checks.js","sourceRoot":"","sources":["../../src/commands/doctor-runtime-checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"doctor-runtime-checks.js","sourceRoot":"","sources":["../../src/commands/doctor-runtime-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { cp, mkdir, readFile, rename, rm } from "node:fs/promises";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { debug, warn } from "../utils/logger.js";
|
|
4
|
+
import { atomicWriteFile, exists } from "../utils/file-system.js";
|
|
5
|
+
import { formatError, isEnoent } from "../utils/errors.js";
|
|
6
|
+
import { CONFIG_FILE } from "../utils/constants.js";
|
|
7
|
+
import { classifyCommands, generateCommandIndex } from "./commands.js";
|
|
8
|
+
export async function installBmadAssets(projectDir, bundledBmadDir, slashCommandsDir, platform) {
|
|
9
|
+
const bmadSwap = await prepareBmadSwap(projectDir, bundledBmadDir);
|
|
10
|
+
// Swap in.
|
|
11
|
+
try {
|
|
12
|
+
await rename(bmadSwap.staged, bmadSwap.dest);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
// Restore original on failure.
|
|
16
|
+
debug(`Rename failed, restoring original: ${formatError(err)}`);
|
|
17
|
+
try {
|
|
18
|
+
await rename(bmadSwap.backup, bmadSwap.dest);
|
|
19
|
+
}
|
|
20
|
+
catch (restoreErr) {
|
|
21
|
+
if (!isEnoent(restoreErr)) {
|
|
22
|
+
debug(`Could not restore _bmad.old: ${formatError(restoreErr)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
const classified = await (async () => {
|
|
28
|
+
try {
|
|
29
|
+
return await finalizeBmadInstall(projectDir, slashCommandsDir, platform);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return await rollbackBmadFinalization(bmadSwap, err);
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
await commitBmadSwap(bmadSwap);
|
|
36
|
+
return classified;
|
|
37
|
+
}
|
|
38
|
+
export async function generateManifests(projectDir) {
|
|
39
|
+
const configDir = join(projectDir, "_bmad/_config");
|
|
40
|
+
await mkdir(configDir, { recursive: true });
|
|
41
|
+
const coreHelpPath = join(projectDir, "_bmad/core/module-help.csv");
|
|
42
|
+
const bmmHelpPath = join(projectDir, "_bmad/bmm/module-help.csv");
|
|
43
|
+
// Validate CSV files exist before reading
|
|
44
|
+
if (!(await exists(coreHelpPath))) {
|
|
45
|
+
throw new Error(`Core module-help.csv not found at ${coreHelpPath}. BMAD installation may be incomplete.`);
|
|
46
|
+
}
|
|
47
|
+
if (!(await exists(bmmHelpPath))) {
|
|
48
|
+
throw new Error(`BMM module-help.csv not found at ${bmmHelpPath}. BMAD installation may be incomplete.`);
|
|
49
|
+
}
|
|
50
|
+
const coreContent = await readFile(coreHelpPath, "utf-8");
|
|
51
|
+
const bmmContent = await readFile(bmmHelpPath, "utf-8");
|
|
52
|
+
// Extract header from core (first line) and data lines from both
|
|
53
|
+
const coreLines = coreContent.trimEnd().split(/\r?\n/);
|
|
54
|
+
const bmmLines = bmmContent.trimEnd().split(/\r?\n/);
|
|
55
|
+
if (!coreLines[0]?.trim()) {
|
|
56
|
+
throw new Error(`Core module-help.csv is empty at ${coreHelpPath}`);
|
|
57
|
+
}
|
|
58
|
+
if (!bmmLines[0]?.trim()) {
|
|
59
|
+
throw new Error(`BMM module-help.csv is empty at ${bmmHelpPath}`);
|
|
60
|
+
}
|
|
61
|
+
const normalize = (line) => line.replace(/,+$/, "");
|
|
62
|
+
const header = normalize(coreLines[0]);
|
|
63
|
+
const bmmHeader = normalize(bmmLines[0]);
|
|
64
|
+
// Validate headers match (warn if mismatch but continue)
|
|
65
|
+
if (header && bmmHeader && header !== bmmHeader) {
|
|
66
|
+
warn(`CSV header mismatch detected. BMAD modules may have incompatible formats.`);
|
|
67
|
+
debug(`CSV header mismatch details - core: "${header.slice(0, 50)}...", bmm: "${bmmHeader.slice(0, 50)}..."`);
|
|
68
|
+
}
|
|
69
|
+
const coreData = coreLines
|
|
70
|
+
.slice(1)
|
|
71
|
+
.filter((l) => l.trim())
|
|
72
|
+
.map(normalize);
|
|
73
|
+
const bmmData = bmmLines
|
|
74
|
+
.slice(1)
|
|
75
|
+
.filter((l) => l.trim())
|
|
76
|
+
.map(normalize);
|
|
77
|
+
const combined = [header, ...coreData, ...bmmData].join("\n") + "\n";
|
|
78
|
+
await atomicWriteFile(join(configDir, "task-manifest.csv"), combined);
|
|
79
|
+
await atomicWriteFile(join(configDir, "workflow-manifest.csv"), combined);
|
|
80
|
+
await atomicWriteFile(join(configDir, "bmad-help.csv"), combined);
|
|
81
|
+
}
|
|
82
|
+
async function prepareBmadSwap(projectDir, bundledBmadDir) {
|
|
83
|
+
const dest = join(projectDir, "_bmad");
|
|
84
|
+
const backup = join(projectDir, "_bmad.old");
|
|
85
|
+
const staged = join(projectDir, "_bmad.new");
|
|
86
|
+
const destExists = await exists(dest);
|
|
87
|
+
const backupExists = await exists(backup);
|
|
88
|
+
let hasBackup = false;
|
|
89
|
+
if (destExists && backupExists) {
|
|
90
|
+
throw new Error("Found both _bmad and _bmad.old from a previous failed install or upgrade. " +
|
|
91
|
+
"Restore or remove one of them before retrying.");
|
|
92
|
+
}
|
|
93
|
+
if (backupExists) {
|
|
94
|
+
hasBackup = true;
|
|
95
|
+
debug("Found existing _bmad.old from previous failed rollback, preserving backup");
|
|
96
|
+
}
|
|
97
|
+
else if (destExists) {
|
|
98
|
+
try {
|
|
99
|
+
await rename(dest, backup);
|
|
100
|
+
hasBackup = true;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (!isEnoent(err))
|
|
104
|
+
throw err;
|
|
105
|
+
debug("_bmad disappeared before it could be preserved, continuing without backup");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
debug("No existing _bmad to preserve (first install)");
|
|
110
|
+
}
|
|
111
|
+
// Stage new content.
|
|
112
|
+
await rm(staged, { recursive: true, force: true });
|
|
113
|
+
await cp(bundledBmadDir, staged, { recursive: true, dereference: false });
|
|
114
|
+
return { dest, backup, staged, hasBackup };
|
|
115
|
+
}
|
|
116
|
+
async function finalizeBmadInstall(projectDir, slashCommandsDir, platform) {
|
|
117
|
+
await generateManifests(projectDir);
|
|
118
|
+
const classified = await classifyCommands(projectDir, slashCommandsDir);
|
|
119
|
+
await generateCommandIndex(projectDir, classified);
|
|
120
|
+
const projectName = await deriveProjectName(projectDir);
|
|
121
|
+
const escapedName = projectName.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
122
|
+
await atomicWriteFile(join(projectDir, "_bmad/config.yaml"), `# BMAD Configuration - Generated by bmalph
|
|
123
|
+
platform: ${platform.id}
|
|
124
|
+
project_name: "${escapedName}"
|
|
125
|
+
output_folder: _bmad-output
|
|
126
|
+
user_name: BMad
|
|
127
|
+
communication_language: English
|
|
128
|
+
document_output_language: English
|
|
129
|
+
user_skill_level: intermediate
|
|
130
|
+
planning_artifacts: _bmad-output/planning-artifacts
|
|
131
|
+
implementation_artifacts: _bmad-output/implementation-artifacts
|
|
132
|
+
project_knowledge: docs
|
|
133
|
+
modules:
|
|
134
|
+
- bmm
|
|
135
|
+
`);
|
|
136
|
+
return classified;
|
|
137
|
+
}
|
|
138
|
+
async function rollbackBmadFinalization(swap, error) {
|
|
139
|
+
debug(`BMAD finalization failed after swap: ${formatError(error)}`);
|
|
140
|
+
try {
|
|
141
|
+
await rm(swap.dest, { recursive: true, force: true });
|
|
142
|
+
if (swap.hasBackup) {
|
|
143
|
+
await rename(swap.backup, swap.dest);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (rollbackErr) {
|
|
147
|
+
throw new Error("BMAD finalization failed after swap and rollback also failed. " +
|
|
148
|
+
`Original error: ${formatError(error)}. ` +
|
|
149
|
+
`Rollback error: ${formatError(rollbackErr)}`, {
|
|
150
|
+
cause: rollbackErr,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (swap.hasBackup) {
|
|
154
|
+
throw new Error("BMAD finalization failed after swap; previous BMAD installation was restored.", {
|
|
155
|
+
cause: error,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
throw new Error("BMAD finalization failed after swap; incomplete BMAD installation was cleaned up.", {
|
|
159
|
+
cause: error,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async function commitBmadSwap(swap) {
|
|
163
|
+
if (!swap.hasBackup)
|
|
164
|
+
return;
|
|
165
|
+
await rm(swap.backup, { recursive: true, force: true });
|
|
166
|
+
}
|
|
167
|
+
async function deriveProjectName(projectDir) {
|
|
168
|
+
try {
|
|
169
|
+
const configPath = join(projectDir, CONFIG_FILE);
|
|
170
|
+
const raw = await readFile(configPath, "utf-8");
|
|
171
|
+
const config = JSON.parse(raw);
|
|
172
|
+
if (config.name)
|
|
173
|
+
return config.name;
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
if (!isEnoent(err)) {
|
|
177
|
+
warn(`Could not read ${CONFIG_FILE}: ${formatError(err)}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return basename(projectDir);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=bmad-assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bmad-assets.js","sourceRoot":"","sources":["../../src/installer/bmad-assets.ts"],"names":[],"mappings":"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"}
|