@adhdev/daemon-core 0.9.53 → 0.9.55

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 (50) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +5 -0
  2. package/dist/cli-adapters/provider-cli-adapter.d.ts +1 -0
  3. package/dist/cli-adapters/provider-cli-config.d.ts +1 -0
  4. package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
  5. package/dist/commands/handler.d.ts +7 -0
  6. package/dist/git/git-commands.d.ts +139 -0
  7. package/dist/git/git-diff.d.ts +17 -0
  8. package/dist/git/git-executor.d.ts +34 -0
  9. package/dist/git/git-monitor.d.ts +57 -0
  10. package/dist/git/git-snapshot-store.d.ts +50 -0
  11. package/dist/git/git-status.d.ts +19 -0
  12. package/dist/git/git-summary.d.ts +10 -0
  13. package/dist/git/git-types.d.ts +113 -0
  14. package/dist/git/index.d.ts +16 -0
  15. package/dist/git/turn-snapshot-tracker.d.ts +16 -0
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +1772 -386
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +1744 -383
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/providers/contracts.d.ts +2 -0
  22. package/dist/shared-types.d.ts +7 -1
  23. package/dist/status/builders.d.ts +2 -0
  24. package/dist/status/snapshot.d.ts +2 -0
  25. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  26. package/package.json +1 -1
  27. package/src/boot/daemon-lifecycle.ts +6 -0
  28. package/src/cli-adapters/provider-cli-adapter.ts +19 -0
  29. package/src/cli-adapters/provider-cli-config.d.ts +1 -0
  30. package/src/cli-adapters/provider-cli-config.ts +2 -0
  31. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  32. package/src/cli-adapters/provider-cli-shared.ts +2 -0
  33. package/src/commands/handler.ts +25 -1
  34. package/src/git/git-commands.ts +582 -0
  35. package/src/git/git-diff.ts +303 -0
  36. package/src/git/git-executor.ts +268 -0
  37. package/src/git/git-monitor.ts +194 -0
  38. package/src/git/git-snapshot-store.ts +238 -0
  39. package/src/git/git-status.ts +193 -0
  40. package/src/git/git-summary.ts +43 -0
  41. package/src/git/git-types.ts +154 -0
  42. package/src/git/index.ts +75 -0
  43. package/src/git/turn-snapshot-tracker.ts +31 -0
  44. package/src/index.ts +4 -0
  45. package/src/providers/contracts.d.ts +8 -0
  46. package/src/providers/contracts.ts +2 -0
  47. package/src/providers/provider-schema.ts +1 -0
  48. package/src/shared-types.ts +33 -1
  49. package/src/status/builders.ts +26 -4
  50. package/src/status/snapshot.ts +10 -2
package/dist/index.mjs CHANGED
@@ -265,7 +265,7 @@ var init_config = __esm({
265
265
 
266
266
  // src/logging/logger.ts
267
267
  import * as fs2 from "fs";
268
- import * as path6 from "path";
268
+ import * as path9 from "path";
269
269
  import * as os4 from "os";
270
270
  function setLogLevel(level) {
271
271
  currentLevel = level;
@@ -281,13 +281,13 @@ function getDaemonLogDir() {
281
281
  return LOG_DIR;
282
282
  }
283
283
  function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
284
- return path6.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
284
+ return path9.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
285
285
  }
286
286
  function checkDateRotation() {
287
287
  const today = getDateStr();
288
288
  if (today !== currentDate) {
289
289
  currentDate = today;
290
- currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
290
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
291
291
  cleanOldLogs();
292
292
  }
293
293
  }
@@ -301,7 +301,7 @@ function cleanOldLogs() {
301
301
  const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
302
302
  if (dateMatch && dateMatch[1] < cutoffStr) {
303
303
  try {
304
- fs2.unlinkSync(path6.join(LOG_DIR, file));
304
+ fs2.unlinkSync(path9.join(LOG_DIR, file));
305
305
  } catch {
306
306
  }
307
307
  }
@@ -311,8 +311,8 @@ function cleanOldLogs() {
311
311
  }
312
312
  function rotateSizeIfNeeded() {
313
313
  try {
314
- const stat = fs2.statSync(currentLogFile);
315
- if (stat.size > MAX_LOG_SIZE) {
314
+ const stat2 = fs2.statSync(currentLogFile);
315
+ if (stat2.size > MAX_LOG_SIZE) {
316
316
  const backup = currentLogFile.replace(".log", ".1.log");
317
317
  try {
318
318
  fs2.unlinkSync(backup);
@@ -424,7 +424,7 @@ var init_logger = __esm({
424
424
  LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
425
425
  LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
426
426
  currentLevel = "info";
427
- LOG_DIR = process.platform === "win32" ? path6.join(process.env.LOCALAPPDATA || process.env.APPDATA || path6.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path6.join(os4.homedir(), "Library", "Logs", "adhdev") : path6.join(os4.homedir(), ".local", "share", "adhdev", "logs");
427
+ LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os4.homedir(), "Library", "Logs", "adhdev") : path9.join(os4.homedir(), ".local", "share", "adhdev", "logs");
428
428
  MAX_LOG_SIZE = 5 * 1024 * 1024;
429
429
  MAX_LOG_DAYS = 7;
430
430
  try {
@@ -432,16 +432,16 @@ var init_logger = __esm({
432
432
  } catch {
433
433
  }
434
434
  currentDate = getDateStr();
435
- currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
435
+ currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
436
436
  cleanOldLogs();
437
437
  try {
438
- const oldLog = path6.join(LOG_DIR, "daemon.log");
438
+ const oldLog = path9.join(LOG_DIR, "daemon.log");
439
439
  if (fs2.existsSync(oldLog)) {
440
- const stat = fs2.statSync(oldLog);
441
- const oldDate = stat.mtime.toISOString().slice(0, 10);
442
- fs2.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
440
+ const stat2 = fs2.statSync(oldLog);
441
+ const oldDate = stat2.mtime.toISOString().slice(0, 10);
442
+ fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
443
443
  }
444
- const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
444
+ const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
445
445
  if (fs2.existsSync(oldLogBackup)) {
446
446
  fs2.unlinkSync(oldLogBackup);
447
447
  }
@@ -473,7 +473,7 @@ var init_logger = __esm({
473
473
  }
474
474
  };
475
475
  interceptorInstalled = false;
476
- LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
476
+ LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
477
477
  }
478
478
  });
479
479
 
@@ -1380,8 +1380,8 @@ var init_pty_transport = __esm({
1380
1380
  if (cwd) {
1381
1381
  try {
1382
1382
  const fs16 = __require("fs");
1383
- const stat = fs16.statSync(cwd);
1384
- if (!stat.isDirectory()) cwd = os8.homedir();
1383
+ const stat2 = fs16.statSync(cwd);
1384
+ if (!stat2.isDirectory()) cwd = os8.homedir();
1385
1385
  } catch {
1386
1386
  cwd = os8.homedir();
1387
1387
  }
@@ -1401,7 +1401,7 @@ var init_pty_transport = __esm({
1401
1401
 
1402
1402
  // src/cli-adapters/provider-cli-shared.ts
1403
1403
  import * as os9 from "os";
1404
- import * as path10 from "path";
1404
+ import * as path13 from "path";
1405
1405
  import { execSync as execSync3 } from "child_process";
1406
1406
  function stripAnsi(str) {
1407
1407
  return str.replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][\s\S]*?\x1B\\/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B\[\d*[A-HJKSTfG]/g, " ").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/ +/g, " ");
@@ -1466,9 +1466,9 @@ function buildCliScreenSnapshot(text) {
1466
1466
  function findBinary(name) {
1467
1467
  const trimmed = String(name || "").trim();
1468
1468
  if (!trimmed) return trimmed;
1469
- const expanded = trimmed.startsWith("~") ? path10.join(os9.homedir(), trimmed.slice(1)) : trimmed;
1470
- if (path10.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
1471
- return path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
1469
+ const expanded = trimmed.startsWith("~") ? path13.join(os9.homedir(), trimmed.slice(1)) : trimmed;
1470
+ if (path13.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
1471
+ return path13.isAbsolute(expanded) ? expanded : path13.resolve(expanded);
1472
1472
  }
1473
1473
  const isWin = os9.platform() === "win32";
1474
1474
  try {
@@ -1484,7 +1484,7 @@ function findBinary(name) {
1484
1484
  }
1485
1485
  }
1486
1486
  function isScriptBinary(binaryPath) {
1487
- if (!path10.isAbsolute(binaryPath)) return false;
1487
+ if (!path13.isAbsolute(binaryPath)) return false;
1488
1488
  try {
1489
1489
  const fs16 = __require("fs");
1490
1490
  const resolved = fs16.realpathSync(binaryPath);
@@ -1500,7 +1500,7 @@ function isScriptBinary(binaryPath) {
1500
1500
  }
1501
1501
  }
1502
1502
  function looksLikeMachOOrElf(filePath) {
1503
- if (!path10.isAbsolute(filePath)) return false;
1503
+ if (!path13.isAbsolute(filePath)) return false;
1504
1504
  try {
1505
1505
  const fs16 = __require("fs");
1506
1506
  const resolved = fs16.realpathSync(filePath);
@@ -1940,6 +1940,7 @@ function resolveCliAdapterConfig(provider) {
1940
1940
  sendDelayMs: typeof provider.sendDelayMs === "number" ? Math.max(0, provider.sendDelayMs) : 0,
1941
1941
  sendKey: typeof provider.sendKey === "string" && provider.sendKey.length > 0 ? provider.sendKey : "\r",
1942
1942
  submitStrategy: provider.submitStrategy === "immediate" ? "immediate" : "wait_for_echo",
1943
+ requirePromptEchoBeforeSubmit: provider.requirePromptEchoBeforeSubmit === true,
1943
1944
  providerResolutionMeta: {
1944
1945
  type: provider.type,
1945
1946
  name: provider.name,
@@ -1961,7 +1962,7 @@ var init_provider_cli_config = __esm({
1961
1962
 
1962
1963
  // src/cli-adapters/provider-cli-runtime.ts
1963
1964
  import * as os10 from "os";
1964
- import * as path11 from "path";
1965
+ import * as path14 from "path";
1965
1966
  import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from "@adhdev/session-host-core";
1966
1967
  function resolveCliSpawnPlan(options) {
1967
1968
  const { provider, runtimeSettings, workingDir, extraArgs } = options;
@@ -1972,9 +1973,9 @@ function resolveCliSpawnPlan(options) {
1972
1973
  const allArgs = [...spawnConfig.args, ...extraArgs];
1973
1974
  let shellCmd;
1974
1975
  let shellArgs;
1975
- const useShellUnix = !isWin && (!!spawnConfig.shell || !path11.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
1976
+ const useShellUnix = !isWin && (!!spawnConfig.shell || !path14.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
1976
1977
  const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
1977
- const useShellWin = !!spawnConfig.shell || isCmdShim || !path11.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
1978
+ const useShellWin = !!spawnConfig.shell || isCmdShim || !path14.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
1978
1979
  const useShell = isWin ? useShellWin : useShellUnix;
1979
1980
  if (useShell) {
1980
1981
  shellCmd = isWin ? "cmd.exe" : process.env.SHELL || "/bin/zsh";
@@ -2204,6 +2205,7 @@ var init_provider_cli_adapter = __esm({
2204
2205
  this.sendDelayMs = resolvedConfig.sendDelayMs;
2205
2206
  this.sendKey = resolvedConfig.sendKey;
2206
2207
  this.submitStrategy = resolvedConfig.submitStrategy;
2208
+ this.requirePromptEchoBeforeSubmit = resolvedConfig.requirePromptEchoBeforeSubmit;
2207
2209
  this.providerResolutionMeta = resolvedConfig.providerResolutionMeta;
2208
2210
  this.cliScripts = provider.scripts || {};
2209
2211
  const scriptNames = listCliScriptNames(this.cliScripts);
@@ -2500,6 +2502,7 @@ var init_provider_cli_adapter = __esm({
2500
2502
  sendDelayMs;
2501
2503
  sendKey;
2502
2504
  submitStrategy;
2505
+ requirePromptEchoBeforeSubmit;
2503
2506
  static SCRIPT_STATUS_DEBOUNCE_MS = 3e3;
2504
2507
  /** Inject CLI scripts after construction (e.g. when resolved by ProviderLoader) */
2505
2508
  setCliScripts(scripts) {
@@ -2851,7 +2854,7 @@ var init_provider_cli_adapter = __esm({
2851
2854
  `[${this.cliType}] Waiting for interactive prompt: status=${status} stableMs=${stableMs} recentOutputMs=${recentlyOutput} screen=${JSON.stringify(summarizeCliTraceText(screenText, 220)).slice(0, 260)}`
2852
2855
  );
2853
2856
  }
2854
- await new Promise((resolve12) => setTimeout(resolve12, 50));
2857
+ await new Promise((resolve15) => setTimeout(resolve15, 50));
2855
2858
  }
2856
2859
  const finalScreenText = this.terminalScreen.getText() || "";
2857
2860
  LOG.warn(
@@ -4020,6 +4023,22 @@ var init_provider_cli_adapter = __esm({
4020
4023
  }
4021
4024
  }
4022
4025
  if (elapsed >= state.maxEchoWaitMs) {
4026
+ const diagnostic = {
4027
+ elapsed,
4028
+ maxEchoWaitMs: state.maxEchoWaitMs,
4029
+ submitDelayMs: state.submitDelayMs,
4030
+ promptSnippet: state.normalizedPromptSnippet,
4031
+ requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
4032
+ screenText: summarizeCliTraceText(screenText, 1e3)
4033
+ };
4034
+ this.recordTrace("submit_echo_missing", diagnostic);
4035
+ if (this.requirePromptEchoBeforeSubmit) {
4036
+ const message = `${this.cliName} prompt echo was not observed on the PTY screen before submit`;
4037
+ LOG.warn("CLI", `[${this.cliType}] ${message} elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs} screen=${JSON.stringify(diagnostic.screenText).slice(0, 240)}`);
4038
+ completion.rejectOnce(new Error(message));
4039
+ return;
4040
+ }
4041
+ LOG.warn("CLI", `[${this.cliType}] prompt echo was not observed before submit; sending submit key anyway elapsed=${elapsed}ms maxEchoWaitMs=${state.maxEchoWaitMs}`);
4023
4042
  this.submitSendKey(state, completion);
4024
4043
  return;
4025
4044
  }
@@ -4039,7 +4058,7 @@ var init_provider_cli_adapter = __esm({
4039
4058
  const deadline = Date.now() + 1e4;
4040
4059
  while (this.startupParseGate && Date.now() < deadline) {
4041
4060
  this.resolveStartupState("send_wait");
4042
- await new Promise((resolve12) => setTimeout(resolve12, 50));
4061
+ await new Promise((resolve15) => setTimeout(resolve15, 50));
4043
4062
  }
4044
4063
  }
4045
4064
  if (!allowInterventionPrompt) {
@@ -4120,13 +4139,13 @@ var init_provider_cli_adapter = __esm({
4120
4139
  }
4121
4140
  this.responseEpoch += 1;
4122
4141
  this.responseSettleIgnoreUntil = Date.now() + submitDelayMs + this.timeouts.outputSettle + 250;
4123
- await new Promise((resolve12, reject) => {
4142
+ await new Promise((resolve15, reject) => {
4124
4143
  let resolved = false;
4125
4144
  const completion = {
4126
4145
  resolveOnce: () => {
4127
4146
  if (resolved) return;
4128
4147
  resolved = true;
4129
- resolve12();
4148
+ resolve15();
4130
4149
  },
4131
4150
  rejectOnce: (error) => {
4132
4151
  if (resolved) return;
@@ -4288,17 +4307,17 @@ var init_provider_cli_adapter = __esm({
4288
4307
  }
4289
4308
  }
4290
4309
  waitForStopped(timeoutMs) {
4291
- return new Promise((resolve12) => {
4310
+ return new Promise((resolve15) => {
4292
4311
  const startedAt = Date.now();
4293
4312
  const timer = setInterval(() => {
4294
4313
  if (!this.ptyProcess || this.currentStatus === "stopped") {
4295
4314
  clearInterval(timer);
4296
- resolve12(true);
4315
+ resolve15(true);
4297
4316
  return;
4298
4317
  }
4299
4318
  if (Date.now() - startedAt >= timeoutMs) {
4300
4319
  clearInterval(timer);
4301
- resolve12(false);
4320
+ resolve15(false);
4302
4321
  }
4303
4322
  }, 100);
4304
4323
  });
@@ -4484,6 +4503,7 @@ var init_provider_cli_adapter = __esm({
4484
4503
  sendDelayMs: this.sendDelayMs,
4485
4504
  sendKey: this.sendKey,
4486
4505
  submitStrategy: this.submitStrategy,
4506
+ requirePromptEchoBeforeSubmit: this.requirePromptEchoBeforeSubmit,
4487
4507
  submitPendingUntil: this.submitPendingUntil,
4488
4508
  responseSettleIgnoreUntil: this.responseSettleIgnoreUntil,
4489
4509
  resizeSuppressUntil: this.resizeSuppressUntil,
@@ -4524,20 +4544,1298 @@ var init_provider_cli_adapter = __esm({
4524
4544
  }
4525
4545
  });
4526
4546
 
4547
+ // src/git/git-executor.ts
4548
+ import { execFile } from "child_process";
4549
+ import { constants } from "fs";
4550
+ import { access, realpath, stat } from "fs/promises";
4551
+ import * as path from "path";
4552
+ import { promisify } from "util";
4553
+ var execFileAsync = promisify(execFile);
4554
+ var DEFAULT_TIMEOUT_MS = 5e3;
4555
+ var DEFAULT_MAX_BUFFER = 1024 * 1024;
4556
+ var GitCommandError = class extends Error {
4557
+ reason;
4558
+ stdout;
4559
+ stderr;
4560
+ exitCode;
4561
+ signal;
4562
+ argv;
4563
+ cwd;
4564
+ constructor(reason, message, details = {}) {
4565
+ super(message);
4566
+ if (details.cause !== void 0) {
4567
+ this.cause = details.cause;
4568
+ }
4569
+ this.name = "GitCommandError";
4570
+ this.reason = reason;
4571
+ this.stdout = normalizeGitOutput(details.stdout);
4572
+ this.stderr = normalizeGitOutput(details.stderr);
4573
+ this.exitCode = details.exitCode;
4574
+ this.signal = details.signal;
4575
+ this.argv = details.argv ? [...details.argv] : void 0;
4576
+ this.cwd = details.cwd;
4577
+ }
4578
+ };
4579
+ async function resolveGitRepository(workspace, options = {}) {
4580
+ const normalizedWorkspace = await validateWorkspace(workspace);
4581
+ const result = await execGitRaw(normalizedWorkspace, ["rev-parse", "--show-toplevel"], options, {
4582
+ mapNotGitRepo: true
4583
+ });
4584
+ const repoRoot = path.resolve(result.stdout.trim());
4585
+ if (!repoRoot) {
4586
+ throw new GitCommandError("not_git_repo", "Git did not return a repository root", {
4587
+ stdout: result.stdout,
4588
+ stderr: result.stderr,
4589
+ argv: ["rev-parse", "--show-toplevel"],
4590
+ cwd: normalizedWorkspace
4591
+ });
4592
+ }
4593
+ return {
4594
+ workspace: normalizedWorkspace,
4595
+ repoRoot,
4596
+ isGitRepo: true
4597
+ };
4598
+ }
4599
+ async function runGit(repoOrWorkspace, argv, options = {}) {
4600
+ validateGitArgv(argv);
4601
+ const repo = typeof repoOrWorkspace === "string" ? await resolveGitRepository(repoOrWorkspace, options) : repoOrWorkspace;
4602
+ if (!repo.repoRoot || !repo.isGitRepo) {
4603
+ throw new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
4604
+ argv,
4605
+ cwd: repo.workspace
4606
+ });
4607
+ }
4608
+ const cwd = options.cwd ? await validateWorkspace(options.cwd) : await validateWorkspace(repo.workspace);
4609
+ const canonicalRepoRoot = await realpath(repo.repoRoot);
4610
+ const canonicalCwd = await realpath(cwd);
4611
+ if (!isPathInside(canonicalRepoRoot, canonicalCwd)) {
4612
+ throw new GitCommandError("path_outside_repo", "Git cwd is outside the repository root", {
4613
+ argv,
4614
+ cwd
4615
+ });
4616
+ }
4617
+ return execGitRaw(cwd, argv, options);
4618
+ }
4619
+ function normalizeGitOutput(value) {
4620
+ if (typeof value === "string") return value.replace(/\r\n/g, "\n");
4621
+ if (Buffer.isBuffer(value)) return value.toString("utf8").replace(/\r\n/g, "\n");
4622
+ if (value == null) return "";
4623
+ return String(value).replace(/\r\n/g, "\n");
4624
+ }
4625
+ function isPathInside(parent, child) {
4626
+ const relative3 = path.relative(path.resolve(parent), path.resolve(child));
4627
+ return relative3 === "" || !relative3.startsWith("..") && !path.isAbsolute(relative3);
4628
+ }
4629
+ async function validateWorkspace(workspace) {
4630
+ if (typeof workspace !== "string" || workspace.length === 0 || workspace.includes("\0")) {
4631
+ throw new GitCommandError("invalid_args", "Workspace must be a non-empty path");
4632
+ }
4633
+ if (!path.isAbsolute(workspace)) {
4634
+ throw new GitCommandError("invalid_args", "Workspace must be an absolute path", { cwd: workspace });
4635
+ }
4636
+ const normalizedWorkspace = path.resolve(workspace);
4637
+ try {
4638
+ const info = await stat(normalizedWorkspace);
4639
+ if (!info.isDirectory()) {
4640
+ throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
4641
+ cwd: normalizedWorkspace
4642
+ });
4643
+ }
4644
+ await access(normalizedWorkspace, constants.R_OK);
4645
+ } catch (error) {
4646
+ if (error instanceof GitCommandError) throw error;
4647
+ throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
4648
+ cwd: normalizedWorkspace,
4649
+ cause: error
4650
+ });
4651
+ }
4652
+ return normalizedWorkspace;
4653
+ }
4654
+ function validateGitArgv(argv) {
4655
+ if (!Array.isArray(argv) || argv.length === 0) {
4656
+ throw new GitCommandError("invalid_args", "Git argv must be a non-empty string array", { argv });
4657
+ }
4658
+ for (const arg of argv) {
4659
+ if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0")) {
4660
+ throw new GitCommandError("invalid_args", "Git argv contains an invalid argument", { argv });
4661
+ }
4662
+ }
4663
+ if (argv.includes("-C") || argv.some((arg) => arg.startsWith("--git-dir") || arg.startsWith("--work-tree"))) {
4664
+ throw new GitCommandError("invalid_args", "Git argv contains unsafe repository override arguments", {
4665
+ argv
4666
+ });
4667
+ }
4668
+ }
4669
+ async function execGitRaw(cwd, argv, options, behavior = {}) {
4670
+ validateGitArgv(argv);
4671
+ try {
4672
+ const result = await execFileAsync("git", [...argv], {
4673
+ cwd,
4674
+ encoding: "utf8",
4675
+ timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
4676
+ maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
4677
+ windowsHide: true
4678
+ });
4679
+ return {
4680
+ stdout: normalizeGitOutput(result.stdout),
4681
+ stderr: normalizeGitOutput(result.stderr)
4682
+ };
4683
+ } catch (error) {
4684
+ throw mapExecError(error, cwd, argv, behavior);
4685
+ }
4686
+ }
4687
+ function mapExecError(error, cwd, argv, behavior) {
4688
+ const execError = error;
4689
+ const stdout = normalizeGitOutput(execError.stdout);
4690
+ const stderr = normalizeGitOutput(execError.stderr);
4691
+ const code = execError.code;
4692
+ const signal = execError.signal;
4693
+ const message = [stderr.trim(), execError.message].filter(Boolean).join("\n");
4694
+ if (code === "ENOENT") {
4695
+ return new GitCommandError("git_not_installed", "Git executable was not found", {
4696
+ stdout,
4697
+ stderr,
4698
+ exitCode: code,
4699
+ signal,
4700
+ argv,
4701
+ cwd,
4702
+ cause: error
4703
+ });
4704
+ }
4705
+ if (execError.killed || /timed out/i.test(execError.message)) {
4706
+ return new GitCommandError("timeout", "Git command timed out", {
4707
+ stdout,
4708
+ stderr,
4709
+ exitCode: code,
4710
+ signal,
4711
+ argv,
4712
+ cwd,
4713
+ cause: error
4714
+ });
4715
+ }
4716
+ if (behavior.mapNotGitRepo && /not a git repository/i.test(stderr + "\n" + execError.message)) {
4717
+ return new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
4718
+ stdout,
4719
+ stderr,
4720
+ exitCode: code,
4721
+ signal,
4722
+ argv,
4723
+ cwd,
4724
+ cause: error
4725
+ });
4726
+ }
4727
+ return new GitCommandError("git_command_failed", message || "Git command failed", {
4728
+ stdout,
4729
+ stderr,
4730
+ exitCode: code,
4731
+ signal,
4732
+ argv,
4733
+ cwd,
4734
+ cause: error
4735
+ });
4736
+ }
4737
+
4738
+ // src/git/git-status.ts
4739
+ async function getGitRepoStatus(workspace, options = {}) {
4740
+ const lastCheckedAt = Date.now();
4741
+ try {
4742
+ const repo = await resolveGitRepository(workspace, options);
4743
+ const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
4744
+ const parsed = parsePorcelainV2Status(statusOutput.stdout);
4745
+ const head = await readHead(repo, options);
4746
+ const stashCount = await readStashCount(repo, options);
4747
+ return {
4748
+ workspace: repo.workspace,
4749
+ repoRoot: repo.repoRoot,
4750
+ isGitRepo: true,
4751
+ branch: parsed.branch,
4752
+ headCommit: head.commit,
4753
+ headMessage: head.message,
4754
+ upstream: parsed.upstream,
4755
+ ahead: parsed.ahead,
4756
+ behind: parsed.behind,
4757
+ staged: parsed.staged,
4758
+ modified: parsed.modified,
4759
+ untracked: parsed.untracked,
4760
+ deleted: parsed.deleted,
4761
+ renamed: parsed.renamed,
4762
+ hasConflicts: parsed.conflictFiles.length > 0,
4763
+ conflictFiles: parsed.conflictFiles,
4764
+ stashCount,
4765
+ lastCheckedAt
4766
+ };
4767
+ } catch (error) {
4768
+ if (error instanceof GitCommandError) {
4769
+ return emptyStatus(workspace, lastCheckedAt, error);
4770
+ }
4771
+ return emptyStatus(
4772
+ workspace,
4773
+ lastCheckedAt,
4774
+ new GitCommandError("git_command_failed", "Failed to read Git status", { cause: error })
4775
+ );
4776
+ }
4777
+ }
4778
+ function parsePorcelainV2Status(output) {
4779
+ const parsed = {
4780
+ branch: null,
4781
+ upstream: null,
4782
+ ahead: 0,
4783
+ behind: 0,
4784
+ staged: 0,
4785
+ modified: 0,
4786
+ untracked: 0,
4787
+ deleted: 0,
4788
+ renamed: 0,
4789
+ conflictFiles: []
4790
+ };
4791
+ for (const line of output.split("\n")) {
4792
+ if (!line) continue;
4793
+ if (line.startsWith("# branch.head ")) {
4794
+ const branch = line.slice("# branch.head ".length).trim();
4795
+ parsed.branch = branch && branch !== "(detached)" ? branch : null;
4796
+ continue;
4797
+ }
4798
+ if (line.startsWith("# branch.upstream ")) {
4799
+ parsed.upstream = line.slice("# branch.upstream ".length).trim() || null;
4800
+ continue;
4801
+ }
4802
+ if (line.startsWith("# branch.ab ")) {
4803
+ const match = line.match(/\+(-?\d+)\s+-(-?\d+)/);
4804
+ if (match) {
4805
+ parsed.ahead = Number.parseInt(match[1] ?? "0", 10) || 0;
4806
+ parsed.behind = Number.parseInt(match[2] ?? "0", 10) || 0;
4807
+ }
4808
+ continue;
4809
+ }
4810
+ if (line.startsWith("? ")) {
4811
+ parsed.untracked += 1;
4812
+ continue;
4813
+ }
4814
+ if (line.startsWith("u ")) {
4815
+ const fields = line.split(" ");
4816
+ const filePath = fields.slice(10).join(" ");
4817
+ if (filePath) parsed.conflictFiles.push(filePath);
4818
+ continue;
4819
+ }
4820
+ if (line.startsWith("1 ") || line.startsWith("2 ")) {
4821
+ const fields = line.split(" ");
4822
+ const xy = fields[1] ?? "..";
4823
+ const indexStatus = xy[0] ?? ".";
4824
+ const worktreeStatus = xy[1] ?? ".";
4825
+ if (isStagedStatus(indexStatus)) parsed.staged += 1;
4826
+ if (worktreeStatus === "M" || worktreeStatus === "T") parsed.modified += 1;
4827
+ if (indexStatus === "D" || worktreeStatus === "D") parsed.deleted += 1;
4828
+ if (indexStatus === "R" || worktreeStatus === "R") parsed.renamed += 1;
4829
+ if (xy.includes("U")) {
4830
+ const filePath = fields.slice(line.startsWith("2 ") ? 9 : 8).join(" ").split(" ")[0] ?? "";
4831
+ if (filePath) parsed.conflictFiles.push(filePath);
4832
+ }
4833
+ }
4834
+ }
4835
+ parsed.conflictFiles = Array.from(new Set(parsed.conflictFiles));
4836
+ return parsed;
4837
+ }
4838
+ async function readHead(repo, options) {
4839
+ try {
4840
+ const result = await runGit(repo, ["log", "-1", "--pretty=%h%x00%s"], options);
4841
+ const text = result.stdout.trimEnd();
4842
+ if (!text) return { commit: null, message: null };
4843
+ const [commit, ...messageParts] = text.split("\0");
4844
+ return {
4845
+ commit: commit || null,
4846
+ message: messageParts.join("\0") || null
4847
+ };
4848
+ } catch {
4849
+ return { commit: null, message: null };
4850
+ }
4851
+ }
4852
+ async function readStashCount(repo, options) {
4853
+ try {
4854
+ const result = await runGit(repo, ["stash", "list", "--format=%gd"], options);
4855
+ return result.stdout.split("\n").filter((line) => line.trim().length > 0).length;
4856
+ } catch {
4857
+ return 0;
4858
+ }
4859
+ }
4860
+ function isStagedStatus(status) {
4861
+ return status !== "." && status !== "?" && status !== "U";
4862
+ }
4863
+ function emptyStatus(workspace, lastCheckedAt, error) {
4864
+ return {
4865
+ workspace,
4866
+ repoRoot: null,
4867
+ isGitRepo: false,
4868
+ branch: null,
4869
+ headCommit: null,
4870
+ headMessage: null,
4871
+ upstream: null,
4872
+ ahead: 0,
4873
+ behind: 0,
4874
+ staged: 0,
4875
+ modified: 0,
4876
+ untracked: 0,
4877
+ deleted: 0,
4878
+ renamed: 0,
4879
+ hasConflicts: false,
4880
+ conflictFiles: [],
4881
+ stashCount: 0,
4882
+ lastCheckedAt,
4883
+ error: error.stderr || error.message,
4884
+ reason: error.reason
4885
+ };
4886
+ }
4887
+
4888
+ // src/git/git-diff.ts
4889
+ import { readFile, realpath as realpath2 } from "fs/promises";
4890
+ import * as path2 from "path";
4891
+ var DEFAULT_MAX_FILES = 200;
4892
+ var DEFAULT_MAX_BYTES = 2e5;
4893
+ async function getGitDiffSummary(workspace, options = {}) {
4894
+ const lastCheckedAt = Date.now();
4895
+ try {
4896
+ const repo = await resolveGitRepository(workspace, options);
4897
+ const repoRoot = repo.repoRoot;
4898
+ const [unstagedNameStatus, unstagedNumstat, stagedNameStatus, stagedNumstat, untracked] = await Promise.all([
4899
+ runGit(repo, ["diff", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
4900
+ runGit(repo, ["diff", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
4901
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--name-status"], { ...options, cwd: repoRoot }),
4902
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--numstat"], { ...options, cwd: repoRoot }),
4903
+ runGit(repo, ["ls-files", "--others", "--exclude-standard"], { ...options, cwd: repoRoot })
4904
+ ]);
4905
+ const outputBytes = byteLength(
4906
+ unstagedNameStatus.stdout + unstagedNumstat.stdout + stagedNameStatus.stdout + stagedNumstat.stdout + untracked.stdout
4907
+ );
4908
+ const changes = [
4909
+ ...combineDiffEntries(unstagedNameStatus.stdout, unstagedNumstat.stdout, false),
4910
+ ...combineDiffEntries(stagedNameStatus.stdout, stagedNumstat.stdout, true),
4911
+ ...parseUntrackedFiles(untracked.stdout)
4912
+ ];
4913
+ const maxFiles = normalizePositiveInteger(options.maxFiles, DEFAULT_MAX_FILES);
4914
+ const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
4915
+ const files = changes.slice(0, maxFiles);
4916
+ const truncated = changes.length > files.length || outputBytes > maxBytes;
4917
+ return {
4918
+ workspace: repo.workspace,
4919
+ repoRoot,
4920
+ isGitRepo: true,
4921
+ files,
4922
+ totalInsertions: files.reduce((sum, file) => sum + file.insertions, 0),
4923
+ totalDeletions: files.reduce((sum, file) => sum + file.deletions, 0),
4924
+ truncated,
4925
+ lastCheckedAt
4926
+ };
4927
+ } catch (error) {
4928
+ const gitError = error instanceof GitCommandError ? error : new GitCommandError("git_command_failed", "Failed to read Git diff summary", { cause: error });
4929
+ return {
4930
+ workspace,
4931
+ repoRoot: null,
4932
+ isGitRepo: false,
4933
+ files: [],
4934
+ totalInsertions: 0,
4935
+ totalDeletions: 0,
4936
+ truncated: false,
4937
+ lastCheckedAt,
4938
+ error: gitError.stderr || gitError.message,
4939
+ reason: gitError.reason
4940
+ };
4941
+ }
4942
+ }
4943
+ async function getGitFileDiff(workspace, filePath, options = {}) {
4944
+ const lastCheckedAt = Date.now();
4945
+ const repo = await resolveGitRepository(workspace, options);
4946
+ const repoRoot = repo.repoRoot;
4947
+ const selected = await resolveRepoFilePath(repoRoot, filePath);
4948
+ const maxBytes = normalizePositiveInteger(options.maxBytes, DEFAULT_MAX_BYTES);
4949
+ const [unstaged, staged] = await Promise.all([
4950
+ runGit(repo, ["diff", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot }),
4951
+ runGit(repo, ["diff", "--cached", "--no-ext-diff", "--", selected.relativePath], { ...options, cwd: repoRoot })
4952
+ ]);
4953
+ let diff = [unstaged.stdout, staged.stdout].filter((part) => part.length > 0).join("\n");
4954
+ if (!diff) {
4955
+ const untracked = await runGit(repo, ["ls-files", "--others", "--exclude-standard", "--", selected.relativePath], {
4956
+ ...options,
4957
+ cwd: repoRoot
4958
+ });
4959
+ const untrackedFiles = untracked.stdout.split("\n").filter(Boolean);
4960
+ if (untrackedFiles.includes(selected.relativePath)) {
4961
+ diff = await buildUntrackedDiff(selected.absolutePath, selected.relativePath, maxBytes + 1);
4962
+ }
4963
+ }
4964
+ const bounded = truncateText(diff, maxBytes);
4965
+ return {
4966
+ workspace: repo.workspace,
4967
+ repoRoot,
4968
+ isGitRepo: true,
4969
+ path: selected.relativePath,
4970
+ diff: bounded.text,
4971
+ truncated: bounded.truncated,
4972
+ lastCheckedAt
4973
+ };
4974
+ }
4975
+ function combineDiffEntries(nameStatusOutput, numstatOutput, staged) {
4976
+ const statusEntries = parseNameStatus(nameStatusOutput);
4977
+ const numstatEntries = parseNumstat(numstatOutput);
4978
+ return statusEntries.map((entry, index) => {
4979
+ const stats = numstatEntries[index];
4980
+ return {
4981
+ path: entry.path,
4982
+ oldPath: entry.oldPath,
4983
+ status: entry.status,
4984
+ staged,
4985
+ insertions: stats?.insertions ?? 0,
4986
+ deletions: stats?.deletions ?? 0,
4987
+ binary: stats?.binary || void 0
4988
+ };
4989
+ });
4990
+ }
4991
+ function parseNameStatus(output) {
4992
+ return output.split("\n").filter(Boolean).map((line) => {
4993
+ const fields = line.split(" ");
4994
+ const code = fields[0] ?? "";
4995
+ const statusLetter = code[0] ?? "M";
4996
+ if (statusLetter === "R") {
4997
+ return {
4998
+ oldPath: fields[1] ?? "",
4999
+ path: fields[2] ?? fields[1] ?? "",
5000
+ status: "renamed"
5001
+ };
5002
+ }
5003
+ if (statusLetter === "C") {
5004
+ return {
5005
+ oldPath: fields[1] ?? "",
5006
+ path: fields[2] ?? fields[1] ?? "",
5007
+ status: "copied"
5008
+ };
5009
+ }
5010
+ return {
5011
+ path: fields[1] ?? "",
5012
+ status: mapNameStatus(statusLetter)
5013
+ };
5014
+ }).filter((entry) => entry.path.length > 0);
5015
+ }
5016
+ function parseNumstat(output) {
5017
+ return output.split("\n").filter(Boolean).map((line) => {
5018
+ const fields = line.split(" ");
5019
+ const insertionsText = fields[0] ?? "0";
5020
+ const deletionsText = fields[1] ?? "0";
5021
+ const binary = insertionsText === "-" || deletionsText === "-";
5022
+ return {
5023
+ path: fields.slice(2).join(" "),
5024
+ insertions: binary ? 0 : Number.parseInt(insertionsText, 10) || 0,
5025
+ deletions: binary ? 0 : Number.parseInt(deletionsText, 10) || 0,
5026
+ binary
5027
+ };
5028
+ });
5029
+ }
5030
+ function parseUntrackedFiles(output) {
5031
+ return output.split("\n").filter(Boolean).map((filePath) => ({
5032
+ path: filePath,
5033
+ status: "untracked",
5034
+ staged: false,
5035
+ insertions: 0,
5036
+ deletions: 0
5037
+ }));
5038
+ }
5039
+ function mapNameStatus(status) {
5040
+ switch (status) {
5041
+ case "A":
5042
+ return "added";
5043
+ case "D":
5044
+ return "deleted";
5045
+ case "R":
5046
+ return "renamed";
5047
+ case "C":
5048
+ return "copied";
5049
+ case "U":
5050
+ return "conflict";
5051
+ case "M":
5052
+ case "T":
5053
+ default:
5054
+ return "modified";
5055
+ }
5056
+ }
5057
+ async function resolveRepoFilePath(repoRoot, filePath) {
5058
+ if (typeof filePath !== "string" || filePath.length === 0 || filePath.includes("\0")) {
5059
+ throw new GitCommandError("invalid_args", "File path must be a non-empty path");
5060
+ }
5061
+ const canonicalRepoRoot = await realpath2(repoRoot).catch(() => path2.resolve(repoRoot));
5062
+ const absolutePath = path2.isAbsolute(filePath) ? path2.resolve(filePath) : path2.resolve(repoRoot, filePath);
5063
+ const checkPath = await realpath2(absolutePath).catch(() => absolutePath);
5064
+ const relativeBase = isPathInside(canonicalRepoRoot, checkPath) ? canonicalRepoRoot : path2.resolve(repoRoot);
5065
+ if (!isPathInside(canonicalRepoRoot, checkPath) && !isPathInside(repoRoot, absolutePath)) {
5066
+ throw new GitCommandError("path_outside_repo", "Selected file path is outside the repository root", {
5067
+ cwd: repoRoot
5068
+ });
5069
+ }
5070
+ const relativePath = path2.relative(relativeBase, checkPath).split(path2.sep).join("/");
5071
+ if (!relativePath || relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
5072
+ throw new GitCommandError("path_outside_repo", "Selected file path is outside the repository root", {
5073
+ cwd: repoRoot
5074
+ });
5075
+ }
5076
+ return { absolutePath, relativePath };
5077
+ }
5078
+ async function buildUntrackedDiff(absolutePath, relativePath, readLimit) {
5079
+ const content = await readFile(absolutePath, "utf8");
5080
+ const limitedContent = content.length > readLimit ? content.slice(0, readLimit) : content;
5081
+ const lines = limitedContent.length > 0 ? limitedContent.split("\n") : [];
5082
+ const plusLines = lines.filter((line, index) => index < lines.length - 1 || line.length > 0).map((line) => `+${line}`).join("\n");
5083
+ const lineCount = plusLines ? plusLines.split("\n").length : 0;
5084
+ return [
5085
+ `diff --git a/${relativePath} b/${relativePath}`,
5086
+ "new file mode 100644",
5087
+ "index 0000000..0000000",
5088
+ "--- /dev/null",
5089
+ `+++ b/${relativePath}`,
5090
+ `@@ -0,0 +1,${lineCount} @@`,
5091
+ plusLines
5092
+ ].filter((line) => line.length > 0).join("\n");
5093
+ }
5094
+ function truncateText(text, maxBytes) {
5095
+ if (byteLength(text) <= maxBytes) return { text, truncated: false };
5096
+ return { text: Buffer.from(text, "utf8").subarray(0, maxBytes).toString("utf8"), truncated: true };
5097
+ }
5098
+ function byteLength(text) {
5099
+ return Buffer.byteLength(text, "utf8");
5100
+ }
5101
+ function normalizePositiveInteger(value, fallback) {
5102
+ if (!Number.isFinite(value) || value == null || value <= 0) return fallback;
5103
+ return Math.floor(value);
5104
+ }
5105
+
5106
+ // src/git/git-summary.ts
5107
+ function countStatusChangedFiles(status) {
5108
+ const conflictCount = status.conflictFiles.length > 0 ? status.conflictFiles.length : status.hasConflicts ? 1 : 0;
5109
+ return status.staged + status.modified + status.untracked + status.deleted + status.renamed + conflictCount;
5110
+ }
5111
+ function createGitCompactSummary(status, diffSummary) {
5112
+ const statusChangedFiles = countStatusChangedFiles(status);
5113
+ const diffChangedFiles = diffSummary?.files.length ?? 0;
5114
+ const changedFiles = Math.max(statusChangedFiles, diffChangedFiles);
5115
+ const conflictCount = status.conflictFiles.length > 0 ? status.conflictFiles.length : status.hasConflicts ? 1 : 0;
5116
+ return {
5117
+ isGitRepo: status.isGitRepo,
5118
+ repoRoot: status.repoRoot,
5119
+ branch: status.branch,
5120
+ dirty: status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0 || conflictCount > 0 || changedFiles > 0,
5121
+ changedFiles,
5122
+ ahead: status.ahead,
5123
+ behind: status.behind,
5124
+ hasConflicts: status.hasConflicts || conflictCount > 0,
5125
+ lastCheckedAt: Math.max(status.lastCheckedAt, diffSummary?.lastCheckedAt ?? status.lastCheckedAt),
5126
+ error: status.error ?? diffSummary?.error,
5127
+ reason: status.reason ?? diffSummary?.reason
5128
+ };
5129
+ }
5130
+ var summarizeGitStatus = createGitCompactSummary;
5131
+
5132
+ // src/git/git-snapshot-store.ts
5133
+ function normalizeCapacity(capacity) {
5134
+ return Math.max(1, Math.floor(capacity ?? 100));
5135
+ }
5136
+ function createEmptyDiffSummary(status) {
5137
+ return {
5138
+ workspace: status.workspace,
5139
+ repoRoot: status.repoRoot,
5140
+ isGitRepo: status.isGitRepo,
5141
+ files: [],
5142
+ totalInsertions: 0,
5143
+ totalDeletions: 0,
5144
+ truncated: false,
5145
+ lastCheckedAt: status.lastCheckedAt,
5146
+ error: status.error,
5147
+ reason: status.reason
5148
+ };
5149
+ }
5150
+ function changedFileKey(file) {
5151
+ return `${file.oldPath ?? ""}\0${file.path}`;
5152
+ }
5153
+ function uniqueSorted(values) {
5154
+ return Array.from(new Set(Array.from(values).filter(Boolean))).sort((a, b) => a.localeCompare(b));
5155
+ }
5156
+ function plural(count, singular, pluralText = `${singular}s`) {
5157
+ return count === 1 ? singular : pluralText;
5158
+ }
5159
+ function compareGitSnapshots(before, after) {
5160
+ const beforeFileKeys = new Set(before.diffSummary.files.map(changedFileKey));
5161
+ const changedAfterFiles = after.diffSummary.files.filter((file) => !beforeFileKeys.has(changedFileKey(file)));
5162
+ const addedFiles = [];
5163
+ const modifiedFiles = [];
5164
+ const deletedFiles = [];
5165
+ const renamedFiles = [];
5166
+ const untrackedFiles = [];
5167
+ const conflictFilesFromDiff = [];
5168
+ let totalInsertions = 0;
5169
+ let totalDeletions = 0;
5170
+ for (const file of changedAfterFiles) {
5171
+ totalInsertions += file.insertions;
5172
+ totalDeletions += file.deletions;
5173
+ switch (file.status) {
5174
+ case "added":
5175
+ case "copied":
5176
+ addedFiles.push(file.path);
5177
+ break;
5178
+ case "modified":
5179
+ modifiedFiles.push(file.path);
5180
+ break;
5181
+ case "deleted":
5182
+ deletedFiles.push(file.path);
5183
+ break;
5184
+ case "renamed":
5185
+ renamedFiles.push({ oldPath: file.oldPath ?? file.path, path: file.path });
5186
+ break;
5187
+ case "untracked":
5188
+ untrackedFiles.push(file.path);
5189
+ break;
5190
+ case "conflict":
5191
+ conflictFilesFromDiff.push(file.path);
5192
+ break;
5193
+ }
5194
+ }
5195
+ renamedFiles.sort((a, b) => `${a.oldPath}\0${a.path}`.localeCompare(`${b.oldPath}\0${b.path}`));
5196
+ const conflictFiles = uniqueSorted([...after.status.conflictFiles, ...conflictFilesFromDiff]);
5197
+ const changedFiles = changedAfterFiles.length;
5198
+ const hasConflicts = after.status.hasConflicts || conflictFiles.length > 0;
5199
+ const summaryParts = [];
5200
+ if (changedFiles > 0) summaryParts.push(`${changedFiles} ${plural(changedFiles, "file")} changed`);
5201
+ if (addedFiles.length > 0) summaryParts.push(`${addedFiles.length} added`);
5202
+ if (modifiedFiles.length > 0) summaryParts.push(`${modifiedFiles.length} modified`);
5203
+ if (deletedFiles.length > 0) summaryParts.push(`${deletedFiles.length} deleted`);
5204
+ if (renamedFiles.length > 0) summaryParts.push(`${renamedFiles.length} renamed`);
5205
+ if (untrackedFiles.length > 0) summaryParts.push(`${untrackedFiles.length} untracked`);
5206
+ if (hasConflicts) summaryParts.push(`${conflictFiles.length || 1} ${plural(conflictFiles.length || 1, "conflict")}`);
5207
+ return {
5208
+ beforeSnapshotId: before.id,
5209
+ afterSnapshotId: after.id,
5210
+ workspace: after.workspace,
5211
+ repoRoot: after.repoRoot,
5212
+ changedFiles,
5213
+ addedFiles: uniqueSorted(addedFiles),
5214
+ modifiedFiles: uniqueSorted(modifiedFiles),
5215
+ deletedFiles: uniqueSorted(deletedFiles),
5216
+ renamedFiles,
5217
+ untrackedFiles: uniqueSorted(untrackedFiles),
5218
+ conflictFiles,
5219
+ totalInsertions,
5220
+ totalDeletions,
5221
+ hasConflicts,
5222
+ currentStatus: after.status,
5223
+ summaryText: summaryParts.length > 0 ? summaryParts.join(", ") : "No file-set changes between snapshots."
5224
+ };
5225
+ }
5226
+ var InMemoryGitSnapshotStore = class {
5227
+ snapshots = /* @__PURE__ */ new Map();
5228
+ order = [];
5229
+ capacity;
5230
+ now;
5231
+ idPrefix;
5232
+ getStatusProvider;
5233
+ getDiffSummaryProvider;
5234
+ counter = 0;
5235
+ constructor(options = {}) {
5236
+ this.capacity = normalizeCapacity(options.capacity);
5237
+ this.now = options.now ?? Date.now;
5238
+ this.idPrefix = options.idPrefix ?? "git-snapshot";
5239
+ this.getStatusProvider = options.getStatus;
5240
+ this.getDiffSummaryProvider = options.getDiffSummary;
5241
+ }
5242
+ async create(input) {
5243
+ const getStatus = input.getStatus ?? this.getStatusProvider;
5244
+ if (!getStatus) {
5245
+ throw new Error("GitSnapshotStore requires an injected getStatus provider");
5246
+ }
5247
+ const status = await getStatus(input.workspace);
5248
+ const getDiffSummary = input.getDiffSummary ?? this.getDiffSummaryProvider;
5249
+ const diffSummary = getDiffSummary ? await getDiffSummary(input.workspace, status) : createEmptyDiffSummary(status);
5250
+ const createdAt = this.now();
5251
+ const id = `${this.idPrefix}-${createdAt}-${++this.counter}`;
5252
+ const snapshot = {
5253
+ id,
5254
+ workspace: input.workspace,
5255
+ repoRoot: status.repoRoot ?? input.workspace,
5256
+ sessionId: input.sessionId,
5257
+ turnId: input.turnId,
5258
+ reason: input.reason,
5259
+ status,
5260
+ diffSummary,
5261
+ createdAt
5262
+ };
5263
+ this.snapshots.set(id, snapshot);
5264
+ this.order.push(id);
5265
+ this.enforceCapacity();
5266
+ return snapshot;
5267
+ }
5268
+ get(id) {
5269
+ return this.snapshots.get(id);
5270
+ }
5271
+ compare(beforeSnapshotId, afterSnapshotId) {
5272
+ const before = this.snapshots.get(beforeSnapshotId);
5273
+ if (!before) throw new Error(`Unknown before snapshot: ${beforeSnapshotId}`);
5274
+ const after = this.snapshots.get(afterSnapshotId);
5275
+ if (!after) throw new Error(`Unknown after snapshot: ${afterSnapshotId}`);
5276
+ return compareGitSnapshots(before, after);
5277
+ }
5278
+ list(query = {}) {
5279
+ const limit = Math.max(1, Math.floor(query.limit ?? this.capacity));
5280
+ return this.order.map((id) => this.snapshots.get(id)).filter((snapshot) => Boolean(snapshot)).filter((snapshot) => !query.workspace || snapshot.workspace === query.workspace).filter((snapshot) => !query.sessionId || snapshot.sessionId === query.sessionId).slice(-limit);
5281
+ }
5282
+ clear() {
5283
+ this.snapshots.clear();
5284
+ this.order.splice(0, this.order.length);
5285
+ }
5286
+ enforceCapacity() {
5287
+ while (this.order.length > this.capacity) {
5288
+ const evictedId = this.order.shift();
5289
+ if (evictedId) this.snapshots.delete(evictedId);
5290
+ }
5291
+ }
5292
+ };
5293
+ function createGitSnapshotStore(options = {}) {
5294
+ return new InMemoryGitSnapshotStore(options);
5295
+ }
5296
+
5297
+ // src/git/git-monitor.ts
5298
+ var DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS = 5e3;
5299
+ var MIN_GIT_WORKSPACE_POLL_INTERVAL_MS = 1e3;
5300
+ function defaultStatusProvider(workspace) {
5301
+ return getGitRepoStatus(workspace);
5302
+ }
5303
+ function defaultDiffSummaryProvider(workspace) {
5304
+ return getGitDiffSummary(workspace);
5305
+ }
5306
+ function normalizeIntervalMs(value, defaultIntervalMs, minIntervalMs) {
5307
+ const requested = Number.isFinite(value) ? Math.floor(value) : defaultIntervalMs;
5308
+ return Math.max(minIntervalMs, requested > 0 ? requested : defaultIntervalMs);
5309
+ }
5310
+ function normalizeGitWorkspaceSubscriptionParams(params, options = {}) {
5311
+ const minIntervalMs = Math.max(1, Math.floor(options.minIntervalMs ?? MIN_GIT_WORKSPACE_POLL_INTERVAL_MS));
5312
+ const defaultIntervalMs = Math.max(minIntervalMs, Math.floor(options.defaultIntervalMs ?? DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS));
5313
+ return {
5314
+ workspace: params.workspace,
5315
+ includeDiffSummary: Boolean(params.includeDiffSummary),
5316
+ intervalMs: normalizeIntervalMs(params.intervalMs, defaultIntervalMs, minIntervalMs)
5317
+ };
5318
+ }
5319
+ var GitWorkspaceMonitor = class {
5320
+ getStatusProvider;
5321
+ getDiffSummaryProvider;
5322
+ now;
5323
+ minIntervalMs;
5324
+ defaultIntervalMs;
5325
+ keyPrefix;
5326
+ cache = /* @__PURE__ */ new Map();
5327
+ listeners = /* @__PURE__ */ new Set();
5328
+ seq = 0;
5329
+ constructor(options = {}) {
5330
+ this.getStatusProvider = options.getStatus ?? defaultStatusProvider;
5331
+ this.getDiffSummaryProvider = options.getDiffSummary ?? defaultDiffSummaryProvider;
5332
+ this.now = options.now ?? Date.now;
5333
+ this.minIntervalMs = Math.max(1, Math.floor(options.minIntervalMs ?? MIN_GIT_WORKSPACE_POLL_INTERVAL_MS));
5334
+ this.defaultIntervalMs = Math.max(
5335
+ this.minIntervalMs,
5336
+ Math.floor(options.defaultIntervalMs ?? DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS)
5337
+ );
5338
+ this.keyPrefix = options.keyPrefix ?? "git";
5339
+ }
5340
+ async refresh(params) {
5341
+ const normalized = this.normalize(typeof params === "string" ? { workspace: params } : params);
5342
+ const status = await this.getStatusProvider(normalized.workspace);
5343
+ const diffSummary = normalized.includeDiffSummary ? await this.getDiffSummaryProvider(normalized.workspace, status) : void 0;
5344
+ const compactSummary = createGitCompactSummary(status, diffSummary);
5345
+ const timestamp = this.now();
5346
+ const seq = ++this.seq;
5347
+ const key = this.keyForWorkspace(normalized.workspace);
5348
+ const update = {
5349
+ topic: "workspace.git",
5350
+ key,
5351
+ workspace: normalized.workspace,
5352
+ status,
5353
+ diffSummary,
5354
+ seq,
5355
+ timestamp
5356
+ };
5357
+ const cacheEntry = {
5358
+ key,
5359
+ workspace: normalized.workspace,
5360
+ status,
5361
+ diffSummary,
5362
+ compactSummary,
5363
+ seq,
5364
+ timestamp
5365
+ };
5366
+ this.cache.set(normalized.workspace, cacheEntry);
5367
+ this.emit(update, cacheEntry);
5368
+ return update;
5369
+ }
5370
+ poll(params) {
5371
+ return this.refresh(params);
5372
+ }
5373
+ getCached(workspace) {
5374
+ return this.cache.get(workspace);
5375
+ }
5376
+ getCompactSummary(workspace) {
5377
+ return this.cache.get(workspace)?.compactSummary;
5378
+ }
5379
+ onUpdate(listener) {
5380
+ this.listeners.add(listener);
5381
+ return () => {
5382
+ this.listeners.delete(listener);
5383
+ };
5384
+ }
5385
+ createSubscription(params, listener) {
5386
+ const normalized = this.normalize(params);
5387
+ const scopedListener = listener ? (update, cacheEntry) => {
5388
+ if (update.workspace === normalized.workspace) listener(update, cacheEntry);
5389
+ } : void 0;
5390
+ const unsubscribe = scopedListener ? this.onUpdate(scopedListener) : () => void 0;
5391
+ return {
5392
+ params: normalized,
5393
+ refresh: () => this.refresh(normalized),
5394
+ getCached: () => this.getCached(normalized.workspace),
5395
+ dispose: unsubscribe
5396
+ };
5397
+ }
5398
+ normalize(params) {
5399
+ return normalizeGitWorkspaceSubscriptionParams(params, {
5400
+ defaultIntervalMs: this.defaultIntervalMs,
5401
+ minIntervalMs: this.minIntervalMs
5402
+ });
5403
+ }
5404
+ keyForWorkspace(workspace) {
5405
+ return `${this.keyPrefix}:${workspace}`;
5406
+ }
5407
+ emit(update, cacheEntry) {
5408
+ for (const listener of this.listeners) {
5409
+ listener(update, cacheEntry);
5410
+ }
5411
+ }
5412
+ };
5413
+ function createGitWorkspaceMonitor(options = {}) {
5414
+ return new GitWorkspaceMonitor(options);
5415
+ }
5416
+
5417
+ // src/git/git-commands.ts
5418
+ import * as path3 from "path";
5419
+ var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
5420
+ "git_status",
5421
+ "git_diff_summary",
5422
+ "git_diff_file",
5423
+ "git_snapshot_create",
5424
+ "git_snapshot_compare",
5425
+ "git_log",
5426
+ "git_checkpoint",
5427
+ "git_stash_push",
5428
+ "git_stash_pop",
5429
+ "git_checkout_files",
5430
+ "git_remote_url"
5431
+ ]);
5432
+ var SNAPSHOT_REASONS = /* @__PURE__ */ new Set([
5433
+ "session_baseline",
5434
+ "before_user_input_dispatch",
5435
+ "before_agent_work",
5436
+ "after_agent_work",
5437
+ "manual"
5438
+ ]);
5439
+ var FAILURE_REASONS = /* @__PURE__ */ new Set([
5440
+ "not_git_repo",
5441
+ "git_not_installed",
5442
+ "timeout",
5443
+ "path_outside_repo",
5444
+ "dirty_index_required",
5445
+ "conflict",
5446
+ "invalid_args",
5447
+ "git_command_failed"
5448
+ ]);
5449
+ function failure(reason, error) {
5450
+ return { success: false, reason, error };
5451
+ }
5452
+ function serviceNotImplemented(command) {
5453
+ return failure("invalid_args", `${command} is not implemented: daemon-core Git service is not configured`);
5454
+ }
5455
+ var defaultSnapshotStore = createGitSnapshotStore({
5456
+ getStatus: (workspace) => getGitRepoStatus(workspace),
5457
+ getDiffSummary: (workspace) => getGitDiffSummary(workspace)
5458
+ });
5459
+ function createDefaultGitCommandServices() {
5460
+ return {
5461
+ getStatus: ({ workspace }) => getGitRepoStatus(workspace),
5462
+ getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
5463
+ getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
5464
+ createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
5465
+ workspace,
5466
+ reason,
5467
+ sessionId,
5468
+ turnId
5469
+ }),
5470
+ compareSnapshots: ({ beforeSnapshotId, afterSnapshotId }) => defaultSnapshotStore.compare(beforeSnapshotId, afterSnapshotId),
5471
+ getLog: ({ workspace, limit, path: filePath, since, until }) => getGitLog(workspace, { limit, path: filePath, since, until }),
5472
+ checkpoint: async ({ workspace, message, includeUntracked = false }) => gitCheckpoint(workspace, message, includeUntracked),
5473
+ stashPush: async ({ workspace, message, includeUntracked = false }) => gitStashPush(workspace, message, includeUntracked),
5474
+ stashPop: async ({ workspace, stashRef }) => gitStashPop(workspace, stashRef),
5475
+ checkoutFiles: async ({ workspace, paths }) => gitCheckoutFiles(workspace, paths),
5476
+ getRemoteUrl: async ({ workspace, remote = "origin" }) => gitGetRemoteUrl(workspace, remote)
5477
+ };
5478
+ }
5479
+ var defaultGitCommandServices = createDefaultGitCommandServices();
5480
+ function validateWorkspace2(args) {
5481
+ if (typeof args?.workspace !== "string") {
5482
+ return failure("invalid_args", "workspace must be a non-empty absolute path");
5483
+ }
5484
+ const workspace = args.workspace.trim();
5485
+ if (!workspace || !path3.isAbsolute(workspace)) {
5486
+ return failure("invalid_args", "workspace must be a non-empty absolute path");
5487
+ }
5488
+ return { workspace };
5489
+ }
5490
+ function validateRepoPath(args) {
5491
+ if (typeof args?.path !== "string" || !args.path.trim()) {
5492
+ return failure("invalid_args", "path must be a non-empty repository-relative path");
5493
+ }
5494
+ return { path: args.path.trim() };
5495
+ }
5496
+ function validateSnapshotId(args, key) {
5497
+ if (typeof args?.[key] !== "string" || !args[key].trim()) {
5498
+ return failure("invalid_args", `${key} must be a non-empty string`);
5499
+ }
5500
+ return args[key].trim();
5501
+ }
5502
+ function parseSnapshotReason(args) {
5503
+ if (args?.reason === void 0 || args?.reason === null || args?.reason === "") {
5504
+ return "manual";
5505
+ }
5506
+ if (typeof args.reason !== "string" || !SNAPSHOT_REASONS.has(args.reason)) {
5507
+ return failure("invalid_args", "reason must be a valid GitSnapshotReason");
5508
+ }
5509
+ return args.reason;
5510
+ }
5511
+ function optionalString(value) {
5512
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
5513
+ }
5514
+ function optionalBoolean(value) {
5515
+ return typeof value === "boolean" ? value : void 0;
5516
+ }
5517
+ function boundedLogLimit(value) {
5518
+ if (value === void 0 || value === null || value === "") return 50;
5519
+ const numeric = typeof value === "number" ? value : Number(value);
5520
+ if (!Number.isFinite(numeric)) return 50;
5521
+ return Math.max(1, Math.min(200, Math.floor(numeric)));
5522
+ }
5523
+ function failureReasonFromError(error) {
5524
+ return typeof error?.reason === "string" && FAILURE_REASONS.has(error.reason) ? error.reason : "git_command_failed";
5525
+ }
5526
+ async function runService(fn) {
5527
+ try {
5528
+ return await fn();
5529
+ } catch (error) {
5530
+ return failure(failureReasonFromError(error), error?.message || "Git command failed");
5531
+ }
5532
+ }
5533
+ function isGitCommandName(command) {
5534
+ return GIT_COMMAND_NAMES.has(command);
5535
+ }
5536
+ async function handleGitCommand(command, args, services = defaultGitCommandServices) {
5537
+ if (!isGitCommandName(command)) {
5538
+ return failure("invalid_args", `Unknown Git command: ${command}`);
5539
+ }
5540
+ const workspaceResult = validateWorkspace2(args);
5541
+ if ("success" in workspaceResult) return workspaceResult;
5542
+ const { workspace } = workspaceResult;
5543
+ switch (command) {
5544
+ case "git_status": {
5545
+ if (!services.getStatus) return serviceNotImplemented(command);
5546
+ const status = await runService(() => services.getStatus({ workspace }));
5547
+ return "success" in status ? status : { success: true, status };
5548
+ }
5549
+ case "git_diff_summary": {
5550
+ if (!services.getDiffSummary) return serviceNotImplemented(command);
5551
+ const diffSummary = await runService(() => services.getDiffSummary({ workspace, staged: optionalBoolean(args?.staged) }));
5552
+ return "success" in diffSummary ? diffSummary : { success: true, diffSummary };
5553
+ }
5554
+ case "git_diff_file": {
5555
+ if (!services.getDiffFile) return serviceNotImplemented(command);
5556
+ const pathResult = validateRepoPath(args);
5557
+ if (typeof pathResult !== "object" || "success" in pathResult) return pathResult;
5558
+ const diff = await runService(() => services.getDiffFile({
5559
+ workspace,
5560
+ path: pathResult.path,
5561
+ staged: optionalBoolean(args?.staged)
5562
+ }));
5563
+ return "success" in diff ? diff : { success: true, diff };
5564
+ }
5565
+ case "git_snapshot_create": {
5566
+ if (!services.createSnapshot) return serviceNotImplemented(command);
5567
+ const reason = parseSnapshotReason(args);
5568
+ if (typeof reason !== "string") return reason;
5569
+ const snapshot = await runService(() => services.createSnapshot({
5570
+ workspace,
5571
+ reason,
5572
+ sessionId: optionalString(args?.sessionId),
5573
+ turnId: optionalString(args?.turnId)
5574
+ }));
5575
+ return "success" in snapshot ? snapshot : { success: true, snapshot };
5576
+ }
5577
+ case "git_snapshot_compare": {
5578
+ if (!services.compareSnapshots) return serviceNotImplemented(command);
5579
+ const beforeSnapshotId = validateSnapshotId(args, "beforeSnapshotId");
5580
+ if (typeof beforeSnapshotId !== "string") return beforeSnapshotId;
5581
+ const afterSnapshotId = validateSnapshotId(args, "afterSnapshotId");
5582
+ if (typeof afterSnapshotId !== "string") return afterSnapshotId;
5583
+ const compare = await runService(() => services.compareSnapshots({ workspace, beforeSnapshotId, afterSnapshotId }));
5584
+ return "success" in compare ? compare : { success: true, compare };
5585
+ }
5586
+ case "git_log": {
5587
+ if (!services.getLog) {
5588
+ return failure("invalid_args", "git_log is not implemented: bounded daemon-core Git log service is not configured");
5589
+ }
5590
+ const log = await runService(() => services.getLog({
5591
+ workspace,
5592
+ limit: boundedLogLimit(args?.limit),
5593
+ path: optionalString(args?.path),
5594
+ since: optionalString(args?.since),
5595
+ until: optionalString(args?.until)
5596
+ }));
5597
+ return "success" in log ? log : { success: true, log };
5598
+ }
5599
+ case "git_checkpoint": {
5600
+ if (!services.checkpoint) return serviceNotImplemented(command);
5601
+ const msg = validateMutatingMessage(args?.message);
5602
+ if (typeof msg !== "string") return msg;
5603
+ const includeUntracked = Boolean(args?.includeUntracked);
5604
+ const checkpoint = await runService(() => services.checkpoint({ workspace, message: msg, includeUntracked }));
5605
+ return "success" in checkpoint ? checkpoint : { success: true, checkpoint };
5606
+ }
5607
+ case "git_stash_push": {
5608
+ if (!services.stashPush) return serviceNotImplemented(command);
5609
+ const msg = validateMutatingMessage(args?.message);
5610
+ if (typeof msg !== "string") return msg;
5611
+ const includeUntracked = Boolean(args?.includeUntracked);
5612
+ const stash = await runService(() => services.stashPush({ workspace, message: msg, includeUntracked }));
5613
+ return "success" in stash ? stash : { success: true, stash };
5614
+ }
5615
+ case "git_stash_pop": {
5616
+ if (!services.stashPop) return serviceNotImplemented(command);
5617
+ const stashRef = optionalString(args?.stashRef);
5618
+ if (stashRef !== void 0 && !/^stash@\{\d+\}$/.test(stashRef)) {
5619
+ return failure("invalid_args", "stashRef must match stash@{N} format");
5620
+ }
5621
+ const popResult = await runService(() => services.stashPop({ workspace, stashRef }));
5622
+ if (popResult !== void 0 && "success" in popResult) return popResult;
5623
+ return { success: true, stashPopped: true };
5624
+ }
5625
+ case "git_checkout_files": {
5626
+ if (!services.checkoutFiles) return serviceNotImplemented(command);
5627
+ const paths = args?.paths;
5628
+ if (!Array.isArray(paths) || paths.length === 0) {
5629
+ return failure("invalid_args", "paths must be a non-empty array");
5630
+ }
5631
+ if (paths.length > 50) {
5632
+ return failure("invalid_args", "paths array exceeds maximum of 50 entries");
5633
+ }
5634
+ const checkoutResult = await runService(() => services.checkoutFiles({ workspace, paths }));
5635
+ return "success" in checkoutResult ? checkoutResult : { success: true, checkedOut: checkoutResult.checkedOut };
5636
+ }
5637
+ case "git_remote_url": {
5638
+ if (!services.getRemoteUrl) return serviceNotImplemented(command);
5639
+ const remote = typeof args?.remote === "string" && args.remote.trim() ? args.remote.trim() : "origin";
5640
+ const remoteResult = await runService(() => services.getRemoteUrl({ workspace, remote }));
5641
+ if ("success" in remoteResult) return remoteResult;
5642
+ return { success: true, remoteUrl: remoteResult.remoteUrl, remote: remoteResult.remote };
5643
+ }
5644
+ default:
5645
+ return failure("invalid_args", `Unknown Git command: ${command}`);
5646
+ }
5647
+ }
5648
+ function validateMutatingMessage(value) {
5649
+ if (typeof value !== "string" || !value.trim()) {
5650
+ return failure("invalid_args", "message must be a non-empty string");
5651
+ }
5652
+ const msg = value.trim();
5653
+ if (msg.length > 200) {
5654
+ return failure("invalid_args", "message must be 200 characters or fewer");
5655
+ }
5656
+ return msg;
5657
+ }
5658
+ async function gitCheckpoint(workspace, message, includeUntracked) {
5659
+ const repo = await resolveGitRepository(workspace);
5660
+ const repoRoot = repo.repoRoot;
5661
+ const statusResult = await getGitRepoStatus(workspace);
5662
+ if (statusResult.hasConflicts) {
5663
+ throw new GitCommandError("conflict", "Repository has conflicts \u2014 resolve before checkpointing");
5664
+ }
5665
+ const addArgs = includeUntracked ? ["-A"] : ["-u"];
5666
+ await runGit(repo, ["add", ...addArgs], { cwd: repoRoot });
5667
+ const fullMsg = `adhdev: checkpoint ${message}`;
5668
+ let commitSha;
5669
+ try {
5670
+ await runGit(repo, ["commit", "-m", fullMsg], { cwd: repoRoot });
5671
+ const revResult = await runGit(repo, ["rev-parse", "HEAD"], { cwd: repoRoot });
5672
+ commitSha = revResult.stdout.trim();
5673
+ } catch (err) {
5674
+ const output = (err?.stdout || "") + (err?.stderr || "");
5675
+ if (/nothing to commit/i.test(output)) {
5676
+ throw new GitCommandError("git_command_failed", "Nothing to commit");
5677
+ }
5678
+ throw err;
5679
+ }
5680
+ return {
5681
+ workspace: repo.workspace,
5682
+ repoRoot,
5683
+ isGitRepo: true,
5684
+ commit: commitSha,
5685
+ message: fullMsg,
5686
+ lastCheckedAt: Date.now()
5687
+ };
5688
+ }
5689
+ async function gitStashPush(workspace, message, includeUntracked) {
5690
+ const repo = await resolveGitRepository(workspace);
5691
+ const repoRoot = repo.repoRoot;
5692
+ const stashArgs = ["stash", "push", "-m", message];
5693
+ if (includeUntracked) stashArgs.push("--include-untracked");
5694
+ const result = await runGit(repo, stashArgs, { cwd: repoRoot });
5695
+ if (/No local changes to save/i.test(result.stdout + result.stderr)) {
5696
+ throw new GitCommandError("git_command_failed", "Nothing to stash");
5697
+ }
5698
+ return {
5699
+ workspace: repo.workspace,
5700
+ repoRoot,
5701
+ isGitRepo: true,
5702
+ stashRef: "stash@{0}",
5703
+ message,
5704
+ lastCheckedAt: Date.now()
5705
+ };
5706
+ }
5707
+ async function gitStashPop(workspace, stashRef) {
5708
+ const repo = await resolveGitRepository(workspace);
5709
+ const repoRoot = repo.repoRoot;
5710
+ const popArgs = stashRef ? ["stash", "pop", stashRef] : ["stash", "pop"];
5711
+ await runGit(repo, popArgs, { cwd: repoRoot });
5712
+ }
5713
+ async function gitCheckoutFiles(workspace, paths) {
5714
+ const repo = await resolveGitRepository(workspace);
5715
+ const repoRoot = repo.repoRoot;
5716
+ const normalizedPaths = [];
5717
+ for (const p of paths) {
5718
+ if (typeof p !== "string" || !p.trim() || p.includes("\0")) {
5719
+ throw new GitCommandError("invalid_args", `Invalid path: ${String(p)}`);
5720
+ }
5721
+ if (path3.isAbsolute(p)) {
5722
+ throw new GitCommandError("invalid_args", `Path must be repository-relative, not absolute: ${p}`);
5723
+ }
5724
+ const normalized = path3.normalize(p.trim()).split(path3.sep).join("/");
5725
+ if (normalized.startsWith("../") || normalized === "..") {
5726
+ throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
5727
+ }
5728
+ const absolutePath = path3.resolve(repoRoot, normalized);
5729
+ if (!isPathInside(repoRoot, absolutePath)) {
5730
+ throw new GitCommandError("path_outside_repo", `Path is outside repository root: ${p}`);
5731
+ }
5732
+ normalizedPaths.push(normalized);
5733
+ }
5734
+ await runGit(repo, ["checkout", "--", ...normalizedPaths], { cwd: repoRoot });
5735
+ return { checkedOut: normalizedPaths };
5736
+ }
5737
+ async function gitGetRemoteUrl(workspace, remote) {
5738
+ const repo = await resolveGitRepository(workspace);
5739
+ const result = await runGit(repo, ["remote", "get-url", remote], { cwd: repo.repoRoot });
5740
+ const remoteUrl = result.stdout.trim();
5741
+ if (!remoteUrl) {
5742
+ throw new GitCommandError("git_command_failed", `Remote '${remote}' has no URL`);
5743
+ }
5744
+ return { remoteUrl, remote };
5745
+ }
5746
+ function formatOptionalGitLogRangeArg(flag, value) {
5747
+ return value ? [`${flag}=${value}`] : [];
5748
+ }
5749
+ async function getGitLog(workspace, options) {
5750
+ const lastCheckedAt = Date.now();
5751
+ const repo = await resolveGitRepository(workspace);
5752
+ const repoRoot = repo.repoRoot;
5753
+ const boundedLimit = Math.max(1, Math.min(200, Math.floor(options.limit || 50)));
5754
+ const selectedPath = options.path ? validateGitLogPath(repoRoot, options.path) : void 0;
5755
+ const result = await runGit(
5756
+ repo,
5757
+ [
5758
+ "log",
5759
+ `--max-count=${boundedLimit}`,
5760
+ "--format=%H%x00%an%x00%ae%x00%at%x00%ct%x00%s",
5761
+ ...formatOptionalGitLogRangeArg("--since", options.since),
5762
+ ...formatOptionalGitLogRangeArg("--until", options.until),
5763
+ "--",
5764
+ ...selectedPath ? [selectedPath] : []
5765
+ ],
5766
+ { cwd: repoRoot }
5767
+ );
5768
+ const entries = result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => {
5769
+ const [commit = "", authorName, authorEmail, authoredAt, committedAt, ...messageParts] = line.split("\0");
5770
+ return {
5771
+ commit,
5772
+ message: messageParts.join("\0"),
5773
+ authorName: authorName || void 0,
5774
+ authorEmail: authorEmail || void 0,
5775
+ authoredAt: authoredAt ? Number.parseInt(authoredAt, 10) * 1e3 : void 0,
5776
+ committedAt: committedAt ? Number.parseInt(committedAt, 10) * 1e3 : void 0
5777
+ };
5778
+ }).filter((entry) => entry.commit.length > 0);
5779
+ return {
5780
+ workspace: repo.workspace,
5781
+ repoRoot,
5782
+ isGitRepo: true,
5783
+ entries,
5784
+ limit: boundedLimit,
5785
+ truncated: entries.length >= boundedLimit,
5786
+ lastCheckedAt
5787
+ };
5788
+ }
5789
+ function validateGitLogPath(repoRoot, filePath) {
5790
+ if (!filePath.trim() || filePath.includes("\0")) {
5791
+ throw new GitCommandError("invalid_args", "path must be a non-empty repository-relative path");
5792
+ }
5793
+ if (path3.isAbsolute(filePath)) {
5794
+ throw new GitCommandError("invalid_args", "path must be repository-relative");
5795
+ }
5796
+ const normalized = path3.normalize(filePath).split(path3.sep).join("/");
5797
+ const absolutePath = path3.resolve(repoRoot, normalized);
5798
+ if (!isPathInside(repoRoot, absolutePath) || normalized.startsWith("../") || normalized === "..") {
5799
+ throw new GitCommandError("path_outside_repo", "Git log path is outside the repository root");
5800
+ }
5801
+ return normalized;
5802
+ }
5803
+
5804
+ // src/git/turn-snapshot-tracker.ts
5805
+ var BUSY_STATUSES = /* @__PURE__ */ new Set(["streaming", "waiting_approval"]);
5806
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["idle", "error"]);
5807
+ var TurnSnapshotTracker = class {
5808
+ lastStatus = /* @__PURE__ */ new Map();
5809
+ onTurnCompleted;
5810
+ constructor(onTurnCompleted) {
5811
+ this.onTurnCompleted = onTurnCompleted;
5812
+ }
5813
+ record(sessionId, status, workspace) {
5814
+ const prev = this.lastStatus.get(sessionId);
5815
+ this.lastStatus.set(sessionId, status);
5816
+ if (workspace && prev && BUSY_STATUSES.has(prev) && TERMINAL_STATUSES.has(status)) {
5817
+ this.onTurnCompleted({ sessionId, workspace });
5818
+ }
5819
+ }
5820
+ forget(sessionId) {
5821
+ this.lastStatus.delete(sessionId);
5822
+ }
5823
+ };
5824
+
4527
5825
  // src/index.ts
4528
5826
  init_config();
4529
5827
 
4530
5828
  // src/config/workspaces.ts
4531
5829
  import * as fs from "fs";
4532
5830
  import * as os from "os";
4533
- import * as path from "path";
5831
+ import * as path4 from "path";
4534
5832
  import { randomUUID as randomUUID2 } from "crypto";
4535
5833
  var MAX_WORKSPACES = 50;
4536
5834
  function expandPath(p) {
4537
5835
  const t = (p || "").trim();
4538
5836
  if (!t) return "";
4539
- if (t.startsWith("~")) return path.join(os.homedir(), t.slice(1).replace(/^\//, ""));
4540
- return path.resolve(t);
5837
+ if (t.startsWith("~")) return path4.join(os.homedir(), t.slice(1).replace(/^\//, ""));
5838
+ return path4.resolve(t);
4541
5839
  }
4542
5840
  function validateWorkspacePath(absPath) {
4543
5841
  try {
@@ -4551,7 +5849,7 @@ function validateWorkspacePath(absPath) {
4551
5849
  }
4552
5850
  }
4553
5851
  function defaultWorkspaceLabel(absPath) {
4554
- const base = path.basename(absPath) || absPath;
5852
+ const base = path4.basename(absPath) || absPath;
4555
5853
  return base;
4556
5854
  }
4557
5855
  function getDefaultWorkspacePath(config) {
@@ -4642,9 +5940,9 @@ function resolveIdeLaunchWorkspace(args, config) {
4642
5940
  return getDefaultWorkspacePath(config) || void 0;
4643
5941
  }
4644
5942
  function findWorkspaceByPath(config, rawPath) {
4645
- const abs = path.resolve(expandPath(rawPath));
5943
+ const abs = path4.resolve(expandPath(rawPath));
4646
5944
  if (!abs) return void 0;
4647
- return (config.workspaces || []).find((w) => path.resolve(expandPath(w.path)) === abs);
5945
+ return (config.workspaces || []).find((w) => path4.resolve(expandPath(w.path)) === abs);
4648
5946
  }
4649
5947
  function addWorkspaceEntry(config, rawPath, label, options) {
4650
5948
  const abs = expandPath(rawPath);
@@ -4660,7 +5958,7 @@ function addWorkspaceEntry(config, rawPath, label, options) {
4660
5958
  const v = validateWorkspacePath(abs);
4661
5959
  if (!v.ok) return { error: v.error };
4662
5960
  const list = [...config.workspaces || []];
4663
- if (list.some((w) => path.resolve(w.path) === abs)) {
5961
+ if (list.some((w) => path4.resolve(w.path) === abs)) {
4664
5962
  return { error: "Workspace already in list" };
4665
5963
  }
4666
5964
  if (list.length >= MAX_WORKSPACES) {
@@ -4694,7 +5992,7 @@ function setDefaultWorkspaceId(config, id) {
4694
5992
  }
4695
5993
 
4696
5994
  // src/config/recent-activity.ts
4697
- import * as path2 from "path";
5995
+ import * as path5 from "path";
4698
5996
 
4699
5997
  // src/providers/summary-metadata.ts
4700
5998
  function normalizeSummaryItem(item) {
@@ -4763,9 +6061,9 @@ var MAX_ACTIVITY = 30;
4763
6061
  function normalizeWorkspace(workspace) {
4764
6062
  if (!workspace) return "";
4765
6063
  try {
4766
- return path2.resolve(expandPath(workspace));
6064
+ return path5.resolve(expandPath(workspace));
4767
6065
  } catch {
4768
- return path2.resolve(workspace);
6066
+ return path5.resolve(workspace);
4769
6067
  }
4770
6068
  }
4771
6069
  function buildRecentActivityKey(entry) {
@@ -4933,14 +6231,14 @@ function markSessionSeen(state, sessionId, seenAt = Date.now(), completionMarker
4933
6231
  }
4934
6232
 
4935
6233
  // src/config/saved-sessions.ts
4936
- import * as path3 from "path";
6234
+ import * as path6 from "path";
4937
6235
  var MAX_SAVED_SESSIONS = 500;
4938
6236
  function normalizeWorkspace2(workspace) {
4939
6237
  if (!workspace) return "";
4940
6238
  try {
4941
- return path3.resolve(expandPath(workspace));
6239
+ return path6.resolve(expandPath(workspace));
4942
6240
  } catch {
4943
- return path3.resolve(workspace);
6241
+ return path6.resolve(workspace);
4944
6242
  }
4945
6243
  }
4946
6244
  function buildSavedProviderSessionKey(providerSessionId) {
@@ -5059,7 +6357,7 @@ function resetState() {
5059
6357
  import { execSync } from "child_process";
5060
6358
  import { existsSync as existsSync4 } from "fs";
5061
6359
  import { platform, homedir as homedir3 } from "os";
5062
- import * as path4 from "path";
6360
+ import * as path7 from "path";
5063
6361
  var BUILTIN_IDE_DEFINITIONS = [];
5064
6362
  var registeredIDEs = /* @__PURE__ */ new Map();
5065
6363
  function registerIDEDefinition(def) {
@@ -5078,9 +6376,9 @@ function getMergedDefinitions() {
5078
6376
  function findCliCommand(command) {
5079
6377
  const trimmed = String(command || "").trim();
5080
6378
  if (!trimmed) return null;
5081
- if (path4.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
5082
- const candidate = trimmed.startsWith("~") ? path4.join(homedir3(), trimmed.slice(1)) : trimmed;
5083
- const resolved = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
6379
+ if (path7.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
6380
+ const candidate = trimmed.startsWith("~") ? path7.join(homedir3(), trimmed.slice(1)) : trimmed;
6381
+ const resolved = path7.isAbsolute(candidate) ? candidate : path7.resolve(candidate);
5084
6382
  return existsSync4(resolved) ? resolved : null;
5085
6383
  }
5086
6384
  try {
@@ -5108,7 +6406,7 @@ function getIdeVersion(cliCommand) {
5108
6406
  function checkPathExists(paths) {
5109
6407
  const home = homedir3();
5110
6408
  for (const p of paths) {
5111
- const normalized = p.startsWith("~") ? path4.join(home, p.slice(1)) : p;
6409
+ const normalized = p.startsWith("~") ? path7.join(home, p.slice(1)) : p;
5112
6410
  if (normalized.includes("*")) {
5113
6411
  const username = home.split(/[\\/]/).pop() || "";
5114
6412
  const resolved = normalized.replace("*", username);
@@ -5166,7 +6464,7 @@ async function detectIDEs(providerLoader) {
5166
6464
  // src/detection/cli-detector.ts
5167
6465
  import { exec } from "child_process";
5168
6466
  import * as os2 from "os";
5169
- import * as path5 from "path";
6467
+ import * as path8 from "path";
5170
6468
  import { existsSync as existsSync5 } from "fs";
5171
6469
  function parseVersion(raw) {
5172
6470
  const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
@@ -5179,36 +6477,36 @@ function shellQuote(value) {
5179
6477
  function expandHome(value) {
5180
6478
  const trimmed = value.trim();
5181
6479
  if (!trimmed.startsWith("~")) return trimmed;
5182
- return path5.join(os2.homedir(), trimmed.slice(1));
6480
+ return path8.join(os2.homedir(), trimmed.slice(1));
5183
6481
  }
5184
6482
  function isExplicitCommandPath(command) {
5185
6483
  const trimmed = command.trim();
5186
- return path5.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
6484
+ return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
5187
6485
  }
5188
6486
  function resolveCommandPath(command) {
5189
6487
  const trimmed = command.trim();
5190
6488
  if (!trimmed) return null;
5191
6489
  if (isExplicitCommandPath(trimmed)) {
5192
6490
  const expanded = expandHome(trimmed);
5193
- const candidate = path5.isAbsolute(expanded) ? expanded : path5.resolve(expanded);
6491
+ const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
5194
6492
  return existsSync5(candidate) ? candidate : null;
5195
6493
  }
5196
6494
  return null;
5197
6495
  }
5198
6496
  function execAsync(cmd, timeoutMs = 5e3) {
5199
- return new Promise((resolve12) => {
6497
+ return new Promise((resolve15) => {
5200
6498
  const child = exec(cmd, {
5201
6499
  encoding: "utf-8",
5202
6500
  timeout: timeoutMs,
5203
6501
  ...process.platform === "win32" ? { windowsHide: true } : {}
5204
6502
  }, (err, stdout) => {
5205
6503
  if (err || !stdout?.trim()) {
5206
- resolve12(null);
6504
+ resolve15(null);
5207
6505
  } else {
5208
- resolve12(stdout.trim());
6506
+ resolve15(stdout.trim());
5209
6507
  }
5210
6508
  });
5211
- child.on("error", () => resolve12(null));
6509
+ child.on("error", () => resolve15(null));
5212
6510
  });
5213
6511
  }
5214
6512
  async function detectCLIs(providerLoader, options) {
@@ -5573,7 +6871,7 @@ var DaemonCdpManager = class {
5573
6871
  * Returns multiple entries if multiple IDE windows are open on same port
5574
6872
  */
5575
6873
  static listAllTargets(port) {
5576
- return new Promise((resolve12) => {
6874
+ return new Promise((resolve15) => {
5577
6875
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
5578
6876
  let data = "";
5579
6877
  res.on("data", (chunk) => data += chunk.toString());
@@ -5589,16 +6887,16 @@ var DaemonCdpManager = class {
5589
6887
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
5590
6888
  );
5591
6889
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
5592
- resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
6890
+ resolve15(mainPages.length > 0 ? mainPages : fallbackPages);
5593
6891
  } catch {
5594
- resolve12([]);
6892
+ resolve15([]);
5595
6893
  }
5596
6894
  });
5597
6895
  });
5598
- req.on("error", () => resolve12([]));
6896
+ req.on("error", () => resolve15([]));
5599
6897
  req.setTimeout(2e3, () => {
5600
6898
  req.destroy();
5601
- resolve12([]);
6899
+ resolve15([]);
5602
6900
  });
5603
6901
  });
5604
6902
  }
@@ -5638,7 +6936,7 @@ var DaemonCdpManager = class {
5638
6936
  }
5639
6937
  }
5640
6938
  findTargetOnPort(port) {
5641
- return new Promise((resolve12) => {
6939
+ return new Promise((resolve15) => {
5642
6940
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
5643
6941
  let data = "";
5644
6942
  res.on("data", (chunk) => data += chunk.toString());
@@ -5649,7 +6947,7 @@ var DaemonCdpManager = class {
5649
6947
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
5650
6948
  );
5651
6949
  if (pages.length === 0) {
5652
- resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
6950
+ resolve15(targets.find((t) => t.webSocketDebuggerUrl) || null);
5653
6951
  return;
5654
6952
  }
5655
6953
  const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -5668,25 +6966,25 @@ var DaemonCdpManager = class {
5668
6966
  this._targetId = selected.target.id;
5669
6967
  }
5670
6968
  this._pageTitle = selected.target.title || "";
5671
- resolve12(selected.target);
6969
+ resolve15(selected.target);
5672
6970
  return;
5673
6971
  }
5674
6972
  if (previousTargetId) {
5675
6973
  this.log(`[CDP] Target ${previousTargetId} not found in page list`);
5676
- resolve12(null);
6974
+ resolve15(null);
5677
6975
  return;
5678
6976
  }
5679
6977
  this._pageTitle = list[0]?.title || "";
5680
- resolve12(list[0]);
6978
+ resolve15(list[0]);
5681
6979
  } catch {
5682
- resolve12(null);
6980
+ resolve15(null);
5683
6981
  }
5684
6982
  });
5685
6983
  });
5686
- req.on("error", () => resolve12(null));
6984
+ req.on("error", () => resolve15(null));
5687
6985
  req.setTimeout(2e3, () => {
5688
6986
  req.destroy();
5689
- resolve12(null);
6987
+ resolve15(null);
5690
6988
  });
5691
6989
  });
5692
6990
  }
@@ -5697,7 +6995,7 @@ var DaemonCdpManager = class {
5697
6995
  this.extensionProviders = providers;
5698
6996
  }
5699
6997
  connectToTarget(wsUrl) {
5700
- return new Promise((resolve12) => {
6998
+ return new Promise((resolve15) => {
5701
6999
  this.ws = new WebSocket(wsUrl);
5702
7000
  this.ws.on("open", async () => {
5703
7001
  this._connected = true;
@@ -5707,17 +7005,17 @@ var DaemonCdpManager = class {
5707
7005
  }
5708
7006
  this.connectBrowserWs().catch(() => {
5709
7007
  });
5710
- resolve12(true);
7008
+ resolve15(true);
5711
7009
  });
5712
7010
  this.ws.on("message", (data) => {
5713
7011
  try {
5714
7012
  const msg = JSON.parse(data.toString());
5715
7013
  if (msg.id && this.pending.has(msg.id)) {
5716
- const { resolve: resolve13, reject } = this.pending.get(msg.id);
7014
+ const { resolve: resolve16, reject } = this.pending.get(msg.id);
5717
7015
  this.pending.delete(msg.id);
5718
7016
  this.failureCount = 0;
5719
7017
  if (msg.error) reject(new Error(msg.error.message));
5720
- else resolve13(msg.result);
7018
+ else resolve16(msg.result);
5721
7019
  } else if (msg.method === "Runtime.executionContextCreated") {
5722
7020
  this.contexts.add(msg.params.context.id);
5723
7021
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -5740,7 +7038,7 @@ var DaemonCdpManager = class {
5740
7038
  this.ws.on("error", (err) => {
5741
7039
  this.log(`[CDP] WebSocket error: ${err.message}`);
5742
7040
  this._connected = false;
5743
- resolve12(false);
7041
+ resolve15(false);
5744
7042
  });
5745
7043
  });
5746
7044
  }
@@ -5754,7 +7052,7 @@ var DaemonCdpManager = class {
5754
7052
  return;
5755
7053
  }
5756
7054
  this.log(`[CDP] Connecting browser WS for target discovery...`);
5757
- await new Promise((resolve12, reject) => {
7055
+ await new Promise((resolve15, reject) => {
5758
7056
  this.browserWs = new WebSocket(browserWsUrl);
5759
7057
  this.browserWs.on("open", async () => {
5760
7058
  this._browserConnected = true;
@@ -5764,16 +7062,16 @@ var DaemonCdpManager = class {
5764
7062
  } catch (e) {
5765
7063
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
5766
7064
  }
5767
- resolve12();
7065
+ resolve15();
5768
7066
  });
5769
7067
  this.browserWs.on("message", (data) => {
5770
7068
  try {
5771
7069
  const msg = JSON.parse(data.toString());
5772
7070
  if (msg.id && this.browserPending.has(msg.id)) {
5773
- const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
7071
+ const { resolve: resolve16, reject: reject2 } = this.browserPending.get(msg.id);
5774
7072
  this.browserPending.delete(msg.id);
5775
7073
  if (msg.error) reject2(new Error(msg.error.message));
5776
- else resolve13(msg.result);
7074
+ else resolve16(msg.result);
5777
7075
  }
5778
7076
  } catch {
5779
7077
  }
@@ -5793,31 +7091,31 @@ var DaemonCdpManager = class {
5793
7091
  }
5794
7092
  }
5795
7093
  getBrowserWsUrl() {
5796
- return new Promise((resolve12) => {
7094
+ return new Promise((resolve15) => {
5797
7095
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
5798
7096
  let data = "";
5799
7097
  res.on("data", (chunk) => data += chunk.toString());
5800
7098
  res.on("end", () => {
5801
7099
  try {
5802
7100
  const info = JSON.parse(data);
5803
- resolve12(info.webSocketDebuggerUrl || null);
7101
+ resolve15(info.webSocketDebuggerUrl || null);
5804
7102
  } catch {
5805
- resolve12(null);
7103
+ resolve15(null);
5806
7104
  }
5807
7105
  });
5808
7106
  });
5809
- req.on("error", () => resolve12(null));
7107
+ req.on("error", () => resolve15(null));
5810
7108
  req.setTimeout(3e3, () => {
5811
7109
  req.destroy();
5812
- resolve12(null);
7110
+ resolve15(null);
5813
7111
  });
5814
7112
  });
5815
7113
  }
5816
7114
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
5817
- return new Promise((resolve12, reject) => {
7115
+ return new Promise((resolve15, reject) => {
5818
7116
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
5819
7117
  const id = this.browserMsgId++;
5820
- this.browserPending.set(id, { resolve: resolve12, reject });
7118
+ this.browserPending.set(id, { resolve: resolve15, reject });
5821
7119
  this.browserWs.send(JSON.stringify({ id, method, params }));
5822
7120
  setTimeout(() => {
5823
7121
  if (this.browserPending.has(id)) {
@@ -5857,11 +7155,11 @@ var DaemonCdpManager = class {
5857
7155
  }
5858
7156
  // ─── CDP Protocol ────────────────────────────────────────
5859
7157
  sendInternal(method, params = {}, timeoutMs = 15e3) {
5860
- return new Promise((resolve12, reject) => {
7158
+ return new Promise((resolve15, reject) => {
5861
7159
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
5862
7160
  if (this.ws.readyState !== WebSocket.OPEN) return reject(new Error("WebSocket not open"));
5863
7161
  const id = this.msgId++;
5864
- this.pending.set(id, { resolve: resolve12, reject });
7162
+ this.pending.set(id, { resolve: resolve15, reject });
5865
7163
  this.ws.send(JSON.stringify({ id, method, params }));
5866
7164
  setTimeout(() => {
5867
7165
  if (this.pending.has(id)) {
@@ -6110,7 +7408,7 @@ var DaemonCdpManager = class {
6110
7408
  const browserWs = this.browserWs;
6111
7409
  let msgId = this.browserMsgId;
6112
7410
  const sendWs = (method, params = {}, sessionId) => {
6113
- return new Promise((resolve12, reject) => {
7411
+ return new Promise((resolve15, reject) => {
6114
7412
  const mid = msgId++;
6115
7413
  this.browserMsgId = msgId;
6116
7414
  const handler = (raw) => {
@@ -6119,7 +7417,7 @@ var DaemonCdpManager = class {
6119
7417
  if (msg.id === mid) {
6120
7418
  browserWs.removeListener("message", handler);
6121
7419
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
6122
- else resolve12(msg.result);
7420
+ else resolve15(msg.result);
6123
7421
  }
6124
7422
  } catch {
6125
7423
  }
@@ -6320,14 +7618,14 @@ var DaemonCdpManager = class {
6320
7618
  if (!ws || ws.readyState !== WebSocket.OPEN) {
6321
7619
  throw new Error("CDP not connected");
6322
7620
  }
6323
- return new Promise((resolve12, reject) => {
7621
+ return new Promise((resolve15, reject) => {
6324
7622
  const id = getNextId();
6325
7623
  pendingMap.set(id, {
6326
7624
  resolve: (result) => {
6327
7625
  if (result?.result?.subtype === "error") {
6328
7626
  reject(new Error(result.result.description));
6329
7627
  } else {
6330
- resolve12(result?.result?.value);
7628
+ resolve15(result?.result?.value);
6331
7629
  }
6332
7630
  },
6333
7631
  reject
@@ -6359,10 +7657,10 @@ var DaemonCdpManager = class {
6359
7657
  throw new Error("CDP not connected");
6360
7658
  }
6361
7659
  const sendViaSession = (method, params = {}) => {
6362
- return new Promise((resolve12, reject) => {
7660
+ return new Promise((resolve15, reject) => {
6363
7661
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
6364
7662
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
6365
- pendingMap.set(id, { resolve: resolve12, reject });
7663
+ pendingMap.set(id, { resolve: resolve15, reject });
6366
7664
  ws.send(JSON.stringify({ id, sessionId, method, params }));
6367
7665
  setTimeout(() => {
6368
7666
  if (pendingMap.has(id)) {
@@ -7109,9 +8407,9 @@ ${cleanBody}`;
7109
8407
  // src/config/chat-history.ts
7110
8408
  init_chat_message_normalization();
7111
8409
  import * as fs3 from "fs";
7112
- import * as path7 from "path";
8410
+ import * as path10 from "path";
7113
8411
  import * as os5 from "os";
7114
- var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
8412
+ var HISTORY_DIR = path10.join(os5.homedir(), ".adhdev", "history");
7115
8413
  var RETAIN_DAYS = 30;
7116
8414
  var SAVED_HISTORY_INDEX_VERSION = 1;
7117
8415
  var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
@@ -7274,8 +8572,8 @@ function extractSavedHistorySessionIdFromFile(file) {
7274
8572
  function buildSavedHistoryFileSignatureMap(dir, files) {
7275
8573
  return new Map(files.map((file) => {
7276
8574
  try {
7277
- const stat = fs3.statSync(path7.join(dir, file));
7278
- return [file, `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`];
8575
+ const stat2 = fs3.statSync(path10.join(dir, file));
8576
+ return [file, `${file}:${stat2.size}:${Math.trunc(stat2.mtimeMs)}`];
7279
8577
  } catch {
7280
8578
  return [file, `${file}:missing`];
7281
8579
  }
@@ -7285,7 +8583,7 @@ function buildSavedHistoryCacheSignature(files, fileSignatures) {
7285
8583
  return files.map((file) => fileSignatures.get(file) || `${file}:missing`).join("|");
7286
8584
  }
7287
8585
  function getSavedHistoryIndexFilePath(dir) {
7288
- return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
8586
+ return path10.join(dir, SAVED_HISTORY_INDEX_FILE);
7289
8587
  }
7290
8588
  function getSavedHistoryIndexLockPath(dir) {
7291
8589
  return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
@@ -7338,8 +8636,8 @@ function acquireSavedHistoryIndexLock(dir) {
7338
8636
  } catch (error) {
7339
8637
  if (error?.code !== "EEXIST") return null;
7340
8638
  try {
7341
- const stat = fs3.statSync(lockPath);
7342
- if (Date.now() - stat.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
8639
+ const stat2 = fs3.statSync(lockPath);
8640
+ if (Date.now() - stat2.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
7343
8641
  fs3.rmSync(lockPath, { recursive: true, force: true });
7344
8642
  continue;
7345
8643
  }
@@ -7387,7 +8685,7 @@ function savePersistedSavedHistoryIndex(dir, entries) {
7387
8685
  }
7388
8686
  for (const file of Array.from(currentEntries.keys())) {
7389
8687
  if (incomingFiles.has(file)) continue;
7390
- if (!fs3.existsSync(path7.join(dir, file))) {
8688
+ if (!fs3.existsSync(path10.join(dir, file))) {
7391
8689
  currentEntries.delete(file);
7392
8690
  }
7393
8691
  }
@@ -7402,8 +8700,8 @@ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
7402
8700
  }
7403
8701
  function buildSavedHistoryIndexFileSignature(dir) {
7404
8702
  try {
7405
- const stat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
7406
- return `index:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
8703
+ const stat2 = fs3.statSync(getSavedHistoryIndexFilePath(dir));
8704
+ return `index:${stat2.size}:${Math.trunc(stat2.mtimeMs)}`;
7407
8705
  } catch {
7408
8706
  return "index:missing";
7409
8707
  }
@@ -7413,8 +8711,8 @@ function historyDirectoryHasFilesNewerThanIndex(dir) {
7413
8711
  const indexStat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
7414
8712
  const files = listHistoryFiles(dir);
7415
8713
  for (const file of files) {
7416
- const stat = fs3.statSync(path7.join(dir, file));
7417
- if (stat.mtimeMs > indexStat.mtimeMs) return true;
8714
+ const stat2 = fs3.statSync(path10.join(dir, file));
8715
+ if (stat2.mtimeMs > indexStat.mtimeMs) return true;
7418
8716
  }
7419
8717
  return false;
7420
8718
  } catch {
@@ -7423,14 +8721,14 @@ function historyDirectoryHasFilesNewerThanIndex(dir) {
7423
8721
  }
7424
8722
  function buildSavedHistoryFileSignature(dir, file) {
7425
8723
  try {
7426
- const stat = fs3.statSync(path7.join(dir, file));
7427
- return `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
8724
+ const stat2 = fs3.statSync(path10.join(dir, file));
8725
+ return `${file}:${stat2.size}:${Math.trunc(stat2.mtimeMs)}`;
7428
8726
  } catch {
7429
8727
  return `${file}:missing`;
7430
8728
  }
7431
8729
  }
7432
8730
  function persistSavedHistoryFileSummaryEntry(agentType, dir, file, updater) {
7433
- const filePath = path7.join(dir, file);
8731
+ const filePath = path10.join(dir, file);
7434
8732
  const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
7435
8733
  const currentEntry = entries.get(file) || null;
7436
8734
  const nextSummary = updater(currentEntry?.summary || null);
@@ -7503,7 +8801,7 @@ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file, histor
7503
8801
  function computeSavedHistoryFileSummary(dir, file) {
7504
8802
  const historySessionId = extractSavedHistorySessionIdFromFile(file);
7505
8803
  if (!historySessionId) return null;
7506
- const filePath = path7.join(dir, file);
8804
+ const filePath = path10.join(dir, file);
7507
8805
  const content = fs3.readFileSync(filePath, "utf-8");
7508
8806
  const lines = content.split("\n").filter(Boolean);
7509
8807
  let messageCount = 0;
@@ -7590,7 +8888,7 @@ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatur
7590
8888
  const summaryBySessionId = /* @__PURE__ */ new Map();
7591
8889
  const nextPersistedEntries = /* @__PURE__ */ new Map();
7592
8890
  for (const file of files.slice().sort()) {
7593
- const filePath = path7.join(dir, file);
8891
+ const filePath = path10.join(dir, file);
7594
8892
  const signature = fileSignatures.get(file) || `${file}:missing`;
7595
8893
  const cached = savedHistoryFileSummaryCache.get(filePath);
7596
8894
  const persisted = persistedEntries.get(file);
@@ -7710,12 +9008,12 @@ var ChatHistoryWriter = class {
7710
9008
  });
7711
9009
  }
7712
9010
  if (newMessages.length === 0) return;
7713
- const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
9011
+ const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
7714
9012
  fs3.mkdirSync(dir, { recursive: true });
7715
9013
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7716
9014
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
7717
9015
  const fileName = `${filePrefix}${date}.jsonl`;
7718
- const filePath = path7.join(dir, fileName);
9016
+ const filePath = path10.join(dir, fileName);
7719
9017
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
7720
9018
  fs3.appendFileSync(filePath, lines, "utf-8");
7721
9019
  updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
@@ -7806,11 +9104,11 @@ var ChatHistoryWriter = class {
7806
9104
  const ws = String(workspace || "").trim();
7807
9105
  if (!id || !ws) return;
7808
9106
  try {
7809
- const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
9107
+ const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
7810
9108
  fs3.mkdirSync(dir, { recursive: true });
7811
9109
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7812
9110
  const fileName = `${this.sanitize(id)}_${date}.jsonl`;
7813
- const filePath = path7.join(dir, fileName);
9111
+ const filePath = path10.join(dir, fileName);
7814
9112
  const record = {
7815
9113
  ts: (/* @__PURE__ */ new Date()).toISOString(),
7816
9114
  receivedAt: Date.now(),
@@ -7856,14 +9154,14 @@ var ChatHistoryWriter = class {
7856
9154
  this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
7857
9155
  this.lastSeenCounts.delete(fromDedupKey);
7858
9156
  }
7859
- const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
9157
+ const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
7860
9158
  if (!fs3.existsSync(dir)) return;
7861
9159
  const fromPrefix = `${this.sanitize(fromId)}_`;
7862
9160
  const toPrefix = `${this.sanitize(toId)}_`;
7863
9161
  const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
7864
9162
  for (const file of files) {
7865
- const sourcePath = path7.join(dir, file);
7866
- const targetPath = path7.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
9163
+ const sourcePath = path10.join(dir, file);
9164
+ const targetPath = path10.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
7867
9165
  const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
7868
9166
  const rewritten = sourceLines.map((line) => {
7869
9167
  try {
@@ -7897,13 +9195,13 @@ var ChatHistoryWriter = class {
7897
9195
  const sessionId = String(historySessionId || "").trim();
7898
9196
  if (!sessionId) return;
7899
9197
  try {
7900
- const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
9198
+ const dir = path10.join(HISTORY_DIR, this.sanitize(agentType));
7901
9199
  if (!fs3.existsSync(dir)) return;
7902
9200
  const prefix = `${this.sanitize(sessionId)}_`;
7903
9201
  const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
7904
9202
  const seen = /* @__PURE__ */ new Set();
7905
9203
  for (const file of files) {
7906
- const filePath = path7.join(dir, file);
9204
+ const filePath = path10.join(dir, file);
7907
9205
  const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
7908
9206
  const next = [];
7909
9207
  for (const line of lines) {
@@ -7957,13 +9255,13 @@ var ChatHistoryWriter = class {
7957
9255
  const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
7958
9256
  const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
7959
9257
  for (const dir of agentDirs) {
7960
- const dirPath = path7.join(HISTORY_DIR, dir.name);
9258
+ const dirPath = path10.join(HISTORY_DIR, dir.name);
7961
9259
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
7962
9260
  let removedAny = false;
7963
9261
  for (const file of files) {
7964
- const filePath = path7.join(dirPath, file);
7965
- const stat = fs3.statSync(filePath);
7966
- if (stat.mtimeMs < cutoff) {
9262
+ const filePath = path10.join(dirPath, file);
9263
+ const stat2 = fs3.statSync(filePath);
9264
+ if (stat2.mtimeMs < cutoff) {
7967
9265
  fs3.unlinkSync(filePath);
7968
9266
  removedAny = true;
7969
9267
  }
@@ -8011,13 +9309,13 @@ function pageHistoryRecords(agentType, records, offset = 0, limit = 30, excludeR
8011
9309
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0, historyBehavior) {
8012
9310
  try {
8013
9311
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
8014
- const dir = path7.join(HISTORY_DIR, sanitized);
9312
+ const dir = path10.join(HISTORY_DIR, sanitized);
8015
9313
  if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
8016
9314
  const files = listHistoryFiles(dir, historySessionId);
8017
9315
  const allMessages = [];
8018
9316
  const seen = /* @__PURE__ */ new Set();
8019
9317
  for (const file of files) {
8020
- const filePath = path7.join(dir, file);
9318
+ const filePath = path10.join(dir, file);
8021
9319
  const content = fs3.readFileSync(filePath, "utf-8");
8022
9320
  const lines = content.trim().split("\n").filter(Boolean);
8023
9321
  for (let i = 0; i < lines.length; i++) {
@@ -8041,7 +9339,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, ex
8041
9339
  function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
8042
9340
  try {
8043
9341
  const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
8044
- const dir = path7.join(HISTORY_DIR, sanitized);
9342
+ const dir = path10.join(HISTORY_DIR, sanitized);
8045
9343
  if (!fs3.existsSync(dir)) {
8046
9344
  savedHistorySessionCache.delete(sanitized);
8047
9345
  return { sessions: [], hasMore: false };
@@ -8102,11 +9400,11 @@ function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
8102
9400
  }
8103
9401
  function readExistingSessionStartRecord(agentType, historySessionId) {
8104
9402
  try {
8105
- const dir = path7.join(HISTORY_DIR, agentType);
9403
+ const dir = path10.join(HISTORY_DIR, agentType);
8106
9404
  if (!fs3.existsSync(dir)) return null;
8107
9405
  const files = listHistoryFiles(dir, historySessionId).sort();
8108
9406
  for (const file of files) {
8109
- const lines = fs3.readFileSync(path7.join(dir, file), "utf-8").split("\n").filter(Boolean);
9407
+ const lines = fs3.readFileSync(path10.join(dir, file), "utf-8").split("\n").filter(Boolean);
8110
9408
  for (const line of lines) {
8111
9409
  try {
8112
9410
  const parsed = JSON.parse(line);
@@ -8126,16 +9424,16 @@ function readExistingSessionStartRecord(agentType, historySessionId) {
8126
9424
  function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
8127
9425
  if (records.length === 0) return false;
8128
9426
  try {
8129
- const dir = path7.join(HISTORY_DIR, agentType);
9427
+ const dir = path10.join(HISTORY_DIR, agentType);
8130
9428
  fs3.mkdirSync(dir, { recursive: true });
8131
9429
  const prefix = `${historySessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}_`;
8132
9430
  for (const file of fs3.readdirSync(dir)) {
8133
9431
  if (file.startsWith(prefix) && file.endsWith(".jsonl")) {
8134
- fs3.unlinkSync(path7.join(dir, file));
9432
+ fs3.unlinkSync(path10.join(dir, file));
8135
9433
  }
8136
9434
  }
8137
9435
  const targetDate = new Date(records[records.length - 1].receivedAt || Date.now()).toISOString().slice(0, 10);
8138
- const filePath = path7.join(dir, `${prefix}${targetDate}.jsonl`);
9436
+ const filePath = path10.join(dir, `${prefix}${targetDate}.jsonl`);
8139
9437
  fs3.writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join("\n")}
8140
9438
  `, "utf-8");
8141
9439
  invalidatePersistedSavedHistoryIndex(agentType, dir);
@@ -10007,6 +11305,10 @@ function shouldIncludeSessionMetadata(profile) {
10007
11305
  function shouldIncludeRuntimeMetadata(profile) {
10008
11306
  return true;
10009
11307
  }
11308
+ function getGitSummaryForWorkspace(workspace, options) {
11309
+ if (!workspace) return void 0;
11310
+ return options.getGitSummaryForWorkspace?.(workspace) || void 0;
11311
+ }
10010
11312
  function findCdpManager(cdpManagers, key) {
10011
11313
  const exact = cdpManagers.get(key);
10012
11314
  if (exact) return exact.isConnected ? exact : null;
@@ -10062,6 +11364,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
10062
11364
  const controlValues = normalizeProviderStateControlValues(state.controlValues);
10063
11365
  const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
10064
11366
  const includeSessionControls = shouldIncludeSessionControls(profile);
11367
+ const workspace = state.workspace || null;
11368
+ const git = getGitSummaryForWorkspace(workspace, options);
10065
11369
  const title = activeChat?.title || state.name;
10066
11370
  return {
10067
11371
  id: state.instanceId || state.type,
@@ -10074,7 +11378,8 @@ function buildIdeWorkspaceSession(state, cdpManagers, options) {
10074
11378
  activeModal: activeChat?.activeModal || null
10075
11379
  }),
10076
11380
  title,
10077
- workspace: state.workspace || null,
11381
+ workspace,
11382
+ ...git && { git },
10078
11383
  activeChat,
10079
11384
  ...summaryMetadata && { summaryMetadata },
10080
11385
  ...includeSessionMetadata && { capabilities: state.sessionCapabilities || IDE_SESSION_CAPABILITIES },
@@ -10095,6 +11400,8 @@ function buildExtensionAgentSession(parent, ext, options) {
10095
11400
  const controlValues = normalizeProviderStateControlValues(ext.controlValues);
10096
11401
  const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
10097
11402
  const includeSessionControls = shouldIncludeSessionControls(profile);
11403
+ const workspace = parent.workspace || null;
11404
+ const git = getGitSummaryForWorkspace(workspace, options);
10098
11405
  return {
10099
11406
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
10100
11407
  parentId: parent.instanceId || parent.type,
@@ -10107,7 +11414,8 @@ function buildExtensionAgentSession(parent, ext, options) {
10107
11414
  activeModal: activeChat?.activeModal || null
10108
11415
  }),
10109
11416
  title: activeChat?.title || ext.name,
10110
- workspace: parent.workspace || null,
11417
+ workspace,
11418
+ ...git && { git },
10111
11419
  activeChat,
10112
11420
  ...summaryMetadata && { summaryMetadata },
10113
11421
  ...includeSessionMetadata && { capabilities: ext.sessionCapabilities || EXTENSION_SESSION_CAPABILITIES },
@@ -10143,6 +11451,8 @@ function buildCliSession(state, options) {
10143
11451
  const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
10144
11452
  const includeRuntimeMetadata = shouldIncludeRuntimeMetadata(profile);
10145
11453
  const includeSessionControls = shouldIncludeSessionControls(profile);
11454
+ const workspace = state.workspace || null;
11455
+ const git = getGitSummaryForWorkspace(workspace, options);
10146
11456
  return {
10147
11457
  id: state.instanceId,
10148
11458
  parentId: null,
@@ -10155,7 +11465,8 @@ function buildCliSession(state, options) {
10155
11465
  activeModal: activeChat?.activeModal || null
10156
11466
  }),
10157
11467
  title: activeChat?.title || state.name,
10158
- workspace: state.workspace || null,
11468
+ workspace,
11469
+ ...git && { git },
10159
11470
  ...includeRuntimeMetadata && {
10160
11471
  runtimeKey: state.runtime?.runtimeKey,
10161
11472
  runtimeDisplayName: state.runtime?.displayName,
@@ -10190,6 +11501,8 @@ function buildAcpSession(state, options) {
10190
11501
  const controlValues = normalizeProviderStateControlValues(state.controlValues);
10191
11502
  const includeSessionMetadata = shouldIncludeSessionMetadata(profile);
10192
11503
  const includeSessionControls = shouldIncludeSessionControls(profile);
11504
+ const workspace = state.workspace || null;
11505
+ const git = getGitSummaryForWorkspace(workspace, options);
10193
11506
  return {
10194
11507
  id: state.instanceId,
10195
11508
  parentId: null,
@@ -10201,7 +11514,8 @@ function buildAcpSession(state, options) {
10201
11514
  activeModal: activeChat?.activeModal || null
10202
11515
  }),
10203
11516
  title: activeChat?.title || state.name,
10204
- workspace: state.workspace || null,
11517
+ workspace,
11518
+ ...git && { git },
10205
11519
  activeChat,
10206
11520
  ...summaryMetadata && { summaryMetadata },
10207
11521
  ...includeSessionMetadata && { capabilities: ACP_SESSION_CAPABILITIES },
@@ -10321,7 +11635,7 @@ function resolveLegacyProviderScript(fn, scriptName, params) {
10321
11635
  init_contracts();
10322
11636
  import * as fs4 from "fs";
10323
11637
  import * as os6 from "os";
10324
- import * as path8 from "path";
11638
+ import * as path11 from "path";
10325
11639
  import { randomUUID as randomUUID4 } from "crypto";
10326
11640
 
10327
11641
  // src/providers/provider-input-support.ts
@@ -11013,7 +12327,7 @@ function buildDebugBundleText(bundle) {
11013
12327
  }
11014
12328
  function getChatDebugBundleDir() {
11015
12329
  const override = typeof process.env.ADHDEV_DEBUG_BUNDLE_DIR === "string" ? process.env.ADHDEV_DEBUG_BUNDLE_DIR.trim() : "";
11016
- return override || path8.join(os6.homedir(), ".adhdev", "debug-bundles", "chat");
12330
+ return override || path11.join(os6.homedir(), ".adhdev", "debug-bundles", "chat");
11017
12331
  }
11018
12332
  function safeBundleIdSegment(value, fallback) {
11019
12333
  const normalized = String(value || fallback).trim().replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
@@ -11046,7 +12360,7 @@ function storeChatDebugBundleOnDaemon(bundle, targetSessionId) {
11046
12360
  const bundleId = createChatDebugBundleId(targetSessionId);
11047
12361
  const dir = getChatDebugBundleDir();
11048
12362
  fs4.mkdirSync(dir, { recursive: true });
11049
- const savedPath = path8.join(dir, `${bundleId}.json`);
12363
+ const savedPath = path11.join(dir, `${bundleId}.json`);
11050
12364
  const json = `${JSON.stringify(bundle, null, 2)}
11051
12365
  `;
11052
12366
  fs4.writeFileSync(savedPath, json, { encoding: "utf8", mode: 384 });
@@ -11240,7 +12554,7 @@ function getCliVisibleTranscriptCount(adapter) {
11240
12554
  async function getStableExtensionBaseline(h) {
11241
12555
  const first = await readExtensionChatState(h);
11242
12556
  if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
11243
- await new Promise((resolve12) => setTimeout(resolve12, 150));
12557
+ await new Promise((resolve15) => setTimeout(resolve15, 150));
11244
12558
  const second = await readExtensionChatState(h);
11245
12559
  return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
11246
12560
  }
@@ -11248,7 +12562,7 @@ async function verifyExtensionSendObserved(h, before) {
11248
12562
  const beforeCount = getStateMessageCount(before);
11249
12563
  const beforeSignature = getStateLastSignature(before);
11250
12564
  for (let attempt = 0; attempt < 12; attempt += 1) {
11251
- await new Promise((resolve12) => setTimeout(resolve12, 250));
12565
+ await new Promise((resolve15) => setTimeout(resolve15, 250));
11252
12566
  const state = await readExtensionChatState(h);
11253
12567
  if (state?.status === "waiting_approval") return true;
11254
12568
  const afterCount = getStateMessageCount(state);
@@ -12184,7 +13498,7 @@ async function handleResolveAction(h, args) {
12184
13498
 
12185
13499
  // src/commands/cdp-commands.ts
12186
13500
  import * as fs5 from "fs";
12187
- import * as path9 from "path";
13501
+ import * as path12 from "path";
12188
13502
  import * as os7 from "os";
12189
13503
  var KEY_TO_VK = {
12190
13504
  Backspace: 8,
@@ -12441,25 +13755,25 @@ function resolveSafePath(requestedPath) {
12441
13755
  const inputPath = rawPath || ".";
12442
13756
  const home = os7.homedir();
12443
13757
  if (inputPath.startsWith("~")) {
12444
- return path9.resolve(path9.join(home, inputPath.slice(1)));
13758
+ return path12.resolve(path12.join(home, inputPath.slice(1)));
12445
13759
  }
12446
13760
  if (process.platform === "win32") {
12447
13761
  const normalized = normalizeWindowsRequestedPath(inputPath);
12448
- if (path9.win32.isAbsolute(normalized)) {
12449
- return path9.win32.normalize(normalized);
13762
+ if (path12.win32.isAbsolute(normalized)) {
13763
+ return path12.win32.normalize(normalized);
12450
13764
  }
12451
- return path9.win32.resolve(normalized);
13765
+ return path12.win32.resolve(normalized);
12452
13766
  }
12453
- if (path9.isAbsolute(inputPath)) {
12454
- return path9.normalize(inputPath);
13767
+ if (path12.isAbsolute(inputPath)) {
13768
+ return path12.normalize(inputPath);
12455
13769
  }
12456
- return path9.resolve(inputPath);
13770
+ return path12.resolve(inputPath);
12457
13771
  }
12458
13772
  function listDirectoryEntriesSafe(dirPath) {
12459
13773
  const entries = fs5.readdirSync(dirPath, { withFileTypes: true });
12460
13774
  const files = [];
12461
13775
  for (const entry of entries) {
12462
- const entryPath = path9.join(dirPath, entry.name);
13776
+ const entryPath = path12.join(dirPath, entry.name);
12463
13777
  try {
12464
13778
  if (entry.isDirectory()) {
12465
13779
  files.push({ name: entry.name, type: "directory" });
@@ -12475,11 +13789,11 @@ function listDirectoryEntriesSafe(dirPath) {
12475
13789
  files.push({ name: entry.name, type: "file", size });
12476
13790
  continue;
12477
13791
  }
12478
- const stat = fs5.statSync(entryPath);
13792
+ const stat2 = fs5.statSync(entryPath);
12479
13793
  files.push({
12480
13794
  name: entry.name,
12481
- type: stat.isDirectory() ? "directory" : "file",
12482
- size: stat.isFile() ? stat.size : void 0
13795
+ type: stat2.isDirectory() ? "directory" : "file",
13796
+ size: stat2.isFile() ? stat2.size : void 0
12483
13797
  });
12484
13798
  } catch {
12485
13799
  }
@@ -12513,7 +13827,7 @@ async function handleFileRead(h, args) {
12513
13827
  async function handleFileWrite(h, args) {
12514
13828
  try {
12515
13829
  const filePath = resolveSafePath(args?.path);
12516
- fs5.mkdirSync(path9.dirname(filePath), { recursive: true });
13830
+ fs5.mkdirSync(path12.dirname(filePath), { recursive: true });
12517
13831
  fs5.writeFileSync(filePath, args?.content || "", "utf-8");
12518
13832
  return { success: true, path: filePath };
12519
13833
  } catch (e) {
@@ -12862,7 +14176,7 @@ async function executeProviderScript(h, args, scriptName) {
12862
14176
  const enterCount = cliCommand.enterCount || 1;
12863
14177
  await adapter.writeRaw(cliCommand.text + "\r");
12864
14178
  for (let i = 1; i < enterCount; i += 1) {
12865
- await new Promise((resolve12) => setTimeout(resolve12, 50));
14179
+ await new Promise((resolve15) => setTimeout(resolve15, 50));
12866
14180
  await adapter.writeRaw("\r");
12867
14181
  }
12868
14182
  }
@@ -13356,6 +14670,12 @@ var DaemonCommandHandler = class {
13356
14670
  this._currentRoute = this.resolveRoute(args);
13357
14671
  const startedAt = Date.now();
13358
14672
  this.logCommandStart(cmd, args);
14673
+ let result;
14674
+ if (isGitCommandName(cmd)) {
14675
+ result = await handleGitCommand(cmd, args, this._ctx.gitCommandServices);
14676
+ this.logCommandEnd(cmd, result, startedAt);
14677
+ return result;
14678
+ }
13359
14679
  const sessionScopedCommands = /* @__PURE__ */ new Set([
13360
14680
  "read_chat",
13361
14681
  "get_chat_debug_bundle",
@@ -13381,7 +14701,6 @@ var DaemonCommandHandler = class {
13381
14701
  this.logCommandEnd(cmd, result2, startedAt);
13382
14702
  return result2;
13383
14703
  }
13384
- let result;
13385
14704
  if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
13386
14705
  const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
13387
14706
  if (cdpCommands.includes(cmd)) {
@@ -13390,6 +14709,16 @@ var DaemonCommandHandler = class {
13390
14709
  return result;
13391
14710
  }
13392
14711
  }
14712
+ if (cmd === "send_chat" && this._ctx.onBeforeSendChat) {
14713
+ const sessionId = this._currentRoute.session?.sessionId;
14714
+ const workspace = sessionId ? this._ctx.instanceManager?.getInstance(sessionId)?.getState?.()?.workspace : void 0;
14715
+ if (workspace && sessionId) {
14716
+ try {
14717
+ this._ctx.onBeforeSendChat({ workspace, sessionId });
14718
+ } catch {
14719
+ }
14720
+ }
14721
+ }
13393
14722
  try {
13394
14723
  result = await this.dispatch(cmd, args);
13395
14724
  this.logCommandEnd(cmd, result, startedAt);
@@ -13532,7 +14861,7 @@ var DaemonCommandHandler = class {
13532
14861
  try {
13533
14862
  const http3 = await import("http");
13534
14863
  const postData = JSON.stringify(body);
13535
- const result = await new Promise((resolve12, reject) => {
14864
+ const result = await new Promise((resolve15, reject) => {
13536
14865
  const req = http3.request({
13537
14866
  hostname: "127.0.0.1",
13538
14867
  port: 19280,
@@ -13544,9 +14873,9 @@ var DaemonCommandHandler = class {
13544
14873
  res.on("data", (chunk) => data += chunk);
13545
14874
  res.on("end", () => {
13546
14875
  try {
13547
- resolve12(JSON.parse(data));
14876
+ resolve15(JSON.parse(data));
13548
14877
  } catch {
13549
- resolve12({ raw: data });
14878
+ resolve15({ raw: data });
13550
14879
  }
13551
14880
  });
13552
14881
  });
@@ -13564,15 +14893,15 @@ var DaemonCommandHandler = class {
13564
14893
  if (!providerType) return { success: false, error: "providerType required" };
13565
14894
  try {
13566
14895
  const http3 = await import("http");
13567
- const result = await new Promise((resolve12, reject) => {
14896
+ const result = await new Promise((resolve15, reject) => {
13568
14897
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
13569
14898
  let data = "";
13570
14899
  res.on("data", (chunk) => data += chunk);
13571
14900
  res.on("end", () => {
13572
14901
  try {
13573
- resolve12(JSON.parse(data));
14902
+ resolve15(JSON.parse(data));
13574
14903
  } catch {
13575
- resolve12({ raw: data });
14904
+ resolve15({ raw: data });
13576
14905
  }
13577
14906
  });
13578
14907
  }).on("error", reject);
@@ -13586,7 +14915,7 @@ var DaemonCommandHandler = class {
13586
14915
  try {
13587
14916
  const http3 = await import("http");
13588
14917
  const postData = JSON.stringify(args || {});
13589
- const result = await new Promise((resolve12, reject) => {
14918
+ const result = await new Promise((resolve15, reject) => {
13590
14919
  const req = http3.request({
13591
14920
  hostname: "127.0.0.1",
13592
14921
  port: 19280,
@@ -13598,9 +14927,9 @@ var DaemonCommandHandler = class {
13598
14927
  res.on("data", (chunk) => data += chunk);
13599
14928
  res.on("end", () => {
13600
14929
  try {
13601
- resolve12(JSON.parse(data));
14930
+ resolve15(JSON.parse(data));
13602
14931
  } catch {
13603
- resolve12({ raw: data });
14932
+ resolve15({ raw: data });
13604
14933
  }
13605
14934
  });
13606
14935
  });
@@ -13618,7 +14947,7 @@ var DaemonCommandHandler = class {
13618
14947
  // src/commands/cli-manager.ts
13619
14948
  init_provider_cli_adapter();
13620
14949
  import * as os13 from "os";
13621
- import * as path13 from "path";
14950
+ import * as path16 from "path";
13622
14951
  import * as crypto4 from "crypto";
13623
14952
  import { existsSync as existsSync10 } from "fs";
13624
14953
  import { execFileSync } from "child_process";
@@ -13628,7 +14957,7 @@ init_config();
13628
14957
  // src/providers/cli-provider-instance.ts
13629
14958
  init_contracts();
13630
14959
  import * as os12 from "os";
13631
- import * as path12 from "path";
14960
+ import * as path15 from "path";
13632
14961
  import * as crypto3 from "crypto";
13633
14962
  import * as fs6 from "fs";
13634
14963
  import { createRequire } from "module";
@@ -13688,7 +15017,7 @@ function buildIncrementalHistoryAppendMessages(previousMessages, currentMessages
13688
15017
  var CachedDatabaseSync = null;
13689
15018
  function getDatabaseSync() {
13690
15019
  if (CachedDatabaseSync) return CachedDatabaseSync;
13691
- const requireFn = typeof __require === "function" ? __require : createRequire(path12.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
15020
+ const requireFn = typeof __require === "function" ? __require : createRequire(path15.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
13692
15021
  const sqliteModule = requireFn(`node:${"sqlite"}`);
13693
15022
  CachedDatabaseSync = sqliteModule.DatabaseSync;
13694
15023
  if (!CachedDatabaseSync) {
@@ -13726,7 +15055,7 @@ async function waitForCliAdapterReady(adapter, options) {
13726
15055
  if (status === "stopped") {
13727
15056
  throw new Error("CLI runtime stopped before it became ready");
13728
15057
  }
13729
- await new Promise((resolve12) => setTimeout(resolve12, pollMs));
15058
+ await new Promise((resolve15) => setTimeout(resolve15, pollMs));
13730
15059
  }
13731
15060
  throw new Error(`CLI runtime did not become ready within ${timeoutMs}ms`);
13732
15061
  }
@@ -14077,7 +15406,7 @@ var CliProviderInstance = class {
14077
15406
  const enterCount = cliCommand.enterCount || 1;
14078
15407
  await this.adapter.writeRaw(cliCommand.text + "\r");
14079
15408
  for (let i = 1; i < enterCount; i += 1) {
14080
- await new Promise((resolve12) => setTimeout(resolve12, 50));
15409
+ await new Promise((resolve15) => setTimeout(resolve15, 50));
14081
15410
  await this.adapter.writeRaw("\r");
14082
15411
  }
14083
15412
  }
@@ -15196,13 +16525,13 @@ var AcpProviderInstance = class {
15196
16525
  }
15197
16526
  this.currentStatus = "waiting_approval";
15198
16527
  this.detectStatusTransition();
15199
- const approved = await new Promise((resolve12) => {
15200
- this.permissionResolvers.push(resolve12);
16528
+ const approved = await new Promise((resolve15) => {
16529
+ this.permissionResolvers.push(resolve15);
15201
16530
  setTimeout(() => {
15202
- const idx = this.permissionResolvers.indexOf(resolve12);
16531
+ const idx = this.permissionResolvers.indexOf(resolve15);
15203
16532
  if (idx >= 0) {
15204
16533
  this.permissionResolvers.splice(idx, 1);
15205
- resolve12(false);
16534
+ resolve15(false);
15206
16535
  }
15207
16536
  }, 3e5);
15208
16537
  });
@@ -15777,11 +17106,11 @@ function shouldRestoreHostedRuntime(record, managerTag) {
15777
17106
  // src/commands/cli-manager.ts
15778
17107
  function isExplicitCommand(command) {
15779
17108
  const trimmed = command.trim();
15780
- return path13.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
17109
+ return path16.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
15781
17110
  }
15782
17111
  function expandExecutable(command) {
15783
17112
  const trimmed = command.trim();
15784
- return trimmed.startsWith("~") ? path13.join(os13.homedir(), trimmed.slice(1)) : trimmed;
17113
+ return trimmed.startsWith("~") ? path16.join(os13.homedir(), trimmed.slice(1)) : trimmed;
15785
17114
  }
15786
17115
  function commandExists(command) {
15787
17116
  const trimmed = command.trim();
@@ -16062,7 +17391,7 @@ var DaemonCliManager = class {
16062
17391
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
16063
17392
  const trimmed = (workingDir || "").trim();
16064
17393
  if (!trimmed) throw new Error("working directory required");
16065
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os13.homedir()) : path13.resolve(trimmed);
17394
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os13.homedir()) : path16.resolve(trimmed);
16066
17395
  const normalizedType = this.providerLoader.resolveAlias(cliType);
16067
17396
  const rawProvider = this.providerLoader.getByAlias(cliType);
16068
17397
  const provider = rawProvider ? this.providerLoader.resolve(normalizedType) || rawProvider : void 0;
@@ -16563,11 +17892,11 @@ Run 'adhdev doctor' for detailed diagnostics.`
16563
17892
  import { execSync as execSync4, spawn as spawn2 } from "child_process";
16564
17893
  import * as net from "net";
16565
17894
  import * as os15 from "os";
16566
- import * as path15 from "path";
17895
+ import * as path18 from "path";
16567
17896
 
16568
17897
  // src/providers/provider-loader.ts
16569
17898
  import * as fs7 from "fs";
16570
- import * as path14 from "path";
17899
+ import * as path17 from "path";
16571
17900
  import * as os14 from "os";
16572
17901
  import * as chokidar from "chokidar";
16573
17902
  init_logger();
@@ -16630,6 +17959,7 @@ var KNOWN_PROVIDER_FIELDS = /* @__PURE__ */ new Set([
16630
17959
  "sendDelayMs",
16631
17960
  "sendKey",
16632
17961
  "submitStrategy",
17962
+ "requirePromptEchoBeforeSubmit",
16633
17963
  "timeouts",
16634
17964
  "disableUpstream"
16635
17965
  ]);
@@ -16832,7 +18162,7 @@ var ProviderLoader = class _ProviderLoader {
16832
18162
  try {
16833
18163
  if (!fs7.existsSync(candidate) || !fs7.statSync(candidate).isDirectory()) return false;
16834
18164
  return ["ide", "extension", "cli", "acp"].some(
16835
- (category) => fs7.existsSync(path14.join(candidate, category))
18165
+ (category) => fs7.existsSync(path17.join(candidate, category))
16836
18166
  );
16837
18167
  } catch {
16838
18168
  return false;
@@ -16840,20 +18170,20 @@ var ProviderLoader = class _ProviderLoader {
16840
18170
  }
16841
18171
  static hasProviderRootMarker(candidate) {
16842
18172
  try {
16843
- return fs7.existsSync(path14.join(candidate, _ProviderLoader.SIBLING_MARKER_FILE));
18173
+ return fs7.existsSync(path17.join(candidate, _ProviderLoader.SIBLING_MARKER_FILE));
16844
18174
  } catch {
16845
18175
  return false;
16846
18176
  }
16847
18177
  }
16848
18178
  detectDefaultUserDir() {
16849
- const fallback = path14.join(os14.homedir(), ".adhdev", "providers");
18179
+ const fallback = path17.join(os14.homedir(), ".adhdev", "providers");
16850
18180
  const envOptIn = process.env[_ProviderLoader.SIBLING_ENV_VAR] === "1";
16851
18181
  const visited = /* @__PURE__ */ new Set();
16852
18182
  for (const start of this.probeStarts) {
16853
- let current = path14.resolve(start);
18183
+ let current = path17.resolve(start);
16854
18184
  while (!visited.has(current)) {
16855
18185
  visited.add(current);
16856
- const siblingCandidate = path14.join(path14.dirname(current), _ProviderLoader.REPO_PROVIDER_DIRNAME);
18186
+ const siblingCandidate = path17.join(path17.dirname(current), _ProviderLoader.REPO_PROVIDER_DIRNAME);
16857
18187
  if (_ProviderLoader.looksLikeProviderRoot(siblingCandidate)) {
16858
18188
  const hasMarker = _ProviderLoader.hasProviderRootMarker(siblingCandidate);
16859
18189
  if (envOptIn || hasMarker) {
@@ -16875,7 +18205,7 @@ var ProviderLoader = class _ProviderLoader {
16875
18205
  return { path: siblingCandidate, source };
16876
18206
  }
16877
18207
  }
16878
- const parent = path14.dirname(current);
18208
+ const parent = path17.dirname(current);
16879
18209
  if (parent === current) break;
16880
18210
  current = parent;
16881
18211
  }
@@ -16885,11 +18215,11 @@ var ProviderLoader = class _ProviderLoader {
16885
18215
  constructor(options) {
16886
18216
  this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
16887
18217
  this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
16888
- this.defaultProvidersDir = path14.join(os14.homedir(), ".adhdev", "providers");
18218
+ this.defaultProvidersDir = path17.join(os14.homedir(), ".adhdev", "providers");
16889
18219
  const detected = this.detectDefaultUserDir();
16890
18220
  this.userDir = detected.path;
16891
18221
  this.userDirSource = detected.source;
16892
- this.upstreamDir = path14.join(this.defaultProvidersDir, ".upstream");
18222
+ this.upstreamDir = path17.join(this.defaultProvidersDir, ".upstream");
16893
18223
  this.disableUpstream = false;
16894
18224
  this.applySourceConfig({
16895
18225
  userDir: options?.userDir,
@@ -16948,7 +18278,7 @@ var ProviderLoader = class _ProviderLoader {
16948
18278
  this.userDir = detected.path;
16949
18279
  this.userDirSource = detected.source;
16950
18280
  }
16951
- this.upstreamDir = path14.join(this.defaultProvidersDir, ".upstream");
18281
+ this.upstreamDir = path17.join(this.defaultProvidersDir, ".upstream");
16952
18282
  this.disableUpstream = this.sourceMode === "no-upstream";
16953
18283
  if (this.explicitProviderDir) {
16954
18284
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
@@ -16962,7 +18292,7 @@ var ProviderLoader = class _ProviderLoader {
16962
18292
  * Canonical provider directory shape for a given root.
16963
18293
  */
16964
18294
  getProviderDir(root, category, type) {
16965
- return path14.join(root, category, type);
18295
+ return path17.join(root, category, type);
16966
18296
  }
16967
18297
  /**
16968
18298
  * Canonical user override directory for a provider.
@@ -16989,7 +18319,7 @@ var ProviderLoader = class _ProviderLoader {
16989
18319
  resolveProviderFile(type, ...segments) {
16990
18320
  const dir = this.findProviderDirInternal(type);
16991
18321
  if (!dir) return null;
16992
- return path14.join(dir, ...segments);
18322
+ return path17.join(dir, ...segments);
16993
18323
  }
16994
18324
  /**
16995
18325
  * Load all providers (3-tier priority)
@@ -17028,7 +18358,7 @@ var ProviderLoader = class _ProviderLoader {
17028
18358
  if (!fs7.existsSync(this.upstreamDir)) return false;
17029
18359
  try {
17030
18360
  return fs7.readdirSync(this.upstreamDir).some(
17031
- (d) => fs7.statSync(path14.join(this.upstreamDir, d)).isDirectory()
18361
+ (d) => fs7.statSync(path17.join(this.upstreamDir, d)).isDirectory()
17032
18362
  );
17033
18363
  } catch {
17034
18364
  return false;
@@ -17525,8 +18855,8 @@ var ProviderLoader = class _ProviderLoader {
17525
18855
  resolved._resolvedScriptDir = entry.scriptDir;
17526
18856
  resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
17527
18857
  if (providerDir) {
17528
- const fullDir = path14.join(providerDir, entry.scriptDir);
17529
- resolved._resolvedScriptsPath = fs7.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
18858
+ const fullDir = path17.join(providerDir, entry.scriptDir);
18859
+ resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
17530
18860
  }
17531
18861
  matched = true;
17532
18862
  }
@@ -17541,8 +18871,8 @@ var ProviderLoader = class _ProviderLoader {
17541
18871
  resolved._resolvedScriptDir = base.defaultScriptDir;
17542
18872
  resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
17543
18873
  if (providerDir) {
17544
- const fullDir = path14.join(providerDir, base.defaultScriptDir);
17545
- resolved._resolvedScriptsPath = fs7.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
18874
+ const fullDir = path17.join(providerDir, base.defaultScriptDir);
18875
+ resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
17546
18876
  }
17547
18877
  }
17548
18878
  resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
@@ -17559,8 +18889,8 @@ var ProviderLoader = class _ProviderLoader {
17559
18889
  resolved._resolvedScriptDir = dirOverride;
17560
18890
  resolved._resolvedScriptsSource = `versions:${range}`;
17561
18891
  if (providerDir) {
17562
- const fullDir = path14.join(providerDir, dirOverride);
17563
- resolved._resolvedScriptsPath = fs7.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
18892
+ const fullDir = path17.join(providerDir, dirOverride);
18893
+ resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
17564
18894
  }
17565
18895
  }
17566
18896
  } else if (override.scripts) {
@@ -17576,8 +18906,8 @@ var ProviderLoader = class _ProviderLoader {
17576
18906
  resolved._resolvedScriptDir = base.defaultScriptDir;
17577
18907
  resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
17578
18908
  if (providerDir) {
17579
- const fullDir = path14.join(providerDir, base.defaultScriptDir);
17580
- resolved._resolvedScriptsPath = fs7.existsSync(path14.join(fullDir, "scripts.js")) ? path14.join(fullDir, "scripts.js") : fullDir;
18909
+ const fullDir = path17.join(providerDir, base.defaultScriptDir);
18910
+ resolved._resolvedScriptsPath = fs7.existsSync(path17.join(fullDir, "scripts.js")) ? path17.join(fullDir, "scripts.js") : fullDir;
17581
18911
  }
17582
18912
  }
17583
18913
  }
@@ -17609,14 +18939,14 @@ var ProviderLoader = class _ProviderLoader {
17609
18939
  this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
17610
18940
  return null;
17611
18941
  }
17612
- const dir = path14.join(providerDir, scriptDir);
18942
+ const dir = path17.join(providerDir, scriptDir);
17613
18943
  if (!fs7.existsSync(dir)) {
17614
18944
  this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
17615
18945
  return null;
17616
18946
  }
17617
18947
  const cached = this.scriptsCache.get(dir);
17618
18948
  if (cached) return cached;
17619
- const scriptsJs = path14.join(dir, "scripts.js");
18949
+ const scriptsJs = path17.join(dir, "scripts.js");
17620
18950
  if (fs7.existsSync(scriptsJs)) {
17621
18951
  try {
17622
18952
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -17658,7 +18988,7 @@ var ProviderLoader = class _ProviderLoader {
17658
18988
  return;
17659
18989
  }
17660
18990
  if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
17661
- this.log(`File changed: ${path14.basename(filePath)}, reloading...`);
18991
+ this.log(`File changed: ${path17.basename(filePath)}, reloading...`);
17662
18992
  this.reload();
17663
18993
  }
17664
18994
  };
@@ -17713,7 +19043,7 @@ var ProviderLoader = class _ProviderLoader {
17713
19043
  }
17714
19044
  const https = __require("https");
17715
19045
  const { execSync: execSync7 } = __require("child_process");
17716
- const metaPath = path14.join(this.upstreamDir, _ProviderLoader.META_FILE);
19046
+ const metaPath = path17.join(this.upstreamDir, _ProviderLoader.META_FILE);
17717
19047
  let prevEtag = "";
17718
19048
  let prevTimestamp = 0;
17719
19049
  try {
@@ -17730,7 +19060,7 @@ var ProviderLoader = class _ProviderLoader {
17730
19060
  return { updated: false };
17731
19061
  }
17732
19062
  try {
17733
- const etag = await new Promise((resolve12, reject) => {
19063
+ const etag = await new Promise((resolve15, reject) => {
17734
19064
  const options = {
17735
19065
  method: "HEAD",
17736
19066
  hostname: "github.com",
@@ -17748,7 +19078,7 @@ var ProviderLoader = class _ProviderLoader {
17748
19078
  headers: { "User-Agent": "adhdev-launcher" },
17749
19079
  timeout: 1e4
17750
19080
  }, (res2) => {
17751
- resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
19081
+ resolve15(res2.headers.etag || res2.headers["last-modified"] || "");
17752
19082
  });
17753
19083
  req2.on("error", reject);
17754
19084
  req2.on("timeout", () => {
@@ -17757,7 +19087,7 @@ var ProviderLoader = class _ProviderLoader {
17757
19087
  });
17758
19088
  req2.end();
17759
19089
  } else {
17760
- resolve12(res.headers.etag || res.headers["last-modified"] || "");
19090
+ resolve15(res.headers.etag || res.headers["last-modified"] || "");
17761
19091
  }
17762
19092
  });
17763
19093
  req.on("error", reject);
@@ -17773,17 +19103,17 @@ var ProviderLoader = class _ProviderLoader {
17773
19103
  return { updated: false };
17774
19104
  }
17775
19105
  this.log("Downloading latest providers from GitHub...");
17776
- const tmpTar = path14.join(os14.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
17777
- const tmpExtract = path14.join(os14.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
19106
+ const tmpTar = path17.join(os14.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
19107
+ const tmpExtract = path17.join(os14.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
17778
19108
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
17779
19109
  fs7.mkdirSync(tmpExtract, { recursive: true });
17780
19110
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
17781
19111
  const extracted = fs7.readdirSync(tmpExtract);
17782
19112
  const rootDir = extracted.find(
17783
- (d) => fs7.statSync(path14.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
19113
+ (d) => fs7.statSync(path17.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
17784
19114
  );
17785
19115
  if (!rootDir) throw new Error("Unexpected tarball structure");
17786
- const sourceDir = path14.join(tmpExtract, rootDir);
19116
+ const sourceDir = path17.join(tmpExtract, rootDir);
17787
19117
  const backupDir = this.upstreamDir + ".bak";
17788
19118
  if (fs7.existsSync(this.upstreamDir)) {
17789
19119
  if (fs7.existsSync(backupDir)) fs7.rmSync(backupDir, { recursive: true, force: true });
@@ -17821,7 +19151,7 @@ var ProviderLoader = class _ProviderLoader {
17821
19151
  downloadFile(url, destPath) {
17822
19152
  const https = __require("https");
17823
19153
  const http3 = __require("http");
17824
- return new Promise((resolve12, reject) => {
19154
+ return new Promise((resolve15, reject) => {
17825
19155
  const doRequest = (reqUrl, redirectCount = 0) => {
17826
19156
  if (redirectCount > 5) {
17827
19157
  reject(new Error("Too many redirects"));
@@ -17841,7 +19171,7 @@ var ProviderLoader = class _ProviderLoader {
17841
19171
  res.pipe(ws);
17842
19172
  ws.on("finish", () => {
17843
19173
  ws.close();
17844
- resolve12();
19174
+ resolve15();
17845
19175
  });
17846
19176
  ws.on("error", reject);
17847
19177
  });
@@ -17858,8 +19188,8 @@ var ProviderLoader = class _ProviderLoader {
17858
19188
  copyDirRecursive(src, dest) {
17859
19189
  fs7.mkdirSync(dest, { recursive: true });
17860
19190
  for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
17861
- const srcPath = path14.join(src, entry.name);
17862
- const destPath = path14.join(dest, entry.name);
19191
+ const srcPath = path17.join(src, entry.name);
19192
+ const destPath = path17.join(dest, entry.name);
17863
19193
  if (entry.isDirectory()) {
17864
19194
  this.copyDirRecursive(srcPath, destPath);
17865
19195
  } else {
@@ -17870,7 +19200,7 @@ var ProviderLoader = class _ProviderLoader {
17870
19200
  /** .meta.json save */
17871
19201
  writeMeta(metaPath, etag, timestamp) {
17872
19202
  try {
17873
- fs7.mkdirSync(path14.dirname(metaPath), { recursive: true });
19203
+ fs7.mkdirSync(path17.dirname(metaPath), { recursive: true });
17874
19204
  fs7.writeFileSync(metaPath, JSON.stringify({
17875
19205
  etag,
17876
19206
  timestamp,
@@ -17887,7 +19217,7 @@ var ProviderLoader = class _ProviderLoader {
17887
19217
  const scan = (d) => {
17888
19218
  try {
17889
19219
  for (const entry of fs7.readdirSync(d, { withFileTypes: true })) {
17890
- if (entry.isDirectory()) scan(path14.join(d, entry.name));
19220
+ if (entry.isDirectory()) scan(path17.join(d, entry.name));
17891
19221
  else if (entry.name === "provider.json") count++;
17892
19222
  }
17893
19223
  } catch {
@@ -18115,17 +19445,17 @@ var ProviderLoader = class _ProviderLoader {
18115
19445
  for (const root of searchRoots) {
18116
19446
  if (!fs7.existsSync(root)) continue;
18117
19447
  const candidate = this.getProviderDir(root, cat, type);
18118
- if (fs7.existsSync(path14.join(candidate, "provider.json"))) return candidate;
18119
- const catDir = path14.join(root, cat);
19448
+ if (fs7.existsSync(path17.join(candidate, "provider.json"))) return candidate;
19449
+ const catDir = path17.join(root, cat);
18120
19450
  if (fs7.existsSync(catDir)) {
18121
19451
  try {
18122
19452
  for (const entry of fs7.readdirSync(catDir, { withFileTypes: true })) {
18123
19453
  if (!entry.isDirectory()) continue;
18124
- const jsonPath = path14.join(catDir, entry.name, "provider.json");
19454
+ const jsonPath = path17.join(catDir, entry.name, "provider.json");
18125
19455
  if (fs7.existsSync(jsonPath)) {
18126
19456
  try {
18127
19457
  const data = JSON.parse(fs7.readFileSync(jsonPath, "utf-8"));
18128
- if (data.type === type) return path14.join(catDir, entry.name);
19458
+ if (data.type === type) return path17.join(catDir, entry.name);
18129
19459
  } catch {
18130
19460
  }
18131
19461
  }
@@ -18142,7 +19472,7 @@ var ProviderLoader = class _ProviderLoader {
18142
19472
  * (template substitution is NOT applied here — scripts.js handles that)
18143
19473
  */
18144
19474
  buildScriptWrappersFromDir(dir) {
18145
- const scriptsJs = path14.join(dir, "scripts.js");
19475
+ const scriptsJs = path17.join(dir, "scripts.js");
18146
19476
  if (fs7.existsSync(scriptsJs)) {
18147
19477
  try {
18148
19478
  delete __require.cache[__require.resolve(scriptsJs)];
@@ -18156,7 +19486,7 @@ var ProviderLoader = class _ProviderLoader {
18156
19486
  for (const file of fs7.readdirSync(dir)) {
18157
19487
  if (!file.endsWith(".js")) continue;
18158
19488
  const scriptName = toCamel(file.replace(".js", ""));
18159
- const filePath = path14.join(dir, file);
19489
+ const filePath = path17.join(dir, file);
18160
19490
  result[scriptName] = (...args) => {
18161
19491
  try {
18162
19492
  let content = fs7.readFileSync(filePath, "utf-8");
@@ -18216,7 +19546,7 @@ var ProviderLoader = class _ProviderLoader {
18216
19546
  }
18217
19547
  const hasJson = entries.some((e) => e.name === "provider.json");
18218
19548
  if (hasJson) {
18219
- const jsonPath = path14.join(d, "provider.json");
19549
+ const jsonPath = path17.join(d, "provider.json");
18220
19550
  try {
18221
19551
  const raw = fs7.readFileSync(jsonPath, "utf-8");
18222
19552
  const mod = JSON.parse(raw);
@@ -18237,7 +19567,7 @@ var ProviderLoader = class _ProviderLoader {
18237
19567
  this.log(`\u26A0 Invalid provider at ${jsonPath}: ${validation.errors.join("; ")}`);
18238
19568
  } else {
18239
19569
  const hasCompatibility = Array.isArray(normalizedProvider.compatibility);
18240
- const scriptsPath = path14.join(d, "scripts.js");
19570
+ const scriptsPath = path17.join(d, "scripts.js");
18241
19571
  if (!hasCompatibility && fs7.existsSync(scriptsPath)) {
18242
19572
  try {
18243
19573
  delete __require.cache[__require.resolve(scriptsPath)];
@@ -18263,7 +19593,7 @@ var ProviderLoader = class _ProviderLoader {
18263
19593
  if (!entry.isDirectory()) continue;
18264
19594
  if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
18265
19595
  if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
18266
- scan(path14.join(d, entry.name));
19596
+ scan(path17.join(d, entry.name));
18267
19597
  }
18268
19598
  }
18269
19599
  };
@@ -18298,9 +19628,9 @@ var ProviderLoader = class _ProviderLoader {
18298
19628
  }
18299
19629
  }
18300
19630
  compareVersions(a, b) {
18301
- const normalize3 = (v) => v.split(/[-_+]/)[0].split(".").map((x) => parseInt(x, 10) || 0);
18302
- const pa = normalize3(a);
18303
- const pb = normalize3(b);
19631
+ const normalize4 = (v) => v.split(/[-_+]/)[0].split(".").map((x) => parseInt(x, 10) || 0);
19632
+ const pa = normalize4(a);
19633
+ const pb = normalize4(b);
18304
19634
  for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
18305
19635
  const va = pa[i] || 0;
18306
19636
  const vb = pb[i] || 0;
@@ -18420,17 +19750,17 @@ async function findFreePort(ports) {
18420
19750
  throw new Error("No free port found");
18421
19751
  }
18422
19752
  function checkPortFree(port) {
18423
- return new Promise((resolve12) => {
19753
+ return new Promise((resolve15) => {
18424
19754
  const server = net.createServer();
18425
19755
  server.unref();
18426
- server.on("error", () => resolve12(false));
19756
+ server.on("error", () => resolve15(false));
18427
19757
  server.listen(port, "127.0.0.1", () => {
18428
- server.close(() => resolve12(true));
19758
+ server.close(() => resolve15(true));
18429
19759
  });
18430
19760
  });
18431
19761
  }
18432
19762
  async function isCdpActive(port) {
18433
- return new Promise((resolve12) => {
19763
+ return new Promise((resolve15) => {
18434
19764
  const req = __require("http").get(`http://127.0.0.1:${port}/json/version`, {
18435
19765
  timeout: 2e3
18436
19766
  }, (res) => {
@@ -18439,16 +19769,16 @@ async function isCdpActive(port) {
18439
19769
  res.on("end", () => {
18440
19770
  try {
18441
19771
  const info = JSON.parse(data);
18442
- resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
19772
+ resolve15(!!info["WebKit-Version"] || !!info["Browser"]);
18443
19773
  } catch {
18444
- resolve12(false);
19774
+ resolve15(false);
18445
19775
  }
18446
19776
  });
18447
19777
  });
18448
- req.on("error", () => resolve12(false));
19778
+ req.on("error", () => resolve15(false));
18449
19779
  req.on("timeout", () => {
18450
19780
  req.destroy();
18451
- resolve12(false);
19781
+ resolve15(false);
18452
19782
  });
18453
19783
  });
18454
19784
  }
@@ -18588,8 +19918,8 @@ function detectCurrentWorkspace(ideId) {
18588
19918
  const appNameMap = getMacAppIdentifiers();
18589
19919
  const appName = appNameMap[ideId];
18590
19920
  if (appName) {
18591
- const storagePath = path15.join(
18592
- process.env.APPDATA || path15.join(os15.homedir(), "AppData", "Roaming"),
19921
+ const storagePath = path18.join(
19922
+ process.env.APPDATA || path18.join(os15.homedir(), "AppData", "Roaming"),
18593
19923
  appName,
18594
19924
  "storage.json"
18595
19925
  );
@@ -18767,9 +20097,9 @@ init_logger();
18767
20097
 
18768
20098
  // src/logging/command-log.ts
18769
20099
  import * as fs8 from "fs";
18770
- import * as path16 from "path";
20100
+ import * as path19 from "path";
18771
20101
  import * as os16 from "os";
18772
- var LOG_DIR2 = process.platform === "win32" ? path16.join(process.env.LOCALAPPDATA || process.env.APPDATA || path16.join(os16.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path16.join(os16.homedir(), "Library", "Logs", "adhdev") : path16.join(os16.homedir(), ".local", "share", "adhdev", "logs");
20102
+ var LOG_DIR2 = process.platform === "win32" ? path19.join(process.env.LOCALAPPDATA || process.env.APPDATA || path19.join(os16.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path19.join(os16.homedir(), "Library", "Logs", "adhdev") : path19.join(os16.homedir(), ".local", "share", "adhdev", "logs");
18773
20103
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
18774
20104
  var MAX_DAYS = 7;
18775
20105
  try {
@@ -18807,13 +20137,13 @@ function getDateStr2() {
18807
20137
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
18808
20138
  }
18809
20139
  var currentDate2 = getDateStr2();
18810
- var currentFile = path16.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
20140
+ var currentFile = path19.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
18811
20141
  var writeCount2 = 0;
18812
20142
  function checkRotation() {
18813
20143
  const today = getDateStr2();
18814
20144
  if (today !== currentDate2) {
18815
20145
  currentDate2 = today;
18816
- currentFile = path16.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
20146
+ currentFile = path19.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
18817
20147
  cleanOldFiles();
18818
20148
  }
18819
20149
  }
@@ -18827,7 +20157,7 @@ function cleanOldFiles() {
18827
20157
  const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
18828
20158
  if (dateMatch && dateMatch[1] < cutoffStr) {
18829
20159
  try {
18830
- fs8.unlinkSync(path16.join(LOG_DIR2, file));
20160
+ fs8.unlinkSync(path19.join(LOG_DIR2, file));
18831
20161
  } catch {
18832
20162
  }
18833
20163
  }
@@ -18837,8 +20167,8 @@ function cleanOldFiles() {
18837
20167
  }
18838
20168
  function checkSize() {
18839
20169
  try {
18840
- const stat = fs8.statSync(currentFile);
18841
- if (stat.size > MAX_FILE_SIZE) {
20170
+ const stat2 = fs8.statSync(currentFile);
20171
+ if (stat2.size > MAX_FILE_SIZE) {
18842
20172
  const backup = currentFile.replace(".jsonl", ".1.jsonl");
18843
20173
  try {
18844
20174
  fs8.unlinkSync(backup);
@@ -19124,12 +20454,18 @@ function buildStatusSnapshot(options) {
19124
20454
  const unreadSourceSessions = buildSessionEntries(
19125
20455
  options.allStates,
19126
20456
  options.cdpManagers,
19127
- { profile: "full" }
20457
+ {
20458
+ profile: "full",
20459
+ getGitSummaryForWorkspace: options.getGitSummaryForWorkspace
20460
+ }
19128
20461
  );
19129
20462
  const sessions = profile === "full" ? unreadSourceSessions : profile === "live" ? unreadSourceSessions.map(projectLiveSessionFromFull) : buildSessionEntries(
19130
20463
  options.allStates,
19131
20464
  options.cdpManagers,
19132
- { profile }
20465
+ {
20466
+ profile,
20467
+ getGitSummaryForWorkspace: options.getGitSummaryForWorkspace
20468
+ }
19133
20469
  );
19134
20470
  const sessionsById = new Map(sessions.map((session) => [session.id, session]));
19135
20471
  for (const sourceSession of unreadSourceSessions) {
@@ -19223,13 +20559,13 @@ import { execFileSync as execFileSync2 } from "child_process";
19223
20559
  import { spawn as spawn3 } from "child_process";
19224
20560
  import * as fs9 from "fs";
19225
20561
  import * as os18 from "os";
19226
- import * as path17 from "path";
20562
+ import * as path20 from "path";
19227
20563
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
19228
20564
  function getUpgradeLogPath() {
19229
20565
  const home = os18.homedir();
19230
- const dir = path17.join(home, ".adhdev");
20566
+ const dir = path20.join(home, ".adhdev");
19231
20567
  fs9.mkdirSync(dir, { recursive: true });
19232
- return path17.join(dir, "daemon-upgrade.log");
20568
+ return path20.join(dir, "daemon-upgrade.log");
19233
20569
  }
19234
20570
  function appendUpgradeLog(message) {
19235
20571
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
@@ -19240,14 +20576,14 @@ function appendUpgradeLog(message) {
19240
20576
  }
19241
20577
  }
19242
20578
  function resolveSiblingNpmInvocation(nodeExecutable, platform10 = process.platform) {
19243
- const binDir = path17.dirname(nodeExecutable);
20579
+ const binDir = path20.dirname(nodeExecutable);
19244
20580
  if (platform10 === "win32") {
19245
- const npmCliPath = path17.join(binDir, "node_modules", "npm", "bin", "npm-cli.js");
20581
+ const npmCliPath = path20.join(binDir, "node_modules", "npm", "bin", "npm-cli.js");
19246
20582
  if (fs9.existsSync(npmCliPath)) {
19247
20583
  return { executable: nodeExecutable, argsPrefix: [npmCliPath], execOptions: getNpmExecOptions(platform10) };
19248
20584
  }
19249
20585
  for (const candidate of ["npm.exe", "npm"]) {
19250
- const candidatePath = path17.join(binDir, candidate);
20586
+ const candidatePath = path20.join(binDir, candidate);
19251
20587
  if (fs9.existsSync(candidatePath)) {
19252
20588
  return { executable: candidatePath, argsPrefix: [], execOptions: getNpmExecOptions(platform10) };
19253
20589
  }
@@ -19255,7 +20591,7 @@ function resolveSiblingNpmInvocation(nodeExecutable, platform10 = process.platfo
19255
20591
  return { executable: nodeExecutable, argsPrefix: [npmCliPath], execOptions: getNpmExecOptions(platform10) };
19256
20592
  }
19257
20593
  for (const candidate of ["npm"]) {
19258
- const candidatePath = path17.join(binDir, candidate);
20594
+ const candidatePath = path20.join(binDir, candidate);
19259
20595
  if (fs9.existsSync(candidatePath)) {
19260
20596
  return { executable: candidatePath, argsPrefix: [], execOptions: getNpmExecOptions(platform10) };
19261
20597
  }
@@ -19272,13 +20608,13 @@ function findCurrentPackageRoot(currentCliPath, packageName) {
19272
20608
  let currentDir = resolvedPath;
19273
20609
  try {
19274
20610
  if (fs9.statSync(resolvedPath).isFile()) {
19275
- currentDir = path17.dirname(resolvedPath);
20611
+ currentDir = path20.dirname(resolvedPath);
19276
20612
  }
19277
20613
  } catch {
19278
- currentDir = path17.dirname(resolvedPath);
20614
+ currentDir = path20.dirname(resolvedPath);
19279
20615
  }
19280
20616
  while (true) {
19281
- const packageJsonPath = path17.join(currentDir, "package.json");
20617
+ const packageJsonPath = path20.join(currentDir, "package.json");
19282
20618
  try {
19283
20619
  if (fs9.existsSync(packageJsonPath)) {
19284
20620
  const parsed = JSON.parse(fs9.readFileSync(packageJsonPath, "utf8"));
@@ -19289,7 +20625,7 @@ function findCurrentPackageRoot(currentCliPath, packageName) {
19289
20625
  }
19290
20626
  } catch {
19291
20627
  }
19292
- const parentDir = path17.dirname(currentDir);
20628
+ const parentDir = path20.dirname(currentDir);
19293
20629
  if (parentDir === currentDir) {
19294
20630
  return null;
19295
20631
  }
@@ -19297,13 +20633,13 @@ function findCurrentPackageRoot(currentCliPath, packageName) {
19297
20633
  }
19298
20634
  }
19299
20635
  function resolveInstallPrefixFromPackageRoot(packageRoot, packageName) {
19300
- const nodeModulesDir = packageName.startsWith("@") ? path17.dirname(path17.dirname(packageRoot)) : path17.dirname(packageRoot);
19301
- if (path17.basename(nodeModulesDir) !== "node_modules") {
20636
+ const nodeModulesDir = packageName.startsWith("@") ? path20.dirname(path20.dirname(packageRoot)) : path20.dirname(packageRoot);
20637
+ if (path20.basename(nodeModulesDir) !== "node_modules") {
19302
20638
  return null;
19303
20639
  }
19304
- const maybeLibDir = path17.dirname(nodeModulesDir);
19305
- if (path17.basename(maybeLibDir) === "lib") {
19306
- return path17.dirname(maybeLibDir);
20640
+ const maybeLibDir = path20.dirname(nodeModulesDir);
20641
+ if (path20.basename(maybeLibDir) === "lib") {
20642
+ return path20.dirname(maybeLibDir);
19307
20643
  }
19308
20644
  return maybeLibDir;
19309
20645
  }
@@ -19411,14 +20747,14 @@ async function waitForPidExit(pid, timeoutMs) {
19411
20747
  while (Date.now() - start < timeoutMs) {
19412
20748
  try {
19413
20749
  process.kill(pid, 0);
19414
- await new Promise((resolve12) => setTimeout(resolve12, 250));
20750
+ await new Promise((resolve15) => setTimeout(resolve15, 250));
19415
20751
  } catch {
19416
20752
  return;
19417
20753
  }
19418
20754
  }
19419
20755
  }
19420
20756
  function stopSessionHostProcesses(appName) {
19421
- const pidFile = path17.join(os18.homedir(), ".adhdev", `${appName}-session-host.pid`);
20757
+ const pidFile = path20.join(os18.homedir(), ".adhdev", `${appName}-session-host.pid`);
19422
20758
  try {
19423
20759
  if (fs9.existsSync(pidFile)) {
19424
20760
  const pid = Number.parseInt(fs9.readFileSync(pidFile, "utf8").trim(), 10);
@@ -19435,7 +20771,7 @@ function stopSessionHostProcesses(appName) {
19435
20771
  }
19436
20772
  }
19437
20773
  function removeDaemonPidFile() {
19438
- const pidFile = path17.join(os18.homedir(), ".adhdev", "daemon.pid");
20774
+ const pidFile = path20.join(os18.homedir(), ".adhdev", "daemon.pid");
19439
20775
  try {
19440
20776
  fs9.unlinkSync(pidFile);
19441
20777
  } catch {
@@ -19446,7 +20782,7 @@ function cleanupStaleGlobalInstallDirs(pkgName, surface) {
19446
20782
  const npmRoot = String(execNpmCommandSync(["root", "-g", ...prefixArgs], { encoding: "utf8" }, surface)).trim();
19447
20783
  if (!npmRoot) return;
19448
20784
  const npmPrefix = surface.installPrefix || String(execNpmCommandSync(["prefix", "-g", ...prefixArgs], { encoding: "utf8" }, surface)).trim();
19449
- const binDir = process.platform === "win32" ? npmPrefix : path17.join(npmPrefix, "bin");
20785
+ const binDir = process.platform === "win32" ? npmPrefix : path20.join(npmPrefix, "bin");
19450
20786
  const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
19451
20787
  const binNames = /* @__PURE__ */ new Set([packageBaseName]);
19452
20788
  if (pkgName === "@adhdev/daemon-standalone") {
@@ -19454,25 +20790,25 @@ function cleanupStaleGlobalInstallDirs(pkgName, surface) {
19454
20790
  }
19455
20791
  if (pkgName.startsWith("@")) {
19456
20792
  const [scope, name] = pkgName.split("/");
19457
- const scopeDir = path17.join(npmRoot, scope);
20793
+ const scopeDir = path20.join(npmRoot, scope);
19458
20794
  if (!fs9.existsSync(scopeDir)) return;
19459
20795
  for (const entry of fs9.readdirSync(scopeDir)) {
19460
20796
  if (!entry.startsWith(`.${name}-`)) continue;
19461
- fs9.rmSync(path17.join(scopeDir, entry), { recursive: true, force: true });
19462
- appendUpgradeLog(`Removed stale scoped staging dir: ${path17.join(scopeDir, entry)}`);
20797
+ fs9.rmSync(path20.join(scopeDir, entry), { recursive: true, force: true });
20798
+ appendUpgradeLog(`Removed stale scoped staging dir: ${path20.join(scopeDir, entry)}`);
19463
20799
  }
19464
20800
  } else {
19465
20801
  for (const entry of fs9.readdirSync(npmRoot)) {
19466
20802
  if (!entry.startsWith(`.${pkgName}-`)) continue;
19467
- fs9.rmSync(path17.join(npmRoot, entry), { recursive: true, force: true });
19468
- appendUpgradeLog(`Removed stale staging dir: ${path17.join(npmRoot, entry)}`);
20803
+ fs9.rmSync(path20.join(npmRoot, entry), { recursive: true, force: true });
20804
+ appendUpgradeLog(`Removed stale staging dir: ${path20.join(npmRoot, entry)}`);
19469
20805
  }
19470
20806
  }
19471
20807
  if (fs9.existsSync(binDir)) {
19472
20808
  for (const entry of fs9.readdirSync(binDir)) {
19473
20809
  if (!Array.from(binNames).some((name) => entry.startsWith(`.${name}-`))) continue;
19474
- fs9.rmSync(path17.join(binDir, entry), { recursive: true, force: true });
19475
- appendUpgradeLog(`Removed stale bin staging entry: ${path17.join(binDir, entry)}`);
20810
+ fs9.rmSync(path20.join(binDir, entry), { recursive: true, force: true });
20811
+ appendUpgradeLog(`Removed stale bin staging entry: ${path20.join(binDir, entry)}`);
19476
20812
  }
19477
20813
  }
19478
20814
  }
@@ -19522,7 +20858,7 @@ async function runDaemonUpgradeHelper(payload) {
19522
20858
  appendUpgradeLog(installOutput.trim());
19523
20859
  }
19524
20860
  if (process.platform === "win32") {
19525
- await new Promise((resolve12) => setTimeout(resolve12, 500));
20861
+ await new Promise((resolve15) => setTimeout(resolve15, 500));
19526
20862
  cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
19527
20863
  appendUpgradeLog("Post-install staging cleanup complete");
19528
20864
  }
@@ -20916,7 +22252,7 @@ var ProviderStreamAdapter = class {
20916
22252
  const beforeCount = this.messageCount(before);
20917
22253
  const beforeSignature = this.lastMessageSignature(before);
20918
22254
  for (let attempt = 0; attempt < 12; attempt += 1) {
20919
- await new Promise((resolve12) => setTimeout(resolve12, 250));
22255
+ await new Promise((resolve15) => setTimeout(resolve15, 250));
20920
22256
  let state;
20921
22257
  try {
20922
22258
  state = await this.readChat(evaluate);
@@ -20938,7 +22274,7 @@ var ProviderStreamAdapter = class {
20938
22274
  if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
20939
22275
  return first;
20940
22276
  }
20941
- await new Promise((resolve12) => setTimeout(resolve12, 150));
22277
+ await new Promise((resolve15) => setTimeout(resolve15, 150));
20942
22278
  const second = await this.readChat(evaluate);
20943
22279
  return this.messageCount(second) >= this.messageCount(first) ? second : first;
20944
22280
  }
@@ -21089,7 +22425,7 @@ var ProviderStreamAdapter = class {
21089
22425
  if (typeof data.error === "string" && data.error.trim()) return false;
21090
22426
  }
21091
22427
  for (let attempt = 0; attempt < 6; attempt += 1) {
21092
- await new Promise((resolve12) => setTimeout(resolve12, 250));
22428
+ await new Promise((resolve15) => setTimeout(resolve15, 250));
21093
22429
  const state = await this.readChat(evaluate);
21094
22430
  const title = this.getStateTitle(state);
21095
22431
  if (this.titlesMatch(title, sessionId)) return true;
@@ -21950,11 +23286,11 @@ init_chat_message_normalization();
21950
23286
 
21951
23287
  // src/providers/version-archive.ts
21952
23288
  import * as fs11 from "fs";
21953
- import * as path18 from "path";
23289
+ import * as path21 from "path";
21954
23290
  import * as os19 from "os";
21955
23291
  import { execSync as execSync5 } from "child_process";
21956
23292
  import { platform as platform8 } from "os";
21957
- var ARCHIVE_PATH = path18.join(os19.homedir(), ".adhdev", "version-history.json");
23293
+ var ARCHIVE_PATH = path21.join(os19.homedir(), ".adhdev", "version-history.json");
21958
23294
  var MAX_ENTRIES_PER_PROVIDER = 20;
21959
23295
  var VersionArchive = class {
21960
23296
  history = {};
@@ -22001,7 +23337,7 @@ var VersionArchive = class {
22001
23337
  }
22002
23338
  save() {
22003
23339
  try {
22004
- fs11.mkdirSync(path18.dirname(ARCHIVE_PATH), { recursive: true });
23340
+ fs11.mkdirSync(path21.dirname(ARCHIVE_PATH), { recursive: true });
22005
23341
  fs11.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
22006
23342
  } catch {
22007
23343
  }
@@ -22058,7 +23394,7 @@ function checkPathExists2(paths) {
22058
23394
  for (const p of paths) {
22059
23395
  if (p.includes("*")) {
22060
23396
  const home = os19.homedir();
22061
- const resolved = p.replace(/\*/g, home.split(path18.sep).pop() || "");
23397
+ const resolved = p.replace(/\*/g, home.split(path21.sep).pop() || "");
22062
23398
  if (fs11.existsSync(resolved)) return resolved;
22063
23399
  } else {
22064
23400
  if (fs11.existsSync(p)) return p;
@@ -22068,7 +23404,7 @@ function checkPathExists2(paths) {
22068
23404
  }
22069
23405
  function getMacAppVersion(appPath) {
22070
23406
  if (platform8() !== "darwin" || !appPath.endsWith(".app")) return null;
22071
- const plistPath = path18.join(appPath, "Contents", "Info.plist");
23407
+ const plistPath = path21.join(appPath, "Contents", "Info.plist");
22072
23408
  if (!fs11.existsSync(plistPath)) return null;
22073
23409
  const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
22074
23410
  return raw || null;
@@ -22094,7 +23430,7 @@ async function detectAllVersions(loader, archive) {
22094
23430
  const cliBin = provider.cli ? findBinary2(provider.cli) : null;
22095
23431
  let resolvedBin = cliBin;
22096
23432
  if (!resolvedBin && appPath && currentOs === "darwin") {
22097
- const bundled = path18.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
23433
+ const bundled = path21.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
22098
23434
  if (provider.cli && fs11.existsSync(bundled)) resolvedBin = bundled;
22099
23435
  }
22100
23436
  info.installed = !!(appPath || resolvedBin);
@@ -22135,7 +23471,7 @@ async function detectAllVersions(loader, archive) {
22135
23471
  // src/daemon/dev-server.ts
22136
23472
  import * as http2 from "http";
22137
23473
  import * as fs15 from "fs";
22138
- import * as path22 from "path";
23474
+ import * as path25 from "path";
22139
23475
  init_config();
22140
23476
 
22141
23477
  // src/daemon/scaffold-template.ts
@@ -22487,7 +23823,7 @@ init_logger();
22487
23823
  // src/daemon/dev-cdp-handlers.ts
22488
23824
  init_logger();
22489
23825
  import * as fs12 from "fs";
22490
- import * as path19 from "path";
23826
+ import * as path22 from "path";
22491
23827
  async function handleCdpEvaluate(ctx, req, res) {
22492
23828
  const body = await ctx.readBody(req);
22493
23829
  const { expression, timeout, ideType } = body;
@@ -22665,17 +24001,17 @@ async function handleScriptHints(ctx, type, _req, res) {
22665
24001
  return;
22666
24002
  }
22667
24003
  let scriptsPath = "";
22668
- const directScripts = path19.join(dir, "scripts.js");
24004
+ const directScripts = path22.join(dir, "scripts.js");
22669
24005
  if (fs12.existsSync(directScripts)) {
22670
24006
  scriptsPath = directScripts;
22671
24007
  } else {
22672
- const scriptsDir = path19.join(dir, "scripts");
24008
+ const scriptsDir = path22.join(dir, "scripts");
22673
24009
  if (fs12.existsSync(scriptsDir)) {
22674
24010
  const versions = fs12.readdirSync(scriptsDir).filter((d) => {
22675
- return fs12.statSync(path19.join(scriptsDir, d)).isDirectory();
24011
+ return fs12.statSync(path22.join(scriptsDir, d)).isDirectory();
22676
24012
  }).sort().reverse();
22677
24013
  for (const ver of versions) {
22678
- const p = path19.join(scriptsDir, ver, "scripts.js");
24014
+ const p = path22.join(scriptsDir, ver, "scripts.js");
22679
24015
  if (fs12.existsSync(p)) {
22680
24016
  scriptsPath = p;
22681
24017
  break;
@@ -23504,7 +24840,7 @@ async function handleDomContext(ctx, type, req, res) {
23504
24840
 
23505
24841
  // src/daemon/dev-cli-debug.ts
23506
24842
  import * as fs13 from "fs";
23507
- import * as path20 from "path";
24843
+ import * as path23 from "path";
23508
24844
  function slugifyFixtureName(value) {
23509
24845
  const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
23510
24846
  return normalized || `fixture-${Date.now()}`;
@@ -23514,11 +24850,11 @@ function getCliFixtureDir(ctx, type) {
23514
24850
  if (!providerDir) {
23515
24851
  throw new Error(`Provider directory not found for '${type}'`);
23516
24852
  }
23517
- return path20.join(providerDir, "fixtures");
24853
+ return path23.join(providerDir, "fixtures");
23518
24854
  }
23519
24855
  function readCliFixture(ctx, type, name) {
23520
24856
  const fixtureDir = getCliFixtureDir(ctx, type);
23521
- const filePath = path20.join(fixtureDir, `${name}.json`);
24857
+ const filePath = path23.join(fixtureDir, `${name}.json`);
23522
24858
  if (!fs13.existsSync(filePath)) {
23523
24859
  throw new Error(`Fixture not found: ${filePath}`);
23524
24860
  }
@@ -23687,7 +25023,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
23687
25023
  return { target, instance, adapter };
23688
25024
  }
23689
25025
  function sleep(ms) {
23690
- return new Promise((resolve12) => setTimeout(resolve12, ms));
25026
+ return new Promise((resolve15) => setTimeout(resolve15, ms));
23691
25027
  }
23692
25028
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
23693
25029
  const startedAt = Date.now();
@@ -23985,7 +25321,7 @@ async function runCliAutoImplVerification(ctx, type, verification) {
23985
25321
  return {
23986
25322
  mode: "fixture_replay_suite",
23987
25323
  pass: results.every((item) => item.pass),
23988
- failures: results.flatMap((item) => item.failures.map((failure) => `${item.fixtureName}: ${failure}`)),
25324
+ failures: results.flatMap((item) => item.failures.map((failure2) => `${item.fixtureName}: ${failure2}`)),
23989
25325
  result: firstFailure.result,
23990
25326
  assertions: firstFailure.assertions,
23991
25327
  fixture: firstFailure.fixture,
@@ -24285,7 +25621,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
24285
25621
  },
24286
25622
  notes: typeof body?.notes === "string" ? body.notes : void 0
24287
25623
  };
24288
- const filePath = path20.join(fixtureDir, `${name}.json`);
25624
+ const filePath = path23.join(fixtureDir, `${name}.json`);
24289
25625
  fs13.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
24290
25626
  ctx.json(res, 200, {
24291
25627
  saved: true,
@@ -24309,7 +25645,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
24309
25645
  return;
24310
25646
  }
24311
25647
  const fixtures = fs13.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
24312
- const fullPath = path20.join(fixtureDir, file);
25648
+ const fullPath = path23.join(fixtureDir, file);
24313
25649
  try {
24314
25650
  const raw = JSON.parse(fs13.readFileSync(fullPath, "utf-8"));
24315
25651
  return {
@@ -24445,7 +25781,7 @@ async function handleCliRaw(ctx, req, res) {
24445
25781
 
24446
25782
  // src/daemon/dev-auto-implement.ts
24447
25783
  import * as fs14 from "fs";
24448
- import * as path21 from "path";
25784
+ import * as path24 from "path";
24449
25785
  import * as os20 from "os";
24450
25786
  function getAutoImplPid(ctx) {
24451
25787
  const pid = ctx.autoImplProcess?.pid;
@@ -24495,22 +25831,22 @@ function getLatestScriptVersionDir(scriptsDir) {
24495
25831
  if (!fs14.existsSync(scriptsDir)) return null;
24496
25832
  const versions = fs14.readdirSync(scriptsDir).filter((d) => {
24497
25833
  try {
24498
- return fs14.statSync(path21.join(scriptsDir, d)).isDirectory();
25834
+ return fs14.statSync(path24.join(scriptsDir, d)).isDirectory();
24499
25835
  } catch {
24500
25836
  return false;
24501
25837
  }
24502
25838
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
24503
25839
  if (versions.length === 0) return null;
24504
- return path21.join(scriptsDir, versions[0]);
25840
+ return path24.join(scriptsDir, versions[0]);
24505
25841
  }
24506
25842
  function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
24507
- const canonicalUserDir = path21.resolve(ctx.providerLoader.getUserProviderDir(category, type));
24508
- const desiredDir = requestedDir ? path21.resolve(requestedDir) : canonicalUserDir;
24509
- const upstreamRoot = path21.resolve(ctx.providerLoader.getUpstreamDir());
24510
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path21.sep}`)) {
25843
+ const canonicalUserDir = path24.resolve(ctx.providerLoader.getUserProviderDir(category, type));
25844
+ const desiredDir = requestedDir ? path24.resolve(requestedDir) : canonicalUserDir;
25845
+ const upstreamRoot = path24.resolve(ctx.providerLoader.getUpstreamDir());
25846
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path24.sep}`)) {
24511
25847
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
24512
25848
  }
24513
- if (path21.basename(desiredDir) !== type) {
25849
+ if (path24.basename(desiredDir) !== type) {
24514
25850
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
24515
25851
  }
24516
25852
  const sourceDir = ctx.findProviderDir(type);
@@ -24518,11 +25854,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
24518
25854
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
24519
25855
  }
24520
25856
  if (!fs14.existsSync(desiredDir)) {
24521
- fs14.mkdirSync(path21.dirname(desiredDir), { recursive: true });
25857
+ fs14.mkdirSync(path24.dirname(desiredDir), { recursive: true });
24522
25858
  fs14.cpSync(sourceDir, desiredDir, { recursive: true });
24523
25859
  ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
24524
25860
  }
24525
- const providerJson = path21.join(desiredDir, "provider.json");
25861
+ const providerJson = path24.join(desiredDir, "provider.json");
24526
25862
  if (!fs14.existsSync(providerJson)) {
24527
25863
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
24528
25864
  }
@@ -24533,13 +25869,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
24533
25869
  const refDir = ctx.findProviderDir(referenceType);
24534
25870
  if (!refDir || !fs14.existsSync(refDir)) return {};
24535
25871
  const referenceScripts = {};
24536
- const scriptsDir = path21.join(refDir, "scripts");
25872
+ const scriptsDir = path24.join(refDir, "scripts");
24537
25873
  const latestDir = getLatestScriptVersionDir(scriptsDir);
24538
25874
  if (!latestDir) return referenceScripts;
24539
25875
  for (const file of fs14.readdirSync(latestDir)) {
24540
25876
  if (!file.endsWith(".js")) continue;
24541
25877
  try {
24542
- referenceScripts[file] = fs14.readFileSync(path21.join(latestDir, file), "utf-8");
25878
+ referenceScripts[file] = fs14.readFileSync(path24.join(latestDir, file), "utf-8");
24543
25879
  } catch {
24544
25880
  }
24545
25881
  }
@@ -24647,9 +25983,9 @@ async function handleAutoImplement(ctx, type, req, res) {
24647
25983
  });
24648
25984
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
24649
25985
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
24650
- const tmpDir = path21.join(os20.tmpdir(), "adhdev-autoimpl");
25986
+ const tmpDir = path24.join(os20.tmpdir(), "adhdev-autoimpl");
24651
25987
  if (!fs14.existsSync(tmpDir)) fs14.mkdirSync(tmpDir, { recursive: true });
24652
- const promptFile = path21.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
25988
+ const promptFile = path24.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
24653
25989
  fs14.writeFileSync(promptFile, prompt, "utf-8");
24654
25990
  ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
24655
25991
  const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
@@ -25081,7 +26417,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
25081
26417
  setMode: "set_mode.js"
25082
26418
  };
25083
26419
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
25084
- const scriptsDir = path21.join(providerDir, "scripts");
26420
+ const scriptsDir = path24.join(providerDir, "scripts");
25085
26421
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
25086
26422
  if (latestScriptsDir) {
25087
26423
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -25092,7 +26428,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
25092
26428
  for (const file of fs14.readdirSync(latestScriptsDir)) {
25093
26429
  if (file.endsWith(".js") && targetFileNames.has(file)) {
25094
26430
  try {
25095
- const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
26431
+ const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
25096
26432
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
25097
26433
  lines.push("```javascript");
25098
26434
  lines.push(content);
@@ -25109,7 +26445,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
25109
26445
  lines.push("");
25110
26446
  for (const file of refFiles) {
25111
26447
  try {
25112
- const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
26448
+ const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
25113
26449
  lines.push(`### \`${file}\` \u{1F512}`);
25114
26450
  lines.push("```javascript");
25115
26451
  lines.push(content);
@@ -25150,10 +26486,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
25150
26486
  lines.push("");
25151
26487
  }
25152
26488
  }
25153
- const docsDir = path21.join(providerDir, "../../docs");
26489
+ const docsDir = path24.join(providerDir, "../../docs");
25154
26490
  const loadGuide = (name) => {
25155
26491
  try {
25156
- const p = path21.join(docsDir, name);
26492
+ const p = path24.join(docsDir, name);
25157
26493
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
25158
26494
  } catch {
25159
26495
  }
@@ -25390,7 +26726,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
25390
26726
  parseApproval: "parse_approval.js"
25391
26727
  };
25392
26728
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
25393
- const scriptsDir = path21.join(providerDir, "scripts");
26729
+ const scriptsDir = path24.join(providerDir, "scripts");
25394
26730
  const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
25395
26731
  if (latestScriptsDir) {
25396
26732
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -25402,7 +26738,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
25402
26738
  if (!file.endsWith(".js")) continue;
25403
26739
  if (!targetFileNames.has(file)) continue;
25404
26740
  try {
25405
- const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
26741
+ const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
25406
26742
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
25407
26743
  lines.push("```javascript");
25408
26744
  lines.push(content);
@@ -25418,7 +26754,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
25418
26754
  lines.push("");
25419
26755
  for (const file of refFiles) {
25420
26756
  try {
25421
- const content = fs14.readFileSync(path21.join(latestScriptsDir, file), "utf-8");
26757
+ const content = fs14.readFileSync(path24.join(latestScriptsDir, file), "utf-8");
25422
26758
  lines.push(`### \`${file}\` \u{1F512}`);
25423
26759
  lines.push("```javascript");
25424
26760
  lines.push(content);
@@ -25451,10 +26787,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
25451
26787
  lines.push("");
25452
26788
  }
25453
26789
  }
25454
- const docsDir = path21.join(providerDir, "../../docs");
26790
+ const docsDir = path24.join(providerDir, "../../docs");
25455
26791
  const loadGuide = (name) => {
25456
26792
  try {
25457
- const p = path21.join(docsDir, name);
26793
+ const p = path24.join(docsDir, name);
25458
26794
  if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
25459
26795
  } catch {
25460
26796
  }
@@ -25901,8 +27237,8 @@ var DevServer = class _DevServer {
25901
27237
  }
25902
27238
  getEndpointList() {
25903
27239
  return this.routes.map((r) => {
25904
- const path23 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
25905
- return `${r.method.padEnd(5)} ${path23}`;
27240
+ const path26 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
27241
+ return `${r.method.padEnd(5)} ${path26}`;
25906
27242
  });
25907
27243
  }
25908
27244
  async start(port = DEV_SERVER_PORT) {
@@ -25933,15 +27269,15 @@ var DevServer = class _DevServer {
25933
27269
  this.json(res, 500, { error: e.message });
25934
27270
  }
25935
27271
  });
25936
- return new Promise((resolve12, reject) => {
27272
+ return new Promise((resolve15, reject) => {
25937
27273
  this.server.listen(port, "127.0.0.1", () => {
25938
27274
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
25939
- resolve12();
27275
+ resolve15();
25940
27276
  });
25941
27277
  this.server.on("error", (e) => {
25942
27278
  if (e.code === "EADDRINUSE") {
25943
27279
  this.log(`Port ${port} in use, skipping dev server`);
25944
- resolve12();
27280
+ resolve15();
25945
27281
  } else {
25946
27282
  reject(e);
25947
27283
  }
@@ -26023,20 +27359,20 @@ var DevServer = class _DevServer {
26023
27359
  child.stderr?.on("data", (d) => {
26024
27360
  stderr += d.toString().slice(0, 2e3);
26025
27361
  });
26026
- await new Promise((resolve12) => {
27362
+ await new Promise((resolve15) => {
26027
27363
  const timer = setTimeout(() => {
26028
27364
  child.kill();
26029
- resolve12();
27365
+ resolve15();
26030
27366
  }, 3e3);
26031
27367
  child.on("exit", () => {
26032
27368
  clearTimeout(timer);
26033
- resolve12();
27369
+ resolve15();
26034
27370
  });
26035
27371
  child.stdout?.once("data", () => {
26036
27372
  setTimeout(() => {
26037
27373
  child.kill();
26038
27374
  clearTimeout(timer);
26039
- resolve12();
27375
+ resolve15();
26040
27376
  }, 500);
26041
27377
  });
26042
27378
  });
@@ -26190,12 +27526,12 @@ var DevServer = class _DevServer {
26190
27526
  // ─── DevConsole SPA ───
26191
27527
  getConsoleDistDir() {
26192
27528
  const candidates = [
26193
- path22.resolve(__dirname, "../../web-devconsole/dist"),
26194
- path22.resolve(__dirname, "../../../web-devconsole/dist"),
26195
- path22.join(process.cwd(), "packages/web-devconsole/dist")
27529
+ path25.resolve(__dirname, "../../web-devconsole/dist"),
27530
+ path25.resolve(__dirname, "../../../web-devconsole/dist"),
27531
+ path25.join(process.cwd(), "packages/web-devconsole/dist")
26196
27532
  ];
26197
27533
  for (const dir of candidates) {
26198
- if (fs15.existsSync(path22.join(dir, "index.html"))) return dir;
27534
+ if (fs15.existsSync(path25.join(dir, "index.html"))) return dir;
26199
27535
  }
26200
27536
  return null;
26201
27537
  }
@@ -26205,7 +27541,7 @@ var DevServer = class _DevServer {
26205
27541
  this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
26206
27542
  return;
26207
27543
  }
26208
- const htmlPath = path22.join(distDir, "index.html");
27544
+ const htmlPath = path25.join(distDir, "index.html");
26209
27545
  try {
26210
27546
  const html = fs15.readFileSync(htmlPath, "utf-8");
26211
27547
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@@ -26230,15 +27566,15 @@ var DevServer = class _DevServer {
26230
27566
  this.json(res, 404, { error: "Not found" });
26231
27567
  return;
26232
27568
  }
26233
- const safePath = path22.normalize(pathname).replace(/^\.\.\//, "");
26234
- const filePath = path22.join(distDir, safePath);
27569
+ const safePath = path25.normalize(pathname).replace(/^\.\.\//, "");
27570
+ const filePath = path25.join(distDir, safePath);
26235
27571
  if (!filePath.startsWith(distDir)) {
26236
27572
  this.json(res, 403, { error: "Forbidden" });
26237
27573
  return;
26238
27574
  }
26239
27575
  try {
26240
27576
  const content = fs15.readFileSync(filePath);
26241
- const ext = path22.extname(filePath);
27577
+ const ext = path25.extname(filePath);
26242
27578
  const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
26243
27579
  res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
26244
27580
  res.end(content);
@@ -26351,10 +27687,10 @@ var DevServer = class _DevServer {
26351
27687
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
26352
27688
  if (entry.isDirectory()) {
26353
27689
  files.push({ path: rel, size: 0, type: "dir" });
26354
- scan(path22.join(d, entry.name), rel);
27690
+ scan(path25.join(d, entry.name), rel);
26355
27691
  } else {
26356
- const stat = fs15.statSync(path22.join(d, entry.name));
26357
- files.push({ path: rel, size: stat.size, type: "file" });
27692
+ const stat2 = fs15.statSync(path25.join(d, entry.name));
27693
+ files.push({ path: rel, size: stat2.size, type: "file" });
26358
27694
  }
26359
27695
  }
26360
27696
  } catch {
@@ -26376,7 +27712,7 @@ var DevServer = class _DevServer {
26376
27712
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
26377
27713
  return;
26378
27714
  }
26379
- const fullPath = path22.resolve(dir, path22.normalize(filePath));
27715
+ const fullPath = path25.resolve(dir, path25.normalize(filePath));
26380
27716
  if (!fullPath.startsWith(dir)) {
26381
27717
  this.json(res, 403, { error: "Forbidden" });
26382
27718
  return;
@@ -26401,14 +27737,14 @@ var DevServer = class _DevServer {
26401
27737
  this.json(res, 404, { error: `Provider directory not found: ${type}` });
26402
27738
  return;
26403
27739
  }
26404
- const fullPath = path22.resolve(dir, path22.normalize(filePath));
27740
+ const fullPath = path25.resolve(dir, path25.normalize(filePath));
26405
27741
  if (!fullPath.startsWith(dir)) {
26406
27742
  this.json(res, 403, { error: "Forbidden" });
26407
27743
  return;
26408
27744
  }
26409
27745
  try {
26410
27746
  if (fs15.existsSync(fullPath)) fs15.copyFileSync(fullPath, fullPath + ".bak");
26411
- fs15.mkdirSync(path22.dirname(fullPath), { recursive: true });
27747
+ fs15.mkdirSync(path25.dirname(fullPath), { recursive: true });
26412
27748
  fs15.writeFileSync(fullPath, content, "utf-8");
26413
27749
  this.log(`File saved: ${fullPath} (${content.length} chars)`);
26414
27750
  this.providerLoader.reload();
@@ -26425,7 +27761,7 @@ var DevServer = class _DevServer {
26425
27761
  return;
26426
27762
  }
26427
27763
  for (const name of ["scripts.js", "provider.json"]) {
26428
- const p = path22.join(dir, name);
27764
+ const p = path25.join(dir, name);
26429
27765
  if (fs15.existsSync(p)) {
26430
27766
  const source = fs15.readFileSync(p, "utf-8");
26431
27767
  this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
@@ -26446,8 +27782,8 @@ var DevServer = class _DevServer {
26446
27782
  this.json(res, 404, { error: `Provider not found: ${type}` });
26447
27783
  return;
26448
27784
  }
26449
- const target = fs15.existsSync(path22.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
26450
- const targetPath = path22.join(dir, target);
27785
+ const target = fs15.existsSync(path25.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
27786
+ const targetPath = path25.join(dir, target);
26451
27787
  try {
26452
27788
  if (fs15.existsSync(targetPath)) fs15.copyFileSync(targetPath, targetPath + ".bak");
26453
27789
  fs15.writeFileSync(targetPath, source, "utf-8");
@@ -26539,14 +27875,14 @@ var DevServer = class _DevServer {
26539
27875
  child.stderr?.on("data", (d) => {
26540
27876
  stderr += d.toString();
26541
27877
  });
26542
- await new Promise((resolve12) => {
27878
+ await new Promise((resolve15) => {
26543
27879
  const timer = setTimeout(() => {
26544
27880
  child.kill();
26545
- resolve12();
27881
+ resolve15();
26546
27882
  }, timeout);
26547
27883
  child.on("exit", () => {
26548
27884
  clearTimeout(timer);
26549
- resolve12();
27885
+ resolve15();
26550
27886
  });
26551
27887
  });
26552
27888
  const elapsed = Date.now() - start;
@@ -26594,7 +27930,7 @@ var DevServer = class _DevServer {
26594
27930
  }
26595
27931
  let targetDir;
26596
27932
  targetDir = this.providerLoader.getUserProviderDir(category, type);
26597
- const jsonPath = path22.join(targetDir, "provider.json");
27933
+ const jsonPath = path25.join(targetDir, "provider.json");
26598
27934
  if (fs15.existsSync(jsonPath)) {
26599
27935
  this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
26600
27936
  return;
@@ -26606,8 +27942,8 @@ var DevServer = class _DevServer {
26606
27942
  const createdFiles = ["provider.json"];
26607
27943
  if (result.files) {
26608
27944
  for (const [relPath, content] of Object.entries(result.files)) {
26609
- const fullPath = path22.join(targetDir, relPath);
26610
- fs15.mkdirSync(path22.dirname(fullPath), { recursive: true });
27945
+ const fullPath = path25.join(targetDir, relPath);
27946
+ fs15.mkdirSync(path25.dirname(fullPath), { recursive: true });
26611
27947
  fs15.writeFileSync(fullPath, content, "utf-8");
26612
27948
  createdFiles.push(relPath);
26613
27949
  }
@@ -26660,22 +27996,22 @@ var DevServer = class _DevServer {
26660
27996
  if (!fs15.existsSync(scriptsDir)) return null;
26661
27997
  const versions = fs15.readdirSync(scriptsDir).filter((d) => {
26662
27998
  try {
26663
- return fs15.statSync(path22.join(scriptsDir, d)).isDirectory();
27999
+ return fs15.statSync(path25.join(scriptsDir, d)).isDirectory();
26664
28000
  } catch {
26665
28001
  return false;
26666
28002
  }
26667
28003
  }).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
26668
28004
  if (versions.length === 0) return null;
26669
- return path22.join(scriptsDir, versions[0]);
28005
+ return path25.join(scriptsDir, versions[0]);
26670
28006
  }
26671
28007
  resolveAutoImplWritableProviderDir(category, type, requestedDir) {
26672
- const canonicalUserDir = path22.resolve(this.providerLoader.getUserProviderDir(category, type));
26673
- const desiredDir = requestedDir ? path22.resolve(requestedDir) : canonicalUserDir;
26674
- const upstreamRoot = path22.resolve(this.providerLoader.getUpstreamDir());
26675
- if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path22.sep}`)) {
28008
+ const canonicalUserDir = path25.resolve(this.providerLoader.getUserProviderDir(category, type));
28009
+ const desiredDir = requestedDir ? path25.resolve(requestedDir) : canonicalUserDir;
28010
+ const upstreamRoot = path25.resolve(this.providerLoader.getUpstreamDir());
28011
+ if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path25.sep}`)) {
26676
28012
  return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
26677
28013
  }
26678
- if (path22.basename(desiredDir) !== type) {
28014
+ if (path25.basename(desiredDir) !== type) {
26679
28015
  return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
26680
28016
  }
26681
28017
  const sourceDir = this.findProviderDir(type);
@@ -26683,11 +28019,11 @@ var DevServer = class _DevServer {
26683
28019
  return { dir: null, reason: `Provider source directory not found for '${type}'` };
26684
28020
  }
26685
28021
  if (!fs15.existsSync(desiredDir)) {
26686
- fs15.mkdirSync(path22.dirname(desiredDir), { recursive: true });
28022
+ fs15.mkdirSync(path25.dirname(desiredDir), { recursive: true });
26687
28023
  fs15.cpSync(sourceDir, desiredDir, { recursive: true });
26688
28024
  this.log(`Auto-implement writable copy created: ${desiredDir}`);
26689
28025
  }
26690
- const providerJson = path22.join(desiredDir, "provider.json");
28026
+ const providerJson = path25.join(desiredDir, "provider.json");
26691
28027
  if (!fs15.existsSync(providerJson)) {
26692
28028
  return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
26693
28029
  }
@@ -26723,7 +28059,7 @@ var DevServer = class _DevServer {
26723
28059
  setMode: "set_mode.js"
26724
28060
  };
26725
28061
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
26726
- const scriptsDir = path22.join(providerDir, "scripts");
28062
+ const scriptsDir = path25.join(providerDir, "scripts");
26727
28063
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
26728
28064
  if (latestScriptsDir) {
26729
28065
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -26734,7 +28070,7 @@ var DevServer = class _DevServer {
26734
28070
  for (const file of fs15.readdirSync(latestScriptsDir)) {
26735
28071
  if (file.endsWith(".js") && targetFileNames.has(file)) {
26736
28072
  try {
26737
- const content = fs15.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
28073
+ const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
26738
28074
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
26739
28075
  lines.push("```javascript");
26740
28076
  lines.push(content);
@@ -26751,7 +28087,7 @@ var DevServer = class _DevServer {
26751
28087
  lines.push("");
26752
28088
  for (const file of refFiles) {
26753
28089
  try {
26754
- const content = fs15.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
28090
+ const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
26755
28091
  lines.push(`### \`${file}\` \u{1F512}`);
26756
28092
  lines.push("```javascript");
26757
28093
  lines.push(content);
@@ -26792,10 +28128,10 @@ var DevServer = class _DevServer {
26792
28128
  lines.push("");
26793
28129
  }
26794
28130
  }
26795
- const docsDir = path22.join(providerDir, "../../docs");
28131
+ const docsDir = path25.join(providerDir, "../../docs");
26796
28132
  const loadGuide = (name) => {
26797
28133
  try {
26798
- const p = path22.join(docsDir, name);
28134
+ const p = path25.join(docsDir, name);
26799
28135
  if (fs15.existsSync(p)) return fs15.readFileSync(p, "utf-8");
26800
28136
  } catch {
26801
28137
  }
@@ -26969,7 +28305,7 @@ var DevServer = class _DevServer {
26969
28305
  parseApproval: "parse_approval.js"
26970
28306
  };
26971
28307
  const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
26972
- const scriptsDir = path22.join(providerDir, "scripts");
28308
+ const scriptsDir = path25.join(providerDir, "scripts");
26973
28309
  const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
26974
28310
  if (latestScriptsDir) {
26975
28311
  lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
@@ -26981,7 +28317,7 @@ var DevServer = class _DevServer {
26981
28317
  if (!file.endsWith(".js")) continue;
26982
28318
  if (!targetFileNames.has(file)) continue;
26983
28319
  try {
26984
- const content = fs15.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
28320
+ const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
26985
28321
  lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
26986
28322
  lines.push("```javascript");
26987
28323
  lines.push(content);
@@ -26997,7 +28333,7 @@ var DevServer = class _DevServer {
26997
28333
  lines.push("");
26998
28334
  for (const file of refFiles) {
26999
28335
  try {
27000
- const content = fs15.readFileSync(path22.join(latestScriptsDir, file), "utf-8");
28336
+ const content = fs15.readFileSync(path25.join(latestScriptsDir, file), "utf-8");
27001
28337
  lines.push(`### \`${file}\` \u{1F512}`);
27002
28338
  lines.push("```javascript");
27003
28339
  lines.push(content);
@@ -27030,10 +28366,10 @@ var DevServer = class _DevServer {
27030
28366
  lines.push("");
27031
28367
  }
27032
28368
  }
27033
- const docsDir = path22.join(providerDir, "../../docs");
28369
+ const docsDir = path25.join(providerDir, "../../docs");
27034
28370
  const loadGuide = (name) => {
27035
28371
  try {
27036
- const p = path22.join(docsDir, name);
28372
+ const p = path25.join(docsDir, name);
27037
28373
  if (fs15.existsSync(p)) return fs15.readFileSync(p, "utf-8");
27038
28374
  } catch {
27039
28375
  }
@@ -27216,14 +28552,14 @@ data: ${JSON.stringify(msg.data)}
27216
28552
  res.end(JSON.stringify(data, null, 2));
27217
28553
  }
27218
28554
  async readBody(req) {
27219
- return new Promise((resolve12) => {
28555
+ return new Promise((resolve15) => {
27220
28556
  let body = "";
27221
28557
  req.on("data", (chunk) => body += chunk);
27222
28558
  req.on("end", () => {
27223
28559
  try {
27224
- resolve12(JSON.parse(body));
28560
+ resolve15(JSON.parse(body));
27225
28561
  } catch {
27226
- resolve12({});
28562
+ resolve15({});
27227
28563
  }
27228
28564
  });
27229
28565
  });
@@ -27738,7 +29074,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
27738
29074
  const deadline = Date.now() + timeoutMs;
27739
29075
  while (Date.now() < deadline) {
27740
29076
  if (await canConnect(endpoint)) return;
27741
- await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
29077
+ await new Promise((resolve15) => setTimeout(resolve15, STARTUP_POLL_MS));
27742
29078
  }
27743
29079
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
27744
29080
  }
@@ -27916,10 +29252,10 @@ async function installExtension(ide, extension) {
27916
29252
  const buffer = Buffer.from(await res.arrayBuffer());
27917
29253
  const fs16 = await import("fs");
27918
29254
  fs16.writeFileSync(vsixPath, buffer);
27919
- return new Promise((resolve12) => {
29255
+ return new Promise((resolve15) => {
27920
29256
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
27921
29257
  exec2(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
27922
- resolve12({
29258
+ resolve15({
27923
29259
  extensionId: extension.id,
27924
29260
  marketplaceId: extension.marketplaceId,
27925
29261
  success: !error,
@@ -27932,11 +29268,11 @@ async function installExtension(ide, extension) {
27932
29268
  } catch (e) {
27933
29269
  }
27934
29270
  }
27935
- return new Promise((resolve12) => {
29271
+ return new Promise((resolve15) => {
27936
29272
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
27937
29273
  exec2(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
27938
29274
  if (error) {
27939
- resolve12({
29275
+ resolve15({
27940
29276
  extensionId: extension.id,
27941
29277
  marketplaceId: extension.marketplaceId,
27942
29278
  success: false,
@@ -27944,7 +29280,7 @@ async function installExtension(ide, extension) {
27944
29280
  error: stderr || error.message
27945
29281
  });
27946
29282
  } else {
27947
- resolve12({
29283
+ resolve15({
27948
29284
  extensionId: extension.id,
27949
29285
  marketplaceId: extension.marketplaceId,
27950
29286
  success: true,
@@ -28148,6 +29484,7 @@ async function initDaemonComponents(config) {
28148
29484
  providerLoader,
28149
29485
  instanceManager,
28150
29486
  sessionRegistry,
29487
+ gitCommandServices: createDefaultGitCommandServices(),
28151
29488
  onProviderSettingChanged: async (providerType) => {
28152
29489
  await refreshProviderAvailability(providerType);
28153
29490
  config.onStatusChange?.();
@@ -28155,7 +29492,8 @@ async function initDaemonComponents(config) {
28155
29492
  onProviderSourceConfigChanged: async () => {
28156
29493
  await refreshProviderAvailability();
28157
29494
  config.onStatusChange?.();
28158
- }
29495
+ },
29496
+ onBeforeSendChat: config.onBeforeSendChat
28159
29497
  });
28160
29498
  agentStreamManager = new DaemonAgentStreamManager(
28161
29499
  LOG.forComponent("AgentStream").asLogFn(),
@@ -28273,6 +29611,7 @@ export {
28273
29611
  DEFAULT_CDP_SCAN_INTERVAL_MS,
28274
29612
  DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS,
28275
29613
  DEFAULT_DAEMON_PORT,
29614
+ DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
28276
29615
  DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
28277
29616
  DEFAULT_SESSION_HOST_APP_NAME,
28278
29617
  DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
@@ -28291,8 +29630,12 @@ export {
28291
29630
  DaemonCommandRouter,
28292
29631
  DaemonStatusReporter,
28293
29632
  DevServer,
29633
+ GitCommandError,
29634
+ GitWorkspaceMonitor,
28294
29635
  IdeProviderInstance,
29636
+ InMemoryGitSnapshotStore,
28295
29637
  LOG,
29638
+ MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
28296
29639
  MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
28297
29640
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
28298
29641
  NodePtyTransportFactory,
@@ -28301,6 +29644,7 @@ export {
28301
29644
  ProviderLoader,
28302
29645
  STANDALONE_CDP_SCAN_INTERVAL_MS,
28303
29646
  SessionHostPtyTransportFactory,
29647
+ TurnSnapshotTracker,
28304
29648
  VersionArchive,
28305
29649
  appendRecentActivity,
28306
29650
  buildAssistantChatMessage,
@@ -28320,9 +29664,14 @@ export {
28320
29664
  buildUserChatMessage,
28321
29665
  classifyHotChatSessionsForSubscriptionFlush,
28322
29666
  clearDebugTrace,
29667
+ compareGitSnapshots,
28323
29668
  configureDebugTraceStore,
28324
29669
  connectCdpManager,
28325
29670
  createDebugTraceStore,
29671
+ createDefaultGitCommandServices,
29672
+ createGitCompactSummary,
29673
+ createGitSnapshotStore,
29674
+ createGitWorkspaceMonitor,
28326
29675
  createInteractionId,
28327
29676
  detectAllVersions,
28328
29677
  detectCLIs,
@@ -28337,6 +29686,9 @@ export {
28337
29686
  getCurrentDaemonLogPath,
28338
29687
  getDaemonLogDir,
28339
29688
  getDebugRuntimeConfig,
29689
+ getGitDiffSummary,
29690
+ getGitFileDiff,
29691
+ getGitRepoStatus,
28340
29692
  getHostMemorySnapshot,
28341
29693
  getLogLevel,
28342
29694
  getNpmExecOptions,
@@ -28348,6 +29700,7 @@ export {
28348
29700
  getSessionHostRecoveryLabel,
28349
29701
  getSessionHostSurfaceKind,
28350
29702
  getWorkspaceState,
29703
+ handleGitCommand,
28351
29704
  hasCdpManager,
28352
29705
  hashSignatureParts,
28353
29706
  initDaemonComponents,
@@ -28356,9 +29709,11 @@ export {
28356
29709
  isBuiltinChatMessageKind,
28357
29710
  isCdpConnected,
28358
29711
  isExtensionInstalled,
29712
+ isGitCommandName,
28359
29713
  isIdeRunning,
28360
29714
  isManagedStatusWaiting,
28361
29715
  isManagedStatusWorking,
29716
+ isPathInside,
28362
29717
  isSessionHostLiveRuntime,
28363
29718
  isSessionHostRecoverySnapshot,
28364
29719
  isSetupComplete,
@@ -28376,10 +29731,13 @@ export {
28376
29731
  normalizeChatMessageKind,
28377
29732
  normalizeChatMessages,
28378
29733
  normalizeChatTailActiveModal,
29734
+ normalizeGitOutput,
29735
+ normalizeGitWorkspaceSubscriptionParams,
28379
29736
  normalizeInputEnvelope,
28380
29737
  normalizeManagedStatus,
28381
29738
  normalizeMessageParts,
28382
29739
  normalizeSessionModalFields,
29740
+ parsePorcelainV2Status,
28383
29741
  parseProviderSourceConfigUpdate,
28384
29742
  partitionSessionHostDiagnosticsSessions,
28385
29743
  partitionSessionHostRecords,
@@ -28395,9 +29753,11 @@ export {
28395
29753
  resolveChatMessageKind,
28396
29754
  resolveCurrentGlobalInstallSurface,
28397
29755
  resolveDebugRuntimeConfig,
29756
+ resolveGitRepository,
28398
29757
  resolveSessionHostAppName,
28399
29758
  resolveSessionHostAppNameResolution,
28400
29759
  runAsyncBatch,
29760
+ runGit,
28401
29761
  saveConfig,
28402
29762
  saveState,
28403
29763
  setDebugRuntimeConfig,
@@ -28408,6 +29768,7 @@ export {
28408
29768
  shutdownDaemonComponents,
28409
29769
  spawnDetachedDaemonUpgradeHelper,
28410
29770
  startDaemonDevSupport,
29771
+ summarizeGitStatus,
28411
29772
  updateConfig,
28412
29773
  upsertSavedProviderSession
28413
29774
  };