@seanyao/roll 3.607.1 → 3.607.2

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 (3) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/roll.mjs +306 -112
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.607.2 — 2026-06-07
4
+
5
+ ### 自动化流水线
6
+
7
+ - 无技能子模块的项目跑 loop 找不到技能、选 agent 无视项目配置;补全局技能兜底、读项目配置选人、新增 pi 接管(FIX-221) `[loop]`
8
+ - loop 选 agent 被项目级单一默认一票否决:所有难度档坍缩成一个 agent,难题不再给 claude;装机探测永真空转;按 v2 链路修复(FIX-223) `[loop]`
9
+ - 非 claude agent 的 loop 接管命令全量 port(kimi/codex/deepseek/qwen/agy/gemini/antigravity)(US-PORT-010)
10
+ - roll loop now 手动触发降噪:交互终端只看关键节点,不再被逐行 JSON 淹没(FIX-220) `[loop]`
11
+
12
+ ### 工程和测试
13
+
14
+ - difftest 卸 oracle 收尾:全仓断言测试期不再起任何旧引擎,桥接表与文档记 oracle 卸任(US-PORT-009e)
15
+ - 看板冻结测试时钟钉死:快照不再随墙钟与时区漂移,任意日期任意时区可复现,提交闸不再被误堵(FIX-222)
16
+
17
+ ### 其他
18
+
19
+ - 验收卡史诗归属适配新档案布局:两级目录解析收敛为单一解析点(PR#513)
20
+
3
21
  ## v3.607.1 — 2026-06-07
4
22
 
5
23
  ### 可见性
package/dist/roll.mjs CHANGED
@@ -3028,6 +3028,21 @@ function agentBinNames(agent) {
3028
3028
  return null;
3029
3029
  }
3030
3030
  }
3031
+ function realAgentEnv() {
3032
+ return {
3033
+ home: homedir(),
3034
+ commandOnPath,
3035
+ dirExists: (p) => existsSync2(p),
3036
+ fileExecutable: (p) => {
3037
+ try {
3038
+ accessSync(p, constants.X_OK);
3039
+ return statSync(p).isFile();
3040
+ } catch {
3041
+ return false;
3042
+ }
3043
+ }
3044
+ };
3045
+ }
3031
3046
  function commandOnPath(bin) {
3032
3047
  for (const dir of (process.env["PATH"] ?? "").split(delimiter)) {
3033
3048
  if (dir === "")
@@ -3130,6 +3145,115 @@ var nodeFileStore = {
3130
3145
  }
3131
3146
  };
3132
3147
 
3148
+ // packages/core/dist/agent/registry.js
3149
+ function agentBinNames2(agent) {
3150
+ switch (agent) {
3151
+ case "claude":
3152
+ return ["claude"];
3153
+ case "codex":
3154
+ case "openai":
3155
+ return ["codex"];
3156
+ case "agy":
3157
+ case "gemini":
3158
+ return ["agy", "gemini"];
3159
+ case "kimi":
3160
+ return ["kimi-code", "kimi-cli", "kimi"];
3161
+ case "deepseek":
3162
+ return ["deepseek"];
3163
+ case "qwen":
3164
+ return ["qwen"];
3165
+ case "pi":
3166
+ return ["pi"];
3167
+ default:
3168
+ return null;
3169
+ }
3170
+ }
3171
+ function joinPath(...parts) {
3172
+ return parts.join("/");
3173
+ }
3174
+ function agentInstalledByName2(env, agent, dir) {
3175
+ const home = env.home;
3176
+ switch (agent) {
3177
+ case "trae":
3178
+ return env.dirExists(joinPath(home, "Library", "Application Support", "Trae")) || env.dirExists(joinPath(home, ".config", "Trae"));
3179
+ case "opencode":
3180
+ return env.fileExecutable(joinPath(home, ".opencode", "bin", "opencode"));
3181
+ case "cursor":
3182
+ return env.commandOnPath("cursor") || env.dirExists(joinPath(home, ".cursor"));
3183
+ case "openclaw":
3184
+ return env.dirExists(joinPath(home, ".openclaw", "workspace"));
3185
+ }
3186
+ const bins = agentBinNames2(agent);
3187
+ if (bins !== null)
3188
+ return bins.some((b) => env.commandOnPath(b));
3189
+ return dir !== void 0 && dir !== "" && env.dirExists(dir);
3190
+ }
3191
+ var FIRST_INSTALLED_ORDER = [
3192
+ "claude",
3193
+ "codex",
3194
+ "kimi",
3195
+ "deepseek",
3196
+ "qwen",
3197
+ "agy",
3198
+ "pi",
3199
+ "cursor",
3200
+ "opencode",
3201
+ "trae",
3202
+ "openclaw"
3203
+ ];
3204
+ function firstInstalledAgent(env) {
3205
+ return FIRST_INSTALLED_ORDER.find((a) => agentInstalledByName2(env, a));
3206
+ }
3207
+ function lineAgentValue(line) {
3208
+ const s = line.replace(/\t/g, " ");
3209
+ if (s.startsWith("agent:"))
3210
+ return s.slice("agent:".length);
3211
+ const m7 = /[ ,{]agent:/.exec(s);
3212
+ if (m7 === null)
3213
+ return void 0;
3214
+ return s.slice(m7.index + m7[0].length);
3215
+ }
3216
+ function cleanAgentValue(raw) {
3217
+ let v = raw;
3218
+ const hash = v.indexOf("#");
3219
+ if (hash >= 0)
3220
+ v = v.slice(0, hash);
3221
+ v = v.replace(/[{}",']/g, "").replace(/,/g, "");
3222
+ return v.trim();
3223
+ }
3224
+ function readSlotFromText(text, slot) {
3225
+ let inBlock = false;
3226
+ let agent = "";
3227
+ let found = false;
3228
+ const slotHeader = `${slot}:`;
3229
+ for (const raw of text.split("\n")) {
3230
+ const line = raw.replace(/\r$/, "");
3231
+ if (line === slotHeader || line.startsWith(`${slot}: `) || line.startsWith(`${slot}:{`)) {
3232
+ inBlock = true;
3233
+ const v = lineAgentValue(line);
3234
+ if (v !== void 0) {
3235
+ agent = v;
3236
+ found = true;
3237
+ }
3238
+ } else if (line.length > 0 && line[0] !== " ") {
3239
+ if (inBlock)
3240
+ break;
3241
+ } else if (inBlock && !found) {
3242
+ const v = lineAgentValue(line);
3243
+ if (v !== void 0) {
3244
+ agent = v;
3245
+ found = true;
3246
+ }
3247
+ }
3248
+ if (found)
3249
+ break;
3250
+ }
3251
+ if (!inBlock)
3252
+ return void 0;
3253
+ const cleaned = cleanAgentValue(agent);
3254
+ return cleaned === "" ? void 0 : cleaned;
3255
+ }
3256
+
3133
3257
  // packages/core/dist/agent/router.js
3134
3258
  var EASY_MAX_MIN = 8;
3135
3259
  var HARD_MIN_MIN = 20;
@@ -6028,14 +6152,22 @@ function findFeatureFile(projectPath, storyId) {
6028
6152
  function reportFileName(storyId) {
6029
6153
  return `${storyId}-report.html`;
6030
6154
  }
6155
+ function isEpicName(name) {
6156
+ return name !== "" && name !== "." && name !== "features";
6157
+ }
6158
+ function epicFromFeaturePath(featureFile) {
6159
+ const storyDir = dirname4(featureFile);
6160
+ const epic = basename3(dirname4(storyDir));
6161
+ if (isEpicName(epic))
6162
+ return epic;
6163
+ const legacy = basename3(storyDir);
6164
+ return isEpicName(legacy) ? legacy : null;
6165
+ }
6031
6166
  function liveEpicOf(projectPath, storyId) {
6032
6167
  const file = findFeatureFile(projectPath, storyId);
6033
6168
  if (file === null)
6034
6169
  return null;
6035
- const epic = basename3(dirname4(file));
6036
- if (epic === "" || epic === "." || epic === "features")
6037
- return null;
6038
- return epic;
6170
+ return epicFromFeaturePath(file);
6039
6171
  }
6040
6172
  function readIndex(projectPath) {
6041
6173
  const p = join5(projectPath, ".roll", "index.json");
@@ -7243,7 +7375,7 @@ function screenshotEvidenceRef(result, href) {
7243
7375
 
7244
7376
  // packages/cli/dist/commands/attest.js
7245
7377
  import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync9, readdirSync as readdirSync6, rmSync as rmSync5, symlinkSync as symlinkSync2, writeFileSync as writeFileSync7 } from "node:fs";
7246
- import { basename as basename4, dirname as dirname9, join as join10, relative } from "node:path";
7378
+ import { basename as basename4, join as join10, relative } from "node:path";
7247
7379
 
7248
7380
  // packages/cli/dist/lib/story-page.js
7249
7381
  var STORY_ID_RE = /^(FIX|US|IDEA|REFACTOR)-/;
@@ -7537,11 +7669,11 @@ function buildCardContext(projectPath, featureFile, storyId, env) {
7537
7669
  summary = extractSummary(readFileSync9(featureFile, "utf8"));
7538
7670
  } catch {
7539
7671
  }
7540
- const epic = basename4(dirname9(featureFile));
7672
+ const epic = epicFromFeaturePath(featureFile);
7541
7673
  const cycleId = env.LOOP_CYCLE_ID;
7542
7674
  const ctx = {
7543
7675
  ...oneLiner !== void 0 && oneLiner !== "" ? { oneLiner } : {},
7544
- ...epic !== "" && epic !== "." && epic !== "features" ? { epic } : {},
7676
+ ...epic !== null ? { epic } : {},
7545
7677
  ...summary !== void 0 ? { summary } : {},
7546
7678
  ...row2.status !== void 0 ? { backlogStatus: row2.status } : {},
7547
7679
  ...cycleId !== void 0 && cycleId !== "" ? { delivery: { cycleId } } : {}
@@ -8443,7 +8575,7 @@ import { spawnSync as spawnSync3 } from "node:child_process";
8443
8575
  // packages/cli/dist/commands/setup-shared.js
8444
8576
  import { copyFileSync, existsSync as existsSync18, lstatSync, mkdirSync as mkdirSync10, readFileSync as readFileSync13, readdirSync as readdirSync7, readlinkSync as readlinkSync2, realpathSync as realpathSync2, rmSync as rmSync6, statSync as statSync8, symlinkSync as symlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
8445
8577
  import { homedir as homedir5 } from "node:os";
8446
- import { basename as basename5, dirname as dirname10, join as join12 } from "node:path";
8578
+ import { basename as basename5, dirname as dirname9, join as join12 } from "node:path";
8447
8579
  function rollHome() {
8448
8580
  return process.env["ROLL_HOME"] ?? join12(homedir5(), ".roll");
8449
8581
  }
@@ -8491,7 +8623,7 @@ function canonicalDir(path) {
8491
8623
  return null;
8492
8624
  }
8493
8625
  }
8494
- function agentBinNames2(agent) {
8626
+ function agentBinNames3(agent) {
8495
8627
  switch (agent) {
8496
8628
  case "claude":
8497
8629
  return ["claude"];
@@ -8528,7 +8660,7 @@ function onPath(bin) {
8528
8660
  }
8529
8661
  return false;
8530
8662
  }
8531
- function agentInstalledByName2(agent, dir = "") {
8663
+ function agentInstalledByName3(agent, dir = "") {
8532
8664
  const home = homedir5();
8533
8665
  switch (agent) {
8534
8666
  case "trae":
@@ -8548,7 +8680,7 @@ function agentInstalledByName2(agent, dir = "") {
8548
8680
  default:
8549
8681
  break;
8550
8682
  }
8551
- const bins = agentBinNames2(agent);
8683
+ const bins = agentBinNames3(agent);
8552
8684
  if (bins !== null) {
8553
8685
  return bins.some((b) => onPath(b));
8554
8686
  }
@@ -8557,13 +8689,13 @@ function agentInstalledByName2(agent, dir = "") {
8557
8689
  function isAiInstalled(aiDir) {
8558
8690
  let bn = basename5(aiDir).replace(/^\./, "");
8559
8691
  if (bn === "agent" || bn === "workspace") {
8560
- bn = basename5(dirname10(aiDir)).replace(/^\./, "");
8692
+ bn = basename5(dirname9(aiDir)).replace(/^\./, "");
8561
8693
  }
8562
8694
  if (bn === "gemini")
8563
8695
  bn = "agy";
8564
8696
  if (bn === "kimi-code")
8565
8697
  bn = "kimi";
8566
- return agentInstalledByName2(bn, aiDir);
8698
+ return agentInstalledByName3(bn, aiDir);
8567
8699
  }
8568
8700
  function listDirEntries(dir) {
8569
8701
  try {
@@ -8594,7 +8726,7 @@ function pruneDir(installedDir, sourceDir) {
8594
8726
  function safeCopy(src, dst, force) {
8595
8727
  if (!existsSync18(src))
8596
8728
  return;
8597
- mkdirSync10(dirname10(dst), { recursive: true });
8729
+ mkdirSync10(dirname9(dst), { recursive: true });
8598
8730
  if (existsSync18(dst) && !force) {
8599
8731
  if (sameFile(src, dst))
8600
8732
  return;
@@ -8771,7 +8903,7 @@ loop_dream_hour: 3
8771
8903
  # loop_dream_minute: 10 # omit to auto-derive
8772
8904
  primary_agent: claude
8773
8905
  `;
8774
- function firstInstalledAgent() {
8906
+ function firstInstalledAgent2() {
8775
8907
  for (const agent of [
8776
8908
  "claude",
8777
8909
  "codex",
@@ -8785,7 +8917,7 @@ function firstInstalledAgent() {
8785
8917
  "trae",
8786
8918
  "openclaw"
8787
8919
  ]) {
8788
- if (agentInstalledByName2(agent))
8920
+ if (agentInstalledByName3(agent))
8789
8921
  return agent;
8790
8922
  }
8791
8923
  return null;
@@ -8810,7 +8942,7 @@ function installLocal(force) {
8810
8942
  }
8811
8943
  if (!existsSync18(cfg)) {
8812
8944
  writeFileSync9(cfg, DEFAULT_CONFIG);
8813
- const detected = firstInstalledAgent();
8945
+ const detected = firstInstalledAgent2();
8814
8946
  if (detected !== null && detected !== "claude")
8815
8947
  replacePrimaryAgent(detected);
8816
8948
  }
@@ -8820,7 +8952,7 @@ function installLocal(force) {
8820
8952
  function syncConventionForTool(src, mainDst, force) {
8821
8953
  if (!existsSync18(src))
8822
8954
  return;
8823
- const dstDir = dirname10(mainDst);
8955
+ const dstDir = dirname9(mainDst);
8824
8956
  if (dstDir !== join12(homedir5(), ".claude") && !isAiInstalled(dstDir) && !existsSync18(dstDir)) {
8825
8957
  return;
8826
8958
  }
@@ -10959,8 +11091,17 @@ function nextCronHint(zh) {
10959
11091
  const nxtSh = toShanghai(new Date(nxtEpoch));
10960
11092
  return `${pad22(nxtSh.getUTCHours())}:${pad22(nxtSh.getUTCMinutes())} \xB7 in ${mins}m ${pad22(secs)}s`;
10961
11093
  }
11094
+ function renderNow() {
11095
+ const v = process.env["ROLL_RENDER_NOW"] ?? "";
11096
+ if (v !== "") {
11097
+ const ms = /^\d+$/.test(v) ? Number(v) : Date.parse(v);
11098
+ if (!Number.isNaN(ms))
11099
+ return new Date(ms);
11100
+ }
11101
+ return /* @__PURE__ */ new Date();
11102
+ }
10962
11103
  function fixtureData() {
10963
- const now = /* @__PURE__ */ new Date();
11104
+ const now = renderNow();
10964
11105
  const events = [];
10965
11106
  const cron = [];
10966
11107
  let cycleId = 0;
@@ -11163,7 +11304,7 @@ function render(out2, events, cron, state, backlog, args) {
11163
11304
  out2.push(" " + c("dim", agentLine));
11164
11305
  let skillLine = "";
11165
11306
  try {
11166
- skillLine = selfScoreSummaryLine();
11307
+ skillLine = selfScoreSummaryLine(process.env["ROLL_NOTES_DIR"]);
11167
11308
  } catch {
11168
11309
  skillLine = "";
11169
11310
  }
@@ -11554,7 +11695,7 @@ roll-loop-status.py: error: unrecognized arguments: ${args.unknown.join(" ")}
11554
11695
  }
11555
11696
  renderState.useColor = !args.noColor && (process.env["NO_COLOR"] ?? "") === "" && (process.stdout.isTTY === true || (process.env["FORCE_COLOR"] ?? "") !== "");
11556
11697
  const lang4 = args.en ? "en" : args.zh ? "zh" : "both";
11557
- const now = /* @__PURE__ */ new Date();
11698
+ const now = renderNow();
11558
11699
  let events;
11559
11700
  let cron;
11560
11701
  let state;
@@ -11901,7 +12042,7 @@ function loopRunsCommand(argv) {
11901
12042
 
11902
12043
  // packages/cli/dist/commands/loop-signals.js
11903
12044
  import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
11904
- import { dirname as dirname11, join as join17 } from "node:path";
12045
+ import { dirname as dirname10, join as join17 } from "node:path";
11905
12046
  function lang2() {
11906
12047
  return resolveLang({ rollLang: process.env["ROLL_LANG"], lcAll: process.env["LC_ALL"], lang: process.env["LANG"] });
11907
12048
  }
@@ -11956,7 +12097,7 @@ function loopSignalsCommand(argv) {
11956
12097
  const seenFile = join17(rtDir, `signals-seen-${slug}`);
11957
12098
  const candFile = join17(projectPath, ".roll", "signals", "candidates.md");
11958
12099
  mkdirSync11(rtDir, { recursive: true });
11959
- mkdirSync11(dirname11(candFile), { recursive: true });
12100
+ mkdirSync11(dirname10(candFile), { recursive: true });
11960
12101
  if (!existsSync22(seenFile))
11961
12102
  writeFileSync10(seenFile, "");
11962
12103
  let lastId = 0;
@@ -12214,7 +12355,7 @@ var out = { lines: [] };
12214
12355
  function emit(line) {
12215
12356
  out.lines.push(line);
12216
12357
  }
12217
- function agentBinNames3(agent) {
12358
+ function agentBinNames4(agent) {
12218
12359
  switch (agent) {
12219
12360
  case "claude":
12220
12361
  return ["claude"];
@@ -12251,7 +12392,7 @@ function commandOnPath2(bin) {
12251
12392
  }
12252
12393
  return false;
12253
12394
  }
12254
- function agentInstalledByName3(agent, dir) {
12395
+ function agentInstalledByName4(agent, dir) {
12255
12396
  const home = homedir9();
12256
12397
  switch (agent) {
12257
12398
  case "trae":
@@ -12270,7 +12411,7 @@ function agentInstalledByName3(agent, dir) {
12270
12411
  case "openclaw":
12271
12412
  return existsSync24(join19(home, ".openclaw", "workspace"));
12272
12413
  default: {
12273
- const bins = agentBinNames3(agent);
12414
+ const bins = agentBinNames4(agent);
12274
12415
  if (bins !== null)
12275
12416
  return bins.some(commandOnPath2);
12276
12417
  return dir !== "" && existsSync24(dir) && safeIsDir(dir);
@@ -12319,7 +12460,7 @@ function agentSection(p) {
12319
12460
  dir = dir.replace(/^ /, "");
12320
12461
  if (dir.startsWith("~"))
12321
12462
  dir = home + dir.slice(1);
12322
- const installed = agentInstalledByName3(name, dir) ? t(v2Catalog, msgLang6(), "doctor.agent_installed") : t(v2Catalog, msgLang6(), "doctor.agent_missing");
12463
+ const installed = agentInstalledByName4(name, dir) ? t(v2Catalog, msgLang6(), "doctor.agent_installed") : t(v2Catalog, msgLang6(), "doctor.agent_missing");
12323
12464
  const dirExists = safeIsDir(dir) ? t(v2Catalog, msgLang6(), "doctor.agent_dir_exists") : t(v2Catalog, msgLang6(), "doctor.agent_dir_missing");
12324
12465
  const tag = name === primary ? ` (${t(v2Catalog, msgLang6(), "doctor.agent_primary_label")})` : "";
12325
12466
  emit(` ${padEnd(name, 10)} ${padEnd(installed, 14)} ${dirExists}${tag}`);
@@ -12525,16 +12666,8 @@ function killLiveAgents(signal2 = "SIGKILL") {
12525
12666
  }
12526
12667
  var AUTORUN_DIRECTIVE = "[roll \u81EA\u4E3B\u6A21\u5F0F] \u4F60\u6B63\u5728\u65E0\u4EBA\u503C\u5B88\u7684\u81EA\u52A8\u5316\u5FAA\u73AF\u4E2D\u8FD0\u884C,\u8FD9\u4E0D\u662F\u5BF9\u8BDD\u3002\u8BF7\u7ACB\u5373\u3001\u5B8C\u6574\u5730\u6267\u884C\u4E0B\u9762\u8FD9\u4EFD\u6280\u80FD\u6587\u6863\u63CF\u8FF0\u7684\u5DE5\u4F5C\u6D41,\u76F4\u5230\u5B8C\u6210\u4EA4\u4ED8\u6216\u5199\u51FA ALERT \u4E3A\u6B62;\u4E25\u7981\u53CD\u95EE\u3001\u4E25\u7981\u7B49\u5F85\u786E\u8BA4\u3001\u4E25\u7981\u53EA\u590D\u8FF0\u6216\u603B\u7ED3\u800C\u4E0D\u52A8\u624B\u3002\u6280\u80FD\u6587\u6863\u5982\u4E0B: ";
12527
12668
  var AGENT_ARGV_TODO = {
12528
- kimi: "kimi-code|kimi-cli|kimi -p <prompt> (FIX-126/133; no stream-json splice)",
12529
- pi: "pi -p <prompt> (text mode; no usage on stdout \u2014 cost via session file)",
12530
- deepseek: "deepseek <prompt> (positional)",
12531
- codex: "codex exec <prompt>",
12532
12669
  openai: "codex exec <prompt>",
12533
- opencode: "opencode run <prompt>",
12534
- gemini: "agy -p --dangerously-skip-permissions <prompt> (FIX-153)",
12535
- agy: "agy -p --dangerously-skip-permissions <prompt> (FIX-153)",
12536
- antigravity: "agy -p --dangerously-skip-permissions <prompt> (FIX-153)",
12537
- qwen: "qwen <prompt> (positional)"
12670
+ opencode: "opencode run <prompt>"
12538
12671
  };
12539
12672
  function storyPinDirective(storyId) {
12540
12673
  return `[\u672C\u5468\u671F\u6307\u5B9A\u6545\u4E8B] \u8C03\u5EA6\u5668\u5DF2\u9501\u5B9A ${storyId} \u5E76\u5728 backlog \u6807\u8BB0 \u{1F528} In Progress\u2014\u2014\u53EA\u6267\u884C\u8FD9\u4E00\u4E2A\u6545\u4E8B,\u4E25\u7981\u91CD\u65B0\u6311\u9009\u6216\u987A\u624B\u505A\u522B\u7684;\u82E5\u5B83\u786E\u5B9E\u4E0D\u53EF\u6267\u884C,\u5199 ALERT \u8BF4\u660E\u539F\u56E0\u540E\u5E72\u51C0\u9000\u51FA\u3002
@@ -12545,7 +12678,7 @@ function buildClaudeArgv(input) {
12545
12678
  const bin = input.bin ?? "claude";
12546
12679
  const pin = input.storyId !== void 0 && input.storyId !== "" ? storyPinDirective(input.storyId) : "";
12547
12680
  const prompt = `${AUTORUN_DIRECTIVE}${pin}${input.skillBody}`;
12548
- const args = [
12681
+ const args = input.interactive ? ["-p", prompt, "--dangerously-skip-permissions", "--add-dir", input.worktree] : [
12549
12682
  "-p",
12550
12683
  prompt,
12551
12684
  "--verbose",
@@ -12557,20 +12690,42 @@ function buildClaudeArgv(input) {
12557
12690
  ];
12558
12691
  return { bin, args };
12559
12692
  }
12560
- var realAgentSpawn = (agent, opts) => {
12561
- if (agent !== "claude") {
12562
- const hint = AGENT_ARGV_TODO[agent] ?? "unknown agent";
12563
- throw new Error(`runner: agent '${agent}' argv not yet ported (only 'claude' is). v2 shape: ${hint}`);
12693
+ function buildSpawnCommand(agent, opts) {
12694
+ const prompt = `${AUTORUN_DIRECTIVE}${opts.storyId !== void 0 && opts.storyId !== "" ? storyPinDirective(opts.storyId) : ""}${opts.skillBody}`;
12695
+ if (agent === "claude") {
12696
+ return buildClaudeArgv({
12697
+ worktree: opts.cwd,
12698
+ skillBody: opts.skillBody,
12699
+ ...opts.storyId !== void 0 ? { storyId: opts.storyId } : {},
12700
+ bin: opts.bin,
12701
+ interactive: opts.interactive
12702
+ });
12564
12703
  }
12565
- const { bin, args } = buildClaudeArgv({
12566
- worktree: opts.cwd,
12567
- skillBody: opts.skillBody,
12568
- ...opts.storyId !== void 0 ? { storyId: opts.storyId } : {},
12569
- bin: opts.bin
12570
- });
12704
+ if (agent === "pi") {
12705
+ return { bin: opts.bin ?? "pi", args: ["-p", prompt] };
12706
+ }
12707
+ if (agent === "kimi") {
12708
+ return { bin: opts.bin ?? "kimi", args: ["-p", prompt] };
12709
+ }
12710
+ if (agent === "codex") {
12711
+ return { bin: opts.bin ?? "codex", args: ["exec", prompt] };
12712
+ }
12713
+ if (agent === "deepseek") {
12714
+ return { bin: opts.bin ?? "deepseek", args: [prompt] };
12715
+ }
12716
+ if (agent === "qwen") {
12717
+ return { bin: opts.bin ?? "qwen", args: [prompt] };
12718
+ }
12719
+ if (agent === "agy" || agent === "gemini" || agent === "antigravity") {
12720
+ return { bin: opts.bin ?? "agy", args: ["-p", "--dangerously-skip-permissions", prompt] };
12721
+ }
12722
+ const hint = AGENT_ARGV_TODO[agent] ?? "unknown agent";
12723
+ throw new Error(`runner: agent '${agent}' argv not yet ported. v2 shape: ${hint}`);
12724
+ }
12725
+ function spawnAndWait(bin, args, opts) {
12571
12726
  process.stderr.write(`[runner] spawn ${bin} argv=${JSON.stringify(args.map((a) => a.length > 80 ? `${a.slice(0, 77)}...` : a))}
12572
12727
  `);
12573
- return new Promise((resolve3, reject) => {
12728
+ return new Promise((resolve3) => {
12574
12729
  const child = spawn(bin, args, {
12575
12730
  cwd: opts.cwd,
12576
12731
  env: opts.env ?? process.env,
@@ -12616,16 +12771,22 @@ var realAgentSpawn = (agent, opts) => {
12616
12771
  settle({ stdout, stderr, exitCode: code ?? 1, timedOut });
12617
12772
  });
12618
12773
  });
12774
+ }
12775
+ var realAgentSpawn = (agent, opts) => {
12776
+ const { bin, args } = buildSpawnCommand(agent, opts);
12777
+ return spawnAndWait(bin, args, opts);
12619
12778
  };
12620
12779
 
12621
12780
  // packages/cli/dist/runner/skill-body.js
12622
12781
  import { existsSync as existsSync25, readFileSync as readFileSync20 } from "node:fs";
12782
+ import { homedir as homedir10 } from "node:os";
12623
12783
  import { join as join20 } from "node:path";
12624
12784
  function readSkillBody(projectPath, opts) {
12625
12785
  const candidates = [
12626
12786
  opts.envOverride ?? "",
12627
12787
  join20(projectPath, ".roll", "skills", opts.skillName, "SKILL.md"),
12628
- join20(projectPath, "skills", opts.skillName, "SKILL.md")
12788
+ join20(projectPath, "skills", opts.skillName, "SKILL.md"),
12789
+ join20(homedir10(), ".roll", "skills", opts.skillName, "SKILL.md")
12629
12790
  ].filter((p) => p !== "");
12630
12791
  for (const p of candidates) {
12631
12792
  if (!existsSync25(p))
@@ -12701,7 +12862,7 @@ dream run-once: \u627E\u4E0D\u5230 roll-.dream SKILL.md \u2014 \u62D2\u7EDD\u76F
12701
12862
  // packages/cli/dist/commands/feedback.js
12702
12863
  import { execFileSync as execFileSync6, spawnSync as spawnSync4 } from "node:child_process";
12703
12864
  import { existsSync as existsSync26, readFileSync as readFileSync22 } from "node:fs";
12704
- import { homedir as homedir10 } from "node:os";
12865
+ import { homedir as homedir11 } from "node:os";
12705
12866
  import { basename as basename8, join as join23 } from "node:path";
12706
12867
 
12707
12868
  // packages/cli/dist/commands/version.js
@@ -12745,7 +12906,7 @@ function err8(line) {
12745
12906
  `);
12746
12907
  }
12747
12908
  function projectAgent2() {
12748
- const readField = (file, key) => {
12909
+ const readField2 = (file, key) => {
12749
12910
  if (!existsSync26(file))
12750
12911
  return void 0;
12751
12912
  let text;
@@ -12764,14 +12925,14 @@ function projectAgent2() {
12764
12925
  };
12765
12926
  const local = ".roll/local.yaml";
12766
12927
  if (existsSync26(local) && /^agent:/m.test(safeRead(local))) {
12767
- return readField(local, /^agent:/) ?? "claude";
12928
+ return readField2(local, /^agent:/) ?? "claude";
12768
12929
  }
12769
12930
  if (existsSync26(".roll.yaml") && /^agent:/m.test(safeRead(".roll.yaml"))) {
12770
- return readField(".roll.yaml", /^agent:/) ?? "claude";
12931
+ return readField2(".roll.yaml", /^agent:/) ?? "claude";
12771
12932
  }
12772
12933
  const cfg = rollConfig2();
12773
12934
  if (existsSync26(cfg) && /primary_agent:/.test(safeRead(cfg))) {
12774
- return readField(cfg, /primary_agent:/) ?? "claude";
12935
+ return readField2(cfg, /primary_agent:/) ?? "claude";
12775
12936
  }
12776
12937
  return "claude";
12777
12938
  }
@@ -12783,7 +12944,7 @@ function safeRead(p) {
12783
12944
  }
12784
12945
  }
12785
12946
  function rollConfig2() {
12786
- const rollHome4 = process.env["ROLL_HOME"] ?? join23(homedir10(), ".roll");
12947
+ const rollHome4 = process.env["ROLL_HOME"] ?? join23(homedir11(), ".roll");
12787
12948
  return join23(rollHome4, "config.yaml");
12788
12949
  }
12789
12950
  function feedbackYamlField(file, field) {
@@ -12830,7 +12991,7 @@ function feedbackDefaultRepo() {
12830
12991
  let v = feedbackYamlField(".roll/local.yaml", "feedback_repo");
12831
12992
  if (v)
12832
12993
  return v;
12833
- v = feedbackYamlField(join23(homedir10(), ".roll", "config.yaml"), "feedback_repo");
12994
+ v = feedbackYamlField(join23(homedir11(), ".roll", "config.yaml"), "feedback_repo");
12834
12995
  if (v)
12835
12996
  return v;
12836
12997
  return feedbackOriginRepo();
@@ -13283,8 +13444,8 @@ ${rows.join("\n")}
13283
13444
  // packages/cli/dist/commands/init.js
13284
13445
  import { spawnSync as spawnSync5 } from "node:child_process";
13285
13446
  import { copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync14, readFileSync as readFileSync23, realpathSync as realpathSync4, statSync as statSync15, writeFileSync as writeFileSync15 } from "node:fs";
13286
- import { homedir as homedir11 } from "node:os";
13287
- import { dirname as dirname12, join as join27 } from "node:path";
13447
+ import { homedir as homedir12 } from "node:os";
13448
+ import { dirname as dirname11, join as join27 } from "node:path";
13288
13449
  function err9(line) {
13289
13450
  const noColor2 = (process.env["NO_COLOR"] ?? "") !== "";
13290
13451
  const RED = noColor2 ? "" : "\x1B[0;31m";
@@ -13293,7 +13454,7 @@ function err9(line) {
13293
13454
  `);
13294
13455
  }
13295
13456
  function rollHome2() {
13296
- return process.env["ROLL_HOME"] ?? join27(homedir11(), ".roll");
13457
+ return process.env["ROLL_HOME"] ?? join27(homedir12(), ".roll");
13297
13458
  }
13298
13459
  function rollGlobal2() {
13299
13460
  return join27(rollHome2(), "conventions", "global");
@@ -13534,7 +13695,7 @@ function writeBacklog(path, summary) {
13534
13695
  summary.push("unchanged|.roll/backlog.md");
13535
13696
  return;
13536
13697
  }
13537
- mkdirSync14(dirname12(path), { recursive: true });
13698
+ mkdirSync14(dirname11(path), { recursive: true });
13538
13699
  writeFileSync15(path, BACKLOG_TEMPLATE);
13539
13700
  summary.push("created|.roll/backlog.md");
13540
13701
  }
@@ -13561,7 +13722,7 @@ function writeFeaturesMd(path, summary) {
13561
13722
  summary.push("unchanged|.roll/features.md");
13562
13723
  return;
13563
13724
  }
13564
- mkdirSync14(dirname12(path), { recursive: true });
13725
+ mkdirSync14(dirname11(path), { recursive: true });
13565
13726
  writeFileSync15(path, FEATURES_TEMPLATE);
13566
13727
  summary.push("created|.roll/features.md");
13567
13728
  }
@@ -13575,7 +13736,7 @@ function initSeedAgentRoutes(templateName, projectDir, summary) {
13575
13736
  if (!existsSync30(src)) {
13576
13737
  return 1;
13577
13738
  }
13578
- mkdirSync14(dirname12(dest), { recursive: true });
13739
+ mkdirSync14(dirname11(dest), { recursive: true });
13579
13740
  copyFileSync2(src, dest);
13580
13741
  summary.push("created|.roll/agent-routes.yaml");
13581
13742
  return 0;
@@ -13760,10 +13921,10 @@ function initCommand(args) {
13760
13921
  // packages/cli/dist/commands/lang.js
13761
13922
  import { execFileSync as execFileSync7 } from "node:child_process";
13762
13923
  import { existsSync as existsSync31, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readFileSync as readFileSync24, renameSync as renameSync2, writeFileSync as writeFileSync16 } from "node:fs";
13763
- import { homedir as homedir12, tmpdir as tmpdir3 } from "node:os";
13764
- import { dirname as dirname13, join as join28 } from "node:path";
13924
+ import { homedir as homedir13, tmpdir as tmpdir3 } from "node:os";
13925
+ import { dirname as dirname12, join as join28 } from "node:path";
13765
13926
  function rollConfigPath4() {
13766
- const rollHome4 = process.env["ROLL_HOME"] ?? join28(homedir12(), ".roll");
13927
+ const rollHome4 = process.env["ROLL_HOME"] ?? join28(homedir13(), ".roll");
13767
13928
  return join28(rollHome4, "config.yaml");
13768
13929
  }
13769
13930
  function configLang2() {
@@ -13843,7 +14004,7 @@ function err10(line) {
13843
14004
  }
13844
14005
  function writeLang(value) {
13845
14006
  const cfg = rollConfigPath4();
13846
- mkdirSync15(dirname13(cfg), { recursive: true });
14007
+ mkdirSync15(dirname12(cfg), { recursive: true });
13847
14008
  const existing = existsSync31(cfg) ? readFileSync24(cfg, "utf8") : "";
13848
14009
  const kept = existing === "" ? [] : existing.split("\n").filter((l) => !/^lang:/.test(l));
13849
14010
  if (kept.length > 0 && kept[kept.length - 1] === "")
@@ -13965,8 +14126,8 @@ async function loopFmtCommand(args) {
13965
14126
  // packages/cli/dist/commands/loop-pr-inbox.js
13966
14127
  import { spawn as spawn2 } from "node:child_process";
13967
14128
  import { appendFileSync as appendFileSync5, existsSync as existsSync32, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
13968
- import { homedir as homedir13 } from "node:os";
13969
- import { dirname as dirname14, join as join29 } from "node:path";
14129
+ import { homedir as homedir14 } from "node:os";
14130
+ import { dirname as dirname13, join as join29 } from "node:path";
13970
14131
  function reducePrView(raw) {
13971
14132
  const reviews = raw.reviews ?? [];
13972
14133
  const botReviews = reviews.filter((r) => r.authorAssociation === "BOT" || r.authorAssociation === "APP");
@@ -14067,17 +14228,17 @@ function tickPath() {
14067
14228
  return join29(runtimeDir(), "pr-tick.jsonl");
14068
14229
  }
14069
14230
  function engineBin() {
14070
- let dir = dirname14(new URL(import.meta.url).pathname);
14231
+ let dir = dirname13(new URL(import.meta.url).pathname);
14071
14232
  for (let i = 0; i < 10; i++) {
14072
14233
  const candidate = join29(dir, "bin", "roll");
14073
14234
  if (existsSync32(candidate))
14074
14235
  return candidate;
14075
- const parent = dirname14(dir);
14236
+ const parent = dirname13(dir);
14076
14237
  if (parent === dir)
14077
14238
  break;
14078
14239
  dir = parent;
14079
14240
  }
14080
- return join29(homedir13(), ".local", "lib", "roll", "bin", "roll");
14241
+ return join29(homedir14(), ".local", "lib", "roll", "bin", "roll");
14081
14242
  }
14082
14243
  function pal2() {
14083
14244
  return (process.env["NO_COLOR"] ?? "") !== "" ? { yellow: "", nc: "" } : { yellow: "\x1B[0;33m", nc: "\x1B[0m" };
@@ -14087,7 +14248,7 @@ function nowSec() {
14087
14248
  }
14088
14249
  function writeTickFile(tick) {
14089
14250
  const file = tickPath();
14090
- mkdirSync16(dirname14(file), { recursive: true });
14251
+ mkdirSync16(dirname13(file), { recursive: true });
14091
14252
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
14092
14253
  appendFileSync5(file, `${JSON.stringify({ ts, ...tick })}
14093
14254
  `);
@@ -14101,7 +14262,7 @@ function writeTickFile(tick) {
14101
14262
  }
14102
14263
  function appendAlert(line) {
14103
14264
  const file = alertPath();
14104
- mkdirSync16(dirname14(file), { recursive: true });
14265
+ mkdirSync16(dirname13(file), { recursive: true });
14105
14266
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
14106
14267
  appendFileSync5(file, `[${ts}] ${line}
14107
14268
  `);
@@ -14117,7 +14278,7 @@ function rebaseCircuitAllowed(num2) {
14117
14278
  writeRebaseAttempts(state, num2, verdict.freshTimestamps);
14118
14279
  if (!verdict.allowed) {
14119
14280
  const file = alertPath();
14120
- mkdirSync16(dirname14(file), { recursive: true });
14281
+ mkdirSync16(dirname13(file), { recursive: true });
14121
14282
  const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", " ");
14122
14283
  writeFileSync17(file, [
14123
14284
  `# ALERT \u2014 PR rebase circuit breaker tripped`,
@@ -14173,7 +14334,7 @@ function upsertRebaseAttempts(stateBody, pr, value) {
14173
14334
  `;
14174
14335
  }
14175
14336
  function writeRebaseAttempts(state, pr, timestamps) {
14176
- mkdirSync16(dirname14(state), { recursive: true });
14337
+ mkdirSync16(dirname13(state), { recursive: true });
14177
14338
  let body = "";
14178
14339
  try {
14179
14340
  body = readFileSync25(state, "utf8");
@@ -14267,12 +14428,12 @@ async function loopPrInboxCommand(_args, deps = realDeps2()) {
14267
14428
 
14268
14429
  // packages/cli/dist/commands/loop-run-once.js
14269
14430
  import { appendFileSync as appendFileSync7, existsSync as existsSync36, mkdirSync as mkdirSync18, readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
14270
- import { dirname as dirname16, join as join33 } from "node:path";
14431
+ import { dirname as dirname15, join as join33 } from "node:path";
14271
14432
 
14272
14433
  // packages/cli/dist/runner/executor.js
14273
14434
  import { execFile as execFile8 } from "node:child_process";
14274
14435
  import { appendFileSync as appendFileSync6, existsSync as existsSync35, lstatSync as lstatSync2, mkdirSync as mkdirSync17, readFileSync as readFileSync27, rmSync as rmSync8, symlinkSync as symlinkSync4, unlinkSync, writeFileSync as writeFileSync18 } from "node:fs";
14275
- import { dirname as dirname15, join as join32 } from "node:path";
14436
+ import { dirname as dirname14, join as join32 } from "node:path";
14276
14437
  import { promisify as promisify8 } from "node:util";
14277
14438
 
14278
14439
  // packages/cli/dist/runner/peer-gate.js
@@ -14503,7 +14664,7 @@ async function executeCommand(cmd, ports, ctx) {
14503
14664
  // execute: spawn the agent (TCR commits happen inside the worktree). The
14504
14665
  // exit code + timeout feed back as agent_exited; usage is captured for cost.
14505
14666
  case "spawn_agent": {
14506
- const livePath = join32(dirname15(ports.paths.eventsPath), "live.log");
14667
+ const livePath = join32(dirname14(ports.paths.eventsPath), "live.log");
14507
14668
  try {
14508
14669
  writeFileSync18(livePath, `\u2500\u2500 cycle ${ctx.cycleId ?? "?"} \xB7 ${ctx.storyId ?? "?"} \xB7 agent ${cmd.agent} \u2500\u2500
14509
14670
  `);
@@ -14523,7 +14684,7 @@ async function executeCommand(cmd, ports, ctx) {
14523
14684
  }
14524
14685
  });
14525
14686
  try {
14526
- const logDir = join32(dirname15(ports.paths.eventsPath), "cycle-logs");
14687
+ const logDir = join32(dirname14(ports.paths.eventsPath), "cycle-logs");
14527
14688
  mkdirSync17(logDir, { recursive: true });
14528
14689
  writeFileSync18(join32(logDir, `${ctx.cycleId ?? "cycle"}.agent.log`), `# exit=${res.exitCode} timedOut=${res.timedOut}
14529
14690
  --- stdout ---
@@ -14572,7 +14733,7 @@ ${res.stderr}
14572
14733
  tcrCount = await ports.git.tcrCount(ports.paths.worktreePath);
14573
14734
  } catch {
14574
14735
  }
14575
- await runPeerGate(ports.paths.worktreePath, dirname15(ports.paths.eventsPath), ctx.cycleId ?? "", {
14736
+ await runPeerGate(ports.paths.worktreePath, dirname14(ports.paths.eventsPath), ctx.cycleId ?? "", {
14576
14737
  alert: (m7) => ports.events.appendAlert(ports.paths.alertsPath, m7),
14577
14738
  event: (p) => ports.events.appendEvent(ports.paths.eventsPath, {
14578
14739
  type: "peer:gate",
@@ -14765,7 +14926,7 @@ async function linkRollIntoWorktree(repoCwd, worktreePath) {
14765
14926
  const exclude = join32(common, "info", "exclude");
14766
14927
  const cur = existsSync35(exclude) ? readFileSync27(exclude, "utf8") : "";
14767
14928
  if (!/^\.roll$/m.test(cur)) {
14768
- mkdirSync17(dirname15(exclude), { recursive: true });
14929
+ mkdirSync17(dirname14(exclude), { recursive: true });
14769
14930
  appendFileSync6(exclude, `${cur === "" || cur.endsWith("\n") ? "" : "\n"}.roll
14770
14931
  `, "utf8");
14771
14932
  }
@@ -14886,7 +15047,7 @@ function nodePorts(opts) {
14886
15047
  };
14887
15048
  }
14888
15049
  function appendAlertLine(alertsPath, message) {
14889
- mkdirSync17(dirname15(alertsPath), { recursive: true });
15050
+ mkdirSync17(dirname14(alertsPath), { recursive: true });
14890
15051
  appendFileSync6(alertsPath, `${message}
14891
15052
  `, "utf8");
14892
15053
  }
@@ -15191,6 +15352,38 @@ function readSkillBody2(projectPath) {
15191
15352
  envOverride: process.env["ROLL_LOOP_SKILL"]
15192
15353
  });
15193
15354
  }
15355
+ function buildLoopRouteDeps(projectPath) {
15356
+ function readSlot(slot) {
15357
+ const agentsYaml = join33(projectPath, ".roll", "agents.yaml");
15358
+ try {
15359
+ return readSlotFromText(readFileSync28(agentsYaml, "utf8"), slot);
15360
+ } catch {
15361
+ return void 0;
15362
+ }
15363
+ }
15364
+ function firstInstalled() {
15365
+ const fromLocal = readField(join33(projectPath, ".roll", "local.yaml"), /^agent:/);
15366
+ if (fromLocal !== void 0)
15367
+ return fromLocal;
15368
+ return firstInstalledAgent(realAgentEnv());
15369
+ }
15370
+ return { readSlot, firstInstalled };
15371
+ }
15372
+ function readField(path, re) {
15373
+ try {
15374
+ const text = readFileSync28(path, "utf8");
15375
+ for (const line of text.split("\n")) {
15376
+ const m7 = line.match(re);
15377
+ if (m7 !== null) {
15378
+ const v = line.slice((m7.index ?? 0) + m7[0].length).trim();
15379
+ if (v !== "")
15380
+ return v.replace(/^["']|["']$/g, "");
15381
+ }
15382
+ }
15383
+ } catch {
15384
+ }
15385
+ return void 0;
15386
+ }
15194
15387
  async function loopRunOnceCommand(args) {
15195
15388
  const dryRun = args.includes("--dry-run");
15196
15389
  const id = await projectIdentity();
@@ -15215,7 +15408,7 @@ async function loopRunOnceCommand(args) {
15215
15408
  }
15216
15409
  const rt = runtimeDir2(id.path);
15217
15410
  const alertsPath = join33(rt, `ALERT-${id.slug}.md`);
15218
- mkdirSync18(dirname16(alertsPath), { recursive: true });
15411
+ mkdirSync18(dirname15(alertsPath), { recursive: true });
15219
15412
  const paths = {
15220
15413
  eventsPath: join33(rt, "events.ndjson"),
15221
15414
  runsPath: join33(rt, "runs.jsonl"),
@@ -15238,15 +15431,16 @@ loop run-once: \u627E\u4E0D\u5230 roll-loop SKILL.md \u2014 \u62D2\u7EDD\u76F2\u
15238
15431
  incrementConsecutiveFails(id.path, id.slug, alertsPath, cycleId, "", "skill_missing");
15239
15432
  return 1;
15240
15433
  }
15241
- const routeDeps = {
15242
- readSlot: () => void 0,
15243
- firstInstalled: () => process.env["ROLL_LOOP_AGENT"] ?? "claude"
15244
- };
15434
+ const routeDeps = buildLoopRouteDeps(id.path);
15435
+ const isInteractive = (process.env["ROLL_LOOP_FORCE"] ?? "").trim() !== "";
15245
15436
  const ports = nodePorts({
15246
15437
  repoCwd: id.path,
15247
15438
  paths,
15248
15439
  skillBody,
15249
- routeDeps
15440
+ routeDeps,
15441
+ ...isInteractive ? {
15442
+ agentSpawn: (agent, opts) => realAgentSpawn(agent, { ...opts, interactive: true })
15443
+ } : {}
15250
15444
  });
15251
15445
  const disposeSignals = installCycleSignalTeardown(paths, cycleId, branch);
15252
15446
  let result;
@@ -15279,14 +15473,14 @@ loop run-once: \u627E\u4E0D\u5230 roll-loop SKILL.md \u2014 \u62D2\u7EDD\u76F2\u
15279
15473
  import { createHash as createHash4 } from "node:crypto";
15280
15474
  import { spawn as spawn4, spawnSync as spawnSync6 } from "node:child_process";
15281
15475
  import { existsSync as existsSync37, mkdirSync as mkdirSync19, readFileSync as readFileSync29, rmSync as rmSync9, writeFileSync as writeFileSync20 } from "node:fs";
15282
- import { homedir as homedir14 } from "node:os";
15283
- import { dirname as dirname17, join as join34 } from "node:path";
15476
+ import { homedir as homedir15 } from "node:os";
15477
+ import { dirname as dirname16, join as join34 } from "node:path";
15284
15478
  function realDeps3() {
15285
15479
  return {
15286
15480
  identity: () => projectIdentity(),
15287
15481
  uid: () => process.getuid?.() ?? 501,
15288
- sharedRoot: () => process.env["ROLL_SHARED_ROOT"] || join34(homedir14(), ".shared", "roll"),
15289
- launchdDir: () => join34(homedir14(), "Library", "LaunchAgents"),
15482
+ sharedRoot: () => process.env["ROLL_SHARED_ROOT"] || join34(homedir15(), ".shared", "roll"),
15483
+ launchdDir: () => join34(homedir15(), "Library", "LaunchAgents"),
15290
15484
  launchd: { reinstall, uninstall, isLoaded },
15291
15485
  execRunner: (runner) => new Promise((resolve3) => {
15292
15486
  const child = spawn4("bash", [runner], {
@@ -15478,7 +15672,7 @@ function parseLoopPeriodMinutes(text) {
15478
15672
  return 30;
15479
15673
  }
15480
15674
  function pathValue() {
15481
- const home = homedir14();
15675
+ const home = homedir15();
15482
15676
  return [
15483
15677
  "/opt/homebrew/bin",
15484
15678
  `${home}/.local/bin`,
@@ -15490,7 +15684,7 @@ function pathValue() {
15490
15684
  ].join(":");
15491
15685
  }
15492
15686
  function writeExecutable(path, content) {
15493
- mkdirSync19(dirname17(path), { recursive: true });
15687
+ mkdirSync19(dirname16(path), { recursive: true });
15494
15688
  writeFileSync20(path, content, { mode: 493 });
15495
15689
  }
15496
15690
  function pauseMarkerPath(projectPath, slug) {
@@ -15617,7 +15811,7 @@ Loop \u5DF2\u505C\u7528(loop/dream/pr \u5747\u5DF2\u5378\u8F7D)
15617
15811
  async function loopPauseCommand(_args, deps = realDeps3()) {
15618
15812
  const id = await deps.identity();
15619
15813
  const marker = pauseMarkerPath(id.path, id.slug);
15620
- mkdirSync19(dirname17(marker), { recursive: true });
15814
+ mkdirSync19(dirname16(marker), { recursive: true });
15621
15815
  const already = existsSync37(marker);
15622
15816
  if (!already)
15623
15817
  writeFileSync20(marker, `${(/* @__PURE__ */ new Date()).toISOString()}
@@ -15699,7 +15893,7 @@ cycle finished \u2014 logs: .roll/loop/cron.log \xB7 .roll/loop/cycle-logs/
15699
15893
  // packages/cli/dist/commands/migrate.js
15700
15894
  import { spawnSync as spawnSync7 } from "node:child_process";
15701
15895
  import { existsSync as existsSync38, mkdirSync as mkdirSync20 } from "node:fs";
15702
- import { dirname as dirname18 } from "node:path";
15896
+ import { dirname as dirname17 } from "node:path";
15703
15897
  function colors() {
15704
15898
  const noColor2 = (process.env["NO_COLOR"] ?? "") !== "";
15705
15899
  return noColor2 ? { RED: "", GREEN: "", YELLOW: "", CYAN: "", NC: "" } : {
@@ -15818,7 +16012,7 @@ function migrateExecute(activeMoves) {
15818
16012
  for (const move of activeMoves) {
15819
16013
  const src = srcOf(move);
15820
16014
  const tgt = tgtOf(move);
15821
- const targetDir = dirname18(tgt);
16015
+ const targetDir = dirname17(tgt);
15822
16016
  if (!existsSync38(targetDir))
15823
16017
  mkdirSync20(targetDir, { recursive: true });
15824
16018
  const r = git3(["mv", src, tgt]);
@@ -16009,7 +16203,7 @@ function migrateFeaturesCommand(args) {
16009
16203
  // packages/cli/dist/commands/offboard.js
16010
16204
  import { spawnSync as spawnSync8 } from "node:child_process";
16011
16205
  import { existsSync as existsSync40, readFileSync as readFileSync31, realpathSync as realpathSync5, rmSync as rmSync10, writeFileSync as writeFileSync22 } from "node:fs";
16012
- import { homedir as homedir15 } from "node:os";
16206
+ import { homedir as homedir16 } from "node:os";
16013
16207
  import { join as join36, resolve as resolve2 } from "node:path";
16014
16208
  function pal3() {
16015
16209
  const noColor2 = (process.env["NO_COLOR"] ?? "") !== "";
@@ -16058,7 +16252,7 @@ function runLaunchctl(args) {
16058
16252
  if (args[0] !== void 0 && readOnly.has(args[0])) {
16059
16253
  return spawnSync8("launchctl", args, { stdio: "inherit" }).status ?? 1;
16060
16254
  }
16061
- const canonical = join36(homedir15(), "Library", "LaunchAgents");
16255
+ const canonical = join36(homedir16(), "Library", "LaunchAgents");
16062
16256
  const launchdDir = process.env["_LAUNCHD_DIR"] ?? canonical;
16063
16257
  if (launchdDir !== canonical)
16064
16258
  return 0;
@@ -16240,7 +16434,7 @@ function offboardCommand(args) {
16240
16434
  }
16241
16435
  }
16242
16436
  for (const item of plists) {
16243
- const plistPath = join36(homedir15(), "Library", "LaunchAgents", item);
16437
+ const plistPath = join36(homedir16(), "Library", "LaunchAgents", item);
16244
16438
  const r = runLaunchctl(["unload", "-w", plistPath]);
16245
16439
  if (r === 0)
16246
16440
  process.stdout.write(` unloaded ${item}
@@ -16711,7 +16905,7 @@ import { join as join41 } from "node:path";
16711
16905
 
16712
16906
  // packages/cli/dist/commands/slides/render.js
16713
16907
  import { readFileSync as readFileSync35, existsSync as existsSync44, readdirSync as readdirSync18, statSync as statSync19 } from "node:fs";
16714
- import { join as join40, dirname as dirname19, basename as basename9 } from "node:path";
16908
+ import { join as join40, dirname as dirname18, basename as basename9 } from "node:path";
16715
16909
  function coerceScalar(v) {
16716
16910
  if (v.length >= 2 && v[0] === v[v.length - 1] && (v[0] === "'" || v[0] === '"')) {
16717
16911
  return v.slice(1, -1);
@@ -17283,7 +17477,7 @@ function isFile(p) {
17283
17477
  function libDirFor(_metaUrl) {
17284
17478
  void _metaUrl;
17285
17479
  void existsSync44;
17286
- void dirname19;
17480
+ void dirname18;
17287
17481
  return join40(repoLibFallback(), "lib");
17288
17482
  }
17289
17483
  function repoLibFallback() {
@@ -17291,7 +17485,7 @@ function repoLibFallback() {
17291
17485
  for (let i = 0; i < 12; i++) {
17292
17486
  if (existsSync44(join40(dir, "bin", "roll")))
17293
17487
  return dir;
17294
- const parent = dirname19(dir);
17488
+ const parent = dirname18(dir);
17295
17489
  if (parent === dir)
17296
17490
  break;
17297
17491
  dir = parent;
@@ -18214,10 +18408,10 @@ function rowThree(a, b, c2) {
18214
18408
  import { execFileSync as execFileSync8 } from "node:child_process";
18215
18409
  import { createHash as createHash5 } from "node:crypto";
18216
18410
  import { existsSync as existsSync45, lstatSync as lstatSync4, readdirSync as readdirSync20, readFileSync as readFileSync38, realpathSync as realpathSync6, statSync as statSync22 } from "node:fs";
18217
- import { homedir as homedir16 } from "node:os";
18218
- import { basename as basename10, dirname as dirname20, join as join42 } from "node:path";
18411
+ import { homedir as homedir17 } from "node:os";
18412
+ import { basename as basename10, dirname as dirname19, join as join42 } from "node:path";
18219
18413
  function rollHome3() {
18220
- return process.env["ROLL_HOME"] ?? join42(homedir16(), ".roll");
18414
+ return process.env["ROLL_HOME"] ?? join42(homedir17(), ".roll");
18221
18415
  }
18222
18416
  var globalDir = () => join42(rollHome3(), "conventions", "global");
18223
18417
  var templatesDir = () => join42(rollHome3(), "conventions", "templates");
@@ -18251,7 +18445,7 @@ function parseAiEntries() {
18251
18445
  const m7 = /^ai_[a-z]+:\s*(.+)/.exec(line);
18252
18446
  if (m7 === null)
18253
18447
  continue;
18254
- const val = (m7[1] ?? "").trim().replaceAll("~", homedir16());
18448
+ const val = (m7[1] ?? "").trim().replaceAll("~", homedir17());
18255
18449
  const parts = val.split("|");
18256
18450
  if (parts.length < 3)
18257
18451
  continue;
@@ -18260,7 +18454,7 @@ function parseAiEntries() {
18260
18454
  const srcFile = (parts[2] ?? "").trim();
18261
18455
  let name = basename10(aiDir).replace(/^\.+/, "");
18262
18456
  if (name === "workspace" || name === "agent") {
18263
- name = basename10(dirname20(aiDir)).replace(/^\.+/, "");
18457
+ name = basename10(dirname19(aiDir)).replace(/^\.+/, "");
18264
18458
  }
18265
18459
  entries.push({ name, ai_dir: aiDir, cfg_file: cfgFile, src_file: srcFile });
18266
18460
  }
@@ -18341,7 +18535,7 @@ function skillsInstalled() {
18341
18535
  }
18342
18536
  function launchdState(service, slug) {
18343
18537
  const label4 = `com.roll.${service}.${slug}`;
18344
- const plist = join42(homedir16(), "Library", "LaunchAgents", `${label4}.plist`);
18538
+ const plist = join42(homedir17(), "Library", "LaunchAgents", `${label4}.plist`);
18345
18539
  if (!existsSync45(plist))
18346
18540
  return "not-installed";
18347
18541
  try {
@@ -18476,7 +18670,7 @@ function renderThisProject(out2, d) {
18476
18670
  }
18477
18671
  function liveData() {
18478
18672
  const slug = projectSlugPy();
18479
- const home = homedir16();
18673
+ const home = homedir17();
18480
18674
  const aiClients = parseAiEntries().map((e) => ({
18481
18675
  name: e.name,
18482
18676
  cfg_file: e.cfg_file,
@@ -18520,7 +18714,7 @@ function statusCommand(args) {
18520
18714
  // packages/cli/dist/commands/test.js
18521
18715
  import { spawnSync as spawnSync11 } from "node:child_process";
18522
18716
  import { existsSync as existsSync46, mkdirSync as mkdirSync24, readFileSync as readFileSync39, rmSync as rmSync12, writeFileSync as writeFileSync24 } from "node:fs";
18523
- import { dirname as dirname21, join as join43 } from "node:path";
18717
+ import { dirname as dirname20, join as join43 } from "node:path";
18524
18718
  function pal5() {
18525
18719
  const noColor2 = (process.env["NO_COLOR"] ?? "") !== "";
18526
18720
  return noColor2 ? { CYAN: "", GREEN: "", YELLOW: "", RED: "", NC: "" } : { CYAN: "\x1B[0;36m", GREEN: "\x1B[0;32m", YELLOW: "\x1B[0;33m", RED: "\x1B[0;31m", NC: "\x1B[0m" };
@@ -18596,7 +18790,7 @@ function resetAcquireLock() {
18596
18790
  const lock = resetLockPath();
18597
18791
  if (existsSync46(lock))
18598
18792
  return false;
18599
- mkdirSync24(dirname21(lock), { recursive: true });
18793
+ mkdirSync24(dirname20(lock), { recursive: true });
18600
18794
  writeFileSync24(lock, `${process.pid}
18601
18795
  `);
18602
18796
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "3.607.1",
3
+ "version": "3.607.2",
4
4
  "description": "Roll \u2014 Roll out features with AI agents",
5
5
  "packageManager": "pnpm@11.1.3",
6
6
  "scripts": {