@4pm/cli 1.27.0 → 1.28.0-c

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 (2) hide show
  1. package/dist/index.js +786 -469
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5713,14 +5713,14 @@ var require_react_development = __commonJS({
5713
5713
  var thenableResult = result;
5714
5714
  var wasAwaited = false;
5715
5715
  var thenable = {
5716
- then: function(resolve12, reject) {
5716
+ then: function(resolve13, reject) {
5717
5717
  wasAwaited = true;
5718
5718
  thenableResult.then(function(returnValue2) {
5719
5719
  popActScope(prevActScopeDepth);
5720
5720
  if (actScopeDepth === 0) {
5721
- recursivelyFlushAsyncActWork(returnValue2, resolve12, reject);
5721
+ recursivelyFlushAsyncActWork(returnValue2, resolve13, reject);
5722
5722
  } else {
5723
- resolve12(returnValue2);
5723
+ resolve13(returnValue2);
5724
5724
  }
5725
5725
  }, function(error2) {
5726
5726
  popActScope(prevActScopeDepth);
@@ -5750,20 +5750,20 @@ var require_react_development = __commonJS({
5750
5750
  ReactCurrentActQueue.current = null;
5751
5751
  }
5752
5752
  var _thenable = {
5753
- then: function(resolve12, reject) {
5753
+ then: function(resolve13, reject) {
5754
5754
  if (ReactCurrentActQueue.current === null) {
5755
5755
  ReactCurrentActQueue.current = [];
5756
- recursivelyFlushAsyncActWork(returnValue, resolve12, reject);
5756
+ recursivelyFlushAsyncActWork(returnValue, resolve13, reject);
5757
5757
  } else {
5758
- resolve12(returnValue);
5758
+ resolve13(returnValue);
5759
5759
  }
5760
5760
  }
5761
5761
  };
5762
5762
  return _thenable;
5763
5763
  } else {
5764
5764
  var _thenable2 = {
5765
- then: function(resolve12, reject) {
5766
- resolve12(returnValue);
5765
+ then: function(resolve13, reject) {
5766
+ resolve13(returnValue);
5767
5767
  }
5768
5768
  };
5769
5769
  return _thenable2;
@@ -5779,7 +5779,7 @@ var require_react_development = __commonJS({
5779
5779
  actScopeDepth = prevActScopeDepth;
5780
5780
  }
5781
5781
  }
5782
- function recursivelyFlushAsyncActWork(returnValue, resolve12, reject) {
5782
+ function recursivelyFlushAsyncActWork(returnValue, resolve13, reject) {
5783
5783
  {
5784
5784
  var queue = ReactCurrentActQueue.current;
5785
5785
  if (queue !== null) {
@@ -5788,16 +5788,16 @@ var require_react_development = __commonJS({
5788
5788
  enqueueTask(function() {
5789
5789
  if (queue.length === 0) {
5790
5790
  ReactCurrentActQueue.current = null;
5791
- resolve12(returnValue);
5791
+ resolve13(returnValue);
5792
5792
  } else {
5793
- recursivelyFlushAsyncActWork(returnValue, resolve12, reject);
5793
+ recursivelyFlushAsyncActWork(returnValue, resolve13, reject);
5794
5794
  }
5795
5795
  });
5796
5796
  } catch (error2) {
5797
5797
  reject(error2);
5798
5798
  }
5799
5799
  } else {
5800
- resolve12(returnValue);
5800
+ resolve13(returnValue);
5801
5801
  }
5802
5802
  }
5803
5803
  }
@@ -16203,9 +16203,9 @@ var require_react_reconciler_development = __commonJS({
16203
16203
  var node = row;
16204
16204
  while (node !== null) {
16205
16205
  if (node.tag === SuspenseComponent) {
16206
- var state2 = node.memoizedState;
16207
- if (state2 !== null) {
16208
- var dehydrated = state2.dehydrated;
16206
+ var state3 = node.memoizedState;
16207
+ if (state3 !== null) {
16208
+ var dehydrated = state3.dehydrated;
16209
16209
  if (dehydrated === null || isSuspenseInstancePending(dehydrated) || isSuspenseInstanceFallback(dehydrated)) {
16210
16210
  return node;
16211
16211
  }
@@ -16549,8 +16549,8 @@ var require_react_reconciler_development = __commonJS({
16549
16549
  stores: null
16550
16550
  };
16551
16551
  }
16552
- function basicStateReducer(state2, action) {
16553
- return typeof action === "function" ? action(state2) : action;
16552
+ function basicStateReducer(state3, action) {
16553
+ return typeof action === "function" ? action(state3) : action;
16554
16554
  }
16555
16555
  function mountReducer(reducer, initialArg, init) {
16556
16556
  var hook = mountWorkInProgressHook();
@@ -18537,10 +18537,10 @@ var require_react_reconciler_development = __commonJS({
18537
18537
  }
18538
18538
  }
18539
18539
  }
18540
- var state2 = workInProgress2.memoizedState = instance.state !== null && instance.state !== void 0 ? instance.state : null;
18540
+ var state3 = workInProgress2.memoizedState = instance.state !== null && instance.state !== void 0 ? instance.state : null;
18541
18541
  adoptClassInstance(workInProgress2, instance);
18542
18542
  {
18543
- if (typeof ctor.getDerivedStateFromProps === "function" && state2 === null) {
18543
+ if (typeof ctor.getDerivedStateFromProps === "function" && state3 === null) {
18544
18544
  var componentName = getComponentNameFromType(ctor) || "Component";
18545
18545
  if (!didWarnAboutUninitializedState.has(componentName)) {
18546
18546
  didWarnAboutUninitializedState.add(componentName);
@@ -19463,8 +19463,8 @@ var require_react_reconciler_development = __commonJS({
19463
19463
  var _instance = workInProgress2.stateNode;
19464
19464
  var ctor = workInProgress2.type;
19465
19465
  var tempInstance = new ctor(workInProgress2.memoizedProps, _instance.context);
19466
- var state2 = tempInstance.state;
19467
- _instance.updater.enqueueSetState(_instance, state2, null);
19466
+ var state3 = tempInstance.state;
19467
+ _instance.updater.enqueueSetState(_instance, state3, null);
19468
19468
  break;
19469
19469
  }
19470
19470
  case true: {
@@ -20239,8 +20239,8 @@ var require_react_reconciler_development = __commonJS({
20239
20239
  var node = firstChild;
20240
20240
  while (node !== null) {
20241
20241
  if (node.tag === SuspenseComponent) {
20242
- var state2 = node.memoizedState;
20243
- if (state2 !== null) {
20242
+ var state3 = node.memoizedState;
20243
+ if (state3 !== null) {
20244
20244
  scheduleSuspenseWorkOnFiber(node, renderLanes2, workInProgress2);
20245
20245
  }
20246
20246
  } else if (node.tag === SuspenseListComponent) {
@@ -20669,9 +20669,9 @@ var require_react_reconciler_development = __commonJS({
20669
20669
  }
20670
20670
  break;
20671
20671
  case SuspenseComponent: {
20672
- var state2 = workInProgress2.memoizedState;
20673
- if (state2 !== null) {
20674
- if (state2.dehydrated !== null) {
20672
+ var state3 = workInProgress2.memoizedState;
20673
+ if (state3 !== null) {
20674
+ if (state3.dehydrated !== null) {
20675
20675
  pushSuspenseContext(workInProgress2, setDefaultShallowSuspenseContext(suspenseStackCursor.current));
20676
20676
  workInProgress2.flags |= DidCapture;
20677
20677
  return null;
@@ -29550,7 +29550,7 @@ async function runLink(serverUrl, explicitProfile, token = null) {
29550
29550
 
29551
29551
  // src/commands/start.ts
29552
29552
  import { spawn as spawn11 } from "child_process";
29553
- import { join as join37 } from "path";
29553
+ import { join as join38 } from "path";
29554
29554
 
29555
29555
  // src/core/update.ts
29556
29556
  import { execSync as execSync2 } from "child_process";
@@ -32769,7 +32769,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
32769
32769
  // src/core/ws-client.ts
32770
32770
  init_wrapper();
32771
32771
  import { randomUUID as randomUUID4 } from "crypto";
32772
- import { basename as basename7, join as join32 } from "path";
32772
+ import { basename as basename7, join as join33 } from "path";
32773
32773
  import { existsSync as existsSync18, mkdirSync as mkdirSync9 } from "fs";
32774
32774
 
32775
32775
  // vendor/@4pm/utils/helpers.ts
@@ -37064,6 +37064,17 @@ function githubHostKind(host) {
37064
37064
  if (h.endsWith(".ghe.com")) return "ghe-cloud";
37065
37065
  return "ghes";
37066
37066
  }
37067
+ var scaffoldPublishRequestSchema = external_exports.object({
37068
+ machineLinkId: external_exports.string().uuid().optional()
37069
+ });
37070
+
37071
+ // vendor/@4pm/dto/git-overview.ts
37072
+ var GIT_SNAPSHOT_LIMITS = {
37073
+ localCommits: 50,
37074
+ filesPerCommit: 100,
37075
+ dirtyFiles: 200,
37076
+ remoteCommits: 200
37077
+ };
37067
37078
 
37068
37079
  // vendor/@4pm/validation/schemas.ts
37069
37080
  var passwordSchema = external_exports.string().min(8, "password: at least 8 characters").max(32, "password: at most 32 characters").regex(/[a-zA-Z]/, "password: needs at least 1 letter").regex(/[0-9]/, "password: needs at least 1 digit");
@@ -37640,7 +37651,7 @@ var userCardsQuerySchema = external_exports.object({
37640
37651
  });
37641
37652
 
37642
37653
  // src/core/scaffold.ts
37643
- import { cp, mkdir, mkdtemp, rm, writeFile } from "fs/promises";
37654
+ import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "fs/promises";
37644
37655
  import { existsSync as existsSync4 } from "fs";
37645
37656
  import { execFile } from "child_process";
37646
37657
  import { tmpdir as tmpdir2 } from "os";
@@ -37749,45 +37760,98 @@ function providerSlug(url) {
37749
37760
  const m2 = /(?:github|gitlab)\.com[/:]+([^/]+\/.+?)(?:\.git)?\/?$/i.exec(url);
37750
37761
  return { provider, slug: m2?.[1] ?? "" };
37751
37762
  }
37752
- async function commitAndOpenPr(root, repo, emit) {
37753
- emit("commit", "Committing the scaffolded project\u2026");
37754
- await run("git", ["add", "-A"], { cwd: root, timeout: 6e4 });
37755
- const staged = (await run("git", ["diff", "--cached", "--name-only"], { cwd: root, timeout: 3e4 })).stdout.trim();
37756
- if (!staged) {
37757
- emit("commit", "Nothing to commit \u2014 skipping the pull request.");
37758
- return;
37759
- }
37760
- const commitArgs = ["commit", "-m", "chore: scaffold project (4PM)"];
37763
+ function errText(err) {
37764
+ const e = err;
37765
+ const raw = (e?.stderr || e?.message || String(err)).trim();
37766
+ return raw.split("\n").filter(Boolean).slice(-3).join(" ").slice(0, 500);
37767
+ }
37768
+ async function gitOut(cwd2, args) {
37761
37769
  try {
37762
- await run("git", commitArgs, { cwd: root, timeout: 6e4 });
37770
+ return (await run("git", args, { cwd: cwd2, timeout: 3e4 })).stdout.trim();
37763
37771
  } catch {
37764
- await run("git", ["-c", "user.name=4PM", "-c", "user.email=noreply@4pm.app", ...commitArgs], { cwd: root, timeout: 6e4 });
37772
+ return "";
37765
37773
  }
37766
- const branch = (repo.branch ?? "").trim();
37774
+ }
37775
+ async function commitAndOpenPr(root, repo, emit) {
37776
+ const result = { committed: false, pushed: false, branch: null, prUrl: null, step: null, error: null };
37777
+ const fail = (step, error) => {
37778
+ result.step = step;
37779
+ result.error = error;
37780
+ return result;
37781
+ };
37782
+ emit("commit", "Committing the scaffolded project\u2026");
37783
+ try {
37784
+ await run("git", ["add", "-A"], { cwd: root, timeout: 6e4 });
37785
+ const staged = (await run("git", ["diff", "--cached", "--name-only"], { cwd: root, timeout: 3e4 })).stdout.trim();
37786
+ if (staged) {
37787
+ const commitArgs = ["commit", "-m", "chore: scaffold project (4PM)"];
37788
+ try {
37789
+ await run("git", commitArgs, { cwd: root, timeout: 6e4 });
37790
+ } catch {
37791
+ await run("git", ["-c", "user.name=4PM", "-c", "user.email=noreply@4pm.app", ...commitArgs], { cwd: root, timeout: 6e4 });
37792
+ }
37793
+ } else {
37794
+ emit("commit", "Nothing new to commit.");
37795
+ }
37796
+ } catch (err) {
37797
+ const msg = errText(err);
37798
+ emit("commit-failed", `Could not commit the scaffold: ${msg}`);
37799
+ return fail("commit", msg);
37800
+ }
37801
+ result.committed = !!await gitOut(root, ["rev-parse", "--verify", "HEAD"]);
37802
+ const declared = (repo.branch ?? "").trim();
37803
+ const head = await gitOut(root, ["rev-parse", "--abbrev-ref", "HEAD"]);
37804
+ const branch = declared || (head && head !== "HEAD" ? head : "");
37805
+ result.branch = branch || null;
37767
37806
  emit("push", "Pushing the scaffold branch\u2026");
37768
37807
  try {
37769
- await run("git", ["push", "-u", "origin", ...branch ? [branch] : ["HEAD"]], { cwd: root, timeout: 12e4 });
37808
+ await run("git", ["push", "-u", "origin", branch || "HEAD"], { cwd: root, timeout: 12e4 });
37809
+ result.pushed = true;
37770
37810
  } catch (err) {
37771
- const msg = err instanceof Error ? err.message : String(err);
37772
- emit("push-failed", `Scaffold committed locally but the push failed: ${msg} \u2014 push it from the Git tab when credentials are available.`);
37773
- return;
37811
+ const msg = errText(err);
37812
+ emit("push-failed", `Scaffold committed locally but the push failed: ${msg} \u2014 retry from the project page when credentials are available.`);
37813
+ return fail("push", msg);
37774
37814
  }
37775
- const { provider, slug } = providerSlug(repo.url ?? "");
37815
+ const remoteHead = (await gitOut(root, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"])).replace(/^origin\//, "");
37816
+ if (branch && remoteHead && branch === remoteHead) {
37817
+ emit("pr", `Pushed to the default branch (${branch}) \u2014 no pull request needed.`);
37818
+ return result;
37819
+ }
37820
+ const url = (repo.url ?? "").trim() || await gitOut(root, ["remote", "get-url", "origin"]);
37821
+ const { provider, slug } = providerSlug(url);
37776
37822
  if (!provider) {
37777
37823
  emit("pr", "Unknown git host \u2014 skipping the pull request.");
37778
- return;
37824
+ return result;
37779
37825
  }
37780
37826
  emit("pr", `Opening a pull request via ${provider}\u2026`);
37781
37827
  try {
37782
- const args = provider === "glab" ? ["mr", "create", "--fill", ...slug ? ["-R", slug] : []] : ["pr", "create", "--fill", ...slug ? ["-R", slug] : []];
37828
+ const args = provider === "glab" ? ["mr", "create", "--fill", "--yes", ...branch ? ["--source-branch", branch] : [], ...slug ? ["-R", slug] : []] : ["pr", "create", "--fill", ...branch ? ["--head", branch] : [], ...slug ? ["-R", slug] : []];
37783
37829
  const { stdout } = await run(provider, args, { cwd: root, timeout: 12e4 });
37784
- const link2 = stdout.trim().split(/\s+/).find((s3) => /^https?:\/\//.test(s3)) ?? stdout.trim().split("\n").pop() ?? "";
37830
+ const link2 = stdout.trim().split(/\s+/).find((x) => /^https?:\/\//.test(x)) ?? null;
37831
+ result.prUrl = link2;
37785
37832
  emit("pr", `Pull request opened${link2 ? `: ${link2}` : "."}`);
37833
+ return result;
37786
37834
  } catch (err) {
37787
- const msg = err instanceof Error ? err.message : String(err);
37835
+ const msg = errText(err);
37836
+ const existing = /already exists/i.test(msg) ? msg.match(/https?:\/\/\S+/)?.[0] ?? null : null;
37837
+ if (existing) {
37838
+ result.prUrl = existing;
37839
+ emit("pr", `A pull request is already open: ${existing}`);
37840
+ return result;
37841
+ }
37788
37842
  emit("pr-failed", `Could not open a pull request (${provider}): ${msg}`);
37843
+ return fail("pr", msg);
37789
37844
  }
37790
37845
  }
37846
+ async function publishScaffold(root, emit) {
37847
+ let repo = {};
37848
+ try {
37849
+ const spec = JSON.parse(await readFile(join6(root, "project.spec.json"), "utf8"));
37850
+ repo = singleRepo(reposOf(spec)) ?? {};
37851
+ } catch {
37852
+ }
37853
+ return commitAndOpenPr(root, repo, emit);
37854
+ }
37791
37855
  function cloneArgs(url, dest, branch) {
37792
37856
  const b2 = (branch ?? "").trim();
37793
37857
  return b2 ? ["clone", "-b", b2, url, dest] : ["clone", url, dest];
@@ -37880,7 +37944,7 @@ function sampleDir() {
37880
37944
  }
37881
37945
  return bundled;
37882
37946
  }
37883
- async function scaffoldProject(payload, profileDir2, onProgress, ai2) {
37947
+ async function scaffoldProject(payload, profileDir2, onProgress, ai2, applyGitAuth2) {
37884
37948
  let lastStep = "start";
37885
37949
  const emit = (step, message) => {
37886
37950
  lastStep = step;
@@ -37892,15 +37956,16 @@ async function scaffoldProject(payload, profileDir2, onProgress, ai2) {
37892
37956
  await mkdir(target, { recursive: true });
37893
37957
  const repo = singleRepo(reposOf(payload.spec));
37894
37958
  if (!repo) throw new Error("A project must declare one repo (ADR-0314).");
37959
+ if (payload.gitAuth !== void 0) applyGitAuth2?.(payload.gitAuth ?? null, payload.gitAuthHost ?? null);
37895
37960
  emit("git", "Cloning repository\u2026");
37896
37961
  await provisionRepo(target, repo, emit, { createMissingBranch: true });
37897
37962
  await scaffoldRepo(target, payload.projectName, payload.spec, emit, { resetClaude: true, ai: ai2 });
37898
37963
  await attachSubmodules(target, submodulesOf(reposOf(payload.spec)), emit, true);
37899
37964
  emit("version", "Writing .4pm/.4pm.json\u2026");
37900
37965
  await writeTemplateMarker(target);
37901
- await commitAndOpenPr(target, repo, emit);
37966
+ const publish = await commitAndOpenPr(target, repo, emit);
37902
37967
  onProgress?.({ projectId: payload.projectId, step: "done", message: "Scaffold complete.", done: true });
37903
- return { ok: true, path: target };
37968
+ return { ok: true, path: target, publish };
37904
37969
  } catch (err) {
37905
37970
  return { ok: false, error: err instanceof Error ? err.message : String(err), step: lastStep };
37906
37971
  }
@@ -38034,7 +38099,7 @@ async function addProject(payload, profileDir2, onProgress, ai2) {
38034
38099
  }
38035
38100
 
38036
38101
  // src/core/worker-metrics.ts
38037
- import { statfs, readFile } from "fs/promises";
38102
+ import { statfs, readFile as readFile2 } from "fs/promises";
38038
38103
  import { existsSync as existsSync5 } from "fs";
38039
38104
  import { totalmem, freemem, cpus, platform, arch } from "os";
38040
38105
  var CGROUP = "/sys/fs/cgroup";
@@ -38042,7 +38107,7 @@ var MB = 1024 * 1024;
38042
38107
  var GB = 1024 * 1024 * 1024;
38043
38108
  async function readCgroup(file) {
38044
38109
  try {
38045
- return (await readFile(`${CGROUP}/${file}`, "utf8")).trim();
38110
+ return (await readFile2(`${CGROUP}/${file}`, "utf8")).trim();
38046
38111
  } catch {
38047
38112
  return null;
38048
38113
  }
@@ -38404,9 +38469,15 @@ var WsChannels = {
38404
38469
  // cli → server (reply): a short-lived GitHub-App installation token for the served project's repo,
38405
38470
  // per job scope; also the cli's "scope revoked" report (ADR-0356). The App key stays on the server.
38406
38471
  GIT_TOKEN: "git.token",
38472
+ // cli → server (one-way): the worker's per-repo git state of the served project (ADR-0369) — head,
38473
+ // branches + tracking, remote, dirty, worker-only commits, origin window. Latest kept per physic × repo.
38474
+ GIT_SNAPSHOT: "git.snapshot",
38407
38475
  PROJECT_CREATE: "project.create",
38408
38476
  PROJECT_ADD: "project.add",
38409
38477
  PROJECT_PROGRESS: "project.progress",
38478
+ // server → cli (reply): retry the scaffold commit → push → PR in the project folder (ADR-0368,
38479
+ // project-0074); the reply carries the new outcome, stored on `projects.scaffold_publish`.
38480
+ PROJECT_PUBLISH: "project.publish",
38410
38481
  // server → cli: keep the physic folder name in sync with the project (folder =
38411
38482
  // project name — ADR-0064) when the project is renamed.
38412
38483
  PHYSIC_SYNC: "physic.sync",
@@ -38671,8 +38742,8 @@ function resolveInstallTimeoutMs(sec) {
38671
38742
  return sec && sec > 0 ? sec * 1e3 : TOOL_INSTALL_TIMEOUT_DEFAULT_MS;
38672
38743
  }
38673
38744
  function sleep(ms2) {
38674
- return new Promise((resolve12) => {
38675
- const t2 = setTimeout(resolve12, ms2);
38745
+ return new Promise((resolve13) => {
38746
+ const t2 = setTimeout(resolve13, ms2);
38676
38747
  t2.unref?.();
38677
38748
  });
38678
38749
  }
@@ -38700,14 +38771,14 @@ function classifyToolFailure(code, out) {
38700
38771
  return { reason: "other", retryable: false };
38701
38772
  }
38702
38773
  function run2(cmd, args, onLine, timeoutMs = 18e4) {
38703
- return new Promise((resolve12) => {
38774
+ return new Promise((resolve13) => {
38704
38775
  let out = "";
38705
38776
  let buf = "";
38706
38777
  let child = null;
38707
38778
  const done = (code) => {
38708
38779
  clearTimeout(timer);
38709
38780
  if (onLine && buf.trim()) onLine(buf.trim());
38710
- resolve12({ code, out });
38781
+ resolve13({ code, out });
38711
38782
  };
38712
38783
  const timer = setTimeout(() => {
38713
38784
  child?.kill();
@@ -39090,11 +39161,11 @@ function getWorkingProfile(profileDir2, cmd) {
39090
39161
  return readState(profileDir2)[cmd] ?? null;
39091
39162
  }
39092
39163
  function setWorkingProfile(profileDir2, cmd, dir2) {
39093
- const state2 = readState(profileDir2);
39094
- if (state2[cmd] === dir2) return;
39095
- state2[cmd] = dir2;
39164
+ const state3 = readState(profileDir2);
39165
+ if (state3[cmd] === dir2) return;
39166
+ state3[cmd] = dir2;
39096
39167
  try {
39097
- writeFileSync4(statePath(profileDir2), JSON.stringify(state2, null, 2), "utf8");
39168
+ writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
39098
39169
  } catch {
39099
39170
  }
39100
39171
  }
@@ -39103,11 +39174,11 @@ function getWorkingCredential(profileDir2) {
39103
39174
  return readState(profileDir2)[WORKING_CREDENTIAL_KEY] ?? null;
39104
39175
  }
39105
39176
  function setWorkingCredential(profileDir2, key) {
39106
- const state2 = readState(profileDir2);
39107
- if (state2[WORKING_CREDENTIAL_KEY] === key) return;
39108
- state2[WORKING_CREDENTIAL_KEY] = key;
39177
+ const state3 = readState(profileDir2);
39178
+ if (state3[WORKING_CREDENTIAL_KEY] === key) return;
39179
+ state3[WORKING_CREDENTIAL_KEY] = key;
39109
39180
  try {
39110
- writeFileSync4(statePath(profileDir2), JSON.stringify(state2, null, 2), "utf8");
39181
+ writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
39111
39182
  } catch {
39112
39183
  }
39113
39184
  }
@@ -39116,20 +39187,20 @@ function getPinnedCredential(profileDir2) {
39116
39187
  return readState(profileDir2)[PINNED_CREDENTIAL_KEY] ?? null;
39117
39188
  }
39118
39189
  function setPinnedCredential(profileDir2, key) {
39119
- const state2 = readState(profileDir2);
39120
- if (state2[PINNED_CREDENTIAL_KEY] === key) return;
39121
- state2[PINNED_CREDENTIAL_KEY] = key;
39190
+ const state3 = readState(profileDir2);
39191
+ if (state3[PINNED_CREDENTIAL_KEY] === key) return;
39192
+ state3[PINNED_CREDENTIAL_KEY] = key;
39122
39193
  try {
39123
- writeFileSync4(statePath(profileDir2), JSON.stringify(state2, null, 2), "utf8");
39194
+ writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
39124
39195
  } catch {
39125
39196
  }
39126
39197
  }
39127
39198
  function clearPinnedCredential(profileDir2) {
39128
- const state2 = readState(profileDir2);
39129
- if (!(PINNED_CREDENTIAL_KEY in state2)) return;
39130
- delete state2[PINNED_CREDENTIAL_KEY];
39199
+ const state3 = readState(profileDir2);
39200
+ if (!(PINNED_CREDENTIAL_KEY in state3)) return;
39201
+ delete state3[PINNED_CREDENTIAL_KEY];
39131
39202
  try {
39132
- writeFileSync4(statePath(profileDir2), JSON.stringify(state2, null, 2), "utf8");
39203
+ writeFileSync4(statePath(profileDir2), JSON.stringify(state3, null, 2), "utf8");
39133
39204
  } catch {
39134
39205
  }
39135
39206
  }
@@ -39514,16 +39585,16 @@ async function fetchUsage(token) {
39514
39585
  }
39515
39586
  }
39516
39587
  function runUsageCli(dir2, aiCli2) {
39517
- return new Promise((resolve12) => {
39588
+ return new Promise((resolve13) => {
39518
39589
  let out = "";
39519
39590
  let child = null;
39520
39591
  const finish = () => {
39521
39592
  clearTimeout(timer);
39522
- resolve12(out);
39593
+ resolve13(out);
39523
39594
  };
39524
39595
  const timer = setTimeout(() => {
39525
39596
  child?.kill();
39526
- resolve12(out);
39597
+ resolve13(out);
39527
39598
  }, 3e4);
39528
39599
  timer.unref?.();
39529
39600
  try {
@@ -40219,7 +40290,7 @@ function parseModeratedAnswer(text) {
40219
40290
  return { body: text.trim() };
40220
40291
  }
40221
40292
  function runClaudeOnce(cmd, profile, prompt, extraEnv, imageDir, denyDirs = [], timeoutMs = ANSWER_TIMEOUT_MS) {
40222
- return new Promise((resolve12, reject) => {
40293
+ return new Promise((resolve13, reject) => {
40223
40294
  const isClaude = cmd.includes("claude");
40224
40295
  const args = [
40225
40296
  "-p",
@@ -40263,7 +40334,7 @@ function runClaudeOnce(cmd, profile, prompt, extraEnv, imageDir, denyDirs = [],
40263
40334
  clearTimeout(timer);
40264
40335
  cleanup();
40265
40336
  out += parser.flush();
40266
- resolve12({ code: code ?? -1, out, err, usage: parser.usage() });
40337
+ resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
40267
40338
  });
40268
40339
  child.stdin.write(prompt);
40269
40340
  child.stdin.end();
@@ -40354,14 +40425,14 @@ function reportToolResult(tool, ok, message) {
40354
40425
  }
40355
40426
 
40356
40427
  // src/core/autonomous.ts
40357
- import { readFile as readFile4, writeFile as writeFile4, chmod } from "fs/promises";
40428
+ import { readFile as readFile5, writeFile as writeFile4, chmod } from "fs/promises";
40358
40429
  import { readFileSync as readFileSync14 } from "fs";
40359
40430
  import { execFile as execFile3, spawn as spawn5 } from "child_process";
40360
40431
  import { join as join17 } from "path";
40361
40432
  import { promisify as promisify3 } from "util";
40362
40433
 
40363
40434
  // src/core/autonomous-config.ts
40364
- import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
40435
+ import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
40365
40436
  import { readFileSync as readFileSync13 } from "fs";
40366
40437
  import { join as join15 } from "path";
40367
40438
  var DEFAULT_AUTONOMOUS_CONFIG = {
@@ -40397,7 +40468,7 @@ function coerce2(raw) {
40397
40468
  }
40398
40469
  async function readAutonomousConfig(profileDir2) {
40399
40470
  try {
40400
- return coerce2(JSON.parse(await readFile2(autonomousConfigPath(profileDir2), "utf8")));
40471
+ return coerce2(JSON.parse(await readFile3(autonomousConfigPath(profileDir2), "utf8")));
40401
40472
  } catch {
40402
40473
  return { ...DEFAULT_AUTONOMOUS_CONFIG };
40403
40474
  }
@@ -40411,7 +40482,7 @@ function readAutonomousConfigSync(profileDir2) {
40411
40482
  }
40412
40483
  async function readAutonomousConfigText(profileDir2) {
40413
40484
  try {
40414
- return await readFile2(autonomousConfigPath(profileDir2), "utf8");
40485
+ return await readFile3(autonomousConfigPath(profileDir2), "utf8");
40415
40486
  } catch {
40416
40487
  return JSON.stringify(DEFAULT_AUTONOMOUS_CONFIG, null, 2) + "\n";
40417
40488
  }
@@ -40431,7 +40502,7 @@ function isInQuietHours(quietHours, now = /* @__PURE__ */ new Date()) {
40431
40502
  }
40432
40503
 
40433
40504
  // src/core/autonomous-history.ts
40434
- import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
40505
+ import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
40435
40506
  import { join as join16 } from "path";
40436
40507
  var HISTORIES_REL = ".claude/.autonomous.histories.json";
40437
40508
  var MAX_RECORDS = 50;
@@ -40441,7 +40512,7 @@ function historiesPath(root) {
40441
40512
  }
40442
40513
  async function readHistories(root) {
40443
40514
  try {
40444
- const d = JSON.parse(await readFile3(historiesPath(root), "utf8"));
40515
+ const d = JSON.parse(await readFile4(historiesPath(root), "utf8"));
40445
40516
  const ticks = d.ticks ?? {};
40446
40517
  return {
40447
40518
  cronApplied: typeof d.cronApplied === "string" ? d.cronApplied : typeof d.cron_applied === "string" ? d.cron_applied : "",
@@ -40483,7 +40554,7 @@ var BOOK_FILES = {
40483
40554
  };
40484
40555
  async function readText(path, fallback = "") {
40485
40556
  try {
40486
- return await readFile4(path, "utf8");
40557
+ return await readFile5(path, "utf8");
40487
40558
  } catch {
40488
40559
  return fallback;
40489
40560
  }
@@ -40548,10 +40619,10 @@ async function crontabList() {
40548
40619
  }
40549
40620
  }
40550
40621
  async function crontabSet(content) {
40551
- return new Promise((resolve12) => {
40622
+ return new Promise((resolve13) => {
40552
40623
  const child = spawn5("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
40553
- child.on("error", () => resolve12(false));
40554
- child.on("close", (code) => resolve12(code === 0));
40624
+ child.on("error", () => resolve13(false));
40625
+ child.on("close", (code) => resolve13(code === 0));
40555
40626
  child.stdin.end(content.endsWith("\n") ? content : content + "\n");
40556
40627
  });
40557
40628
  }
@@ -40724,14 +40795,14 @@ function detectContainerized() {
40724
40795
  }
40725
40796
  }
40726
40797
  function canReach(host, port) {
40727
- return new Promise((resolve12) => {
40798
+ return new Promise((resolve13) => {
40728
40799
  const socket = connect({ host, port });
40729
40800
  let done = false;
40730
40801
  const finish = (ok) => {
40731
40802
  if (done) return;
40732
40803
  done = true;
40733
40804
  socket.destroy();
40734
- resolve12(ok);
40805
+ resolve13(ok);
40735
40806
  };
40736
40807
  socket.setTimeout(PROBE_TIMEOUT_MS);
40737
40808
  socket.once("connect", () => finish(true));
@@ -40916,7 +40987,7 @@ async function issueGitToken(scope, host, path) {
40916
40987
  }
40917
40988
  }
40918
40989
  function revokeAtGithub(t2) {
40919
- return new Promise((resolve12) => {
40990
+ return new Promise((resolve13) => {
40920
40991
  try {
40921
40992
  const req = httpsRequest(
40922
40993
  new URL(`${t2.apiBase.replace(/\/+$/, "")}/installation/token`),
@@ -40927,14 +40998,14 @@ function revokeAtGithub(t2) {
40927
40998
  },
40928
40999
  (res) => {
40929
41000
  res.resume();
40930
- res.on("end", () => resolve12());
41001
+ res.on("end", () => resolve13());
40931
41002
  }
40932
41003
  );
40933
41004
  req.on("timeout", () => req.destroy());
40934
- req.on("error", () => resolve12());
41005
+ req.on("error", () => resolve13());
40935
41006
  req.end();
40936
41007
  } catch {
40937
- resolve12();
41008
+ resolve13();
40938
41009
  }
40939
41010
  });
40940
41011
  }
@@ -40956,26 +41027,113 @@ async function revokeAll() {
40956
41027
  await Promise.all([...state.scopes.keys()].map((s3) => endGitScope(s3)));
40957
41028
  }
40958
41029
 
40959
- // src/core/ws-client/transcript.ts
40960
- function toDtoEntry(e) {
40961
- return {
40962
- id: e.id,
40963
- source: e.source,
40964
- kind: e.kind,
40965
- text: e.text,
40966
- level: e.level,
40967
- resultKind: e.resultKind,
40968
- // Processing time on a terminal `exit` entry (ADR-0249) — the web renders it inline.
40969
- ...e.durationMs != null ? { durationMs: e.durationMs } : {},
40970
- // AI-run metadata on an `aireq` marker — powers the web's clickable CLI name → details modal.
40971
- ...e.aiMeta ? { aiMeta: e.aiMeta } : {}
40972
- };
41030
+ // src/core/git-snapshot.ts
41031
+ import { execFile as execFile6 } from "child_process";
41032
+ import { stat } from "fs/promises";
41033
+ import { join as join20, resolve as resolve5 } from "path";
41034
+ import { promisify as promisify6 } from "util";
41035
+
41036
+ // src/core/git-history.ts
41037
+ import { execFile as execFile4 } from "child_process";
41038
+ import { resolve as resolve4, sep as sep2 } from "path";
41039
+ import { promisify as promisify4 } from "util";
41040
+ var run4 = promisify4(execFile4);
41041
+ var FS = "";
41042
+ var RS = "";
41043
+ function resolveRepo(root, repo) {
41044
+ if (!root) return null;
41045
+ const base = resolve4(root);
41046
+ const requested = resolve4(base, repo && repo.trim() ? repo : ".");
41047
+ return requested === base || requested.startsWith(base + sep2) ? requested : base;
40973
41048
  }
40974
- function envReconnectMaxSec() {
40975
- const raw = Number(process.env.FOURPM_RECONNECT_MAX_BACKOFF_SEC);
40976
- if (!Number.isFinite(raw)) return null;
40977
- const { min, max } = CLI_SETTINGS_BOUNDS.reconnectMaxBackoffSec;
40978
- return Math.min(Math.max(Math.floor(raw), min), max);
41049
+ async function git(cwd2, args) {
41050
+ try {
41051
+ const { stdout } = await run4("git", args, {
41052
+ cwd: cwd2,
41053
+ timeout: 15e3,
41054
+ maxBuffer: 8 * 1024 * 1024
41055
+ });
41056
+ return stdout;
41057
+ } catch {
41058
+ return "";
41059
+ }
41060
+ }
41061
+ async function gitRepos(root) {
41062
+ if (!root) return { repos: [] };
41063
+ const base = resolve4(root);
41064
+ const top = (await git(base, ["rev-parse", "--show-toplevel"])).trim();
41065
+ if (!top || !(top === base || top.startsWith(base + sep2))) return { repos: [] };
41066
+ const repos = [];
41067
+ const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
41068
+ const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
41069
+ repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
41070
+ const subOut = await git(base, ["submodule", "status"]);
41071
+ for (const line of subOut.split("\n")) {
41072
+ const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
41073
+ const subPath = m2?.[1];
41074
+ if (!subPath) continue;
41075
+ const abs = resolve4(base, subPath);
41076
+ if (!(abs === base || abs.startsWith(base + sep2))) continue;
41077
+ const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
41078
+ const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
41079
+ repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
41080
+ }
41081
+ return { repos };
41082
+ }
41083
+ async function gitLog(root, repo, skip, limit) {
41084
+ const cwd2 = resolveRepo(root, repo);
41085
+ if (!cwd2) return { entries: [], hasMore: false };
41086
+ const capped = Math.min(Math.max(limit, 1), 200);
41087
+ const format = ["%H", "%h", "%an", "%aI", "%s"].join(FS) + RS;
41088
+ const stdout = await git(cwd2, [
41089
+ "log",
41090
+ `--skip=${Math.max(skip, 0)}`,
41091
+ `-n`,
41092
+ String(capped + 1),
41093
+ `--pretty=format:${format}`
41094
+ ]);
41095
+ const rows = stdout.split(RS).map((r) => r.replace(/^\n/, "")).filter((r) => r.length > 0);
41096
+ const entries2 = rows.slice(0, capped).map((row) => {
41097
+ const [hash = "", shortHash = "", author = "", date = "", subject = ""] = row.split(FS);
41098
+ return { hash, shortHash, author, date, subject };
41099
+ });
41100
+ return { entries: entries2, hasMore: rows.length > capped };
41101
+ }
41102
+ async function gitCommit(root, repo, hash) {
41103
+ const cwd2 = resolveRepo(root, repo);
41104
+ const empty = { hash, author: "", date: "", subject: "", files: [] };
41105
+ if (!cwd2 || !hash) return empty;
41106
+ const format = ["%H", "%an", "%aI", "%s"].join(FS);
41107
+ const stdout = await git(cwd2, [
41108
+ "show",
41109
+ "--name-status",
41110
+ "-M",
41111
+ `--pretty=format:${format}`,
41112
+ hash
41113
+ ]);
41114
+ if (!stdout) return empty;
41115
+ const lines = stdout.split("\n");
41116
+ const [full = hash, author = "", date = "", subject = ""] = (lines[0] ?? "").split(FS);
41117
+ const files = [];
41118
+ for (const line of lines.slice(1)) {
41119
+ if (!line.trim()) continue;
41120
+ const parts = line.split(" ");
41121
+ const status = (parts[0] ?? "").charAt(0);
41122
+ if (!status) continue;
41123
+ if (status === "R" || status === "C") {
41124
+ files.push({ path: parts[2] ?? "", status, oldPath: parts[1] ?? void 0 });
41125
+ } else {
41126
+ files.push({ path: parts[1] ?? "", status });
41127
+ }
41128
+ }
41129
+ return { hash: full, author, date, subject, files };
41130
+ }
41131
+ async function gitCommitDiff(root, repo, hash, path) {
41132
+ const cwd2 = resolveRepo(root, repo);
41133
+ if (!cwd2 || !hash || !path) return { path, oldContent: "", newContent: "" };
41134
+ const oldContent = await git(cwd2, ["show", `${hash}^:${path}`]);
41135
+ const newContent = await git(cwd2, ["show", `${hash}:${path}`]);
41136
+ return { path, oldContent, newContent };
40979
41137
  }
40980
41138
 
40981
41139
  // src/core/ws-client/command-dispatch.ts
@@ -41064,7 +41222,7 @@ async function runCommand(dispatch, emitRaw, opts) {
41064
41222
  });
41065
41223
  child.stdout?.on("data", (data) => batcher.push(data.toString("utf8")));
41066
41224
  child.stderr?.on("data", (data) => batcher.push(data.toString("utf8")));
41067
- await new Promise((resolve12) => {
41225
+ await new Promise((resolve13) => {
41068
41226
  child.on("close", (exitCode) => {
41069
41227
  clearTimers();
41070
41228
  batcher.flush();
@@ -41078,7 +41236,7 @@ async function runCommand(dispatch, emitRaw, opts) {
41078
41236
  // note instead of spinning.
41079
41237
  exitCode: cancelled ? COMMAND_CANCELLED_EXIT_CODE : timedOut ? 124 : exitCode ?? -1
41080
41238
  });
41081
- resolve12();
41239
+ resolve13();
41082
41240
  });
41083
41241
  child.on("error", (err) => {
41084
41242
  clearTimers();
@@ -41090,7 +41248,7 @@ async function runCommand(dispatch, emitRaw, opts) {
41090
41248
  done: true,
41091
41249
  exitCode: -1
41092
41250
  });
41093
- resolve12();
41251
+ resolve13();
41094
41252
  });
41095
41253
  });
41096
41254
  }
@@ -41113,15 +41271,15 @@ function isOutOfCredits(text) {
41113
41271
  );
41114
41272
  }
41115
41273
  function runAttempt(commandId, cmd, args, stdin, cwd2, env3, onChunk, timeoutMs, signal) {
41116
- return new Promise((resolve12) => {
41274
+ return new Promise((resolve13) => {
41117
41275
  runCommand(
41118
41276
  { commandId, cmd, args, path: cwd2, env: env3 },
41119
41277
  (out) => {
41120
41278
  if (out.chunk) onChunk(out.chunk);
41121
- if (out.done) resolve12(out.exitCode ?? -1);
41279
+ if (out.done) resolve13(out.exitCode ?? -1);
41122
41280
  },
41123
41281
  { stdin, ...timeoutMs > 0 ? { timeoutMs } : {}, ...signal ? { signal } : {} }
41124
- ).catch(() => resolve12(-1));
41282
+ ).catch(() => resolve13(-1));
41125
41283
  });
41126
41284
  }
41127
41285
  async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0, signal) {
@@ -41342,8 +41500,8 @@ function runHistory(ctx) {
41342
41500
  ctx.print(t("slash.history.header", { n: recent.length, total: all.length }));
41343
41501
  recent.forEach((e, i) => {
41344
41502
  const time = new Date(e.startedAt).toLocaleTimeString();
41345
- const state2 = e.status === "done" ? "\u2713" : e.status === "failed" ? `\u2717 exit ${e.exitCode}` : t("slash.history.running");
41346
- ctx.print(` [${i + 1}] ${time} ${commandLine(e.cmd, e.args)} \xB7 ${state2}`);
41503
+ const state3 = e.status === "done" ? "\u2713" : e.status === "failed" ? `\u2717 exit ${e.exitCode}` : t("slash.history.running");
41504
+ ctx.print(` [${i + 1}] ${time} ${commandLine(e.cmd, e.args)} \xB7 ${state3}`);
41347
41505
  });
41348
41506
  }
41349
41507
  function runOutput(ctx) {
@@ -41354,8 +41512,8 @@ function runOutput(ctx) {
41354
41512
  return;
41355
41513
  }
41356
41514
  const entry = all.slice().reverse()[n - 1];
41357
- const state2 = entry.exitCode != null ? ` (exit ${entry.exitCode})` : "";
41358
- ctx.print(t("slash.output.header", { n, cmd: commandLine(entry.cmd, entry.args), status: entry.status, state: state2 }));
41515
+ const state3 = entry.exitCode != null ? ` (exit ${entry.exitCode})` : "";
41516
+ ctx.print(t("slash.output.header", { n, cmd: commandLine(entry.cmd, entry.args), status: entry.status, state: state3 }));
41359
41517
  const out = readCommandOutput(entry.commandId);
41360
41518
  if (out == null) {
41361
41519
  ctx.print(t("slash.output.none"), "warn");
@@ -41650,7 +41808,7 @@ function buildPrompt2(input) {
41650
41808
  ].join("\n");
41651
41809
  }
41652
41810
  function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
41653
- return new Promise((resolve12, reject) => {
41811
+ return new Promise((resolve13, reject) => {
41654
41812
  const args = [
41655
41813
  "-p",
41656
41814
  "--output-format",
@@ -41685,7 +41843,7 @@ function runOnce(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
41685
41843
  });
41686
41844
  child.on("close", (code) => {
41687
41845
  clearTimeout(timer);
41688
- resolve12({ code: code ?? -1, out, err });
41846
+ resolve13({ code: code ?? -1, out, err });
41689
41847
  });
41690
41848
  child.stdin.write(prompt);
41691
41849
  child.stdin.end();
@@ -41712,13 +41870,13 @@ async function runMemoryCompaction(ai2, cwd2, input) {
41712
41870
  }
41713
41871
 
41714
41872
  // src/core/git-commit-identity.ts
41715
- import { execFile as execFile4 } from "child_process";
41716
- import { promisify as promisify4 } from "util";
41717
- var run4 = promisify4(execFile4);
41873
+ import { execFile as execFile5 } from "child_process";
41874
+ import { promisify as promisify5 } from "util";
41875
+ var run5 = promisify5(execFile5);
41718
41876
  async function setCommitAuthor(cwd2, machineUsername) {
41719
41877
  if (!machineUsername) return;
41720
41878
  try {
41721
- await run4("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
41879
+ await run5("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
41722
41880
  } catch {
41723
41881
  }
41724
41882
  }
@@ -41726,13 +41884,13 @@ async function setCommitAuthor(cwd2, machineUsername) {
41726
41884
  // src/core/ws-client/run-slot.ts
41727
41885
  var NO_SLOT = { release: () => void 0 };
41728
41886
  function sleep2(ms2, signal) {
41729
- return new Promise((resolve12) => {
41730
- if (signal?.aborted) return resolve12();
41887
+ return new Promise((resolve13) => {
41888
+ if (signal?.aborted) return resolve13();
41731
41889
  const timer = setTimeout(done, ms2);
41732
41890
  function done() {
41733
41891
  clearTimeout(timer);
41734
41892
  signal?.removeEventListener("abort", done);
41735
- resolve12();
41893
+ resolve13();
41736
41894
  }
41737
41895
  signal?.addEventListener("abort", done, { once: true });
41738
41896
  });
@@ -41794,11 +41952,41 @@ function leaveQueue(ctx, ticket) {
41794
41952
  // src/core/ws-client/command-dispatch.ts
41795
41953
  var MEMORY_SEED_HEADER = "CONTEXT MEMORY from earlier in this conversation (may span prior sessions/accounts). Use it as background; do not repeat it back unless relevant:";
41796
41954
  var activeRuns = /* @__PURE__ */ new Map();
41955
+ var GIT_STATE_SUBCOMMANDS = /* @__PURE__ */ new Set([
41956
+ "add",
41957
+ "branch",
41958
+ "checkout",
41959
+ "cherry-pick",
41960
+ "commit",
41961
+ "fetch",
41962
+ "merge",
41963
+ "pull",
41964
+ "push",
41965
+ "rebase",
41966
+ "reset",
41967
+ "restore",
41968
+ "revert",
41969
+ "rm",
41970
+ "stash",
41971
+ "switch",
41972
+ "tag",
41973
+ "mv"
41974
+ ]);
41975
+ function changesGitState(cmd, args) {
41976
+ if (cmd === "gh" || cmd === "glab") return args.includes("create") || args.includes("merge");
41977
+ if (cmd !== "git") return false;
41978
+ let i = 0;
41979
+ while (args[i] === "-C" || args[i] === "-c") i += 2;
41980
+ return GIT_STATE_SUBCOMMANDS.has(args[i] ?? "");
41981
+ }
41982
+ function activeAiRunCount() {
41983
+ return activeRuns.size;
41984
+ }
41797
41985
  function cancelActiveRun(commandId) {
41798
- const run11 = activeRuns.get(commandId);
41799
- if (!run11) return { ok: false, state: "unknown" };
41800
- run11.controller.abort();
41801
- return { ok: true, state: run11.state };
41986
+ const run12 = activeRuns.get(commandId);
41987
+ if (!run12) return { ok: false, state: "unknown" };
41988
+ run12.controller.abort();
41989
+ return { ok: true, state: run12.state };
41802
41990
  }
41803
41991
  function handleCommandChannels(ctx, message, payload) {
41804
41992
  if (message.channel === WsChannels.COMMAND_CANCEL) {
@@ -41863,6 +42051,7 @@ function handleCommandChannels(ctx, message, payload) {
41863
42051
  ctx.bus.endBusy(dispatch.cmd);
41864
42052
  const code = out.exitCode ?? -1;
41865
42053
  finishCommand(out.commandId, code);
42054
+ if (changesGitState(dispatch.cmd, dispatch.args)) requestGitSnapshot("git-write");
41866
42055
  ctx.bus.push({
41867
42056
  source: "server",
41868
42057
  kind: "exit",
@@ -41894,15 +42083,16 @@ async function runLocalCommand(ctx, input) {
41894
42083
  await runAiPrompt(ctx, prompt, commandId, "local");
41895
42084
  }
41896
42085
  async function runAiPrompt(ctx, prompt, commandId, origin, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
41897
- const run11 = { controller: new AbortController(), state: "queued" };
41898
- activeRuns.set(commandId, run11);
42086
+ const run12 = { controller: new AbortController(), state: "queued" };
42087
+ activeRuns.set(commandId, run12);
41899
42088
  try {
41900
- await runAiPromptInner(ctx, prompt, commandId, origin, run11, oneShot, images, aiConfig, readOnly, bypass);
42089
+ await runAiPromptInner(ctx, prompt, commandId, origin, run12, oneShot, images, aiConfig, readOnly, bypass);
41901
42090
  } finally {
41902
42091
  activeRuns.delete(commandId);
42092
+ if (!readOnly) requestGitSnapshot("ai-run");
41903
42093
  }
41904
42094
  }
41905
- async function runAiPromptInner(ctx, prompt, commandId, origin, run11, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
42095
+ async function runAiPromptInner(ctx, prompt, commandId, origin, run12, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
41906
42096
  const config = readProfileConfig(ctx.profileDir);
41907
42097
  const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
41908
42098
  const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
@@ -42083,7 +42273,7 @@ ${guardedPrompt}`;
42083
42273
  queue: { position, limit, running: running2 }
42084
42274
  });
42085
42275
  },
42086
- run11.controller.signal
42276
+ run12.controller.signal
42087
42277
  );
42088
42278
  if (slot.kind === "timeout") {
42089
42279
  neverStarted = true;
@@ -42104,9 +42294,9 @@ ${guardedPrompt}`;
42104
42294
  result = { exitCode: COMMAND_CANCELLED_EXIT_CODE, workedDir: null, workedKey: null, workedCmd: null, usage: { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 }, sessionId: "", cancelled: true };
42105
42295
  } else {
42106
42296
  if (slot.queued) ctx.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: "", log: true, queue: null });
42107
- run11.state = "running";
42297
+ run12.state = "running";
42108
42298
  try {
42109
- result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs, run11.controller.signal);
42299
+ result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs, run12.controller.signal);
42110
42300
  } finally {
42111
42301
  slot.handle.release();
42112
42302
  }
@@ -42417,10 +42607,200 @@ function rejectByReview(ctx, commandId, origin, prompt, reasons) {
42417
42607
  });
42418
42608
  }
42419
42609
 
42610
+ // src/core/git-snapshot.ts
42611
+ var run6 = promisify6(execFile6);
42612
+ var US = "";
42613
+ var RS2 = "";
42614
+ var MIN_SPACING_MS = 15e3;
42615
+ var DEFAULT_INTERVAL_MIN = 10;
42616
+ async function git2(cwd2, args, timeout = 2e4) {
42617
+ try {
42618
+ const { stdout } = await run6("git", args, { cwd: cwd2, timeout, maxBuffer: 16 * 1024 * 1024 });
42619
+ return stdout;
42620
+ } catch {
42621
+ return "";
42622
+ }
42623
+ }
42624
+ function parseTrack(track) {
42625
+ const ahead = /ahead (\d+)/.exec(track)?.[1];
42626
+ const behind = /behind (\d+)/.exec(track)?.[1];
42627
+ return { ahead: ahead ? Number(ahead) : 0, behind: behind ? Number(behind) : 0 };
42628
+ }
42629
+ function parseStatus(out) {
42630
+ const dirty = { staged: 0, unstaged: 0, untracked: 0, conflicted: 0 };
42631
+ const files = [];
42632
+ for (const line of out.split("\n")) {
42633
+ if (!line || line.startsWith("#")) continue;
42634
+ const kind = line[0];
42635
+ if (kind === "?") {
42636
+ dirty.untracked++;
42637
+ files.push({ status: "?", path: line.slice(2) });
42638
+ } else if (kind === "u") {
42639
+ dirty.conflicted++;
42640
+ files.push({ status: "U", path: line.split(" ").slice(10).join(" ") });
42641
+ } else if (kind === "1" || kind === "2") {
42642
+ const xy = line.slice(2, 4);
42643
+ const x = xy[0] ?? ".";
42644
+ const y = xy[1] ?? ".";
42645
+ if (x !== ".") dirty.staged++;
42646
+ if (y !== ".") dirty.unstaged++;
42647
+ const parts = line.split(" ");
42648
+ const path = (kind === "1" ? parts.slice(8) : parts.slice(9)).join(" ").split(" ")[0] ?? "";
42649
+ files.push({ status: y !== "." ? y : x, path });
42650
+ }
42651
+ }
42652
+ return { dirty, files };
42653
+ }
42654
+ function parseLog(out, withFiles) {
42655
+ const commits = [];
42656
+ for (const rec of out.split(RS2)) {
42657
+ if (!rec.trim()) continue;
42658
+ const [header = "", ...rest] = rec.split("\n");
42659
+ const [sha = "", parents = "", author = "", date = "", subject = ""] = header.split(US);
42660
+ if (!sha) continue;
42661
+ const files = withFiles ? rest.map((l) => l.trim()).filter(Boolean).map((l) => {
42662
+ const cols = l.split(" ");
42663
+ return { status: (cols[0] ?? "M").charAt(0), path: cols[cols.length - 1] ?? "" };
42664
+ }) : [];
42665
+ commits.push({ sha, parents: parents.split(" ").filter(Boolean), author, date, subject, files });
42666
+ }
42667
+ return commits;
42668
+ }
42669
+ async function buildRepoSnapshot(dir2, subdir) {
42670
+ const L2 = GIT_SNAPSHOT_LIMITS;
42671
+ const fmt = `--format=${RS2}%H${US}%P${US}%an${US}%aI${US}%s`;
42672
+ const [abbrev, headSha, heads, remotes, remoteHead, status, gitDir] = await Promise.all([
42673
+ git2(dir2, ["rev-parse", "--abbrev-ref", "HEAD"]),
42674
+ git2(dir2, ["rev-parse", "--verify", "HEAD"]),
42675
+ git2(dir2, ["for-each-ref", "refs/heads", `--format=%(refname:short)${US}%(objectname)${US}%(upstream:short)${US}%(upstream:track,nobracket)`]),
42676
+ git2(dir2, ["for-each-ref", "refs/remotes/origin", `--format=%(refname:short)${US}%(objectname)${US}%(symref)`]),
42677
+ git2(dir2, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]),
42678
+ git2(dir2, ["status", "--porcelain=v2", "--untracked-files=all"]),
42679
+ git2(dir2, ["rev-parse", "--absolute-git-dir"])
42680
+ ]);
42681
+ const branchName = abbrev.trim();
42682
+ const detached = branchName === "HEAD";
42683
+ const branches = heads.split("\n").filter(Boolean).map((l) => {
42684
+ const [name = "", sha = "", upstream2 = "", track = ""] = l.split(US);
42685
+ return { name, sha, upstream: upstream2 || null, ...parseTrack(track) };
42686
+ });
42687
+ const remoteBranches = remotes.split("\n").filter(Boolean).map((l) => l.split(US)).filter(([, , symref]) => !symref).map(([name = "", sha = ""]) => ({ name: name.replace(/^origin\//, ""), sha })).filter((b2) => b2.name && b2.name !== "origin");
42688
+ const { dirty, files } = parseStatus(status);
42689
+ const [localOut, remoteOut] = await Promise.all([
42690
+ git2(dir2, ["log", "--branches", "--not", "--remotes", "-n", String(L2.localCommits + 1), "--name-status", fmt], 3e4),
42691
+ git2(dir2, ["log", "--remotes", "--date-order", "-n", String(L2.remoteCommits), fmt], 3e4)
42692
+ ]);
42693
+ const localAll = parseLog(localOut, true);
42694
+ const localCommits = localAll.slice(0, L2.localCommits).map((c) => ({
42695
+ ...c,
42696
+ branches: branches.filter((b2) => b2.sha === c.sha).map((b2) => b2.name),
42697
+ files: c.files.slice(0, L2.filesPerCommit),
42698
+ filesTruncated: c.files.length > L2.filesPerCommit
42699
+ }));
42700
+ const remoteCommits = parseLog(remoteOut, false).map(({ files: _f, ...c }) => c);
42701
+ const defaultBranch = remoteHead.trim().replace(/^origin\//, "") || null;
42702
+ const upstream = branches.find((b2) => b2.name === branchName)?.upstream;
42703
+ const baseRef = upstream ?? (defaultBranch ? `origin/${defaultBranch}` : null);
42704
+ const baseSha = headSha.trim() && baseRef ? (await git2(dir2, ["merge-base", "HEAD", baseRef])).trim() || null : null;
42705
+ let lastFetchAt = null;
42706
+ try {
42707
+ if (gitDir.trim()) lastFetchAt = (await stat(join20(gitDir.trim(), "FETCH_HEAD"))).mtime.toISOString();
42708
+ } catch {
42709
+ lastFetchAt = null;
42710
+ }
42711
+ return {
42712
+ subdir,
42713
+ head: { branch: detached || !branchName ? null : branchName, sha: headSha.trim() || null, detached },
42714
+ branches,
42715
+ remote: { name: "origin", defaultBranch, branches: remoteBranches },
42716
+ dirty,
42717
+ dirtyFiles: files.slice(0, L2.dirtyFiles),
42718
+ dirtyTruncated: files.length > L2.dirtyFiles,
42719
+ localCommits,
42720
+ localTruncated: localAll.length > L2.localCommits,
42721
+ remoteCommits,
42722
+ baseSha,
42723
+ lastFetchAt
42724
+ };
42725
+ }
42726
+ async function buildGitSnapshot(root) {
42727
+ const base = resolve5(root);
42728
+ const { repos } = await gitRepos(base);
42729
+ const snaps = await Promise.all(repos.map((r) => buildRepoSnapshot(r.subdir ? resolve5(base, r.subdir) : base, r.subdir)));
42730
+ return { takenAt: (/* @__PURE__ */ new Date()).toISOString(), repos: snaps };
42731
+ }
42732
+ async function fetchAll(root) {
42733
+ const base = resolve5(root);
42734
+ const { repos } = await gitRepos(base);
42735
+ for (const r of repos) await git2(r.subdir ? resolve5(base, r.subdir) : base, ["fetch", "--prune", "--quiet", "origin"], 6e4);
42736
+ }
42737
+ var state2 = { host: null, lastSentAt: 0, pending: null, periodic: null, running: false };
42738
+ async function reportNow(fetchFirst) {
42739
+ const host = state2.host;
42740
+ const root = host?.physicRoot;
42741
+ if (!host || !root || state2.running || activeAiRunCount() > 0) return;
42742
+ state2.running = true;
42743
+ try {
42744
+ if (fetchFirst) await fetchAll(root);
42745
+ const payload = await buildGitSnapshot(root);
42746
+ if (payload.repos.length === 0) return;
42747
+ host.send(WsChannels.GIT_SNAPSHOT, payload);
42748
+ state2.lastSentAt = Date.now();
42749
+ } catch (err) {
42750
+ logger.warn("git.snapshot.failed", { error: String(err) });
42751
+ } finally {
42752
+ state2.running = false;
42753
+ }
42754
+ }
42755
+ function requestGitSnapshot(reason) {
42756
+ if (!state2.host || state2.pending) return;
42757
+ const wait = Math.max(0, state2.lastSentAt + MIN_SPACING_MS - Date.now());
42758
+ state2.pending = setTimeout(() => {
42759
+ state2.pending = null;
42760
+ logger.debug("git.snapshot.request", { reason });
42761
+ void reportNow(false);
42762
+ }, wait || 1e3);
42763
+ state2.pending.unref?.();
42764
+ }
42765
+ function startGitSnapshots(host) {
42766
+ state2.host = host;
42767
+ if (state2.periodic) clearInterval(state2.periodic);
42768
+ state2.periodic = null;
42769
+ const raw = readProfileConfig(host.profileDir).gitSnapshotIntervalMin;
42770
+ const minutes = typeof raw === "number" && Number.isFinite(raw) && raw >= 0 ? raw : DEFAULT_INTERVAL_MIN;
42771
+ if (minutes > 0) {
42772
+ state2.periodic = setInterval(() => void reportNow(true), minutes * 6e4);
42773
+ state2.periodic.unref?.();
42774
+ }
42775
+ requestGitSnapshot("connect");
42776
+ }
42777
+
42778
+ // src/core/ws-client/transcript.ts
42779
+ function toDtoEntry(e) {
42780
+ return {
42781
+ id: e.id,
42782
+ source: e.source,
42783
+ kind: e.kind,
42784
+ text: e.text,
42785
+ level: e.level,
42786
+ resultKind: e.resultKind,
42787
+ // Processing time on a terminal `exit` entry (ADR-0249) — the web renders it inline.
42788
+ ...e.durationMs != null ? { durationMs: e.durationMs } : {},
42789
+ // AI-run metadata on an `aireq` marker — powers the web's clickable CLI name → details modal.
42790
+ ...e.aiMeta ? { aiMeta: e.aiMeta } : {}
42791
+ };
42792
+ }
42793
+ function envReconnectMaxSec() {
42794
+ const raw = Number(process.env.FOURPM_RECONNECT_MAX_BACKOFF_SEC);
42795
+ if (!Number.isFinite(raw)) return null;
42796
+ const { min, max } = CLI_SETTINGS_BOUNDS.reconnectMaxBackoffSec;
42797
+ return Math.min(Math.max(Math.floor(raw), min), max);
42798
+ }
42799
+
42420
42800
  // src/core/autonomous-cycle.ts
42421
42801
  import { randomUUID } from "crypto";
42422
- import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile5, readdir as readdir2, rm as rm2, stat } from "fs/promises";
42423
- import { join as join20 } from "path";
42802
+ import { appendFile as appendFile2, mkdir as mkdir3, readFile as readFile6, readdir as readdir2, rm as rm2, stat as stat2 } from "fs/promises";
42803
+ import { join as join21 } from "path";
42424
42804
  function buildAutonomousCyclePrompt(opts = {}) {
42425
42805
  const capNote = opts.intakeBlocked ? `
42426
42806
 
@@ -42620,11 +43000,11 @@ async function reportNewBookRows(ctx, root, before) {
42620
43000
  }
42621
43001
  async function dayLog(root, line) {
42622
43002
  try {
42623
- const dir2 = join20(root, ".claude", "logs");
43003
+ const dir2 = join21(root, ".claude", "logs");
42624
43004
  await mkdir3(dir2, { recursive: true });
42625
43005
  const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
42626
43006
  const ts2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
42627
- await appendFile2(join20(dir2, `autonomous-tick-${day}.log`), `${ts2} ${line}
43007
+ await appendFile2(join21(dir2, `autonomous-tick-${day}.log`), `${ts2} ${line}
42628
43008
  `, "utf8");
42629
43009
  } catch {
42630
43010
  }
@@ -42632,12 +43012,12 @@ async function dayLog(root, line) {
42632
43012
  async function pruneLogs(root, retentionDays) {
42633
43013
  if (!(retentionDays > 0)) return;
42634
43014
  try {
42635
- const dir2 = join20(root, ".claude", "logs");
43015
+ const dir2 = join21(root, ".claude", "logs");
42636
43016
  const cutoff = Date.now() - retentionDays * 864e5;
42637
43017
  for (const f2 of await readdir2(dir2)) {
42638
43018
  if (!/^autonomous-tick-.*\.log$/.test(f2)) continue;
42639
- const st2 = await stat(join20(dir2, f2)).catch(() => null);
42640
- if (st2 && st2.mtimeMs < cutoff) await rm2(join20(dir2, f2), { force: true }).catch(() => void 0);
43019
+ const st2 = await stat2(join21(dir2, f2)).catch(() => null);
43020
+ if (st2 && st2.mtimeMs < cutoff) await rm2(join21(dir2, f2), { force: true }).catch(() => void 0);
42641
43021
  }
42642
43022
  } catch {
42643
43023
  }
@@ -42649,15 +43029,15 @@ async function recordRun(root, hist, status, note, today) {
42649
43029
  }
42650
43030
  async function readJsonObject(path) {
42651
43031
  try {
42652
- return JSON.parse(await readFile5(path, "utf8"));
43032
+ return JSON.parse(await readFile6(path, "utf8"));
42653
43033
  } catch {
42654
43034
  return {};
42655
43035
  }
42656
43036
  }
42657
43037
  async function hasWork(root) {
42658
- const approvals = await readJsonObject(join20(root, ".claude/.autonomous.approvals.json"));
43038
+ const approvals = await readJsonObject(join21(root, ".claude/.autonomous.approvals.json"));
42659
43039
  const approved = Object.entries(approvals).filter(([, v2]) => v2 && typeof v2 === "object" && v2.approved === true).map(([k2]) => k2);
42660
- const read = (f2) => readFile5(join20(root, f2), "utf8").catch(() => "");
43040
+ const read = (f2) => readFile6(join21(root, f2), "utf8").catch(() => "");
42661
43041
  const [userTodo, userQa, aiTodo, aiProgress] = await Promise.all([
42662
43042
  read("USER_TODO.md"),
42663
43043
  read("USER_QA.md"),
@@ -42695,12 +43075,12 @@ async function allAiProfilesExhausted(ctx, cfg) {
42695
43075
 
42696
43076
  // src/core/fs-browse.ts
42697
43077
  import { readdir as readdir3 } from "fs/promises";
42698
- import { resolve as resolve4, sep as sep2 } from "path";
43078
+ import { resolve as resolve6, sep as sep3 } from "path";
42699
43079
  async function listDir(path, root) {
42700
43080
  if (!root) return { path: "", entries: [] };
42701
- const base = resolve4(root);
42702
- const requested = resolve4(base, path && path.trim() ? path : ".");
42703
- const target = requested === base || requested.startsWith(base + sep2) ? requested : base;
43081
+ const base = resolve6(root);
43082
+ const requested = resolve6(base, path && path.trim() ? path : ".");
43083
+ const target = requested === base || requested.startsWith(base + sep3) ? requested : base;
42704
43084
  try {
42705
43085
  const dirents = await readdir3(target, { withFileTypes: true });
42706
43086
  const entries2 = dirents.map((d) => ({
@@ -42717,14 +43097,14 @@ async function listDir(path, root) {
42717
43097
  }
42718
43098
 
42719
43099
  // src/core/fs-read.ts
42720
- import { readFile as readFile6 } from "fs/promises";
42721
- import { resolve as resolve5, sep as sep3 } from "path";
43100
+ import { readFile as readFile7 } from "fs/promises";
43101
+ import { resolve as resolve7, sep as sep4 } from "path";
42722
43102
  var MAX_BYTES = 256 * 1024;
42723
43103
  function resolveInRoot(root, relPath) {
42724
43104
  if (!root) return null;
42725
- const base = resolve5(root);
42726
- const target = resolve5(base, relPath || ".");
42727
- return target === base || target.startsWith(base + sep3) ? target : null;
43105
+ const base = resolve7(root);
43106
+ const target = resolve7(base, relPath || ".");
43107
+ return target === base || target.startsWith(base + sep4) ? target : null;
42728
43108
  }
42729
43109
  async function readWorkerFile(root, path) {
42730
43110
  const target = resolveInRoot(root, path);
@@ -42732,7 +43112,7 @@ async function readWorkerFile(root, path) {
42732
43112
  return { path, content: "", truncated: false };
42733
43113
  }
42734
43114
  try {
42735
- const buf = await readFile6(target);
43115
+ const buf = await readFile7(target);
42736
43116
  const truncated = buf.byteLength > MAX_BYTES;
42737
43117
  const content = buf.subarray(0, MAX_BYTES).toString("utf8");
42738
43118
  return { path: target, content, truncated };
@@ -42743,13 +43123,13 @@ async function readWorkerFile(root, path) {
42743
43123
 
42744
43124
  // src/core/fs-write.ts
42745
43125
  import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
42746
- import { dirname as dirname5, resolve as resolve6, sep as sep4 } from "path";
43126
+ import { dirname as dirname5, resolve as resolve8, sep as sep5 } from "path";
42747
43127
  var MAX_BYTES2 = 2 * 1024 * 1024;
42748
43128
  function resolveInRoot2(root, relPath) {
42749
43129
  if (!root) return null;
42750
- const base = resolve6(root);
42751
- const target = resolve6(base, relPath || ".");
42752
- return target === base || target.startsWith(base + sep4) ? target : null;
43130
+ const base = resolve8(root);
43131
+ const target = resolve8(base, relPath || ".");
43132
+ return target === base || target.startsWith(base + sep5) ? target : null;
42753
43133
  }
42754
43134
  async function writeWorkerFile(root, relPath, content) {
42755
43135
  const target = resolveInRoot2(root, relPath);
@@ -42770,24 +43150,24 @@ async function writeWorkerFile(root, relPath, content) {
42770
43150
  }
42771
43151
 
42772
43152
  // src/core/fs-mutate.ts
42773
- import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as stat2 } from "fs/promises";
42774
- import { basename as basename3, dirname as dirname6, resolve as resolve7, sep as sep5 } from "path";
43153
+ import { mkdir as mkdir5, writeFile as writeFile6, rename, rm as rm3, stat as stat3 } from "fs/promises";
43154
+ import { basename as basename3, dirname as dirname6, resolve as resolve9, sep as sep6 } from "path";
42775
43155
  var MAX_BYTES3 = 2 * 1024 * 1024;
42776
43156
  function resolveInRoot3(root, rel) {
42777
- const base = resolve7(root);
42778
- const target = resolve7(base, rel || ".");
42779
- return target === base || target.startsWith(base + sep5) ? target : null;
43157
+ const base = resolve9(root);
43158
+ const target = resolve9(base, rel || ".");
43159
+ return target === base || target.startsWith(base + sep6) ? target : null;
42780
43160
  }
42781
43161
  async function isDir(p2) {
42782
43162
  try {
42783
- return (await stat2(p2)).isDirectory();
43163
+ return (await stat3(p2)).isDirectory();
42784
43164
  } catch {
42785
43165
  return false;
42786
43166
  }
42787
43167
  }
42788
43168
  async function exists(p2) {
42789
43169
  try {
42790
- await stat2(p2);
43170
+ await stat3(p2);
42791
43171
  return true;
42792
43172
  } catch {
42793
43173
  return false;
@@ -42800,13 +43180,13 @@ async function mutateFs(root, req) {
42800
43180
  switch (req.op) {
42801
43181
  case "mkdir": {
42802
43182
  const target = resolveInRoot3(root, req.path ?? "");
42803
- if (!target || target === resolve7(root)) return escaped(req.path ?? "");
43183
+ if (!target || target === resolve9(root)) return escaped(req.path ?? "");
42804
43184
  await mkdir5(target, { recursive: true });
42805
43185
  return { ok: true, path: target };
42806
43186
  }
42807
43187
  case "create": {
42808
43188
  const target = resolveInRoot3(root, req.path ?? "");
42809
- if (!target || target === resolve7(root)) return escaped(req.path ?? "");
43189
+ if (!target || target === resolve9(root)) return escaped(req.path ?? "");
42810
43190
  if (await exists(target)) return { ok: false, path: target, error: "already exists" };
42811
43191
  const content = req.content ?? "";
42812
43192
  if (Buffer.byteLength(content, "utf8") > MAX_BYTES3) {
@@ -42820,9 +43200,9 @@ async function mutateFs(root, req) {
42820
43200
  const from = resolveInRoot3(root, req.from ?? "");
42821
43201
  let to2 = resolveInRoot3(root, req.to ?? "");
42822
43202
  if (!from || !to2) return escaped(req.from ?? req.to ?? "");
42823
- if (from === resolve7(root)) return { ok: false, path: from, error: "cannot move the project root" };
42824
- if (await isDir(to2)) to2 = resolve7(to2, basename3(from));
42825
- if (to2 !== resolve7(root) && !to2.startsWith(resolve7(root) + sep5)) return escaped(req.to ?? "");
43203
+ if (from === resolve9(root)) return { ok: false, path: from, error: "cannot move the project root" };
43204
+ if (await isDir(to2)) to2 = resolve9(to2, basename3(from));
43205
+ if (to2 !== resolve9(root) && !to2.startsWith(resolve9(root) + sep6)) return escaped(req.to ?? "");
42826
43206
  if (await exists(to2)) return { ok: false, path: to2, error: "destination already exists" };
42827
43207
  await mkdir5(dirname6(to2), { recursive: true });
42828
43208
  await rename(from, to2);
@@ -42831,7 +43211,7 @@ async function mutateFs(root, req) {
42831
43211
  case "delete": {
42832
43212
  const target = resolveInRoot3(root, req.path ?? "");
42833
43213
  if (!target) return escaped(req.path ?? "");
42834
- if (target === resolve7(root)) return { ok: false, path: target, error: "cannot delete the project root" };
43214
+ if (target === resolve9(root)) return { ok: false, path: target, error: "cannot delete the project root" };
42835
43215
  await rm3(target, { recursive: true, force: true });
42836
43216
  return { ok: true, path: target };
42837
43217
  }
@@ -42844,13 +43224,13 @@ async function mutateFs(root, req) {
42844
43224
  }
42845
43225
 
42846
43226
  // src/core/fs-transfer.ts
42847
- import { mkdir as mkdir6, readFile as readFile7, stat as stat3, writeFile as writeFile7 } from "fs/promises";
42848
- import { basename as basename4, dirname as dirname7, extname, resolve as resolve8, sep as sep6 } from "path";
43227
+ import { mkdir as mkdir6, readFile as readFile8, stat as stat4, writeFile as writeFile7 } from "fs/promises";
43228
+ import { basename as basename4, dirname as dirname7, extname, resolve as resolve10, sep as sep7 } from "path";
42849
43229
  function resolveInRoot4(root, relPath) {
42850
43230
  if (!root) return null;
42851
- const base = resolve8(root);
42852
- const target = resolve8(base, relPath || ".");
42853
- return target === base || target.startsWith(base + sep6) ? target : null;
43231
+ const base = resolve10(root);
43232
+ const target = resolve10(base, relPath || ".");
43233
+ return target === base || target.startsWith(base + sep7) ? target : null;
42854
43234
  }
42855
43235
  var MIME_BY_EXT = {
42856
43236
  ".png": "image/png",
@@ -42899,12 +43279,12 @@ async function downloadBinaryFile(root, relPath) {
42899
43279
  const target = resolveInRoot4(root, relPath);
42900
43280
  if (!target) return { error: "path escapes the project root" };
42901
43281
  try {
42902
- const info = await stat3(target);
43282
+ const info = await stat4(target);
42903
43283
  if (!info.isFile()) return { error: "not a file" };
42904
43284
  if (info.size > FS_TRANSFER_MAX_BYTES) {
42905
43285
  return { error: `file too large (max ${FS_TRANSFER_MAX_BYTES} bytes)` };
42906
43286
  }
42907
- const buf = await readFile7(target);
43287
+ const buf = await readFile8(target);
42908
43288
  return {
42909
43289
  contentBase64: buf.toString("base64"),
42910
43290
  contentType: guessMime(target),
@@ -42963,16 +43343,16 @@ function handleFsChannels(ctx, message, payload) {
42963
43343
  }
42964
43344
 
42965
43345
  // src/core/git-diff.ts
42966
- import { readFile as readFile8 } from "fs/promises";
42967
- import { execFile as execFile5 } from "child_process";
42968
- import { basename as basename5, dirname as dirname8, resolve as resolve9, sep as sep7 } from "path";
42969
- import { promisify as promisify5 } from "util";
42970
- var run5 = promisify5(execFile5);
43346
+ import { readFile as readFile9 } from "fs/promises";
43347
+ import { execFile as execFile7 } from "child_process";
43348
+ import { basename as basename5, dirname as dirname8, resolve as resolve11, sep as sep8 } from "path";
43349
+ import { promisify as promisify7 } from "util";
43350
+ var run7 = promisify7(execFile7);
42971
43351
  function resolveInRoot5(root, relPath) {
42972
43352
  if (!root) return null;
42973
- const base = resolve9(root);
42974
- const target = resolve9(base, relPath || ".");
42975
- return target === base || target.startsWith(base + sep7) ? target : null;
43353
+ const base = resolve11(root);
43354
+ const target = resolve11(base, relPath || ".");
43355
+ return target === base || target.startsWith(base + sep8) ? target : null;
42976
43356
  }
42977
43357
  function looksBinary(buf) {
42978
43358
  return buf.subarray(0, 8e3).includes(0);
@@ -42990,12 +43370,12 @@ async function gitDiff(root, path) {
42990
43370
  const base = basename5(target);
42991
43371
  let newBuf = null;
42992
43372
  try {
42993
- newBuf = await readFile8(target);
43373
+ newBuf = await readFile9(target);
42994
43374
  } catch {
42995
43375
  }
42996
43376
  let oldBuf = null;
42997
43377
  try {
42998
- const { stdout } = await run5("git", ["show", `HEAD:./${base}`], {
43378
+ const { stdout } = await run7("git", ["show", `HEAD:./${base}`], {
42999
43379
  cwd: dir2,
43000
43380
  timeout: 15e3,
43001
43381
  maxBuffer: FS_TRANSFER_MAX_BYTES + 1024,
@@ -43025,12 +43405,12 @@ async function gitDiff(root, path) {
43025
43405
  }
43026
43406
 
43027
43407
  // src/core/git-env.ts
43028
- import { execFile as execFile6 } from "child_process";
43029
- import { promisify as promisify6 } from "util";
43030
- var run6 = promisify6(execFile6);
43408
+ import { execFile as execFile8 } from "child_process";
43409
+ import { promisify as promisify8 } from "util";
43410
+ var run8 = promisify8(execFile8);
43031
43411
  async function hasBinary(bin, args = ["--version"]) {
43032
43412
  try {
43033
- await run6(bin, args, { timeout: 5e3 });
43413
+ await run8(bin, args, { timeout: 5e3 });
43034
43414
  return true;
43035
43415
  } catch {
43036
43416
  return false;
@@ -43043,7 +43423,7 @@ async function checkGitEnv(provider) {
43043
43423
  let account = null;
43044
43424
  if (installed) {
43045
43425
  try {
43046
- const { stdout, stderr } = await run6(bin, ["auth", "status"], { timeout: 8e3 });
43426
+ const { stdout, stderr } = await run8(bin, ["auth", "status"], { timeout: 8e3 });
43047
43427
  const out = `${stdout}
43048
43428
  ${stderr}`;
43049
43429
  authenticated = /logged in|logged into|✓/i.test(out);
@@ -43056,33 +43436,33 @@ ${stderr}`;
43056
43436
  }
43057
43437
 
43058
43438
  // src/core/git-ssh-key.ts
43059
- import { execFile as execFile7 } from "child_process";
43060
- import { readFile as readFile9, rm as rm4, mkdir as mkdir7 } from "fs/promises";
43061
- import { join as join21 } from "path";
43062
- import { promisify as promisify7 } from "util";
43063
- var run7 = promisify7(execFile7);
43439
+ import { execFile as execFile9 } from "child_process";
43440
+ import { readFile as readFile10, rm as rm4, mkdir as mkdir7 } from "fs/promises";
43441
+ import { join as join22 } from "path";
43442
+ import { promisify as promisify9 } from "util";
43443
+ var run9 = promisify9(execFile9);
43064
43444
  function keyPaths(profileDir2) {
43065
- const dir2 = join21(profileDir2, ".ssh");
43066
- const key = join21(dir2, "id_4pm");
43445
+ const dir2 = join22(profileDir2, ".ssh");
43446
+ const key = join22(dir2, "id_4pm");
43067
43447
  return { dir: dir2, key, pub: `${key}.pub` };
43068
43448
  }
43069
43449
  async function readPublicKey(pub) {
43070
43450
  try {
43071
- return (await readFile9(pub, "utf8")).trim();
43451
+ return (await readFile10(pub, "utf8")).trim();
43072
43452
  } catch {
43073
43453
  return null;
43074
43454
  }
43075
43455
  }
43076
43456
  async function fingerprintOf(pub) {
43077
43457
  try {
43078
- const { stdout } = await run7("ssh-keygen", ["-lf", pub], { timeout: 5e3 });
43458
+ const { stdout } = await run9("ssh-keygen", ["-lf", pub], { timeout: 5e3 });
43079
43459
  return stdout.match(/SHA256:[A-Za-z0-9+/=]+/)?.[0] ?? null;
43080
43460
  } catch {
43081
43461
  return null;
43082
43462
  }
43083
43463
  }
43084
43464
  async function configureGit(key) {
43085
- await run7(
43465
+ await run9(
43086
43466
  "git",
43087
43467
  ["config", "--global", "core.sshCommand", `ssh -i ${key} -o IdentitiesOnly=yes`],
43088
43468
  { timeout: 5e3 }
@@ -43093,7 +43473,7 @@ async function generate(profileDir2) {
43093
43473
  await mkdir7(dir2, { recursive: true });
43094
43474
  await rm4(key, { force: true });
43095
43475
  await rm4(pub, { force: true });
43096
- await run7("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
43476
+ await run9("ssh-keygen", ["-t", "ed25519", "-f", key, "-N", "", "-C", "4pm-deploy-key"], {
43097
43477
  timeout: 15e3
43098
43478
  });
43099
43479
  await configureGit(key);
@@ -43103,7 +43483,7 @@ async function remove(profileDir2) {
43103
43483
  const { key, pub } = keyPaths(profileDir2);
43104
43484
  await rm4(key, { force: true });
43105
43485
  await rm4(pub, { force: true });
43106
- await run7("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
43486
+ await run9("git", ["config", "--global", "--unset", "core.sshCommand"], { timeout: 5e3 }).catch(
43107
43487
  () => void 0
43108
43488
  );
43109
43489
  return { publicKey: null, fingerprint: null };
@@ -43123,109 +43503,6 @@ async function manageSshKey(op, profileDir2) {
43123
43503
  }
43124
43504
  }
43125
43505
 
43126
- // src/core/git-history.ts
43127
- import { execFile as execFile8 } from "child_process";
43128
- import { resolve as resolve10, sep as sep8 } from "path";
43129
- import { promisify as promisify8 } from "util";
43130
- var run8 = promisify8(execFile8);
43131
- var FS = "";
43132
- var RS = "";
43133
- function resolveRepo(root, repo) {
43134
- if (!root) return null;
43135
- const base = resolve10(root);
43136
- const requested = resolve10(base, repo && repo.trim() ? repo : ".");
43137
- return requested === base || requested.startsWith(base + sep8) ? requested : base;
43138
- }
43139
- async function git(cwd2, args) {
43140
- try {
43141
- const { stdout } = await run8("git", args, {
43142
- cwd: cwd2,
43143
- timeout: 15e3,
43144
- maxBuffer: 8 * 1024 * 1024
43145
- });
43146
- return stdout;
43147
- } catch {
43148
- return "";
43149
- }
43150
- }
43151
- async function gitRepos(root) {
43152
- if (!root) return { repos: [] };
43153
- const base = resolve10(root);
43154
- const top = (await git(base, ["rev-parse", "--show-toplevel"])).trim();
43155
- if (!top || !(top === base || top.startsWith(base + sep8))) return { repos: [] };
43156
- const repos = [];
43157
- const rootRemote = (await git(base, ["remote", "get-url", "origin"])).trim();
43158
- const rootBranch = (await git(base, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
43159
- repos.push({ subdir: "", name: "", remote: rootRemote || null, branch: rootBranch || null });
43160
- const subOut = await git(base, ["submodule", "status"]);
43161
- for (const line of subOut.split("\n")) {
43162
- const m2 = line.trim().match(/^[-+U ]?[0-9a-f]{7,40}\s+(\S+)/);
43163
- const subPath = m2?.[1];
43164
- if (!subPath) continue;
43165
- const abs = resolve10(base, subPath);
43166
- if (!(abs === base || abs.startsWith(base + sep8))) continue;
43167
- const remote = (await git(abs, ["remote", "get-url", "origin"])).trim();
43168
- const branch = (await git(abs, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
43169
- repos.push({ subdir: subPath, name: subPath, remote: remote || null, branch: branch || null });
43170
- }
43171
- return { repos };
43172
- }
43173
- async function gitLog(root, repo, skip, limit) {
43174
- const cwd2 = resolveRepo(root, repo);
43175
- if (!cwd2) return { entries: [], hasMore: false };
43176
- const capped = Math.min(Math.max(limit, 1), 200);
43177
- const format = ["%H", "%h", "%an", "%aI", "%s"].join(FS) + RS;
43178
- const stdout = await git(cwd2, [
43179
- "log",
43180
- `--skip=${Math.max(skip, 0)}`,
43181
- `-n`,
43182
- String(capped + 1),
43183
- `--pretty=format:${format}`
43184
- ]);
43185
- const rows = stdout.split(RS).map((r) => r.replace(/^\n/, "")).filter((r) => r.length > 0);
43186
- const entries2 = rows.slice(0, capped).map((row) => {
43187
- const [hash = "", shortHash = "", author = "", date = "", subject = ""] = row.split(FS);
43188
- return { hash, shortHash, author, date, subject };
43189
- });
43190
- return { entries: entries2, hasMore: rows.length > capped };
43191
- }
43192
- async function gitCommit(root, repo, hash) {
43193
- const cwd2 = resolveRepo(root, repo);
43194
- const empty = { hash, author: "", date: "", subject: "", files: [] };
43195
- if (!cwd2 || !hash) return empty;
43196
- const format = ["%H", "%an", "%aI", "%s"].join(FS);
43197
- const stdout = await git(cwd2, [
43198
- "show",
43199
- "--name-status",
43200
- "-M",
43201
- `--pretty=format:${format}`,
43202
- hash
43203
- ]);
43204
- if (!stdout) return empty;
43205
- const lines = stdout.split("\n");
43206
- const [full = hash, author = "", date = "", subject = ""] = (lines[0] ?? "").split(FS);
43207
- const files = [];
43208
- for (const line of lines.slice(1)) {
43209
- if (!line.trim()) continue;
43210
- const parts = line.split(" ");
43211
- const status = (parts[0] ?? "").charAt(0);
43212
- if (!status) continue;
43213
- if (status === "R" || status === "C") {
43214
- files.push({ path: parts[2] ?? "", status, oldPath: parts[1] ?? void 0 });
43215
- } else {
43216
- files.push({ path: parts[1] ?? "", status });
43217
- }
43218
- }
43219
- return { hash: full, author, date, subject, files };
43220
- }
43221
- async function gitCommitDiff(root, repo, hash, path) {
43222
- const cwd2 = resolveRepo(root, repo);
43223
- if (!cwd2 || !hash || !path) return { path, oldContent: "", newContent: "" };
43224
- const oldContent = await git(cwd2, ["show", `${hash}^:${path}`]);
43225
- const newContent = await git(cwd2, ["show", `${hash}:${path}`]);
43226
- return { path, oldContent, newContent };
43227
- }
43228
-
43229
43506
  // src/core/ws-client/handlers/git.ts
43230
43507
  function handleGitChannels(ctx, message, payload) {
43231
43508
  switch (message.channel) {
@@ -43278,12 +43555,12 @@ function handleGitChannels(ctx, message, payload) {
43278
43555
  }
43279
43556
 
43280
43557
  // src/core/ws-client/handlers/misc.ts
43281
- import { join as join22 } from "path";
43558
+ import { join as join23 } from "path";
43282
43559
 
43283
43560
  // src/core/ai-assist.ts
43284
- import { execFile as execFile9 } from "child_process";
43285
- import { promisify as promisify9 } from "util";
43286
- var run9 = promisify9(execFile9);
43561
+ import { execFile as execFile10 } from "child_process";
43562
+ import { promisify as promisify10 } from "util";
43563
+ var run10 = promisify10(execFile10);
43287
43564
  function aiCli() {
43288
43565
  return process.env.AI_CLI || "claude";
43289
43566
  }
@@ -43326,7 +43603,7 @@ function parseAi(cli, stdout, prompt) {
43326
43603
  }
43327
43604
  async function runAi(prompt) {
43328
43605
  const cli = aiCli();
43329
- const { stdout } = await run9(cli, aiArgs(cli, prompt), {
43606
+ const { stdout } = await run10(cli, aiArgs(cli, prompt), {
43330
43607
  timeout: 12e4,
43331
43608
  maxBuffer: 8 * 1024 * 1024
43332
43609
  });
@@ -43428,7 +43705,7 @@ function handleMiscChannels(ctx, message, payload) {
43428
43705
  }
43429
43706
  case WsChannels.LOG_READ: {
43430
43707
  const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
43431
- const lines = readRecentLogLines(join22(ctx.profileDir, "logs"), limit);
43708
+ const lines = readRecentLogLines(join23(ctx.profileDir, "logs"), limit);
43432
43709
  ctx.send(WsChannels.LOG_READ, { lines }, message.id);
43433
43710
  return true;
43434
43711
  }
@@ -43460,7 +43737,7 @@ function handleMiscChannels(ctx, message, payload) {
43460
43737
 
43461
43738
  // src/core/ws-client/handlers/project.ts
43462
43739
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, renameSync, rmSync as rmSync7 } from "fs";
43463
- import { join as join23 } from "path";
43740
+ import { join as join24 } from "path";
43464
43741
 
43465
43742
  // src/core/ai-task.ts
43466
43743
  import { randomUUID as randomUUID2 } from "crypto";
@@ -43532,8 +43809,8 @@ function handleProjectChannels(ctx, message, payload) {
43532
43809
  case WsChannels.PHYSIC_SYNC: {
43533
43810
  const sync = payload;
43534
43811
  const oldName = sync.oldName;
43535
- const oldPath = oldName ? join23(ctx.profileDir, oldName) : null;
43536
- const newPath = join23(ctx.profileDir, sync.newName);
43812
+ const oldPath = oldName ? join24(ctx.profileDir, oldName) : null;
43813
+ const newPath = join24(ctx.profileDir, sync.newName);
43537
43814
  if (oldName) {
43538
43815
  const oldRoot = ctx.physicFolderPath(oldName);
43539
43816
  if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
@@ -43545,6 +43822,7 @@ function handleProjectChannels(ctx, message, payload) {
43545
43822
  mkdirSync7(newPath, { recursive: true });
43546
43823
  }
43547
43824
  ctx.physicRoot = ctx.physicFolderPath(sync.newName);
43825
+ requestGitSnapshot("physic-sync");
43548
43826
  ctx.bus.setProject(sync.newName);
43549
43827
  ctx.bus.log(t("project.folderSynced", { name: sync.newName }));
43550
43828
  void ctx.cloneServingRepos().catch(() => void 0);
@@ -43555,7 +43833,7 @@ function handleProjectChannels(ctx, message, payload) {
43555
43833
  if (del.name) {
43556
43834
  const delRoot = ctx.physicFolderPath(del.name);
43557
43835
  if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
43558
- rmSync7(join23(ctx.profileDir, del.name), { recursive: true, force: true });
43836
+ rmSync7(join24(ctx.profileDir, del.name), { recursive: true, force: true });
43559
43837
  ctx.bus.log(t("project.folderDeleted", { name: del.name }));
43560
43838
  }
43561
43839
  void manageSshKey("delete", ctx.profileDir).catch(() => void 0);
@@ -43574,6 +43852,7 @@ function handleProjectChannels(ctx, message, payload) {
43574
43852
  projectAiMemoryBudgetChars: tokens.memory?.budgetChars ?? 0
43575
43853
  });
43576
43854
  ctx.setRestrictToFolder(tokens.restrictToFolder === true);
43855
+ if (tokens.gitAuth !== void 0) applyGitAuth(ctx, tokens.gitAuth, tokens.gitAuthHost ?? null);
43577
43856
  logger.info("project.tokens.applied", {
43578
43857
  aiRunTimeoutSec: tokens.aiRunTimeoutSec ?? 0,
43579
43858
  autoClearIdleMinutes: tokens.autoClearIdleMinutes ?? 0
@@ -43586,9 +43865,28 @@ function handleProjectChannels(ctx, message, payload) {
43586
43865
  ctx.profileDir,
43587
43866
  (p2) => ctx.send(WsChannels.PROJECT_PROGRESS, p2),
43588
43867
  // AI init on the standard AI path + one org run slot (ADR-0362).
43589
- createAiTaskRunner(ctx)
43590
- ).then((reply) => ctx.send(WsChannels.PROJECT_CREATE, reply, message.id));
43868
+ createAiTaskRunner(ctx),
43869
+ (method, host) => applyGitAuth(ctx, method, host)
43870
+ ).then((reply) => {
43871
+ ctx.send(WsChannels.PROJECT_CREATE, reply, message.id);
43872
+ requestGitSnapshot("scaffold");
43873
+ });
43874
+ return true;
43875
+ case WsChannels.PROJECT_PUBLISH: {
43876
+ const root = ctx.physicRoot;
43877
+ if (!root) {
43878
+ ctx.send(WsChannels.PROJECT_PUBLISH, { publish: null, error: "This worker has no project folder." }, message.id);
43879
+ return true;
43880
+ }
43881
+ const projectId = payload.projectId;
43882
+ void publishScaffold(root, (step, msg) => ctx.send(WsChannels.PROJECT_PROGRESS, { projectId, step, message: msg })).then((publish) => {
43883
+ ctx.send(WsChannels.PROJECT_PUBLISH, { publish }, message.id);
43884
+ requestGitSnapshot("publish");
43885
+ }).catch(
43886
+ (err) => ctx.send(WsChannels.PROJECT_PUBLISH, { publish: null, error: String(err) }, message.id)
43887
+ );
43591
43888
  return true;
43889
+ }
43592
43890
  case WsChannels.PROJECT_ADD:
43593
43891
  void addProject(
43594
43892
  payload,
@@ -43596,12 +43894,23 @@ function handleProjectChannels(ctx, message, payload) {
43596
43894
  (p2) => ctx.send(WsChannels.PROJECT_PROGRESS, p2),
43597
43895
  // An add-with-scaffold runs AI init too (ADR-0362).
43598
43896
  createAiTaskRunner(ctx)
43599
- ).then((reply) => ctx.send(WsChannels.PROJECT_ADD, reply, message.id));
43897
+ ).then((reply) => {
43898
+ ctx.send(WsChannels.PROJECT_ADD, reply, message.id);
43899
+ requestGitSnapshot("provision");
43900
+ });
43600
43901
  return true;
43601
43902
  default:
43602
43903
  return false;
43603
43904
  }
43604
43905
  }
43906
+ function applyGitAuth(ctx, method, host) {
43907
+ configureGitAuth(
43908
+ method ?? null,
43909
+ host,
43910
+ ctx.profileDir,
43911
+ (req) => ctx.request(WsChannels.GIT_TOKEN, req)
43912
+ );
43913
+ }
43605
43914
 
43606
43915
  // src/core/knowledge-compose.ts
43607
43916
  import { spawn as spawn8 } from "child_process";
@@ -43623,7 +43932,7 @@ Extra focus from the author: ${req.prompt}` : "";
43623
43932
  ].join("\n");
43624
43933
  }
43625
43934
  function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
43626
- return new Promise((resolve12, reject) => {
43935
+ return new Promise((resolve13, reject) => {
43627
43936
  const args = ["-p", ...profile?.model ? ["--model", profile.model] : [], ...denySettingsArgs(cmd, [...denyDirs, profile?.dir])];
43628
43937
  const env3 = {
43629
43938
  ...process.env,
@@ -43646,7 +43955,7 @@ function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
43646
43955
  });
43647
43956
  child.on("close", (code) => {
43648
43957
  clearTimeout(timer);
43649
- resolve12({ code: code ?? -1, out, err });
43958
+ resolve13({ code: code ?? -1, out, err });
43650
43959
  });
43651
43960
  child.stdin.write(prompt);
43652
43961
  child.stdin.end();
@@ -43676,13 +43985,13 @@ async function runKnowledgeCompose(req, ai2, physicRoot) {
43676
43985
  }
43677
43986
 
43678
43987
  // src/core/faq-compose.ts
43679
- import { spawn as spawn9, execFile as execFile10 } from "child_process";
43988
+ import { spawn as spawn9, execFile as execFile11 } from "child_process";
43680
43989
  import { existsSync as existsSync15, mkdirSync as mkdirSync8, rmSync as rmSync8 } from "fs";
43681
43990
  import { randomUUID as randomUUID3 } from "crypto";
43682
- import { join as join24 } from "path";
43991
+ import { join as join25 } from "path";
43683
43992
  import { tmpdir as tmpdir4 } from "os";
43684
- import { promisify as promisify10 } from "util";
43685
- var execFileAsync2 = promisify10(execFile10);
43993
+ import { promisify as promisify11 } from "util";
43994
+ var execFileAsync2 = promisify11(execFile11);
43686
43995
  var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
43687
43996
  var COMPOSE_TIMEOUT_MS2 = 10 * 60 * 1e3;
43688
43997
  var AGENT_DENIED_TOOLS = ["Bash", "WebFetch", "WebSearch", "NotebookEdit", "Task"];
@@ -43747,7 +44056,7 @@ function parsePerTicket(text) {
43747
44056
  return [];
43748
44057
  }
43749
44058
  function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
43750
- return new Promise((resolve12, reject) => {
44059
+ return new Promise((resolve13, reject) => {
43751
44060
  const isClaude = cmd.includes("claude");
43752
44061
  const args = [
43753
44062
  "-p",
@@ -43780,7 +44089,7 @@ function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
43780
44089
  child.on("close", (code) => {
43781
44090
  clearTimeout(timer);
43782
44091
  out += parser.flush();
43783
- resolve12({ code: code ?? -1, out, err, usage: parser.usage() });
44092
+ resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
43784
44093
  });
43785
44094
  child.stdin.write(prompt);
43786
44095
  child.stdin.end();
@@ -43800,7 +44109,7 @@ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
43800
44109
  var MAX_OUTPUT_CHARS = 8e3;
43801
44110
  async function runFaqCompose(req, ai2) {
43802
44111
  if (!req.repo.token) return { prUrl: "", error: "no write token" };
43803
- const workDir = join24(tmpdir4(), "4pm-faq-sync", randomUUID3());
44112
+ const workDir = join25(tmpdir4(), "4pm-faq-sync", randomUUID3());
43804
44113
  const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
43805
44114
  const auth = gitAuthArgs("x-access-token", req.repo.token);
43806
44115
  try {
@@ -44028,8 +44337,8 @@ function handleToolsChannels(ctx, message, payload) {
44028
44337
  }
44029
44338
 
44030
44339
  // src/core/agents.ts
44031
- import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
44032
- import { join as join25 } from "path";
44340
+ import { readFile as readFile11, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
44341
+ import { join as join26 } from "path";
44033
44342
  var AGENTS_REL = ".claude/agents";
44034
44343
  var SKILLS_REL = ".claude/skills";
44035
44344
  var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
@@ -44038,7 +44347,7 @@ function safeName(name) {
44038
44347
  }
44039
44348
  async function readText2(path, fallback = "") {
44040
44349
  try {
44041
- return await readFile10(path, "utf8");
44350
+ return await readFile11(path, "utf8");
44042
44351
  } catch {
44043
44352
  return fallback;
44044
44353
  }
@@ -44053,17 +44362,17 @@ async function listAgents(root) {
44053
44362
  const subagents = [];
44054
44363
  const skills = [];
44055
44364
  try {
44056
- for (const e of await readdir4(join25(root, AGENTS_REL), { withFileTypes: true })) {
44365
+ for (const e of await readdir4(join26(root, AGENTS_REL), { withFileTypes: true })) {
44057
44366
  if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
44058
- const fm = frontmatter(await readText2(join25(root, AGENTS_REL, e.name)));
44367
+ const fm = frontmatter(await readText2(join26(root, AGENTS_REL, e.name)));
44059
44368
  subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
44060
44369
  }
44061
44370
  } catch {
44062
44371
  }
44063
44372
  try {
44064
- for (const e of await readdir4(join25(root, SKILLS_REL), { withFileTypes: true })) {
44373
+ for (const e of await readdir4(join26(root, SKILLS_REL), { withFileTypes: true })) {
44065
44374
  if (!e.isDirectory()) continue;
44066
- const fm = frontmatter(await readText2(join25(root, SKILLS_REL, e.name, "SKILL.md")));
44375
+ const fm = frontmatter(await readText2(join26(root, SKILLS_REL, e.name, "SKILL.md")));
44067
44376
  skills.push({ name: e.name, description: fm.description });
44068
44377
  }
44069
44378
  } catch {
@@ -44074,15 +44383,15 @@ async function listAgents(root) {
44074
44383
  }
44075
44384
  function itemPath(root, kind, name) {
44076
44385
  const n = safeName(name);
44077
- return kind === "subagent" ? join25(root, AGENTS_REL, `${n}.md`) : join25(root, SKILLS_REL, n, "SKILL.md");
44386
+ return kind === "subagent" ? join26(root, AGENTS_REL, `${n}.md`) : join26(root, SKILLS_REL, n, "SKILL.md");
44078
44387
  }
44079
44388
  async function readAgent(root, kind, name) {
44080
44389
  const content = await readText2(itemPath(root, kind, name));
44081
44390
  if (kind !== "subagent") return { content };
44082
44391
  const n = safeName(name);
44083
44392
  const [short, long] = await Promise.all([
44084
- readText2(join25(root, AGENTS_REL, "short-memory", `${n}.md`)),
44085
- readText2(join25(root, AGENTS_REL, "long-memory", `${n}.md`))
44393
+ readText2(join26(root, AGENTS_REL, "short-memory", `${n}.md`)),
44394
+ readText2(join26(root, AGENTS_REL, "long-memory", `${n}.md`))
44086
44395
  ]);
44087
44396
  return { content, memory: { short, long } };
44088
44397
  }
@@ -44092,12 +44401,12 @@ async function writeAgent(root, req) {
44092
44401
  try {
44093
44402
  if (req.action === "delete") {
44094
44403
  if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
44095
- else await rm5(join25(root, SKILLS_REL, n), { recursive: true, force: true });
44404
+ else await rm5(join26(root, SKILLS_REL, n), { recursive: true, force: true });
44096
44405
  return { ok: true };
44097
44406
  }
44098
44407
  if (req.content === void 0) return { ok: false, error: "content is required" };
44099
44408
  const path = itemPath(root, req.kind, n);
44100
- await mkdir8(join25(path, ".."), { recursive: true });
44409
+ await mkdir8(join26(path, ".."), { recursive: true });
44101
44410
  await writeFile8(path, req.content, "utf8");
44102
44411
  return { ok: true };
44103
44412
  } catch (err) {
@@ -44106,21 +44415,21 @@ async function writeAgent(root, req) {
44106
44415
  }
44107
44416
 
44108
44417
  // src/core/secrets.ts
44109
- import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
44110
- import { join as join26 } from "path";
44418
+ import { readFile as readFile12, writeFile as writeFile9 } from "fs/promises";
44419
+ import { join as join27 } from "path";
44111
44420
  var SECURITY_REL = "AI_SECURITY.md";
44112
44421
  var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
44113
44422
  var SECRETS_REL = "project.secrets.json";
44114
44423
  async function readText3(path, fallback = "") {
44115
44424
  try {
44116
- return await readFile11(path, "utf8");
44425
+ return await readFile12(path, "utf8");
44117
44426
  } catch {
44118
44427
  return fallback;
44119
44428
  }
44120
44429
  }
44121
44430
  async function readSecretsObject(root) {
44122
44431
  try {
44123
- const v2 = JSON.parse(await readFile11(join26(root, SECRETS_REL), "utf8"));
44432
+ const v2 = JSON.parse(await readFile12(join27(root, SECRETS_REL), "utf8"));
44124
44433
  return v2 && typeof v2 === "object" ? v2 : {};
44125
44434
  } catch {
44126
44435
  return {};
@@ -44139,8 +44448,8 @@ function placeholderKeys(text) {
44139
44448
  }
44140
44449
  async function readSecrets(root) {
44141
44450
  const [security, placeholder, secrets] = await Promise.all([
44142
- readText3(join26(root, SECURITY_REL)),
44143
- readText3(join26(root, PLACEHOLDER_REL)),
44451
+ readText3(join27(root, SECURITY_REL)),
44452
+ readText3(join27(root, PLACEHOLDER_REL)),
44144
44453
  readSecretsObject(root)
44145
44454
  ]);
44146
44455
  const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
@@ -44153,14 +44462,14 @@ async function readSecrets(root) {
44153
44462
  async function writeSecrets(root, req) {
44154
44463
  try {
44155
44464
  if (req.kind === "security") {
44156
- await writeFile9(join26(root, SECURITY_REL), req.content, "utf8");
44465
+ await writeFile9(join27(root, SECURITY_REL), req.content, "utf8");
44157
44466
  } else if (req.kind === "placeholder") {
44158
- await writeFile9(join26(root, PLACEHOLDER_REL), req.content, "utf8");
44467
+ await writeFile9(join27(root, PLACEHOLDER_REL), req.content, "utf8");
44159
44468
  } else {
44160
44469
  const secrets = await readSecretsObject(root);
44161
44470
  if (req.kind === "secret") secrets[req.key] = req.value;
44162
44471
  else delete secrets[req.key];
44163
- await writeFile9(join26(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
44472
+ await writeFile9(join27(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
44164
44473
  }
44165
44474
  return { ok: true };
44166
44475
  } catch (err) {
@@ -44169,17 +44478,17 @@ async function writeSecrets(root, req) {
44169
44478
  }
44170
44479
 
44171
44480
  // src/core/agent-tools.ts
44172
- import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
44173
- import { dirname as dirname9, join as join27 } from "path";
44481
+ import { readFile as readFile13, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
44482
+ import { dirname as dirname9, join as join28 } from "path";
44174
44483
  var SHARED_REL = ".claude/settings.json";
44175
44484
  var LOCAL_REL = ".claude/settings.local.json";
44176
44485
  var SECRETS_DENY = "Read(./project.secrets.json)";
44177
44486
  function fileFor(root, scope) {
44178
- return join27(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
44487
+ return join28(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
44179
44488
  }
44180
44489
  async function readSettingsObject(path) {
44181
44490
  try {
44182
- const v2 = JSON.parse(await readFile12(path, "utf8"));
44491
+ const v2 = JSON.parse(await readFile13(path, "utf8"));
44183
44492
  return v2 && typeof v2 === "object" ? v2 : {};
44184
44493
  } catch {
44185
44494
  return {};
@@ -44211,8 +44520,8 @@ async function writeAgentTools(root, scope, permissions) {
44211
44520
 
44212
44521
  // src/core/packages.ts
44213
44522
  import { createHash as createHash4 } from "crypto";
44214
- import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat4 } from "fs/promises";
44215
- import { dirname as dirname10, join as join28, relative } from "path";
44523
+ import { readFile as readFile14, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat5 } from "fs/promises";
44524
+ import { dirname as dirname10, join as join29, relative } from "path";
44216
44525
  var AGENTS_REL2 = ".claude/agents";
44217
44526
  var SKILLS_REL2 = ".claude/skills";
44218
44527
  var MANIFEST_REL = ".claude/.4pm-packages.json";
@@ -44221,7 +44530,7 @@ function safeName2(name) {
44221
44530
  }
44222
44531
  async function readMaybe(path) {
44223
44532
  try {
44224
- return await readFile13(path);
44533
+ return await readFile14(path);
44225
44534
  } catch {
44226
44535
  return null;
44227
44536
  }
@@ -44236,7 +44545,7 @@ async function listFilesRel(dir2) {
44236
44545
  return;
44237
44546
  }
44238
44547
  for (const e of entries2) {
44239
- const full = join28(d, e.name);
44548
+ const full = join29(d, e.name);
44240
44549
  if (e.isDirectory()) await walk2(full);
44241
44550
  else if (e.isFile()) out.push(relative(dir2, full));
44242
44551
  }
@@ -44247,16 +44556,16 @@ async function listFilesRel(dir2) {
44247
44556
  async function readArtifactFiles(root, kind, slug) {
44248
44557
  const files = [];
44249
44558
  if (kind === "subagent") {
44250
- const agent = await readMaybe(join28(root, AGENTS_REL2, `${slug}.md`));
44559
+ const agent = await readMaybe(join29(root, AGENTS_REL2, `${slug}.md`));
44251
44560
  if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
44252
- const short = await readMaybe(join28(root, AGENTS_REL2, "short-memory", `${slug}.md`));
44561
+ const short = await readMaybe(join29(root, AGENTS_REL2, "short-memory", `${slug}.md`));
44253
44562
  if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
44254
- const long = await readMaybe(join28(root, AGENTS_REL2, "long-memory", `${slug}.md`));
44563
+ const long = await readMaybe(join29(root, AGENTS_REL2, "long-memory", `${slug}.md`));
44255
44564
  if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
44256
44565
  } else {
44257
- const base = join28(root, SKILLS_REL2, slug);
44566
+ const base = join29(root, SKILLS_REL2, slug);
44258
44567
  for (const rel of await listFilesRel(base)) {
44259
- const buf = await readMaybe(join28(base, rel));
44568
+ const buf = await readMaybe(join29(base, rel));
44260
44569
  if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
44261
44570
  }
44262
44571
  }
@@ -44274,16 +44583,16 @@ async function artifactSha256(root, kind, slug) {
44274
44583
  return h.digest("hex");
44275
44584
  }
44276
44585
  async function artifactExists(root, kind, slug) {
44277
- const target = kind === "subagent" ? join28(root, AGENTS_REL2, `${slug}.md`) : join28(root, SKILLS_REL2, slug);
44586
+ const target = kind === "subagent" ? join29(root, AGENTS_REL2, `${slug}.md`) : join29(root, SKILLS_REL2, slug);
44278
44587
  try {
44279
- await stat4(target);
44588
+ await stat5(target);
44280
44589
  return true;
44281
44590
  } catch {
44282
44591
  return false;
44283
44592
  }
44284
44593
  }
44285
44594
  async function readManifest(root) {
44286
- const buf = await readMaybe(join28(root, MANIFEST_REL));
44595
+ const buf = await readMaybe(join29(root, MANIFEST_REL));
44287
44596
  if (!buf) return {};
44288
44597
  try {
44289
44598
  return JSON.parse(buf.toString("utf8"));
@@ -44292,7 +44601,7 @@ async function readManifest(root) {
44292
44601
  }
44293
44602
  }
44294
44603
  async function writeManifest(root, manifest) {
44295
- const path = join28(root, MANIFEST_REL);
44604
+ const path = join29(root, MANIFEST_REL);
44296
44605
  await mkdir10(dirname10(path), { recursive: true });
44297
44606
  await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
44298
44607
  }
@@ -44301,24 +44610,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
44301
44610
  if (kind === "subagent") {
44302
44611
  const agent = byPath.get("agent.md");
44303
44612
  if (agent) {
44304
- await mkdir10(join28(root, AGENTS_REL2), { recursive: true });
44305
- await writeFile11(join28(root, AGENTS_REL2, `${slug}.md`), agent);
44613
+ await mkdir10(join29(root, AGENTS_REL2), { recursive: true });
44614
+ await writeFile11(join29(root, AGENTS_REL2, `${slug}.md`), agent);
44306
44615
  }
44307
44616
  const short = byPath.get("short-memory.md");
44308
44617
  if (short) {
44309
- await mkdir10(join28(root, AGENTS_REL2, "short-memory"), { recursive: true });
44310
- await writeFile11(join28(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
44618
+ await mkdir10(join29(root, AGENTS_REL2, "short-memory"), { recursive: true });
44619
+ await writeFile11(join29(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
44311
44620
  }
44312
44621
  const long = byPath.get("long-memory.md");
44313
44622
  if (long) {
44314
- await mkdir10(join28(root, AGENTS_REL2, "long-memory"), { recursive: true });
44315
- await writeFile11(join28(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
44623
+ await mkdir10(join29(root, AGENTS_REL2, "long-memory"), { recursive: true });
44624
+ await writeFile11(join29(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
44316
44625
  }
44317
44626
  } else {
44318
- const base = join28(root, SKILLS_REL2, slug);
44627
+ const base = join29(root, SKILLS_REL2, slug);
44319
44628
  for (const [rel, buf] of byPath) {
44320
44629
  if (rel.includes("..") || rel.startsWith("/")) continue;
44321
- const dest = join28(base, rel);
44630
+ const dest = join29(base, rel);
44322
44631
  await mkdir10(dirname10(dest), { recursive: true });
44323
44632
  await writeFile11(dest, buf);
44324
44633
  }
@@ -44384,11 +44693,11 @@ async function removePackage(root, slug) {
44384
44693
  const entry = manifest[n];
44385
44694
  if (!entry) return { ok: false, error: "not installed" };
44386
44695
  if (entry.kind === "subagent") {
44387
- await rm6(join28(root, AGENTS_REL2, `${n}.md`), { force: true });
44388
- await rm6(join28(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
44389
- await rm6(join28(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
44696
+ await rm6(join29(root, AGENTS_REL2, `${n}.md`), { force: true });
44697
+ await rm6(join29(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
44698
+ await rm6(join29(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
44390
44699
  } else {
44391
- await rm6(join28(root, SKILLS_REL2, n), { recursive: true, force: true });
44700
+ await rm6(join29(root, SKILLS_REL2, n), { recursive: true, force: true });
44392
44701
  }
44393
44702
  delete manifest[n];
44394
44703
  await writeManifest(root, manifest);
@@ -44399,8 +44708,8 @@ async function removePackage(root, slug) {
44399
44708
  }
44400
44709
 
44401
44710
  // src/core/graph.ts
44402
- import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
44403
- import { join as join29, relative as relative2, dirname as dirname11, resolve as resolve11, extname as extname2, basename as basename6 } from "path";
44711
+ import { readFile as readFile15, readdir as readdir6 } from "fs/promises";
44712
+ import { join as join30, relative as relative2, dirname as dirname11, resolve as resolve12, extname as extname2, basename as basename6 } from "path";
44404
44713
  var MAX_FILES = 2e3;
44405
44714
  var MAX_NODES = 3e3;
44406
44715
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
@@ -44420,7 +44729,7 @@ async function walk(root, accept) {
44420
44729
  if (e.name.startsWith(".") && e.name !== ".claude") {
44421
44730
  if (e.isDirectory()) continue;
44422
44731
  }
44423
- const full = join29(dir2, e.name);
44732
+ const full = join30(dir2, e.name);
44424
44733
  if (e.isDirectory()) {
44425
44734
  if (!SKIP_DIRS.has(e.name)) stack.push(full);
44426
44735
  } else if (accept(full)) {
@@ -44448,14 +44757,14 @@ async function buildDocs(root) {
44448
44757
  const rel = relative2(root, f2).split("\\").join("/");
44449
44758
  let text = "";
44450
44759
  try {
44451
- text = await readFile14(f2, "utf8");
44760
+ text = await readFile15(f2, "utf8");
44452
44761
  } catch {
44453
44762
  continue;
44454
44763
  }
44455
44764
  for (const m2 of text.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
44456
44765
  const href = (m2[1] ?? "").split("#")[0].trim();
44457
44766
  if (!href || !href.endsWith(".md") || href.startsWith("http")) continue;
44458
- const target = relative2(root, resolve11(dirname11(f2), href)).split("\\").join("/");
44767
+ const target = relative2(root, resolve12(dirname11(f2), href)).split("\\").join("/");
44459
44768
  if (ids.has(target) && target !== rel) {
44460
44769
  const key = `${rel}->${target}`;
44461
44770
  if (!seen.has(key)) {
@@ -44487,7 +44796,7 @@ async function buildCode(root) {
44487
44796
  const rel = relative2(root, f2).split("\\").join("/");
44488
44797
  let text = "";
44489
44798
  try {
44490
- text = await readFile14(f2, "utf8");
44799
+ text = await readFile15(f2, "utf8");
44491
44800
  } catch {
44492
44801
  continue;
44493
44802
  }
@@ -44539,17 +44848,17 @@ async function buildGraph(root, mode) {
44539
44848
  }
44540
44849
 
44541
44850
  // src/core/rag.ts
44542
- import { readFile as readFile15, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
44851
+ import { readFile as readFile16, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
44543
44852
  import { existsSync as existsSync16 } from "fs";
44544
- import { execFile as execFile11, spawn as spawn10 } from "child_process";
44853
+ import { execFile as execFile12, spawn as spawn10 } from "child_process";
44545
44854
  import { totalmem as totalmem2, cpus as cpus2 } from "os";
44546
- import { join as join30 } from "path";
44547
- import { promisify as promisify11 } from "util";
44548
- var run10 = promisify11(execFile11);
44855
+ import { join as join31 } from "path";
44856
+ import { promisify as promisify12 } from "util";
44857
+ var run11 = promisify12(execFile12);
44549
44858
  var RAG_REL = ".claude/rag";
44550
44859
  async function tryRun(cmd, args) {
44551
44860
  try {
44552
- const { stdout } = await run10(cmd, args, { timeout: 8e3 });
44861
+ const { stdout } = await run11(cmd, args, { timeout: 8e3 });
44553
44862
  return { ok: true, out: stdout.trim() };
44554
44863
  } catch {
44555
44864
  return { ok: false, out: "" };
@@ -44564,7 +44873,7 @@ async function detectPython() {
44564
44873
  }
44565
44874
  async function installLog(root) {
44566
44875
  try {
44567
- const text = await readFile15(join30(root, RAG_REL, "install.log"), "utf8");
44876
+ const text = await readFile16(join31(root, RAG_REL, "install.log"), "utf8");
44568
44877
  return text.split("\n").filter((l) => l.length > 0).slice(-200);
44569
44878
  } catch {
44570
44879
  return [];
@@ -44626,14 +44935,14 @@ async function ragStatus(root) {
44626
44935
  };
44627
44936
  let modelName = "";
44628
44937
  try {
44629
- modelName = (await readFile15(join30(root, RAG_REL, ".installed"), "utf8")).trim();
44938
+ modelName = (await readFile16(join31(root, RAG_REL, ".installed"), "utf8")).trim();
44630
44939
  } catch {
44631
44940
  modelName = "";
44632
44941
  }
44633
44942
  const modelPresent = modelName.length > 0;
44634
44943
  return {
44635
44944
  installed: fastembed && sqliteVec && modelPresent,
44636
- installing: existsSync16(join30(root, RAG_REL, ".installing")),
44945
+ installing: existsSync16(join31(root, RAG_REL, ".installing")),
44637
44946
  python: { found: py.found, version: py.version },
44638
44947
  deps: { pip, fastembed, sqliteVec },
44639
44948
  model: { present: modelPresent, name: modelName },
@@ -44644,9 +44953,9 @@ async function ragStatus(root) {
44644
44953
  };
44645
44954
  }
44646
44955
  async function readIndexMeta(root) {
44647
- const indexing = existsSync16(join30(root, RAG_REL, ".indexing"));
44956
+ const indexing = existsSync16(join31(root, RAG_REL, ".indexing"));
44648
44957
  try {
44649
- const m2 = JSON.parse(await readFile15(join30(root, RAG_REL, ".index.json"), "utf8"));
44958
+ const m2 = JSON.parse(await readFile16(join31(root, RAG_REL, ".index.json"), "utf8"));
44650
44959
  return {
44651
44960
  present: true,
44652
44961
  indexing,
@@ -44713,14 +45022,14 @@ if __name__ == "__main__":
44713
45022
  elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
44714
45023
  `;
44715
45024
  async function ensureEngine(root) {
44716
- await mkdir11(join30(root, RAG_REL), { recursive: true }).catch(() => void 0);
44717
- await writeFile12(join30(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
45025
+ await mkdir11(join31(root, RAG_REL), { recursive: true }).catch(() => void 0);
45026
+ await writeFile12(join31(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
44718
45027
  }
44719
45028
  async function ragReindex(root) {
44720
45029
  const py = await detectPython();
44721
45030
  if (!py.found) return { ok: false, started: false, error: "python3 not found" };
44722
- if (!existsSync16(join30(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
44723
- if (existsSync16(join30(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
45031
+ if (!existsSync16(join31(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
45032
+ if (existsSync16(join31(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
44724
45033
  await ensureEngine(root);
44725
45034
  const script = `
44726
45035
  set +e
@@ -44740,12 +45049,12 @@ rm -f "${RAG_REL}/.indexing"
44740
45049
  async function ragQuery(root, queryText, k2 = 8) {
44741
45050
  const py = await detectPython();
44742
45051
  if (!py.found) return { results: [], error: "python3 not found" };
44743
- if (!existsSync16(join30(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
45052
+ if (!existsSync16(join31(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
44744
45053
  await ensureEngine(root);
44745
45054
  try {
44746
- const { stdout } = await run10(
45055
+ const { stdout } = await run11(
44747
45056
  py.bin,
44748
- [join30(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
45057
+ [join31(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
44749
45058
  { cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
44750
45059
  );
44751
45060
  const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
@@ -44760,9 +45069,9 @@ async function ragInstall(root, model) {
44760
45069
  }
44761
45070
  const py = await detectPython();
44762
45071
  if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
44763
- if (existsSync16(join30(root, RAG_REL, ".installing"))) return { ok: true, started: true };
45072
+ if (existsSync16(join31(root, RAG_REL, ".installing"))) return { ok: true, started: true };
44764
45073
  try {
44765
- await mkdir11(join30(root, RAG_REL), { recursive: true });
45074
+ await mkdir11(join31(root, RAG_REL), { recursive: true });
44766
45075
  } catch {
44767
45076
  }
44768
45077
  const script = `
@@ -44790,7 +45099,7 @@ rm -f "${RAG_REL}/.installing"
44790
45099
 
44791
45100
  // src/core/config-sync.ts
44792
45101
  import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
44793
- import { join as join31 } from "path";
45102
+ import { join as join32 } from "path";
44794
45103
  var SERVER_MANAGED_KEYS = [
44795
45104
  "physicPath",
44796
45105
  "sessionSwitchPct",
@@ -44807,7 +45116,7 @@ var SERVER_MANAGED_KEYS = [
44807
45116
  "projectAiMemoryBudgetChars"
44808
45117
  ];
44809
45118
  function readConfigText(profileDir2) {
44810
- const path = join31(profileDir2, "config.json");
45119
+ const path = join32(profileDir2, "config.json");
44811
45120
  if (existsSync17(path)) {
44812
45121
  try {
44813
45122
  return readFileSync16(path, "utf8");
@@ -45294,14 +45603,14 @@ var WsClient = class {
45294
45603
  */
45295
45604
  awaitConnected(timeoutMs) {
45296
45605
  if (this.sessionKey && this.bus.status === "connected") return Promise.resolve(true);
45297
- return new Promise((resolve12) => {
45606
+ return new Promise((resolve13) => {
45298
45607
  const waiter = () => {
45299
45608
  clearTimeout(timer);
45300
- resolve12(true);
45609
+ resolve13(true);
45301
45610
  };
45302
45611
  const timer = setTimeout(() => {
45303
45612
  this.connectWaiters = this.connectWaiters.filter((w2) => w2 !== waiter);
45304
- resolve12(false);
45613
+ resolve13(false);
45305
45614
  }, timeoutMs);
45306
45615
  this.connectWaiters.push(waiter);
45307
45616
  });
@@ -45328,16 +45637,16 @@ var WsClient = class {
45328
45637
  this.forceReconnect = false;
45329
45638
  return Promise.resolve();
45330
45639
  }
45331
- return new Promise((resolve12) => {
45640
+ return new Promise((resolve13) => {
45332
45641
  const timer = setTimeout(() => {
45333
45642
  this.wakeReconnect = null;
45334
- resolve12();
45643
+ resolve13();
45335
45644
  }, ms2);
45336
45645
  this.wakeReconnect = () => {
45337
45646
  clearTimeout(timer);
45338
45647
  this.wakeReconnect = null;
45339
45648
  this.forceReconnect = false;
45340
- resolve12();
45649
+ resolve13();
45341
45650
  };
45342
45651
  });
45343
45652
  }
@@ -45391,7 +45700,7 @@ var WsClient = class {
45391
45700
  * One WS session: token → connect → handshake → handle messages until close.
45392
45701
  */
45393
45702
  connectOnce() {
45394
- return new Promise((resolve12, reject) => {
45703
+ return new Promise((resolve13, reject) => {
45395
45704
  void (async () => {
45396
45705
  this.sessionEstablished = false;
45397
45706
  this.bus.setStatus("connecting");
@@ -45492,7 +45801,7 @@ var WsClient = class {
45492
45801
  if (code === CLOSE_DRAINING) {
45493
45802
  this.bus.log(t("error.serverDraining"));
45494
45803
  }
45495
- resolve12();
45804
+ resolve13();
45496
45805
  });
45497
45806
  socket.on("error", (err) => {
45498
45807
  this.clearHeartbeat();
@@ -45530,6 +45839,14 @@ var WsClient = class {
45530
45839
  this.startUsagePolling();
45531
45840
  this.startLogUpload();
45532
45841
  this.startKbRefresh();
45842
+ const servedRoot = () => this.physicRoot;
45843
+ startGitSnapshots({
45844
+ get physicRoot() {
45845
+ return servedRoot();
45846
+ },
45847
+ profileDir: this.context.profileDir,
45848
+ send: (channel, data) => this.send(channel, data, null)
45849
+ });
45533
45850
  setToolHealthSink((report) => this.send(WsChannels.TOOL_HEALTH, report));
45534
45851
  this.connectWaiters.splice(0).forEach((fn) => fn());
45535
45852
  return;
@@ -45618,7 +45935,7 @@ var WsClient = class {
45618
45935
  * Used for quota.check before spawning an AI cli (ADR-0020).
45619
45936
  */
45620
45937
  request(channel, data) {
45621
- return new Promise((resolve12, reject) => {
45938
+ return new Promise((resolve13, reject) => {
45622
45939
  if (!this.socket || !this.sessionKey) {
45623
45940
  reject(new Error("Not connected to the server."));
45624
45941
  return;
@@ -45638,7 +45955,7 @@ var WsClient = class {
45638
45955
  }, 15e3);
45639
45956
  timer.unref?.();
45640
45957
  this.pending.set(envelope.id, {
45641
- resolve: (value) => resolve12(value),
45958
+ resolve: (value) => resolve13(value),
45642
45959
  timer
45643
45960
  });
45644
45961
  this.socket.send(JSON.stringify(envelope));
@@ -45707,7 +46024,7 @@ var WsClient = class {
45707
46024
  */
45708
46025
  physicFolderPath(projectName) {
45709
46026
  const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
45710
- return safe ? join32(this.context.profileDir, safe) : null;
46027
+ return safe ? join33(this.context.profileDir, safe) : null;
45711
46028
  }
45712
46029
  /**
45713
46030
  * Report machine.status: version + the state of the physic project being served
@@ -45819,7 +46136,7 @@ var WsClient = class {
45819
46136
  */
45820
46137
  uploadLog() {
45821
46138
  try {
45822
- const upload = readLogUpload(join32(this.context.profileDir, "logs"));
46139
+ const upload = readLogUpload(join33(this.context.profileDir, "logs"));
45823
46140
  if (!upload) return;
45824
46141
  const sig = `${upload.fileName}:${upload.totalBytes}`;
45825
46142
  if (sig === this.lastLogSig) return;
@@ -45982,9 +46299,9 @@ var WsClient = class {
45982
46299
 
45983
46300
  // src/core/instance-lock.ts
45984
46301
  import { readFileSync as readFileSync17, rmSync as rmSync9, writeFileSync as writeFileSync9 } from "fs";
45985
- import { join as join33 } from "path";
46302
+ import { join as join34 } from "path";
45986
46303
  function lockPath(profileDir2) {
45987
- return join33(profileDir2, "start.lock");
46304
+ return join34(profileDir2, "start.lock");
45988
46305
  }
45989
46306
  function isAlive(pid) {
45990
46307
  try {
@@ -46306,7 +46623,7 @@ var SessionBus = class {
46306
46623
  // src/core/control-server.ts
46307
46624
  import { createServer } from "net";
46308
46625
  import { chmodSync as chmodSync5, rmSync as rmSync10 } from "fs";
46309
- import { join as join35 } from "path";
46626
+ import { join as join36 } from "path";
46310
46627
 
46311
46628
  // src/core/control-protocol.ts
46312
46629
  var CONTROL_SOCKET_FILE = "control.sock";
@@ -46337,9 +46654,9 @@ function createFrameParser() {
46337
46654
  // src/core/control-token.ts
46338
46655
  import { chmodSync as chmodSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
46339
46656
  import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
46340
- import { join as join34 } from "path";
46657
+ import { join as join35 } from "path";
46341
46658
  function controlTokenPath(profileDir2) {
46342
- return join34(profileDir2, CONTROL_TOKEN_FILE);
46659
+ return join35(profileDir2, CONTROL_TOKEN_FILE);
46343
46660
  }
46344
46661
  function writeControlToken(profileDir2) {
46345
46662
  const token = randomBytes3(32).toString("hex");
@@ -46368,7 +46685,7 @@ function tokenMatches(expected, given) {
46368
46685
 
46369
46686
  // src/core/control-server.ts
46370
46687
  function startControlServer(bus, profileDir2, info) {
46371
- const socketPath = join35(profileDir2, CONTROL_SOCKET_FILE);
46688
+ const socketPath = join36(profileDir2, CONTROL_SOCKET_FILE);
46372
46689
  try {
46373
46690
  rmSync10(socketPath, { force: true });
46374
46691
  } catch {
@@ -51544,24 +51861,24 @@ var App = class extends import_react9.PureComponent {
51544
51861
  })
51545
51862
  }));
51546
51863
  };
51547
- findNextFocusable = (state2) => {
51548
- const activeIndex = state2.focusables.findIndex((focusable) => {
51549
- return focusable.id === state2.activeFocusId;
51864
+ findNextFocusable = (state3) => {
51865
+ const activeIndex = state3.focusables.findIndex((focusable) => {
51866
+ return focusable.id === state3.activeFocusId;
51550
51867
  });
51551
- for (let index = activeIndex + 1; index < state2.focusables.length; index++) {
51552
- const focusable = state2.focusables[index];
51868
+ for (let index = activeIndex + 1; index < state3.focusables.length; index++) {
51869
+ const focusable = state3.focusables[index];
51553
51870
  if (focusable?.isActive) {
51554
51871
  return focusable.id;
51555
51872
  }
51556
51873
  }
51557
51874
  return void 0;
51558
51875
  };
51559
- findPreviousFocusable = (state2) => {
51560
- const activeIndex = state2.focusables.findIndex((focusable) => {
51561
- return focusable.id === state2.activeFocusId;
51876
+ findPreviousFocusable = (state3) => {
51877
+ const activeIndex = state3.focusables.findIndex((focusable) => {
51878
+ return focusable.id === state3.activeFocusId;
51562
51879
  });
51563
51880
  for (let index = activeIndex - 1; index >= 0; index--) {
51564
- const focusable = state2.focusables[index];
51881
+ const focusable = state3.focusables[index];
51565
51882
  if (focusable?.isActive) {
51566
51883
  return focusable.id;
51567
51884
  }
@@ -51757,8 +52074,8 @@ var Ink = class {
51757
52074
  }
51758
52075
  }
51759
52076
  async waitUntilExit() {
51760
- this.exitPromise ||= new Promise((resolve12, reject) => {
51761
- this.resolveExitPromise = resolve12;
52077
+ this.exitPromise ||= new Promise((resolve13, reject) => {
52078
+ this.resolveExitPromise = resolve13;
51762
52079
  this.rejectExitPromise = reject;
51763
52080
  });
51764
52081
  return this.exitPromise;
@@ -52126,10 +52443,10 @@ var import_react26 = __toESM(require_react(), 1);
52126
52443
 
52127
52444
  // src/core/input-history.ts
52128
52445
  import { existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
52129
- import { join as join36 } from "path";
52446
+ import { join as join37 } from "path";
52130
52447
  var MAX_HISTORY = 200;
52131
52448
  function historyPath(profileDir2) {
52132
- return join36(profileDir2, "input-history.json");
52449
+ return join37(profileDir2, "input-history.json");
52133
52450
  }
52134
52451
  function loadInputHistory(profileDir2) {
52135
52452
  const path = historyPath(profileDir2);
@@ -52743,7 +53060,7 @@ async function runStart(profileDir2, profileName) {
52743
53060
  return;
52744
53061
  }
52745
53062
  logger.configure({
52746
- dir: join37(profileDir2, "logs"),
53063
+ dir: join38(profileDir2, "logs"),
52747
53064
  level: resolveLogLevel(),
52748
53065
  base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
52749
53066
  });
@@ -52811,7 +53128,7 @@ async function runStart(profileDir2, profileName) {
52811
53128
  logger.error("ws.run.crash", { message: e.message, stack: e.stack });
52812
53129
  if (client.isStopped) return;
52813
53130
  bus.log(t("start.recovered", { message: e.message }), "error");
52814
- await new Promise((resolve12) => setTimeout(resolve12, 1e3));
53131
+ await new Promise((resolve13) => setTimeout(resolve13, 1e3));
52815
53132
  }
52816
53133
  }
52817
53134
  };
@@ -52853,12 +53170,12 @@ async function runStart(profileDir2, profileName) {
52853
53170
  }
52854
53171
 
52855
53172
  // src/commands/attach.ts
52856
- import { join as join38 } from "path";
53173
+ import { join as join39 } from "path";
52857
53174
 
52858
53175
  // src/core/control-client.ts
52859
53176
  import { connect as connect2 } from "net";
52860
53177
  function connectControl(socketPath, bus, token) {
52861
- return new Promise((resolve12, reject) => {
53178
+ return new Promise((resolve13, reject) => {
52862
53179
  const socket = connect2(socketPath);
52863
53180
  socket.setEncoding("utf8");
52864
53181
  let ready = false;
@@ -52911,7 +53228,7 @@ function connectControl(socketPath, bus, token) {
52911
53228
  bus.setTokens(frame.tokens);
52912
53229
  for (const entry of frame.transcript) bus.pushEntry(entry);
52913
53230
  ready = true;
52914
- resolve12({ info: frame.info, close });
53231
+ resolve13({ info: frame.info, close });
52915
53232
  break;
52916
53233
  case "transcript":
52917
53234
  bus.pushEntry(frame.entry);
@@ -52962,7 +53279,7 @@ async function runAttach(profileDir2, profileName) {
52962
53279
  process.exitCode = 1;
52963
53280
  return;
52964
53281
  }
52965
- const socketPath = join38(profileDir2, CONTROL_SOCKET_FILE);
53282
+ const socketPath = join39(profileDir2, CONTROL_SOCKET_FILE);
52966
53283
  const bus = new SessionBus();
52967
53284
  let conn;
52968
53285
  try {
@@ -52993,13 +53310,13 @@ async function runAttach(profileDir2, profileName) {
52993
53310
 
52994
53311
  // src/commands/auto-run.ts
52995
53312
  import { connect as connect3 } from "net";
52996
- import { join as join39 } from "path";
53313
+ import { join as join40 } from "path";
52997
53314
  var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
52998
53315
  async function runAutoRun(profileDir2, profileName) {
52999
53316
  initI18n(readProfileConfig(profileDir2).locale);
53000
- const socketPath = join39(profileDir2, CONTROL_SOCKET_FILE);
53317
+ const socketPath = join40(profileDir2, CONTROL_SOCKET_FILE);
53001
53318
  const token = readControlToken(profileDir2);
53002
- const code = await new Promise((resolve12) => {
53319
+ const code = await new Promise((resolve13) => {
53003
53320
  let settled = false;
53004
53321
  const done = (c) => {
53005
53322
  if (settled) return;
@@ -53009,7 +53326,7 @@ async function runAutoRun(profileDir2, profileName) {
53009
53326
  socket.destroy();
53010
53327
  } catch {
53011
53328
  }
53012
- resolve12(c);
53329
+ resolve13(c);
53013
53330
  };
53014
53331
  const socket = connect3(socketPath);
53015
53332
  socket.setEncoding("utf8");
@@ -53054,22 +53371,22 @@ async function runAutoRun(profileDir2, profileName) {
53054
53371
 
53055
53372
  // src/commands/git-credential.ts
53056
53373
  import { connect as connect4 } from "net";
53057
- import { join as join40 } from "path";
53374
+ import { join as join41 } from "path";
53058
53375
  var REQUEST_TIMEOUT_MS = 2e4;
53059
53376
  function requestToken(host, path) {
53060
53377
  const profileDir2 = process.env[PROFILE_DIR_ENV];
53061
53378
  if (!profileDir2) return Promise.resolve(null);
53062
53379
  const token = readControlToken(profileDir2);
53063
- return new Promise((resolve12) => {
53380
+ return new Promise((resolve13) => {
53064
53381
  let done = false;
53065
53382
  const finish = (value) => {
53066
53383
  if (done) return;
53067
53384
  done = true;
53068
53385
  clearTimeout(timer);
53069
53386
  socket.destroy();
53070
- resolve12(value);
53387
+ resolve13(value);
53071
53388
  };
53072
- const socket = connect4(join40(profileDir2, CONTROL_SOCKET_FILE));
53389
+ const socket = connect4(join41(profileDir2, CONTROL_SOCKET_FILE));
53073
53390
  socket.setEncoding("utf8");
53074
53391
  const timer = setTimeout(() => finish(null), REQUEST_TIMEOUT_MS);
53075
53392
  socket.on("connect", () => {
@@ -53127,16 +53444,16 @@ import { spawn as spawn12 } from "child_process";
53127
53444
  import { mkdirSync as mkdirSync10 } from "fs";
53128
53445
  import { basename as basename8 } from "path";
53129
53446
  function runProviderLogin(cmd, envVar, dir2) {
53130
- return new Promise((resolve12) => {
53447
+ return new Promise((resolve13) => {
53131
53448
  const child = spawn12(cmd, [], {
53132
53449
  stdio: "inherit",
53133
53450
  env: { ...process.env, [envVar]: dir2 }
53134
53451
  });
53135
53452
  child.on("error", (err) => {
53136
53453
  console.error(t("aiLogin.launchFailed", { cmd, message: err.message }));
53137
- resolve12(1);
53454
+ resolve13(1);
53138
53455
  });
53139
- child.on("exit", (code) => resolve12(code ?? 0));
53456
+ child.on("exit", (code) => resolve13(code ?? 0));
53140
53457
  });
53141
53458
  }
53142
53459
  async function runAiLogin(profileDir2, profileName, aiFilter) {
@@ -53276,10 +53593,10 @@ function Picker({
53276
53593
  ] });
53277
53594
  }
53278
53595
  function selectFromList(items, title) {
53279
- return new Promise((resolve12) => {
53596
+ return new Promise((resolve13) => {
53280
53597
  let chosen = null;
53281
53598
  const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Picker, { items, title, onDone: (v2) => chosen = v2 }));
53282
- void app.waitUntilExit().then(() => resolve12(chosen));
53599
+ void app.waitUntilExit().then(() => resolve13(chosen));
53283
53600
  });
53284
53601
  }
53285
53602