ralph-lisa-loop 0.3.12 → 0.3.13

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/dist/cli.js CHANGED
@@ -98,6 +98,12 @@ switch (cmd) {
98
98
  case "notify":
99
99
  (0, commands_js_1.cmdNotify)(rest);
100
100
  break;
101
+ case "smoke-test":
102
+ (0, commands_js_1.cmdSmokeTest)(rest);
103
+ break;
104
+ case "smoke-check":
105
+ (0, commands_js_1.cmdSmokeCheck)(rest);
106
+ break;
101
107
  case "help":
102
108
  case "--help":
103
109
  case "-h":
@@ -183,6 +189,10 @@ function showHelp() {
183
189
  console.log(' ralph-lisa emergency-msg <agent> "msg" Send emergency message to agent pane');
184
190
  console.log(' ralph-lisa notify "message" Send notification via RL_NOTIFY_CMD');
185
191
  console.log("");
192
+ console.log("Smoke Testing:");
193
+ console.log(" ralph-lisa smoke-test Run smoke test (uses RL_SMOKE_CMD)");
194
+ console.log(" ralph-lisa smoke-check Check smoke test environment");
195
+ console.log("");
186
196
  console.log("Diagnostics:");
187
197
  console.log(" ralph-lisa state-dir Show state directory resolution");
188
198
  console.log(" ralph-lisa state-dir /path Set state directory (tmux env)");
@@ -58,4 +58,14 @@ export declare function cmdAddContext(args: string[]): void;
58
58
  export declare function cmdDoctor(args: string[]): void;
59
59
  export declare function cmdEmergencyMsg(args: string[]): void;
60
60
  export declare function cmdNotify(args: string[]): void;
61
+ /**
62
+ * Run smoke test command and persist results.
63
+ * Called automatically during step transition or manually via `ralph-lisa smoke-test`.
64
+ */
65
+ export declare function runSmokeTest(dir: string): {
66
+ passed: boolean;
67
+ output: string;
68
+ };
69
+ export declare function cmdSmokeTest(_args: string[]): void;
70
+ export declare function cmdSmokeCheck(_args: string[]): void;
61
71
  export {};
package/dist/commands.js CHANGED
@@ -69,6 +69,9 @@ exports.cmdAddContext = cmdAddContext;
69
69
  exports.cmdDoctor = cmdDoctor;
70
70
  exports.cmdEmergencyMsg = cmdEmergencyMsg;
71
71
  exports.cmdNotify = cmdNotify;
72
+ exports.runSmokeTest = runSmokeTest;
73
+ exports.cmdSmokeTest = cmdSmokeTest;
74
+ exports.cmdSmokeCheck = cmdSmokeCheck;
72
75
  const fs = __importStar(require("node:fs"));
73
76
  const path = __importStar(require("node:path"));
74
77
  const crypto = __importStar(require("node:crypto"));
@@ -236,6 +239,7 @@ function cmdInit(args) {
236
239
  (0, state_js_1.writeFile)(path.join(dir, "round.txt"), "1");
237
240
  (0, state_js_1.writeFile)(path.join(dir, "step.txt"), "planning");
238
241
  (0, state_js_1.writeFile)(path.join(dir, "turn.txt"), "ralph");
242
+ (0, state_js_1.writeFile)(path.join(dir, ".project_root"), process.cwd()); // persist project root for smoke-test cwd
239
243
  (0, state_js_1.writeFile)(path.join(dir, "last_action.txt"), "(No action yet)");
240
244
  (0, state_js_1.writeFile)(path.join(dir, "plan.md"), "# Plan\n\n(To be drafted by Ralph and reviewed by Lisa)\n");
241
245
  (0, state_js_1.writeFile)(path.join(dir, "work.md"), "# Ralph Work\n\n(Waiting for Ralph to submit)\n");
@@ -775,6 +779,26 @@ function cmdStep(args) {
775
779
  process.exit(1);
776
780
  }
777
781
  }
782
+ // Auto smoke test: run if current step had CODE/FIX submissions (step50)
783
+ if (process.env.RL_SMOKE_AUTO !== "false") {
784
+ const history = (0, state_js_1.readFile)(path.join(dir, "history.md"));
785
+ const currentStep = (0, state_js_1.getStep)();
786
+ const escaped = currentStep.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
787
+ const stepBlockRe = new RegExp(`^# Step: ${escaped}\\n\\nStarted:`, "gm");
788
+ let lastMatch = null;
789
+ let m;
790
+ while ((m = stepBlockRe.exec(history)) !== null) {
791
+ lastMatch = m;
792
+ }
793
+ const stepSection = lastMatch ? history.slice(lastMatch.index) : "";
794
+ if (/\[CODE\]|\[FIX\]/.test(stepSection)) {
795
+ console.log(`[Smoke] Development step detected (CODE/FIX found). Running smoke test...`);
796
+ runSmokeTest(dir);
797
+ }
798
+ else {
799
+ console.log(`[Smoke] Planning step (no CODE/FIX). Skipping smoke test.`);
800
+ }
801
+ }
778
802
  (0, state_js_1.setStep)(stepName);
779
803
  (0, state_js_1.setRound)(1);
780
804
  (0, state_js_1.setTurn)("ralph");
@@ -3344,3 +3368,98 @@ function cmdNotify(args) {
3344
3368
  notifyUser(message);
3345
3369
  console.log(`Notification sent: ${message}`);
3346
3370
  }
3371
+ // ─── smoke test ───────────────────────────────────
3372
+ /**
3373
+ * Run smoke test command and persist results.
3374
+ * Called automatically during step transition or manually via `ralph-lisa smoke-test`.
3375
+ */
3376
+ function runSmokeTest(dir) {
3377
+ const cmd = process.env.RL_SMOKE_CMD;
3378
+ if (!cmd) {
3379
+ console.log("[Smoke] No RL_SMOKE_CMD configured. Skipping smoke test.");
3380
+ return { passed: true, output: "Skipped: RL_SMOKE_CMD not configured" };
3381
+ }
3382
+ const debug = process.env.RL_SMOKE_DEBUG === "true";
3383
+ console.log(`[Smoke] Running: ${cmd}`);
3384
+ const ts = new Date().toISOString();
3385
+ const startTime = Date.now();
3386
+ let exitCode = 0;
3387
+ let output = "";
3388
+ try {
3389
+ output = (0, node_child_process_1.execSync)(cmd, {
3390
+ cwd: (0, state_js_1.readFile)(path.join(dir, ".project_root")).trim() || (0, state_js_1.findProjectRoot)() || process.cwd(), // persisted root > upward search > cwd
3391
+ encoding: "utf-8",
3392
+ timeout: 300000, // 5 min
3393
+ stdio: ["pipe", "pipe", "pipe"],
3394
+ });
3395
+ }
3396
+ catch (e) {
3397
+ exitCode = e.status || 1;
3398
+ output = (e.stdout || "") + "\n" + (e.stderr || "");
3399
+ }
3400
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
3401
+ const passed = exitCode === 0;
3402
+ // Persist results (append)
3403
+ const resultsFile = path.join(dir, "smoke-results.md");
3404
+ const last50 = output.split("\n").slice(-50).join("\n");
3405
+ const entry = `\n## Run: ${ts}\n- Command: ${cmd}\n- Exit code: ${exitCode}\n- Duration: ${duration}s\n- Result: ${passed ? "PASSED" : "FAILED"}\n\n### Output (last 50 lines)\n\`\`\`\n${last50}\n\`\`\`\n`;
3406
+ // Create header if file doesn't exist
3407
+ if (!fs.existsSync(resultsFile)) {
3408
+ fs.writeFileSync(resultsFile, "# Smoke Test Results\n");
3409
+ }
3410
+ fs.appendFileSync(resultsFile, entry);
3411
+ // Debug log (full output, overwrite)
3412
+ if (debug) {
3413
+ const debugFile = path.join(dir, "smoke-debug.log");
3414
+ fs.writeFileSync(debugFile, `# Smoke Debug Log\n# ${ts}\n# Command: ${cmd}\n# Exit code: ${exitCode}\n\n${output}`);
3415
+ console.log(`[Smoke] Debug log: ${debugFile}`);
3416
+ }
3417
+ if (passed) {
3418
+ console.log(`[Smoke] PASSED (${duration}s)`);
3419
+ }
3420
+ else {
3421
+ console.log(`[Smoke] FAILED (exit code ${exitCode}, ${duration}s)`);
3422
+ console.log("[Smoke] Results saved to .dual-agent/smoke-results.md");
3423
+ }
3424
+ return { passed, output: last50 };
3425
+ }
3426
+ function cmdSmokeTest(_args) {
3427
+ const dir = (0, state_js_1.stateDir)();
3428
+ const result = runSmokeTest(dir);
3429
+ process.exit(result.passed ? 0 : 1);
3430
+ }
3431
+ function cmdSmokeCheck(_args) {
3432
+ const cmd = process.env.RL_SMOKE_CMD;
3433
+ console.log(line());
3434
+ console.log("Smoke Test Environment Check");
3435
+ console.log(line());
3436
+ if (!cmd) {
3437
+ console.log(" RL_SMOKE_CMD: (not set)");
3438
+ console.log("");
3439
+ console.log(" Configure your project's smoke test command:");
3440
+ console.log(' export RL_SMOKE_CMD="npm run test:e2e"');
3441
+ console.log(' export RL_SMOKE_CMD="pytest tests/smoke/"');
3442
+ console.log(' export RL_SMOKE_CMD="flutter test integration_test/"');
3443
+ console.log("");
3444
+ console.log(" Then run: ralph-lisa smoke-check");
3445
+ process.exit(1);
3446
+ }
3447
+ console.log(` RL_SMOKE_CMD: ${cmd}`);
3448
+ // Check base command exists
3449
+ const baseCmd = cmd.split(/\s+/)[0];
3450
+ try {
3451
+ (0, node_child_process_1.execSync)(`command -v "${baseCmd}" 2>/dev/null`, { stdio: "pipe" });
3452
+ console.log(` Base command '${baseCmd}': OK`);
3453
+ }
3454
+ catch {
3455
+ console.log(` Base command '${baseCmd}': NOT FOUND`);
3456
+ console.log("");
3457
+ console.log(` Install '${baseCmd}' or adjust RL_SMOKE_CMD.`);
3458
+ process.exit(1);
3459
+ }
3460
+ console.log(` RL_SMOKE_AUTO: ${process.env.RL_SMOKE_AUTO || "true"} (auto-trigger on step transition)`);
3461
+ console.log(` RL_SMOKE_DEBUG: ${process.env.RL_SMOKE_DEBUG || "false"} (full output capture)`);
3462
+ console.log("");
3463
+ console.log(` Tip: run the full command manually to verify: ${cmd}`);
3464
+ console.log(line());
3465
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ralph-lisa-loop",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
4
4
  "description": "Turn-based dual-agent collaboration: Ralph codes, Lisa reviews, consensus required.",
5
5
  "bin": {
6
6
  "ralph-lisa": "dist/cli.js"
@@ -9,6 +9,7 @@
9
9
  "scripts": {
10
10
  "build": "tsc",
11
11
  "test": "node --test dist/test/*.js",
12
+ "test:smoke": "node --test dist/test/smoke.test.js",
12
13
  "prepack": "npm run build"
13
14
  },
14
15
  "files": [