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 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 50 slash commands (45 BMAD + 5 bmalph). Command delivery varies by platform:
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
- If something goes wrong, you can manually reset:
434
+ The simplest way to remove all bmalph files:
409
435
 
410
436
  ```bash
411
- # Remove bmalph directories (preserves your project code)
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 {};
@@ -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
- })();
@@ -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, hasExistingBmadDir, previewInstall, getBundledVersions, } from "../installer.js";
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 hasExistingBmadDir(projectDir)) {
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");
@@ -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 (nextAction) {
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,6 @@
1
+ interface WatchCommandOptions {
2
+ interval?: string;
3
+ projectDir: string;
4
+ }
5
+ export declare function watchCommand(options: WatchCommandOptions): Promise<void>;
6
+ export {};
@@ -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
+ }
@@ -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, validateArtifacts } from "./artifacts.js";
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";
@@ -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, validateArtifacts } from "./artifacts.js";
8
+ export { findArtifactsDir } from "./artifacts.js";
9
9
  // Context
10
10
  export { extractSection, extractProjectContext, generateProjectContextMd, generatePrompt, } from "./context.js";
11
11
  // Specs changelog
@@ -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>;
@@ -176,6 +176,7 @@ const BASH_STATUS_MAP = {
176
176
  stopped: "blocked",
177
177
  completed: "completed",
178
178
  success: "completed",
179
+ graceful_exit: "completed",
179
180
  };
180
181
  export function normalizeRalphStatus(data) {
181
182
  assertObject(data, "normalizeRalphStatus");
@@ -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,9 @@
1
+ export declare class FileWatcher {
2
+ private intervalId;
3
+ private readonly intervalMs;
4
+ private readonly callback;
5
+ constructor(callback: () => void | Promise<void>, intervalMs?: number);
6
+ start(): void;
7
+ stop(): void;
8
+ private tick;
9
+ }
@@ -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;