nightralph 0.0.22 → 0.0.24

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/index.js CHANGED
@@ -79,7 +79,6 @@ function setup(opts) {
79
79
  bundledSkillsDir,
80
80
  docsTemplatesDir,
81
81
  projectDir,
82
- claude,
83
82
  tracker
84
83
  } = opts;
85
84
  const agentsSkillsDir = join(
@@ -104,29 +103,27 @@ function setup(opts) {
104
103
  ` ${skill}/ (${files.join(", ")})`
105
104
  );
106
105
  }
107
- if (claude) {
108
- const claudeSkillsDir = join(
109
- projectDir,
110
- ".claude",
111
- "skills"
112
- );
113
- mkdirSync(claudeSkillsDir, { recursive: true });
114
- for (const skill of SKILL_DIRS) {
115
- const linkPath = join(claudeSkillsDir, skill);
116
- const target = join(
117
- "..",
118
- "..",
119
- ".agents",
120
- "skills",
121
- skill
122
- );
123
- if (existsSync(linkPath)) continue;
124
- symlinkSync(target, linkPath);
125
- }
126
- console.log(
127
- "\nCreated symlinks in .claude/skills/"
106
+ const claudeSkillsDir = join(
107
+ projectDir,
108
+ ".claude",
109
+ "skills"
110
+ );
111
+ mkdirSync(claudeSkillsDir, { recursive: true });
112
+ for (const skill of SKILL_DIRS) {
113
+ const linkPath = join(claudeSkillsDir, skill);
114
+ const target = join(
115
+ "..",
116
+ "..",
117
+ ".agents",
118
+ "skills",
119
+ skill
128
120
  );
121
+ if (existsSync(linkPath)) continue;
122
+ symlinkSync(target, linkPath);
129
123
  }
124
+ console.log(
125
+ "\nCreated symlinks in .claude/skills/"
126
+ );
130
127
  const docsAgentsDir = join(
131
128
  projectDir,
132
129
  "docs",
@@ -296,7 +293,7 @@ function createWorktree(opts) {
296
293
  return result;
297
294
  }
298
295
  __name(createWorktree, "createWorktree");
299
- async function removeWorktree(worktreePath, opts) {
296
+ async function removeWorktreeNow(worktreePath, opts) {
300
297
  const args = [
301
298
  "-C",
302
299
  worktreePath,
@@ -307,6 +304,15 @@ async function removeWorktree(worktreePath, opts) {
307
304
  args.push(worktreePath);
308
305
  await execFileAsync("git", args);
309
306
  }
307
+ __name(removeWorktreeNow, "removeWorktreeNow");
308
+ function removeWorktree(worktreePath, opts) {
309
+ const result = worktreeQueue.then(
310
+ () => removeWorktreeNow(worktreePath, opts)
311
+ );
312
+ worktreeQueue = result.catch(() => {
313
+ });
314
+ return result;
315
+ }
310
316
  __name(removeWorktree, "removeWorktree");
311
317
  function hasNewCommits(repoRoot, base, branch) {
312
318
  const stdout = execFileSync(
@@ -1025,6 +1031,9 @@ function runTestCmd(opts) {
1025
1031
  stdio: ["ignore", "pipe", "pipe"],
1026
1032
  detached: true
1027
1033
  });
1034
+ if (child.pid !== void 0 && opts.registry) {
1035
+ opts.registry.add(child.pid);
1036
+ }
1028
1037
  const chunks = [];
1029
1038
  child.stdout.on("data", (d) => chunks.push(String(d)));
1030
1039
  child.stderr.on("data", (d) => chunks.push(String(d)));
@@ -1043,6 +1052,9 @@ function runTestCmd(opts) {
1043
1052
  if (settled) return;
1044
1053
  settled = true;
1045
1054
  clearTimeout(timer);
1055
+ if (child.pid !== void 0 && opts.registry) {
1056
+ opts.registry.delete(child.pid);
1057
+ }
1046
1058
  chunks.push(String(err));
1047
1059
  resolve({
1048
1060
  exitCode: 1,
@@ -1053,6 +1065,9 @@ function runTestCmd(opts) {
1053
1065
  if (settled) return;
1054
1066
  settled = true;
1055
1067
  clearTimeout(timer);
1068
+ if (child.pid !== void 0 && opts.registry) {
1069
+ opts.registry.delete(child.pid);
1070
+ }
1056
1071
  resolve({
1057
1072
  exitCode: code ?? 1,
1058
1073
  output: chunks.join("")
@@ -1243,6 +1258,7 @@ function renderMergePrompt(specBody, ticket) {
1243
1258
  "3. Commit the resolved files.",
1244
1259
  "",
1245
1260
  "Do NOT re-implement the ticket from scratch.",
1261
+ "Do NOT read or modify files outside your current directory (the worktree).",
1246
1262
  "Do NOT run git push."
1247
1263
  ].join("\n");
1248
1264
  }
@@ -1624,6 +1640,7 @@ function spawnAgent(opts) {
1624
1640
  __name(finish, "finish");
1625
1641
  proc.on("exit", (code) => {
1626
1642
  graceTimer = setTimeout(() => {
1643
+ killProcessGroup(proc.pid);
1627
1644
  proc.stdout.destroy();
1628
1645
  proc.stderr.destroy();
1629
1646
  finish(code ?? 1);
@@ -1734,7 +1751,7 @@ Test command: ${opts.testCmd ?? "(none)"}`
1734
1751
  `Retries: ${opts.retries ?? DEFAULT_RETRIES}`
1735
1752
  );
1736
1753
  console.log(
1737
- `Retry delay: ${opts.retryDelay ?? DEFAULT_RETRY_DELAY}s`
1754
+ `Retry delay: ${opts.retryDelay ?? 0}s`
1738
1755
  );
1739
1756
  console.log(
1740
1757
  `
@@ -1747,7 +1764,8 @@ async function runTestGate(opts) {
1747
1764
  const result = await runTestCmd({
1748
1765
  cmd: opts.testCmd,
1749
1766
  cwd: opts.worktreePath,
1750
- timeout: opts.timeout
1767
+ timeout: opts.timeout,
1768
+ registry: opts.registry
1751
1769
  });
1752
1770
  const testLogPath = attemptLogPath(
1753
1771
  opts.logsDir,
@@ -1823,7 +1841,8 @@ async function respawnAndRetryMerge(opts) {
1823
1841
  worktreePath: opts.worktreePath,
1824
1842
  timeout: opts.timeout,
1825
1843
  logsDir: opts.logsDir,
1826
- log
1844
+ log,
1845
+ registry: runningAgents
1827
1846
  });
1828
1847
  if (!passed) {
1829
1848
  log(
@@ -1939,7 +1958,12 @@ async function runTicketAttempt(opts) {
1939
1958
  opts.baseBranch,
1940
1959
  worktree.branchName
1941
1960
  )) {
1942
- return { kind: "no-commits" };
1961
+ await removeWorktreeQuietly(worktree.worktreePath, d);
1962
+ return {
1963
+ kind: "no-commits",
1964
+ worktreePath: worktree.worktreePath,
1965
+ branchName: worktree.branchName
1966
+ };
1943
1967
  }
1944
1968
  if (opts.testCmd) {
1945
1969
  const passed = await runTestGate({
@@ -1949,7 +1973,8 @@ async function runTicketAttempt(opts) {
1949
1973
  timeout: opts.timeout,
1950
1974
  logsDir: opts.logsDir,
1951
1975
  attempt: opts.attempt,
1952
- log: /* @__PURE__ */ __name((msg) => d.log(msg), "log")
1976
+ log: /* @__PURE__ */ __name((msg) => d.log(msg), "log"),
1977
+ registry: runningAgents
1953
1978
  });
1954
1979
  if (!passed) {
1955
1980
  await removeWorktreeQuietly(
@@ -1978,7 +2003,11 @@ async function runTicketAttempts(opts) {
1978
2003
  const maxAttempts = opts.retries + 1;
1979
2004
  const isPi = basename2(opts.agentCmd) === "pi";
1980
2005
  let thinking = opts.thinking;
1981
- let outcome = { kind: "no-commits" };
2006
+ let outcome = {
2007
+ kind: "no-commits",
2008
+ worktreePath: "",
2009
+ branchName: ""
2010
+ };
1982
2011
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1983
2012
  if (attempt === 1) {
1984
2013
  d.log(` Starting ${ticket.filename}`);
@@ -2239,6 +2268,18 @@ async function runWavesIsolated(opts) {
2239
2268
  d.log(
2240
2269
  ` ${ticket.filename}: failed to create worktree: ${formatErrorMessage(outcome.error)}`
2241
2270
  );
2271
+ checkTicket(progress, ticket.num, {
2272
+ branch: "",
2273
+ exitCode: -1,
2274
+ done: false
2275
+ });
2276
+ await saveProgress(
2277
+ repoRoot,
2278
+ progressPath,
2279
+ progress,
2280
+ `nightralph: ${ticket.filename} worktree error`,
2281
+ d
2282
+ );
2242
2283
  continue;
2243
2284
  }
2244
2285
  if (outcome.kind === "error") {
@@ -2247,6 +2288,18 @@ async function runWavesIsolated(opts) {
2247
2288
  d.log(
2248
2289
  ` ${ticket.filename}: attempt error: ` + formatErrorMessage(outcome.error)
2249
2290
  );
2291
+ checkTicket(progress, ticket.num, {
2292
+ branch: outcome.branchName ?? "",
2293
+ exitCode: -1,
2294
+ done: false
2295
+ });
2296
+ await saveProgress(
2297
+ repoRoot,
2298
+ progressPath,
2299
+ progress,
2300
+ `nightralph: ${ticket.filename} error`,
2301
+ d
2302
+ );
2250
2303
  continue;
2251
2304
  }
2252
2305
  if (outcome.kind === "no-commits") {
@@ -2255,6 +2308,18 @@ async function runWavesIsolated(opts) {
2255
2308
  d.log(
2256
2309
  ` ${ticket.filename}: agent exited 0 but made no changes; leaving ticket ready-for-agent`
2257
2310
  );
2311
+ checkTicket(progress, ticket.num, {
2312
+ branch: outcome.branchName,
2313
+ exitCode: 0,
2314
+ done: false
2315
+ });
2316
+ await saveProgress(
2317
+ repoRoot,
2318
+ progressPath,
2319
+ progress,
2320
+ `nightralph: ${ticket.filename} no commits`,
2321
+ d
2322
+ );
2258
2323
  continue;
2259
2324
  }
2260
2325
  if (outcome.kind === "failed") {
@@ -2525,11 +2590,7 @@ var scriptDir = getScriptDir(import.meta.url);
2525
2590
  yargs(hideBin(process.argv)).scriptName("nightralph").usage("$0 <command>").command(
2526
2591
  "init",
2527
2592
  "Install bundled skills and doc templates",
2528
- (y) => y.option("claude", {
2529
- type: "boolean",
2530
- describe: "Also create .claude/skills/ symlinks",
2531
- default: true
2532
- }),
2593
+ (y) => y,
2533
2594
  async (argv) => {
2534
2595
  const tracker = await select2({
2535
2596
  message: "Issue tracker format",
@@ -2561,7 +2622,6 @@ yargs(hideBin(process.argv)).scriptName("nightralph").usage("$0 <command>").comm
2561
2622
  "docs-templates"
2562
2623
  ),
2563
2624
  projectDir: process.cwd(),
2564
- claude: argv.claude,
2565
2625
  tracker
2566
2626
  });
2567
2627
  }