bmalph 2.4.0 → 2.5.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/README.md +50 -16
- package/dist/cli.js +6 -0
- package/dist/commands/doctor.d.ts +0 -11
- package/dist/commands/doctor.js +0 -49
- package/dist/commands/init.js +4 -2
- package/dist/commands/status.js +14 -1
- package/dist/commands/watch.d.ts +6 -0
- package/dist/commands/watch.js +19 -0
- package/dist/installer.d.ts +0 -6
- package/dist/installer.js +0 -10
- package/dist/transition/artifacts.d.ts +0 -2
- package/dist/transition/artifacts.js +0 -27
- package/dist/transition/index.d.ts +1 -1
- package/dist/transition/index.js +1 -1
- package/dist/utils/state.d.ts +0 -2
- package/dist/utils/validate.js +1 -0
- package/dist/watch/dashboard.d.ts +4 -0
- package/dist/watch/dashboard.js +60 -0
- package/dist/watch/file-watcher.d.ts +9 -0
- package/dist/watch/file-watcher.js +27 -0
- package/dist/watch/renderer.d.ts +16 -0
- package/dist/watch/renderer.js +241 -0
- package/dist/watch/state-reader.d.ts +9 -0
- package/dist/watch/state-reader.js +190 -0
- package/dist/watch/types.d.ts +55 -0
- package/dist/watch/types.js +1 -0
- package/package.json +9 -4
- package/ralph/lib/circuit_breaker.sh +86 -59
- package/ralph/lib/enable_core.sh +3 -6
- package/ralph/lib/response_analyzer.sh +5 -29
- package/ralph/lib/task_sources.sh +45 -11
- package/ralph/lib/wizard_utils.sh +9 -0
- package/ralph/ralph_import.sh +7 -2
- package/ralph/ralph_loop.sh +29 -34
- package/ralph/ralph_monitor.sh +4 -0
- package/slash-commands/bmalph-watch.md +20 -0
package/README.md
CHANGED
|
@@ -26,6 +26,10 @@ bmalph provides:
|
|
|
26
26
|
- `bmalph upgrade` — Update to latest versions
|
|
27
27
|
- `bmalph doctor` — Check installation health
|
|
28
28
|
- `bmalph implement` — Transition from BMAD to Ralph
|
|
29
|
+
- `bmalph check-updates` — Check for upstream updates
|
|
30
|
+
- `bmalph status` — Show project status and phase
|
|
31
|
+
- `bmalph reset` — Remove all bmalph files
|
|
32
|
+
- `bmalph watch` — Live Ralph loop dashboard
|
|
29
33
|
|
|
30
34
|
## Supported Platforms
|
|
31
35
|
|
|
@@ -203,6 +207,8 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
203
207
|
| `bmalph check-updates` | Check if bundled BMAD/Ralph versions are up to date |
|
|
204
208
|
| `bmalph status` | Show current project status and phase |
|
|
205
209
|
| `bmalph implement` | Transition BMAD planning artifacts to Ralph format |
|
|
210
|
+
| `bmalph reset` | Remove all bmalph files from the project |
|
|
211
|
+
| `bmalph watch` | Live dashboard showing Ralph loop status |
|
|
206
212
|
|
|
207
213
|
### Global options
|
|
208
214
|
|
|
@@ -222,6 +228,7 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
222
228
|
| `-n, --name <name>` | Project name | directory name |
|
|
223
229
|
| `-d, --description <desc>` | Project description | (prompted) |
|
|
224
230
|
| `--platform <id>` | Target platform (`claude-code`, `codex`, `cursor`, `windsurf`, `copilot`, `aider`) | auto-detect |
|
|
231
|
+
| `--dry-run` | Preview changes without writing files | |
|
|
225
232
|
|
|
226
233
|
### implement options
|
|
227
234
|
|
|
@@ -235,6 +242,12 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
235
242
|
| -------- | -------------- |
|
|
236
243
|
| `--json` | Output as JSON |
|
|
237
244
|
|
|
245
|
+
### doctor options
|
|
246
|
+
|
|
247
|
+
| Flag | Description |
|
|
248
|
+
| -------- | -------------- |
|
|
249
|
+
| `--json` | Output as JSON |
|
|
250
|
+
|
|
238
251
|
### status options
|
|
239
252
|
|
|
240
253
|
| Flag | Description |
|
|
@@ -248,9 +261,22 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
248
261
|
| `--force` | Skip confirmation prompts |
|
|
249
262
|
| `--dry-run` | Preview changes |
|
|
250
263
|
|
|
264
|
+
### reset options
|
|
265
|
+
|
|
266
|
+
| Flag | Description |
|
|
267
|
+
| ----------- | ------------------------ |
|
|
268
|
+
| `--dry-run` | Preview changes |
|
|
269
|
+
| `--force` | Skip confirmation prompt |
|
|
270
|
+
|
|
271
|
+
### watch options
|
|
272
|
+
|
|
273
|
+
| Flag | Description |
|
|
274
|
+
| ----------------- | ------------------------------------------------ |
|
|
275
|
+
| `--interval <ms>` | Refresh interval in milliseconds (default: 2000) |
|
|
276
|
+
|
|
251
277
|
## Slash Commands
|
|
252
278
|
|
|
253
|
-
bmalph installs
|
|
279
|
+
bmalph installs 51 slash commands (45 BMAD + 6 bmalph). Command delivery varies by platform:
|
|
254
280
|
|
|
255
281
|
- **Claude Code** — installed as files in `.claude/commands/` (invoke with `/command-name`)
|
|
256
282
|
- **OpenAI Codex** — inlined in `AGENTS.md` (reference agents by name)
|
|
@@ -405,25 +431,33 @@ ls -la .ralph/
|
|
|
405
431
|
|
|
406
432
|
### Reset Installation
|
|
407
433
|
|
|
408
|
-
|
|
434
|
+
The simplest way to remove all bmalph files:
|
|
409
435
|
|
|
410
436
|
```bash
|
|
411
|
-
|
|
412
|
-
rm -rf _bmad .ralph bmalph
|
|
413
|
-
|
|
414
|
-
# Also remove platform-specific files:
|
|
415
|
-
# Claude Code: rm -rf .claude/commands/ and remove bmalph section from CLAUDE.md
|
|
416
|
-
# Codex: remove bmalph sections from AGENTS.md
|
|
417
|
-
# Cursor: rm .cursor/rules/bmad.mdc
|
|
418
|
-
# Windsurf: rm .windsurf/rules/bmad.md
|
|
419
|
-
# Copilot: remove bmalph sections from .github/copilot-instructions.md
|
|
420
|
-
# Aider: remove bmalph sections from CONVENTIONS.md
|
|
421
|
-
# See the Supported Platforms table for your platform's files.
|
|
422
|
-
|
|
423
|
-
# Reinitialize
|
|
424
|
-
bmalph init
|
|
437
|
+
bmalph reset
|
|
425
438
|
```
|
|
426
439
|
|
|
440
|
+
Use `--dry-run` to preview what will be removed, or `--force` to skip confirmation.
|
|
441
|
+
|
|
442
|
+
#### Manual removal
|
|
443
|
+
|
|
444
|
+
If the CLI is unavailable, remove these directories and files manually:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
rm -rf _bmad/ .ralph/ bmalph/
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Then remove the bmalph-managed sections from your instructions file. The file depends on your platform:
|
|
451
|
+
|
|
452
|
+
- **Claude Code** — remove `.claude/commands/` and bmalph section from `CLAUDE.md`
|
|
453
|
+
- **Codex** — remove bmalph sections from `AGENTS.md`
|
|
454
|
+
- **Cursor** — remove `.cursor/rules/bmad.mdc`
|
|
455
|
+
- **Windsurf** — remove `.windsurf/rules/bmad.md`
|
|
456
|
+
- **Copilot** — remove bmalph sections from `.github/copilot-instructions.md`
|
|
457
|
+
- **Aider** — remove bmalph sections from `CONVENTIONS.md`
|
|
458
|
+
|
|
459
|
+
See the [Supported Platforms](#supported-platforms) table for details. After manual removal, run `bmalph init` to reinitialize.
|
|
460
|
+
|
|
427
461
|
## Quick Examples
|
|
428
462
|
|
|
429
463
|
### Initialize a new project
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { checkUpdatesCommand } from "./commands/check-updates.js";
|
|
|
8
8
|
import { statusCommand } from "./commands/status.js";
|
|
9
9
|
import { implementCommand } from "./commands/implement.js";
|
|
10
10
|
import { resetCommand } from "./commands/reset.js";
|
|
11
|
+
import { watchCommand } from "./commands/watch.js";
|
|
11
12
|
import { setVerbose, setQuiet } from "./utils/logger.js";
|
|
12
13
|
import { getPackageVersion } from "./installer.js";
|
|
13
14
|
import { isEnoent } from "./utils/errors.js";
|
|
@@ -92,4 +93,9 @@ program
|
|
|
92
93
|
.option("--dry-run", "Preview changes without removing files")
|
|
93
94
|
.option("--force", "Skip confirmation prompt")
|
|
94
95
|
.action(async (opts) => resetCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
|
|
96
|
+
program
|
|
97
|
+
.command("watch")
|
|
98
|
+
.description("Live dashboard showing Ralph loop status, circuit breaker, and activity")
|
|
99
|
+
.option("--interval <ms>", "Refresh interval in milliseconds (default: 2000)")
|
|
100
|
+
.action(async (opts) => watchCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
|
|
95
101
|
void program.parseAsync();
|
|
@@ -37,15 +37,4 @@ export declare function runDoctor(options: DoctorOptions): Promise<DoctorResult>
|
|
|
37
37
|
* Core checks + platform doctor checks + trailing checks.
|
|
38
38
|
*/
|
|
39
39
|
export declare function buildCheckRegistry(platform: Platform): CheckDefinition[];
|
|
40
|
-
/**
|
|
41
|
-
* Static registry for backward compatibility with existing tests.
|
|
42
|
-
* Uses claude-code platform checks (slash-command + claude-md).
|
|
43
|
-
*
|
|
44
|
-
* Note: The check ids here ("slash-command", "claude-md") intentionally differ
|
|
45
|
-
* from the live buildCheckRegistry path which uses platform-provided ids
|
|
46
|
-
* ("instructions-file"). This is for test backward compatibility only.
|
|
47
|
-
*
|
|
48
|
-
* @deprecated Use `buildCheckRegistry(platform)` for platform-aware checks.
|
|
49
|
-
*/
|
|
50
|
-
export declare const CHECK_REGISTRY: CheckDefinition[];
|
|
51
40
|
export {};
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { readFile, stat } from "fs/promises";
|
|
3
|
-
import { exists } from "../utils/file-system.js";
|
|
4
3
|
import { join } from "path";
|
|
5
4
|
import { readJsonFile } from "../utils/json.js";
|
|
6
5
|
import { readConfig } from "../utils/config.js";
|
|
@@ -451,51 +450,3 @@ export function buildCheckRegistry(platform) {
|
|
|
451
450
|
}));
|
|
452
451
|
return [...CORE_CHECKS, ...platformChecks, ...TRAILING_CHECKS];
|
|
453
452
|
}
|
|
454
|
-
/**
|
|
455
|
-
* Static registry for backward compatibility with existing tests.
|
|
456
|
-
* Uses claude-code platform checks (slash-command + claude-md).
|
|
457
|
-
*
|
|
458
|
-
* Note: The check ids here ("slash-command", "claude-md") intentionally differ
|
|
459
|
-
* from the live buildCheckRegistry path which uses platform-provided ids
|
|
460
|
-
* ("instructions-file"). This is for test backward compatibility only.
|
|
461
|
-
*
|
|
462
|
-
* @deprecated Use `buildCheckRegistry(platform)` for platform-aware checks.
|
|
463
|
-
*/
|
|
464
|
-
export const CHECK_REGISTRY = (() => {
|
|
465
|
-
// Inline claude-code platform checks to avoid async import at module level
|
|
466
|
-
const slashCommandCheck = {
|
|
467
|
-
id: "slash-command",
|
|
468
|
-
run: async (projectDir) => {
|
|
469
|
-
if (await exists(join(projectDir, ".claude/commands/bmalph.md"))) {
|
|
470
|
-
return { label: ".claude/commands/bmalph.md present", passed: true };
|
|
471
|
-
}
|
|
472
|
-
return {
|
|
473
|
-
label: ".claude/commands/bmalph.md present",
|
|
474
|
-
passed: false,
|
|
475
|
-
detail: "not found",
|
|
476
|
-
hint: "Run: bmalph init",
|
|
477
|
-
};
|
|
478
|
-
},
|
|
479
|
-
};
|
|
480
|
-
const claudeMdCheck = {
|
|
481
|
-
id: "claude-md",
|
|
482
|
-
run: async (projectDir) => {
|
|
483
|
-
const label = "CLAUDE.md contains BMAD snippet";
|
|
484
|
-
const hint = "Run: bmalph init";
|
|
485
|
-
try {
|
|
486
|
-
const content = await readFile(join(projectDir, "CLAUDE.md"), "utf-8");
|
|
487
|
-
if (content.includes("BMAD-METHOD Integration")) {
|
|
488
|
-
return { label, passed: true };
|
|
489
|
-
}
|
|
490
|
-
return { label, passed: false, detail: "missing BMAD-METHOD Integration section", hint };
|
|
491
|
-
}
|
|
492
|
-
catch (err) {
|
|
493
|
-
if (isEnoent(err)) {
|
|
494
|
-
return { label, passed: false, detail: "CLAUDE.md not found", hint };
|
|
495
|
-
}
|
|
496
|
-
return { label, passed: false, detail: `error: ${formatError(err)}`, hint };
|
|
497
|
-
}
|
|
498
|
-
},
|
|
499
|
-
};
|
|
500
|
-
return [...CORE_CHECKS, slashCommandCheck, claudeMdCheck, ...TRAILING_CHECKS];
|
|
501
|
-
})();
|
package/dist/commands/init.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
3
|
import { writeConfig } from "../utils/config.js";
|
|
4
|
-
import { installProject, mergeInstructionsFile, isInitialized,
|
|
4
|
+
import { installProject, mergeInstructionsFile, isInitialized, previewInstall, getBundledVersions, } from "../installer.js";
|
|
5
5
|
import { formatDryRunSummary } from "../utils/dryrun.js";
|
|
6
6
|
import { validateProjectName } from "../utils/validate.js";
|
|
7
7
|
import { withErrorHandling } from "../utils/errors.js";
|
|
8
|
+
import { exists } from "../utils/file-system.js";
|
|
9
|
+
import { join } from "path";
|
|
8
10
|
import { isPlatformId, getPlatform } from "../platform/registry.js";
|
|
9
11
|
import { detectPlatform } from "../platform/detect.js";
|
|
10
12
|
export async function initCommand(options) {
|
|
@@ -62,7 +64,7 @@ async function runInit(options) {
|
|
|
62
64
|
console.log("Use 'bmalph upgrade' to update bundled assets to the latest version.");
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
|
-
if (await
|
|
67
|
+
if (await exists(join(projectDir, "_bmad"))) {
|
|
66
68
|
console.log(chalk.cyan("Existing BMAD installation detected."));
|
|
67
69
|
console.log("Framework files in _bmad/ will be replaced with the managed version.");
|
|
68
70
|
console.log("Planning artifacts in _bmad-output/ will not be modified.\n");
|
package/dist/commands/status.js
CHANGED
|
@@ -43,6 +43,11 @@ export async function runStatus(options) {
|
|
|
43
43
|
const nextAction = artifactScan && phaseDetected
|
|
44
44
|
? artifactScan.nextAction
|
|
45
45
|
: getNextAction(phase, status, ralphStatus, platform);
|
|
46
|
+
// Detect when Ralph completed but bmalph state hasn't caught up
|
|
47
|
+
const completionMismatch = phase === 4 &&
|
|
48
|
+
status === "implementing" &&
|
|
49
|
+
ralphStatus !== null &&
|
|
50
|
+
ralphStatus.status === "completed";
|
|
46
51
|
if (options.json) {
|
|
47
52
|
const output = {
|
|
48
53
|
phase,
|
|
@@ -68,6 +73,9 @@ export async function runStatus(options) {
|
|
|
68
73
|
if (nextAction) {
|
|
69
74
|
output.nextAction = nextAction;
|
|
70
75
|
}
|
|
76
|
+
if (completionMismatch) {
|
|
77
|
+
output.completionMismatch = true;
|
|
78
|
+
}
|
|
71
79
|
console.log(JSON.stringify(output, null, 2));
|
|
72
80
|
return;
|
|
73
81
|
}
|
|
@@ -97,7 +105,12 @@ export async function runStatus(options) {
|
|
|
97
105
|
console.log(chalk.bold(" Ralph Loop"));
|
|
98
106
|
console.log(` ${chalk.cyan("Status:")} ${chalk.dim("not started")}`);
|
|
99
107
|
}
|
|
100
|
-
if (
|
|
108
|
+
if (completionMismatch) {
|
|
109
|
+
console.log("");
|
|
110
|
+
console.log(chalk.green(" Ralph has completed all tasks."));
|
|
111
|
+
console.log(` ${chalk.cyan("Next:")} Review changes and update project phase`);
|
|
112
|
+
}
|
|
113
|
+
else if (nextAction) {
|
|
101
114
|
console.log("");
|
|
102
115
|
console.log(` ${chalk.cyan("Next:")} ${nextAction}`);
|
|
103
116
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readConfig } from "../utils/config.js";
|
|
2
|
+
import { withErrorHandling } from "../utils/errors.js";
|
|
3
|
+
import { startDashboard } from "../watch/dashboard.js";
|
|
4
|
+
const DEFAULT_INTERVAL_MS = 2000;
|
|
5
|
+
export async function watchCommand(options) {
|
|
6
|
+
await withErrorHandling(() => runWatch(options));
|
|
7
|
+
}
|
|
8
|
+
async function runWatch(options) {
|
|
9
|
+
const projectDir = options.projectDir;
|
|
10
|
+
const config = await readConfig(projectDir);
|
|
11
|
+
if (!config) {
|
|
12
|
+
throw new Error("Project not initialized. Run: bmalph init");
|
|
13
|
+
}
|
|
14
|
+
const interval = options.interval ? parseInt(options.interval, 10) : DEFAULT_INTERVAL_MS;
|
|
15
|
+
if (isNaN(interval) || interval < 500) {
|
|
16
|
+
throw new Error("Interval must be a number >= 500 (milliseconds)");
|
|
17
|
+
}
|
|
18
|
+
await startDashboard({ projectDir, interval });
|
|
19
|
+
}
|
package/dist/installer.d.ts
CHANGED
|
@@ -28,12 +28,6 @@ export declare function generateManifests(projectDir: string): Promise<void>;
|
|
|
28
28
|
* Creates the file if it doesn't exist, replaces an existing BMAD section on upgrade.
|
|
29
29
|
*/
|
|
30
30
|
export declare function mergeInstructionsFile(projectDir: string, platform?: Platform): Promise<void>;
|
|
31
|
-
/**
|
|
32
|
-
* @deprecated Use `mergeInstructionsFile(projectDir)` instead.
|
|
33
|
-
* Kept for backward compatibility during migration.
|
|
34
|
-
*/
|
|
35
|
-
export declare function mergeClaudeMd(projectDir: string): Promise<void>;
|
|
36
31
|
export declare function isInitialized(projectDir: string): Promise<boolean>;
|
|
37
|
-
export declare function hasExistingBmadDir(projectDir: string): Promise<boolean>;
|
|
38
32
|
export declare function previewInstall(projectDir: string, platform?: Platform): Promise<PreviewInstallResult>;
|
|
39
33
|
export declare function previewUpgrade(projectDir: string, platform?: Platform): Promise<PreviewUpgradeResult>;
|
package/dist/installer.js
CHANGED
|
@@ -424,19 +424,9 @@ export async function mergeInstructionsFile(projectDir, platform) {
|
|
|
424
424
|
}
|
|
425
425
|
await atomicWriteFile(instructionsPath, existing + snippet);
|
|
426
426
|
}
|
|
427
|
-
/**
|
|
428
|
-
* @deprecated Use `mergeInstructionsFile(projectDir)` instead.
|
|
429
|
-
* Kept for backward compatibility during migration.
|
|
430
|
-
*/
|
|
431
|
-
export async function mergeClaudeMd(projectDir) {
|
|
432
|
-
return mergeInstructionsFile(projectDir);
|
|
433
|
-
}
|
|
434
427
|
export async function isInitialized(projectDir) {
|
|
435
428
|
return exists(join(projectDir, CONFIG_FILE));
|
|
436
429
|
}
|
|
437
|
-
export async function hasExistingBmadDir(projectDir) {
|
|
438
|
-
return exists(join(projectDir, "_bmad"));
|
|
439
|
-
}
|
|
440
430
|
export async function previewInstall(projectDir, platform) {
|
|
441
431
|
const p = platform ?? (await getDefaultPlatform());
|
|
442
432
|
const wouldCreate = [];
|
|
@@ -1,3 +1 @@
|
|
|
1
1
|
export declare function findArtifactsDir(projectDir: string): Promise<string | null>;
|
|
2
|
-
/** @deprecated Use `runPreflight` from `./preflight.js` instead. Kept for backward compatibility. */
|
|
3
|
-
export declare function validateArtifacts(files: string[], artifactsDir: string): Promise<string[]>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
2
1
|
import { join } from "path";
|
|
3
2
|
import { debug } from "../utils/logger.js";
|
|
4
3
|
import { exists } from "../utils/file-system.js";
|
|
@@ -19,29 +18,3 @@ export async function findArtifactsDir(projectDir) {
|
|
|
19
18
|
debug(`No artifacts found. Checked: ${candidates.join(", ")}`);
|
|
20
19
|
return null;
|
|
21
20
|
}
|
|
22
|
-
/** @deprecated Use `runPreflight` from `./preflight.js` instead. Kept for backward compatibility. */
|
|
23
|
-
export async function validateArtifacts(files, artifactsDir) {
|
|
24
|
-
const warnings = [];
|
|
25
|
-
const hasPrd = files.some((f) => /prd/i.test(f));
|
|
26
|
-
if (!hasPrd) {
|
|
27
|
-
warnings.push("No PRD document found in planning artifacts");
|
|
28
|
-
}
|
|
29
|
-
const hasArchitecture = files.some((f) => /architect/i.test(f));
|
|
30
|
-
if (!hasArchitecture) {
|
|
31
|
-
warnings.push("No architecture document found in planning artifacts");
|
|
32
|
-
}
|
|
33
|
-
// Check readiness report for NO-GO
|
|
34
|
-
const readinessFile = files.find((f) => /readiness/i.test(f));
|
|
35
|
-
if (readinessFile) {
|
|
36
|
-
try {
|
|
37
|
-
const content = await readFile(join(artifactsDir, readinessFile), "utf-8");
|
|
38
|
-
if (/NO[-\s]?GO/i.test(content)) {
|
|
39
|
-
warnings.push("Readiness report indicates NO-GO status");
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
warnings.push("Could not read readiness report — NO-GO status unverified");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return warnings;
|
|
47
|
-
}
|
|
@@ -2,7 +2,7 @@ export type { ProjectContext, Story, TechStack, FixPlanItem, SpecsChange, Transi
|
|
|
2
2
|
export { parseStories, parseStoriesWithWarnings } from "./story-parsing.js";
|
|
3
3
|
export { generateFixPlan, hasFixPlanProgress, parseFixPlan, mergeFixPlanProgress, } from "./fix-plan.js";
|
|
4
4
|
export { detectTechStack, customizeAgentMd } from "./tech-stack.js";
|
|
5
|
-
export { findArtifactsDir
|
|
5
|
+
export { findArtifactsDir } from "./artifacts.js";
|
|
6
6
|
export { extractSection, extractProjectContext, generateProjectContextMd, generatePrompt, } from "./context.js";
|
|
7
7
|
export { generateSpecsChangelog, formatChangelog } from "./specs-changelog.js";
|
|
8
8
|
export { detectSpecFileType, determinePriority, extractDescription, generateSpecsIndex, formatSpecsIndexMd, } from "./specs-index.js";
|
package/dist/transition/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { generateFixPlan, hasFixPlanProgress, parseFixPlan, mergeFixPlanProgress
|
|
|
5
5
|
// Tech stack detection
|
|
6
6
|
export { detectTechStack, customizeAgentMd } from "./tech-stack.js";
|
|
7
7
|
// Artifacts
|
|
8
|
-
export { findArtifactsDir
|
|
8
|
+
export { findArtifactsDir } from "./artifacts.js";
|
|
9
9
|
// Context
|
|
10
10
|
export { extractSection, extractProjectContext, generateProjectContextMd, generatePrompt, } from "./context.js";
|
|
11
11
|
// Specs changelog
|
package/dist/utils/state.d.ts
CHANGED
|
@@ -21,6 +21,4 @@ export declare function readState(projectDir: string): Promise<BmalphState | nul
|
|
|
21
21
|
export declare function writeState(projectDir: string, state: BmalphState): Promise<void>;
|
|
22
22
|
export declare function getPhaseLabel(phase: number): string;
|
|
23
23
|
export declare function getPhaseInfo(phase: number): PhaseInfo;
|
|
24
|
-
/** @deprecated Use RalphLoopStatus from validate.ts instead */
|
|
25
|
-
export type RalphStatus = RalphLoopStatus;
|
|
26
24
|
export declare function readRalphStatus(projectDir: string): Promise<RalphLoopStatus>;
|
package/dist/utils/validate.js
CHANGED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WatchOptions } from "./types.js";
|
|
2
|
+
export declare function createRefreshCallback(projectDir: string, write: (s: string) => void): () => Promise<void>;
|
|
3
|
+
export declare function setupTerminal(): () => void;
|
|
4
|
+
export declare function startDashboard(options: WatchOptions): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readDashboardState } from "./state-reader.js";
|
|
2
|
+
import { renderDashboard } from "./renderer.js";
|
|
3
|
+
import { FileWatcher } from "./file-watcher.js";
|
|
4
|
+
const CLEAR_SCREEN = "\x1B[2J\x1B[H";
|
|
5
|
+
const HIDE_CURSOR = "\x1B[?25l";
|
|
6
|
+
const SHOW_CURSOR = "\x1B[?25h";
|
|
7
|
+
export function createRefreshCallback(projectDir, write) {
|
|
8
|
+
return async () => {
|
|
9
|
+
const state = await readDashboardState(projectDir);
|
|
10
|
+
const output = renderDashboard(state);
|
|
11
|
+
write(CLEAR_SCREEN + output + "\n");
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function setupTerminal() {
|
|
15
|
+
if (process.stdout.isTTY) {
|
|
16
|
+
process.stdout.write(HIDE_CURSOR);
|
|
17
|
+
}
|
|
18
|
+
return () => {
|
|
19
|
+
if (process.stdout.isTTY) {
|
|
20
|
+
process.stdout.write(SHOW_CURSOR);
|
|
21
|
+
}
|
|
22
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
23
|
+
process.stdin.setRawMode(false);
|
|
24
|
+
}
|
|
25
|
+
process.stdin.pause();
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function startDashboard(options) {
|
|
29
|
+
const { projectDir, interval } = options;
|
|
30
|
+
const cleanup = setupTerminal();
|
|
31
|
+
const refresh = createRefreshCallback(projectDir, (s) => process.stdout.write(s));
|
|
32
|
+
const watcher = new FileWatcher(refresh, interval);
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const stop = () => {
|
|
35
|
+
watcher.stop();
|
|
36
|
+
cleanup();
|
|
37
|
+
resolve();
|
|
38
|
+
};
|
|
39
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
40
|
+
process.stdin.setRawMode(true);
|
|
41
|
+
process.stdin.resume();
|
|
42
|
+
process.stdin.setEncoding("utf-8");
|
|
43
|
+
process.stdin.on("data", (data) => {
|
|
44
|
+
if (data === "q" || data === "\x03") {
|
|
45
|
+
stop();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const onResize = () => {
|
|
50
|
+
void refresh();
|
|
51
|
+
};
|
|
52
|
+
process.stdout.on("resize", onResize);
|
|
53
|
+
const onSignal = () => {
|
|
54
|
+
stop();
|
|
55
|
+
};
|
|
56
|
+
process.on("SIGINT", onSignal);
|
|
57
|
+
process.on("SIGTERM", onSignal);
|
|
58
|
+
watcher.start();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class FileWatcher {
|
|
2
|
+
intervalId = null;
|
|
3
|
+
intervalMs;
|
|
4
|
+
callback;
|
|
5
|
+
constructor(callback, intervalMs = 2000) {
|
|
6
|
+
this.callback = callback;
|
|
7
|
+
this.intervalMs = intervalMs;
|
|
8
|
+
}
|
|
9
|
+
start() {
|
|
10
|
+
void this.tick();
|
|
11
|
+
this.intervalId = setInterval(() => void this.tick(), this.intervalMs);
|
|
12
|
+
}
|
|
13
|
+
stop() {
|
|
14
|
+
if (this.intervalId !== null) {
|
|
15
|
+
clearInterval(this.intervalId);
|
|
16
|
+
this.intervalId = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async tick() {
|
|
20
|
+
try {
|
|
21
|
+
await this.callback();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Swallow errors to keep polling alive
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DashboardState, LoopInfo, CircuitBreakerInfo, StoryProgress, AnalysisInfo, LogEntry, ExecutionProgress, SessionInfo } from "./types.js";
|
|
2
|
+
export declare function padRight(str: string, len: number): string;
|
|
3
|
+
export declare function progressBar(completed: number, total: number, width: number): string;
|
|
4
|
+
export declare function formatSessionAge(createdAt: string): string;
|
|
5
|
+
export declare function formatStatus(status: string): string;
|
|
6
|
+
export declare function formatCBState(state: string): string;
|
|
7
|
+
export declare function box(title: string, lines: string[], cols: number): string;
|
|
8
|
+
export declare function renderHeader(cols: number): string;
|
|
9
|
+
export declare function renderLoopPanel(loop: LoopInfo | null, execution: ExecutionProgress | null, session: SessionInfo | null, cols: number): string;
|
|
10
|
+
export declare function renderCircuitBreakerPanel(cb: CircuitBreakerInfo | null, cols: number): string;
|
|
11
|
+
export declare function renderStoriesPanel(stories: StoryProgress | null, cols: number): string;
|
|
12
|
+
export declare function renderSideBySide(left: string, right: string, cols: number): string;
|
|
13
|
+
export declare function renderAnalysisPanel(analysis: AnalysisInfo | null, cols: number): string;
|
|
14
|
+
export declare function renderLogsPanel(logs: LogEntry[], cols: number): string;
|
|
15
|
+
export declare function renderFooter(lastUpdated: Date, cols: number): string;
|
|
16
|
+
export declare function renderDashboard(state: DashboardState, cols?: number): string;
|