compounding-loop 0.0.0-stage → 0.2.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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -3
  3. package/bin/loop.js +2 -0
  4. package/dist/cli/checkLanes.js +60 -0
  5. package/dist/cli/checkLanes.js.map +1 -0
  6. package/dist/cli/dashboard.js +52 -0
  7. package/dist/cli/dashboard.js.map +1 -0
  8. package/dist/cli/doctor.js +54 -0
  9. package/dist/cli/doctor.js.map +1 -0
  10. package/dist/cli/gh.js +29 -0
  11. package/dist/cli/gh.js.map +1 -0
  12. package/dist/cli/index.js +228 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli/init.js +73 -0
  15. package/dist/cli/init.js.map +1 -0
  16. package/dist/cli/laneStatus.js +52 -0
  17. package/dist/cli/laneStatus.js.map +1 -0
  18. package/dist/cli/lanesInit.js +56 -0
  19. package/dist/cli/lanesInit.js.map +1 -0
  20. package/dist/cli/lock.js +42 -0
  21. package/dist/cli/lock.js.map +1 -0
  22. package/dist/cli/loops.js +92 -0
  23. package/dist/cli/loops.js.map +1 -0
  24. package/dist/cli/new.js +54 -0
  25. package/dist/cli/new.js.map +1 -0
  26. package/dist/cli/run.js +169 -0
  27. package/dist/cli/run.js.map +1 -0
  28. package/dist/core/blocked.js +42 -0
  29. package/dist/core/blocked.js.map +1 -0
  30. package/dist/core/brief.js +91 -0
  31. package/dist/core/brief.js.map +1 -0
  32. package/dist/core/doctor.js +49 -0
  33. package/dist/core/doctor.js.map +1 -0
  34. package/dist/core/gh.js +2 -0
  35. package/dist/core/gh.js.map +1 -0
  36. package/dist/core/init.js +59 -0
  37. package/dist/core/init.js.map +1 -0
  38. package/dist/core/laneStatus.js +82 -0
  39. package/dist/core/laneStatus.js.map +1 -0
  40. package/dist/core/lanes.js +139 -0
  41. package/dist/core/lanes.js.map +1 -0
  42. package/dist/core/lanesInit.js +143 -0
  43. package/dist/core/lanesInit.js.map +1 -0
  44. package/dist/core/repo.js +81 -0
  45. package/dist/core/repo.js.map +1 -0
  46. package/dist/core/rounds.js +50 -0
  47. package/dist/core/rounds.js.map +1 -0
  48. package/dist/core/runPrompt.js +31 -0
  49. package/dist/core/runPrompt.js.map +1 -0
  50. package/dist/core/status.js +21 -0
  51. package/dist/core/status.js.map +1 -0
  52. package/dist/core/table.js +43 -0
  53. package/dist/core/table.js.map +1 -0
  54. package/dist/core/template.js +29 -0
  55. package/dist/core/template.js.map +1 -0
  56. package/dist/core/types.js +3 -0
  57. package/dist/core/types.js.map +1 -0
  58. package/dist/server/data.js +50 -0
  59. package/dist/server/data.js.map +1 -0
  60. package/dist/server/events.js +106 -0
  61. package/dist/server/events.js.map +1 -0
  62. package/dist/server/five-sites.json +3071 -0
  63. package/dist/server/health.js +193 -0
  64. package/dist/server/health.js.map +1 -0
  65. package/dist/server/index.js +134 -0
  66. package/dist/server/index.js.map +1 -0
  67. package/dist/server/loops.js +145 -0
  68. package/dist/server/loops.js.map +1 -0
  69. package/dist/server/settings.js +25 -0
  70. package/dist/server/settings.js.map +1 -0
  71. package/dist/server/static-export.js +28 -0
  72. package/dist/server/static-export.js.map +1 -0
  73. package/dist/server/static.js +80 -0
  74. package/dist/server/static.js.map +1 -0
  75. package/dist/server/this-repo.json +371 -0
  76. package/dist/web/assets/index-B16QCSnY.css +2 -0
  77. package/dist/web/assets/index-CS82DKbF.js +49 -0
  78. package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  79. package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  80. package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  81. package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  82. package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  83. package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  84. package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  85. package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  86. package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  87. package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  88. package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  89. package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  90. package/dist/web/favicon.svg +1 -0
  91. package/dist/web/index.html +14 -0
  92. package/dist/web/logo.svg +1 -0
  93. package/method/COMPOUNDING_LOOP.md +603 -0
  94. package/method/example/K-0001.md +16 -0
  95. package/method/example/K-0002.md +16 -0
  96. package/method/example/K-0003.md +16 -0
  97. package/method/knowledge-index.md +5 -0
  98. package/method/operating-card.md +40 -0
  99. package/method/retro.md +10 -0
  100. package/method/reviewer-prompt.md +13 -0
  101. package/method/stuck-protocol.md +12 -0
  102. package/method/task-template.md +41 -0
  103. package/package.json +76 -4
  104. package/plugin/.claude-plugin/plugin.json +7 -0
  105. package/plugin/commands/handoff.md +10 -0
  106. package/plugin/commands/loop-init.md +13 -0
  107. package/plugin/commands/retro.md +11 -0
  108. package/plugin/commands/stuck.md +10 -0
  109. package/runners/github-actions/lanes.yml +103 -0
  110. package/runners/github-actions/loop.yml +51 -0
  111. package/runners/local/README.md +9 -0
  112. package/runners/local/crontab.txt +6 -0
  113. package/runners/local/launchd.plist +36 -0
  114. package/runners/local/task-scheduler.xml +32 -0
  115. package/runners/routine/README.md +56 -0
  116. package/runners/routine/control-room-prompt.md +16 -0
  117. package/runners/routine/lane-prompt.md +12 -0
  118. package/runners/routine/prompt.md +13 -0
  119. package/templates/chrome-extension/CLAUDE.md +18 -0
  120. package/templates/chrome-extension/IDEA.md +22 -0
  121. package/templates/chrome-extension/extension/background.js +5 -0
  122. package/templates/chrome-extension/extension/icons/icon128.png +0 -0
  123. package/templates/chrome-extension/extension/icons/icon16.png +0 -0
  124. package/templates/chrome-extension/extension/icons/icon48.png +0 -0
  125. package/templates/chrome-extension/extension/manifest.json +25 -0
  126. package/templates/chrome-extension/extension/popup.css +3 -0
  127. package/templates/chrome-extension/extension/popup.html +15 -0
  128. package/templates/chrome-extension/extension/popup.js +16 -0
  129. package/templates/chrome-extension/gitignore +6 -0
  130. package/templates/chrome-extension/package.json +12 -0
  131. package/templates/chrome-extension/playwright.config.js +22 -0
  132. package/templates/chrome-extension/scripts/check.mjs +51 -0
  133. package/templates/chrome-extension/scripts/test.mjs +59 -0
  134. package/templates/chrome-extension/tests/popup.spec.js +37 -0
  135. package/templates/lanes/README.md +23 -0
  136. package/templates/lanes/ci-check-lanes.yml +38 -0
  137. package/templates/lanes/claude-section.md +17 -0
  138. package/templates/lanes/control-room.md +3 -0
  139. package/templates/lanes/outbox.md +5 -0
  140. package/templates/lanes/task.md +36 -0
  141. package/templates/static-site/.ai/index.md +5 -0
  142. package/templates/static-site/.ai/log.md +3 -0
  143. package/templates/static-site/.ai/loop-prompt.md +10 -0
  144. package/templates/static-site/.ai/task.md +27 -0
  145. package/templates/static-site/.gitattributes +1 -0
  146. package/templates/static-site/CLAUDE.md +113 -0
  147. package/templates/static-site/IDEA.md +36 -0
  148. package/templates/static-site/gitignore +6 -0
  149. package/templates/static-site/netlify.toml +14 -0
  150. package/templates/static-site/package.json +14 -0
  151. package/templates/static-site/playwright.config.js +27 -0
  152. package/templates/static-site/scripts/check.mjs +188 -0
  153. package/templates/static-site/scripts/serve.mjs +44 -0
  154. package/templates/static-site/scripts/test.mjs +59 -0
  155. package/templates/static-site/site/index.html +23 -0
  156. package/templates/static-site/site/robots.txt +4 -0
  157. package/templates/static-site/site/sitemap.xml +4 -0
  158. package/templates/static-site/tests/pages.spec.js +21 -0
  159. package/templates/static-site/vercel.json +19 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { briefBlocker, planInit } from '../core/init.js';
5
+ /** Root of the installed package (or this checkout): two levels above `dist/cli` or `src/cli`. */
6
+ export const packageRoot = fileURLToPath(new URL('../../', import.meta.url));
7
+ const FALLBACK_INDEX = '# Knowledge index\n\nOne line per entry in `.ai/knowledge/`: `K-0001 · trigger (error text, file path or topic) · one-line lesson`.\n\n<!-- empty: the loop adds entries as it learns -->\n';
8
+ const FALLBACK_LOG = '# Log\n\nOne line per finished task: `date · task · runs used · repeat errors · result`.\n';
9
+ const FALLBACK_TASK = '# TASK: Build v1 of IDEA.md\n\nRun: 0 / 30\n\n## Contract\n- Goal:\n- Done when:\n- Constraints:\n- Out of scope:\n\n## Handoff\n(each run ends by writing: where it stopped, the exact next step, anything half-done)\n';
10
+ async function readIfExists(file) {
11
+ try {
12
+ return await readFile(file, 'utf8');
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ /** Loads the method files `loop init` installs from the package's `method/` folder. */
19
+ export async function loadKit(root = packageRoot) {
20
+ const missing = [];
21
+ const read = async (rel, fallback) => {
22
+ const text = await readIfExists(path.join(root, rel));
23
+ if (text === undefined)
24
+ missing.push(rel);
25
+ return text ?? fallback;
26
+ };
27
+ const card = await read('method/operating-card.md');
28
+ const method = await read('method/COMPOUNDING_LOOP.md');
29
+ const task = await read('method/task-template.md', FALLBACK_TASK);
30
+ const index = await read('method/knowledge-index.md', FALLBACK_INDEX);
31
+ const files = [
32
+ { dest: '.ai/index.md', content: index ?? FALLBACK_INDEX, keep: true },
33
+ { dest: '.ai/log.md', content: FALLBACK_LOG, keep: true },
34
+ { dest: '.ai/task.md', content: task ?? FALLBACK_TASK, keep: true },
35
+ { dest: '.ai/knowledge/.gitkeep', content: '', keep: true },
36
+ { dest: '.ai/plans/.gitkeep', content: '', keep: true },
37
+ ];
38
+ if (method !== undefined)
39
+ files.push({ dest: '.ai/method.md', content: method });
40
+ return { files, card, missing };
41
+ }
42
+ export async function runInit(target, options, kit) {
43
+ const loadedKit = kit ?? (await loadKit());
44
+ const loaded = options.skipTask ? { ...loadedKit, files: loadedKit.files.filter((f) => f.dest !== '.ai/task.md') } : loadedKit;
45
+ const brief = await readIfExists(path.join(target, 'IDEA.md'));
46
+ const blocker = options.force ? undefined : briefBlocker(brief);
47
+ if (blocker)
48
+ return { actions: [], missing: loaded.missing, refused: blocker };
49
+ const existing = new Map();
50
+ for (const dest of [...loaded.files.map((f) => f.dest), 'CLAUDE.md', 'AGENTS.md']) {
51
+ const text = await readIfExists(path.join(target, ...dest.split('/')));
52
+ if (text !== undefined)
53
+ existing.set(dest, text);
54
+ }
55
+ let actions;
56
+ try {
57
+ actions = planInit({ kit: loaded.files, card: loaded.card, existing, force: options.force });
58
+ }
59
+ catch (error) {
60
+ return { actions: [], missing: loaded.missing, refused: error instanceof Error ? error.message : String(error) };
61
+ }
62
+ if (!options.dryRun) {
63
+ for (const action of actions) {
64
+ if (action.content === undefined)
65
+ continue;
66
+ const file = path.join(target, ...action.dest.split('/'));
67
+ await mkdir(path.dirname(file), { recursive: true });
68
+ await writeFile(file, action.content);
69
+ }
70
+ }
71
+ return { actions, missing: loaded.missing };
72
+ }
73
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { readFile, stat } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import { deriveLaneState, stallMinutesFor, parseOutbox, summarizeOutboxes } from '../core/laneStatus.js';
6
+ import { parseLanesConfig } from '../core/lanes.js';
7
+ import { parseLockTime, parseRunCounter } from '../core/repo.js';
8
+ const execFileAsync = promisify(execFile);
9
+ const read = (file) => readFile(file, 'utf8').catch(() => undefined);
10
+ const exists = (file) => stat(file).then(() => true, () => false);
11
+ async function lastCommitOf(dir, lane) {
12
+ try {
13
+ // Lane names are validated to [a-z0-9-], so they are safe inside the regex. Round 1 commits have only the `<lane>: ` subject prefix.
14
+ const { stdout } = await execFileAsync('git', ['log', '-1', `--grep=^Lane: ${lane}$`, `--grep=^${lane}: `, '--format=%H%x1f%s%x1f%cI'], {
15
+ cwd: dir,
16
+ env: { ...process.env, GIT_CEILING_DIRECTORIES: path.dirname(path.resolve(dir)) },
17
+ });
18
+ const [sha, subject, at] = stdout.trim().split('\u001f');
19
+ return sha && subject !== undefined && at ? { sha, subject, at: new Date(at).toISOString() } : undefined;
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ }
25
+ /** One status per lane in `.ai/lanes.json`; undefined when the folder is not a laned loop. */
26
+ export async function readLaneStatuses(dir, now = new Date()) {
27
+ const text = await read(path.join(dir, '.ai', 'lanes.json'));
28
+ const config = text === undefined ? undefined : parseLanesConfig(text).config;
29
+ if (!config)
30
+ return undefined;
31
+ const outboxes = new Map();
32
+ const partial = await Promise.all(config.lanes.map(async ({ name, cron }) => {
33
+ const laneDir = path.join(dir, '.ai', 'lanes', name);
34
+ outboxes.set(name, parseOutbox(name, (await read(path.join(laneDir, 'outbox.md'))) ?? ''));
35
+ const lockText = await read(path.join(laneDir, 'session.lock'));
36
+ const lockAt = lockText === undefined ? undefined : parseLockTime(lockText);
37
+ const lastCommit = await lastCommitOf(dir, name);
38
+ const counter = parseRunCounter((await read(path.join(laneDir, 'task.md'))) ?? '');
39
+ const state = deriveLaneState({
40
+ hasDone: (await exists(path.join(laneDir, 'DONE.md'))) || (await exists(path.join(dir, 'DONE.md'))),
41
+ hasBlocked: (await exists(path.join(laneDir, 'BLOCKED.md'))) || (await exists(path.join(dir, 'BLOCKED.md'))),
42
+ stallMinutes: stallMinutesFor(cron),
43
+ ...(lockAt && { lockAt }),
44
+ ...(lastCommit && { lastCommitAt: new Date(lastCommit.at) }),
45
+ }, now);
46
+ return { name, state, run: counter?.run ?? 0, limit: counter?.limit ?? 0, ...(lastCommit && { lastCommit }), locked: lockAt !== undefined && state === 'building' };
47
+ }));
48
+ const commitTimes = new Map(partial.flatMap((l) => (l.lastCommit ? [[l.name, l.lastCommit.at.slice(0, 16) + ':00.000Z']] : [])));
49
+ const { unanswered, waitingOn } = summarizeOutboxes(outboxes, commitTimes);
50
+ return partial.map((lane) => ({ ...lane, ...(waitingOn.has(lane.name) && { waitingOn: waitingOn.get(lane.name) }), unanswered: unanswered.get(lane.name) ?? [] }));
51
+ }
52
+ //# sourceMappingURL=laneStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"laneStatus.js","sourceRoot":"","sources":["../../src/cli/laneStatus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { parseLanesConfig } from '../core/lanes.js';
4
+ import { addLanes, FALLBACK_LANE_TEMPLATES, planLanes, staggeredCron } from '../core/lanesInit.js';
5
+ import { packageRoot } from './init.js';
6
+ const readIfExists = (file) => readFile(file, 'utf8').catch(() => undefined);
7
+ /** Reads `templates/lanes/{task,outbox,control-room,claude-section}.md` from the package, with built-in fallbacks. */
8
+ export async function loadLaneTemplates(root = packageRoot) {
9
+ const missing = [];
10
+ const read = async (name, fallback) => {
11
+ const text = await readIfExists(path.join(root, 'templates', 'lanes', name));
12
+ if (text === undefined)
13
+ missing.push(`templates/lanes/${name}`);
14
+ return text ?? fallback;
15
+ };
16
+ const templates = {
17
+ task: await read('task.md', FALLBACK_LANE_TEMPLATES.task),
18
+ outbox: await read('outbox.md', FALLBACK_LANE_TEMPLATES.outbox),
19
+ controlRoom: await read('control-room.md', FALLBACK_LANE_TEMPLATES.controlRoom),
20
+ claudeSection: await read('claude-section.md', FALLBACK_LANE_TEMPLATES.claudeSection),
21
+ };
22
+ return { templates, missing };
23
+ }
24
+ /** Adds lanes to the repo at `target` (creating `.ai/lanes.json` if needed) and writes their files. */
25
+ export async function runAddLanes(target, additions, options, root = packageRoot) {
26
+ const configText = await readIfExists(path.join(target, '.ai', 'lanes.json'));
27
+ let existing;
28
+ if (configText !== undefined) {
29
+ const parsed = parseLanesConfig(configText);
30
+ if (!parsed.config)
31
+ throw new Error(`.ai/lanes.json is invalid, so lanes cannot be added:\n- ${parsed.errors.join('\n- ')}`);
32
+ existing = parsed.config;
33
+ }
34
+ const fresh = options.existingOk ? additions.filter((l) => !existing?.lanes.some((e) => e.name === l.name)) : additions;
35
+ const config = addLanes(existing, fresh.map((l, i) => ({ ...l, cron: l.cron ?? staggeredCron((existing?.lanes.length ?? 0) + i) })));
36
+ const { templates, missing } = await loadLaneTemplates(root);
37
+ const current = new Map();
38
+ const dests = ['.ai/lanes.json', '.ai/control-room.md', 'CLAUDE.md', 'AGENTS.md', ...additions.flatMap((l) => [`.ai/lanes/${l.name}/task.md`, `.ai/lanes/${l.name}/outbox.md`])];
39
+ for (const dest of dests) {
40
+ const text = await readIfExists(path.join(target, ...dest.split('/')));
41
+ if (text !== undefined)
42
+ current.set(dest, text);
43
+ }
44
+ const actions = planLanes({ config, newLanes: additions.map((l) => l.name), templates, existing: current, force: options.force });
45
+ if (!options.dryRun) {
46
+ for (const action of actions) {
47
+ if (action.content === undefined)
48
+ continue;
49
+ const file = path.join(target, ...action.dest.split('/'));
50
+ await mkdir(path.dirname(file), { recursive: true });
51
+ await writeFile(file, action.content);
52
+ }
53
+ }
54
+ return { actions, missing };
55
+ }
56
+ //# sourceMappingURL=lanesInit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lanesInit.js","sourceRoot":"","sources":["../../src/cli/lanesInit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { rmSync } from 'node:fs';
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ /**
5
+ * Holds a session lock file (one UTC timestamp line) while `fn` runs, and removes it when `fn` succeeds, throws,
6
+ * or the process gets Ctrl-C / SIGTERM, so a crashed round never blocks the next one for 90 minutes.
7
+ * On a signal the lock is kept until `fn` settles (the terminal also signals the `claude` child, which needs a moment
8
+ * to stop) or `graceMs` passes, so a second session cannot start while the first is still writing.
9
+ */
10
+ export async function withSessionLock(lockFile, fn, options = {}) {
11
+ const exit = options.exit ?? ((code) => process.exit(code));
12
+ await mkdir(path.dirname(lockFile), { recursive: true });
13
+ await writeFile(lockFile, `${(options.now ?? (() => new Date()))().toISOString()}\n`);
14
+ const release = () => rmSync(lockFile, { force: true });
15
+ const graceMs = options.graceMs ?? 10_000;
16
+ let finished;
17
+ const settled = new Promise((resolve) => { finished = resolve; });
18
+ let signalled = false;
19
+ const onSignal = (code) => () => {
20
+ if (signalled) {
21
+ release();
22
+ exit(code);
23
+ return;
24
+ } // a second Ctrl-C means "now"
25
+ signalled = true;
26
+ const grace = new Promise((resolve) => setTimeout(resolve, graceMs).unref());
27
+ void Promise.race([settled, grace]).finally(() => { release(); exit(code); });
28
+ };
29
+ const handlers = { SIGINT: onSignal(130), SIGTERM: onSignal(143), SIGHUP: onSignal(129) };
30
+ for (const [signal, handler] of Object.entries(handlers))
31
+ process.on(signal, handler);
32
+ try {
33
+ return await fn();
34
+ }
35
+ finally {
36
+ finished();
37
+ for (const [signal, handler] of Object.entries(handlers))
38
+ process.off(signal, handler);
39
+ release();
40
+ }
41
+ }
42
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/cli/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAI,QAAgB,EAAE,EAAoB,EAAE,OAAO,GAAgB,EAAE;IACxG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC;IAC1C,IAAI,QAAqB,CAAC;IAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,GAAG,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,EAAE;QACtC,IAAI,SAAS,EAAE,CAAC;YAAC,OAAO,EAAE,CAAC;YAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC,CAAC,8BAA8B;QAChF,SAAS,GAAG,IAAI,CAAC;QACjB,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7E,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChF,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAW,CAAC;IACnG,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,CAAC,EAAE,CAAC,MAAwB,EAAE,OAAO,CAAC,CAAC;IACxG,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,QAAQ,EAAE,CAAC;QACX,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,MAAwB,EAAE,OAAO,CAAC,CAAC;QACzG,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,92 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { readdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import { addAnswerToTask, BUDGET_EXTENSION, extendRunBudget, formatAnswer, isBudgetBlock, parseBlocked } from '../core/blocked.js';
6
+ import { readLoopFacts, readRunCounter } from '../core/repo.js';
7
+ import { buildNextRoundTask, extractNextItems, nextDoneFileName } from '../core/rounds.js';
8
+ import { formatFleet, formatLanes } from '../core/table.js';
9
+ import { readLaneStatuses } from './laneStatus.js';
10
+ const execFileAsync = promisify(execFile);
11
+ const git = (cwd, ...args) => execFileAsync('git', args, { cwd });
12
+ /** `loop status`: one row per folder. */
13
+ export async function runStatus(dirs, now = new Date()) {
14
+ const rows = [];
15
+ for (const dir of dirs) {
16
+ const run = await readRunCounter(dir);
17
+ rows.push({ name: path.basename(path.resolve(dir)), facts: await readLoopFacts(dir), ...(run && { run }) });
18
+ }
19
+ const blocks = [formatFleet(rows, now)];
20
+ for (const dir of dirs) {
21
+ const lanes = await readLaneStatuses(dir, now);
22
+ if (lanes)
23
+ blocks.push(`Lanes in ${path.basename(path.resolve(dir))}:\n${formatLanes(lanes, now)}`);
24
+ }
25
+ return blocks.join('\n\n');
26
+ }
27
+ /** `loop next-round`: archive DONE.md as `.ai/done-vN.md` and write the next task.md from its "next 10" list. */
28
+ export async function runNextRound(dir, options) {
29
+ const donePath = path.join(dir, 'DONE.md');
30
+ const doneText = await readFile(donePath, 'utf8').catch(() => undefined);
31
+ if (doneText === undefined)
32
+ throw new Error('No DONE.md here: this loop has not finished a round.');
33
+ const items = extractNextItems(doneText);
34
+ if (items.length === 0)
35
+ throw new Error('DONE.md has no "next 10" list to turn into the next round.');
36
+ const aiDir = path.join(dir, '.ai');
37
+ const archivedAs = nextDoneFileName(await readdir(aiDir).catch(() => []));
38
+ const round = Number(/\d+/.exec(archivedAs)?.[0] ?? '1') + 1;
39
+ const previous = await readRunCounter(dir);
40
+ if (!options.dryRun) {
41
+ await rename(donePath, path.join(aiDir, archivedAs));
42
+ await writeFile(path.join(aiDir, 'task.md'), buildNextRoundTask(items, round, previous?.limit));
43
+ await rm(path.join(aiDir, 'session.lock'), { force: true });
44
+ }
45
+ return { archivedAs, units: items.length };
46
+ }
47
+ /** `loop answer`: record the owner's answer in task.md, remove BLOCKED.md, and commit so the next run restarts. */
48
+ export async function runAnswer(dir, answer, options) {
49
+ const blockedPath = path.join(dir, 'BLOCKED.md');
50
+ const text = await readFile(blockedPath, 'utf8').catch(() => undefined);
51
+ if (text === undefined)
52
+ throw new Error('No BLOCKED.md here: nothing to answer.');
53
+ const note = parseBlocked(text);
54
+ const budget = isBudgetBlock(text);
55
+ const acceptedGuess = note.bestGuess || (budget ? `Continue: ${BUDGET_EXTENSION} more runs.` : '');
56
+ const finalAnswer = answer === 'accept' ? acceptedGuess : answer;
57
+ if (!finalAnswer.trim())
58
+ throw new Error('BLOCKED.md has no best guess to accept; pass an answer instead.');
59
+ if (!options.dryRun) {
60
+ // Fail before changing any file when this is not a git repo.
61
+ await git(dir, 'rev-parse', '--git-dir').catch(() => {
62
+ throw new Error('This folder is not a git repository, so the answer cannot be committed.');
63
+ });
64
+ const taskPath = path.join(dir, '.ai', 'task.md');
65
+ const task = await readFile(taskPath, 'utf8').catch(() => '');
66
+ const date = (options.now ?? new Date()).toISOString().slice(0, 10);
67
+ const withAnswer = addAnswerToTask(task, formatAnswer(note, finalAnswer, date));
68
+ // Without a higher limit the next run would hit the budget and block again at once.
69
+ await writeFile(taskPath, budget ? extendRunBudget(withAnswer) : withAnswer);
70
+ await rm(blockedPath);
71
+ // Only our two files: the user's other staged or unstaged work stays out of this commit.
72
+ const blockedTracked = (await git(dir, 'ls-files', '--', 'BLOCKED.md')).stdout.trim() !== '';
73
+ const paths = ['.ai/task.md', ...(blockedTracked ? ['BLOCKED.md'] : [])];
74
+ await git(dir, 'add', '-A', '--', ...paths);
75
+ await git(dir, '-c', 'user.name=Compounding Loop', '-c', 'user.email=loop@users.noreply.github.com', 'commit', '-qm', 'Answer BLOCKED.md and restart the loop', '--', ...paths);
76
+ }
77
+ const result = { question: note.question, bestGuess: note.bestGuess, answer: finalAnswer };
78
+ if (options.push && !options.dryRun) {
79
+ try {
80
+ await git(dir, 'push');
81
+ result.pushed = true;
82
+ }
83
+ catch (error) {
84
+ result.pushed = false;
85
+ const output = `${error.stderr ?? ''}\n${error instanceof Error ? error.message : String(error)}`;
86
+ const lines = output.split('\n').map((l) => l.trim()).filter(Boolean);
87
+ result.pushError = lines.find((l) => /rejected|fatal|error:/i.test(l)) ?? lines[0] ?? 'git push failed';
88
+ }
89
+ }
90
+ return result;
91
+ }
92
+ //# sourceMappingURL=loops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loops.js","sourceRoot":"","sources":["../../src/cli/loops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { cp, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fillPlaceholders, hostLabel, isTextFile, TEMPLATE_IGNORED, templateTargetName, validateProjectName } from '../core/template.js';
4
+ import { packageRoot, runInit } from './init.js';
5
+ /** Project templates for `loop new`; `lanes` holds the files `loop init --lanes` writes, not a template. */
6
+ export async function listTemplates(root = packageRoot) {
7
+ const entries = await readdir(path.join(root, 'templates'), { withFileTypes: true }).catch(() => []);
8
+ return entries.filter((e) => e.isDirectory() && e.name !== 'lanes').map((e) => e.name).sort();
9
+ }
10
+ /** Copies `source` to `dest`, filling `{{placeholders}}` in text files. */
11
+ async function copyTemplate(source, dest, vars) {
12
+ await mkdir(dest, { recursive: true });
13
+ for (const entry of await readdir(source, { withFileTypes: true })) {
14
+ if (TEMPLATE_IGNORED.has(entry.name))
15
+ continue;
16
+ const from = path.join(source, entry.name);
17
+ const to = path.join(dest, templateTargetName(entry.name));
18
+ if (entry.isDirectory())
19
+ await copyTemplate(from, to, vars);
20
+ else if (isTextFile(templateTargetName(entry.name)))
21
+ await writeFile(to, fillPlaceholders(await readFile(from, 'utf8'), vars));
22
+ else
23
+ await cp(from, to);
24
+ }
25
+ }
26
+ export async function runNew(template, name, options) {
27
+ const root = options.root ?? packageRoot;
28
+ const nameError = validateProjectName(name);
29
+ if (nameError)
30
+ throw new Error(nameError);
31
+ const available = await listTemplates(root);
32
+ if (!available.includes(template)) {
33
+ throw new Error(`Unknown template "${template}". Available: ${available.join(', ') || 'none'}.`);
34
+ }
35
+ const dir = path.join(options.parentDir, name);
36
+ if (await stat(dir).then(() => true, () => false))
37
+ throw new Error(`${dir} already exists; pick another name.`);
38
+ await copyTemplate(path.join(root, 'templates', template), dir, { name, template, host: hostLabel(name) });
39
+ // The brief is written after creation (dashboard wizard or by hand), so the brief gate is skipped here.
40
+ const init = await runInit(dir, { force: true, dryRun: false });
41
+ if (init.refused)
42
+ throw new Error(init.refused);
43
+ if (options.dryRun)
44
+ return { dir };
45
+ try {
46
+ const repoUrl = await options.gh.createRepoFromFolder(dir, name, { private: !options.publicRepo });
47
+ return { dir, repoUrl };
48
+ }
49
+ catch (error) {
50
+ // Keep the folder: it holds the user's work from here on, and deleting it would also lose any edits made meanwhile.
51
+ throw new Error(`Could not create the GitHub repo (is \`gh auth login\` done, and is the name free?). The folder was kept at ${dir}.\nTo finish later: cd ${name} && gh repo create ${name} --private --source . --push\n${error instanceof Error ? error.message : String(error)}`);
52
+ }
53
+ }
54
+ //# sourceMappingURL=new.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/cli/new.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { appendFile, mkdir, readFile, stat } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { parseLockTime } from '../core/repo.js';
5
+ import { LOCK_MINUTES } from '../core/status.js';
6
+ import { buildClaudeArgs, buildRunPrompt, DEFAULT_PROMPT } from '../core/runPrompt.js';
7
+ import { parseLanesConfig } from '../core/lanes.js';
8
+ import { packageRoot } from './init.js';
9
+ import { withSessionLock } from './lock.js';
10
+ const exists = (file) => stat(file).then(() => true, () => false);
11
+ /** First readable prompt: the repo's own, then the routine prompt in the repo, then the package's, then the built-in. */
12
+ export async function loadPromptText(dir, root = packageRoot, lane) {
13
+ const candidates = [
14
+ ...(lane ? [path.join(dir, '.ai', 'lanes', lane, 'prompt.md'), path.join(dir, 'runners', 'routine', 'lane-prompt.md'), path.join(root, 'runners', 'routine', 'lane-prompt.md')] : []),
15
+ path.join(dir, '.ai', 'loop-prompt.md'),
16
+ path.join(dir, 'runners', 'routine', 'prompt.md'),
17
+ path.join(root, 'runners', 'routine', 'prompt.md'),
18
+ ];
19
+ for (const [index, file] of candidates.entries()) {
20
+ const text = await readFile(file, 'utf8').catch(() => undefined);
21
+ if (text === undefined)
22
+ continue;
23
+ // The first three candidates are written for a lane; any other prompt is generic and needs the intro.
24
+ const forLane = lane !== undefined && index < 3;
25
+ return { text: lane && !forLane ? `${laneIntro(lane)}\n\n${text}` : text, source: file };
26
+ }
27
+ return { text: lane ? `${laneIntro(lane)}\n\n${DEFAULT_PROMPT}` : DEFAULT_PROMPT, source: 'built-in default' };
28
+ }
29
+ /** Used when no lane prompt file exists: points the generic round prompt at this lane's files. */
30
+ export function laneIntro(lane) {
31
+ return `You are the **${lane}** lane of a multi-lane loop. Your lane's task file is \`.ai/lanes/${lane}/task.md\` (wherever this prompt says \`.ai/task.md\`, use it), your outbox is \`.ai/lanes/${lane}/outbox.md\`, your lock is \`.ai/lanes/${lane}/session.lock\`, and your stop files are \`.ai/lanes/${lane}/DONE.md\` and \`BLOCKED.md\`. Edit only the paths \`${lane}\` owns in \`.ai/lanes.json\` (plus shared ones), and end every commit message with the trailer line \`Lane: ${lane}\`.`;
32
+ }
33
+ /** Fails early, with the valid names, when `--lane` is not in `.ai/lanes.json`. */
34
+ export async function assertKnownLane(dir, lane) {
35
+ const text = await readFile(path.join(dir, '.ai', 'lanes.json'), 'utf8').catch(() => undefined);
36
+ if (text === undefined)
37
+ throw new Error('No .ai/lanes.json here, so --lane has nothing to refer to. Run `loop init --lanes a,b,c` first, or drop --lane.');
38
+ const { config, errors } = parseLanesConfig(text);
39
+ if (!config)
40
+ throw new Error(`.ai/lanes.json is invalid:\n- ${errors.join('\n- ')}`);
41
+ if (!config.lanes.some((l) => l.name === lane))
42
+ throw new Error(`Unknown lane "${lane}". Lanes in .ai/lanes.json: ${config.lanes.map((l) => l.name).join(', ')}.`);
43
+ }
44
+ /**
45
+ * Finds an executable on PATH. Node cannot start `.cmd` shims without a shell (and we never build shell strings),
46
+ * so on Windows only `.exe` files count; a lone `claude.cmd` gets an explanatory error.
47
+ */
48
+ export async function resolveExecutable(name, platform = process.platform, pathEnv = process.env.PATH ?? '', has = exists) {
49
+ if (platform !== 'win32')
50
+ return name;
51
+ if (/\.(?:cmd|bat)$/i.test(name))
52
+ throw new Error(`Node cannot start ${name} safely. Point at a native .exe instead.`);
53
+ // A full path (e.g. process.execPath) needs no PATH search, and appending `.exe` would break it.
54
+ if (path.win32.isAbsolute(name))
55
+ return name;
56
+ const dirs = pathEnv.split(path.win32.delimiter).filter(Boolean);
57
+ for (const dir of dirs)
58
+ if (await has(path.win32.join(dir, `${name}.exe`)))
59
+ return path.win32.join(dir, `${name}.exe`);
60
+ for (const dir of dirs) {
61
+ if (await has(path.win32.join(dir, `${name}.cmd`))) {
62
+ throw new Error(`Found ${name}.cmd but Node cannot start it safely. Install the native ${name}.exe build of Claude Code, or run the round from a terminal with \`${name} -p\`.`);
63
+ }
64
+ }
65
+ throw new Error(`Could not find ${name}.exe on PATH. Is Claude Code installed?`);
66
+ }
67
+ /**
68
+ * Where an npm-installed `<name>.cmd` shim really points: the `bin` entry of the package installed next to it.
69
+ * Returns undefined when the layout is not the usual global one.
70
+ */
71
+ export async function shimEntry(shim, packageName, binName, read = (file) => readFile(file, 'utf8').catch(() => undefined)) {
72
+ const packageDir = path.win32.join(path.win32.dirname(shim), 'node_modules', ...packageName.split('/'));
73
+ const text = await read(path.win32.join(packageDir, 'package.json'));
74
+ if (text === undefined)
75
+ return undefined;
76
+ let bin;
77
+ try {
78
+ bin = JSON.parse(text).bin;
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ const rel = typeof bin === 'string' ? bin : bin && typeof bin === 'object' ? (bin[binName] ?? Object.values(bin).find((v) => typeof v === 'string')) : undefined;
84
+ return typeof rel === 'string' ? path.win32.join(packageDir, rel) : undefined;
85
+ }
86
+ /**
87
+ * Fallback for other layouts (pnpm and similar; Volta shims call its own launcher and are not covered): npm-style `.cmd` shims name their script as
88
+ * `"%dp0%\\path\\cli.js"` (or `%~dp0`), relative to the shim's own folder.
89
+ */
90
+ export async function shimScript(shim, read = (file) => readFile(file, 'utf8').catch(() => undefined)) {
91
+ const text = await read(shim);
92
+ const match = text === undefined ? null : /%(?:~dp0|dp0%)\\?([^"%\r\n]+\.(?:c|m)?js)"/i.exec(text);
93
+ return match?.[1] ? path.win32.join(path.win32.dirname(shim), match[1]) : undefined;
94
+ }
95
+ /**
96
+ * Like `resolveExecutable`, but also copes with the usual Windows install (`npm i -g @anthropic-ai/claude-code`),
97
+ * which only has `claude.cmd`: we run the package's own JS entry with this Node, no shell involved.
98
+ */
99
+ export async function resolveInvocation(name, args, platform = process.platform, pathEnv = process.env.PATH ?? '', has = exists, read, nodePath = process.execPath) {
100
+ try {
101
+ return { command: await resolveExecutable(name, platform, pathEnv, has), args };
102
+ }
103
+ catch (error) {
104
+ if (platform !== 'win32' || path.win32.isAbsolute(name))
105
+ throw error;
106
+ for (const dir of pathEnv.split(path.win32.delimiter).filter(Boolean)) {
107
+ const shim = path.win32.join(dir, `${name}.cmd`);
108
+ if (!(await has(shim)))
109
+ continue;
110
+ const entry = (await shimEntry(shim, name === 'claude' ? '@anthropic-ai/claude-code' : name, name, read)) ?? (await shimScript(shim, read));
111
+ if (!entry)
112
+ throw error;
113
+ return /\.exe$/i.test(entry) ? { command: entry, args } : { command: nodePath, args: [entry, ...args] };
114
+ }
115
+ throw error;
116
+ }
117
+ }
118
+ const realSpawn = async (command, rawArgs, cwd) => {
119
+ const { command: executable, args } = await resolveInvocation(command, rawArgs);
120
+ return new Promise((resolve, reject) => {
121
+ const child = spawn(executable, args, { cwd, stdio: 'inherit' });
122
+ child.on('error', (error) => reject(new Error(`Could not start \`${command}\`: ${error.message}. Is Claude Code installed and on your PATH?`)));
123
+ child.on('close', (code) => resolve(code ?? 1));
124
+ });
125
+ };
126
+ /** Minutes left on a fresh `.ai/session.lock`, or undefined when no session holds the repo. */
127
+ export function lockMinutesLeft(lockText, now) {
128
+ const at = lockText === undefined ? undefined : parseLockTime(lockText);
129
+ if (!at)
130
+ return undefined;
131
+ const left = LOCK_MINUTES - (now.getTime() - at.getTime()) / 60_000;
132
+ return left > 0 ? Math.ceil(left) : undefined;
133
+ }
134
+ /** One `{"startedAt","endedAt"}` line per round in `.ai/runs.jsonl`; the dashboard's health checks read it. */
135
+ export async function recordRun(dir, startedAt, endedAt) {
136
+ await mkdir(path.join(dir, '.ai'), { recursive: true });
137
+ await appendFile(path.join(dir, '.ai', 'runs.jsonl'), `${JSON.stringify({ startedAt: startedAt.toISOString(), endedAt: endedAt.toISOString() })}\n`);
138
+ }
139
+ /** `loop run`: one build round now, using the same prompt the scheduler uses. */
140
+ export async function runRound(dir, options, spawner = realSpawn) {
141
+ const { lane } = options;
142
+ if (lane)
143
+ await assertKnownLane(dir, lane);
144
+ const laneDir = lane ? path.join('.ai', 'lanes', lane) : '.ai';
145
+ const { text, source } = await loadPromptText(dir, packageRoot, lane);
146
+ const lockFile = path.join(dir, laneDir, 'session.lock');
147
+ const lockNote = `Note from \`loop run\`: it created \`${path.posix.join(...laneDir.split(path.sep), 'session.lock')}\` for this very session. That lock is yours, not another session's: ignore it in the lock step and do not delete it.`;
148
+ const prompt = `${buildRunPrompt(text, { name: path.basename(path.resolve(dir)), lane: lane ?? '' })}\n${lockNote}\n`;
149
+ const stopFiles = [...(lane ? [path.join(laneDir, 'DONE.md'), path.join(laneDir, 'BLOCKED.md')] : []), 'DONE.md', 'BLOCKED.md'];
150
+ for (const stop of stopFiles) {
151
+ if (await exists(path.join(dir, stop)))
152
+ return { skipped: `${stop} exists: nothing to do`, prompt, source };
153
+ }
154
+ const lockLeft = lockMinutesLeft(await readFile(lockFile, 'utf8').catch(() => undefined), new Date());
155
+ if (lockLeft !== undefined)
156
+ return { skipped: `another session holds ${path.join(laneDir, 'session.lock')} (about ${lockLeft} min left): nothing to do`, prompt, source };
157
+ if (options.dryRun)
158
+ return { prompt, source };
159
+ console.error(`Running one round in ${dir}${lane ? ` as lane ${lane}` : ''} (prompt: ${source})`);
160
+ const startedAt = new Date();
161
+ try {
162
+ const exitCode = await withSessionLock(lockFile, () => spawner('claude', buildClaudeArgs(prompt, { skipPermissions: options.skipPermissions, ...(options.model && { model: options.model }) }), dir));
163
+ return { prompt, source, exitCode };
164
+ }
165
+ finally {
166
+ await recordRun(dir, startedAt, new Date()); // a failed round still counts for the dashboard's fast-failure check
167
+ }
168
+ }
169
+ //# sourceMappingURL=run.js.map