deepcode-ai 1.1.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1657,6 +1657,8 @@ import { Command as Command2 } from "commander";
1657
1657
 
1658
1658
  // ../../packages/core/dist/index.js
1659
1659
  import { Effect } from "effect";
1660
+ import { lstat, readdir } from "fs/promises";
1661
+ import path2 from "path";
1660
1662
  import { zodToJsonSchema } from "zod-to-json-schema";
1661
1663
 
1662
1664
  // ../../packages/shared/dist/index.js
@@ -2227,20 +2229,20 @@ var SessionTelemetrySchema = z.object({
2227
2229
 
2228
2230
  // ../../packages/core/dist/index.js
2229
2231
  import { z as z2 } from "zod";
2230
- import { spawn } from "child_process";
2232
+ import { execFile, spawn } from "child_process";
2233
+ import { spawn as spawn2 } from "child_process";
2231
2234
  import { createInterface } from "readline";
2232
2235
  import { Effect as Effect3 } from "effect";
2233
2236
  import { z as z22 } from "zod";
2234
2237
  import { Effect as Effect2 } from "effect";
2235
2238
  import { createHash } from "crypto";
2236
2239
  import { mkdir as mkdir2, readFile, rm as rm2, writeFile as writeFile2 } from "fs/promises";
2237
- import path2 from "path";
2240
+ import path22 from "path";
2238
2241
  import { mkdir as mkdir22, readFile as readFile2 } from "fs/promises";
2239
2242
  import os from "os";
2240
- import path22 from "path";
2243
+ import path3 from "path";
2241
2244
  import { EventEmitter } from "events";
2242
2245
  import { z as z3 } from "zod";
2243
- import { execFile, spawn as spawn2 } from "child_process";
2244
2246
  import { spawn as spawn3 } from "child_process";
2245
2247
  import { URL as URL2 } from "url";
2246
2248
  import { URLSearchParams } from "url";
@@ -2248,27 +2250,27 @@ import { execFile as execFile2 } from "child_process";
2248
2250
  import { z as z4 } from "zod";
2249
2251
  import { spawn as spawn4 } from "child_process";
2250
2252
  import { existsSync } from "fs";
2251
- import path3 from "path";
2252
- import { mkdir as mkdir3, appendFile } from "fs/promises";
2253
2253
  import path4 from "path";
2254
+ import { mkdir as mkdir3, appendFile } from "fs/promises";
2255
+ import path5 from "path";
2254
2256
  import os2 from "os";
2255
2257
  import { access, realpath } from "fs/promises";
2256
- import path5 from "path";
2257
- import os3 from "os";
2258
2258
  import path6 from "path";
2259
- import { mkdir as mkdir4, readFile as readFile3, readdir } from "fs/promises";
2259
+ import os3 from "os";
2260
2260
  import path7 from "path";
2261
+ import { mkdir as mkdir4, readFile as readFile3, readdir as readdir2 } from "fs/promises";
2262
+ import path8 from "path";
2261
2263
  import { readFile as readFile5 } from "fs/promises";
2262
- import path9 from "path";
2264
+ import path10 from "path";
2263
2265
  import { Effect as Effect4 } from "effect";
2264
2266
  import { z as z5 } from "zod";
2265
- import { mkdir as mkdir6, readFile as readFile6, readdir as readdir3, stat, writeFile as writeFile22 } from "fs/promises";
2266
- import path10 from "path";
2267
+ import { lstat as lstat2, mkdir as mkdir6, readFile as readFile6, readdir as readdir4, stat, writeFile as writeFile22 } from "fs/promises";
2268
+ import path11 from "path";
2267
2269
  import { Effect as Effect5 } from "effect";
2268
2270
  import { z as z6 } from "zod";
2269
2271
  import { Effect as Effect6 } from "effect";
2270
2272
  import { z as z7 } from "zod";
2271
- import path11 from "path";
2273
+ import path12 from "path";
2272
2274
  import { Effect as Effect7 } from "effect";
2273
2275
  import { z as z8 } from "zod";
2274
2276
  import { Effect as Effect8 } from "effect";
@@ -2298,8 +2300,8 @@ var PermissionDeniedError = class extends DeepCodeError {
2298
2300
  }
2299
2301
  };
2300
2302
  var PathNotAllowedError = class extends DeepCodeError {
2301
- constructor(path12, reason) {
2302
- super(`Path is not allowed: ${path12}. ${reason}`, "PATH_NOT_ALLOWED");
2303
+ constructor(path13, reason) {
2304
+ super(`Path is not allowed: ${path13}. ${reason}`, "PATH_NOT_ALLOWED");
2303
2305
  this.name = "PathNotAllowedError";
2304
2306
  }
2305
2307
  };
@@ -2525,8 +2527,15 @@ var PlannedTaskSchema = z2.object({
2525
2527
  dependencies: z2.array(z2.string()).default([])
2526
2528
  });
2527
2529
  var PlannedTaskArraySchema = z2.array(PlannedTaskSchema).min(1, "At least one task is required.");
2530
+ var READ_ONLY_DISCOVERY_VERB_PATTERN = /\b(?:list|show|find|search|inspect|check|track|listar|mostre|mostrar|busque|buscar|procure|procurar|inspecione|verifique|rastrear|rastreie)\b/i;
2531
+ var READ_ONLY_DISCOVERY_NOUN_PATTERN = /\b(?:project|projects|repo|repos|repository|repositories|directory|directories|folder|folders|workspace|projeto|projetos|repositorio|repositorios|diretorio|diretorios|pasta|pastas)\b/i;
2532
+ var READ_ONLY_MUTATION_PATTERN = /\b(?:git\s+init|git\s+add|git\s+commit|git\s+push|git\s+tag|git\s+stash|initialize\s+(?:a\s+)?git\s+repository|initialise\s+(?:a\s+)?git\s+repository|create\s+(?:a\s+)?\.?gitignore|stage\s+all\s+files|commit\s+all\s+files)\b/i;
2533
+ function isReadOnlyDiscoveryObjective(objective) {
2534
+ return READ_ONLY_DISCOVERY_VERB_PATTERN.test(objective) && READ_ONLY_DISCOVERY_NOUN_PATTERN.test(objective);
2535
+ }
2528
2536
  var TaskPlanner = class {
2529
2537
  async plan(objective, complete) {
2538
+ const readOnlyDiscoveryObjective = isReadOnlyDiscoveryObjective(objective);
2530
2539
  const raw = await complete(`Create an execution plan for this coding task.
2531
2540
  Return only JSON in this shape:
2532
2541
  [
@@ -2539,6 +2548,9 @@ Requirements:
2539
2548
  - Type must be one of: research, code, test, verify
2540
2549
  - Dependencies must reference existing task IDs
2541
2550
  - Tasks should be ordered logically
2551
+ ${readOnlyDiscoveryObjective ? `- This is a read-only discovery request; prefer the fewest inspection steps needed
2552
+ - Do not propose initializing git repositories, creating .gitignore, staging files, committing, or pushing
2553
+ - Do not propose filesystem mutations for this request` : ""}
2542
2554
 
2543
2555
  Task:
2544
2556
  ${objective}`);
@@ -2555,6 +2567,9 @@ Task:
2555
2567
  throw new Error(`Task "${task.id}" has unknown dependency: "${dep}"`);
2556
2568
  }
2557
2569
  }
2570
+ if (readOnlyDiscoveryObjective && READ_ONLY_MUTATION_PATTERN.test(task.description)) {
2571
+ throw new Error(`Unsafe mutating task in read-only plan: "${task.description}"`);
2572
+ }
2558
2573
  }
2559
2574
  return {
2560
2575
  objective,
@@ -3008,9 +3023,70 @@ function firstObjectField(payload, keys) {
3008
3023
  function collapseFallbackWhitespace(input) {
3009
3024
  return input.replace(/\n{3,}/g, "\n\n").trim();
3010
3025
  }
3026
+ function execFileAsync(command, args, options) {
3027
+ return new Promise((resolve3, reject) => {
3028
+ execFile(
3029
+ command,
3030
+ args,
3031
+ {
3032
+ cwd: options.cwd,
3033
+ timeout: options.timeoutMs,
3034
+ signal: options.signal,
3035
+ maxBuffer: 20 * 1024 * 1024,
3036
+ env: { ...process.env, FORCE_COLOR: "1" }
3037
+ },
3038
+ (error, stdout, stderr) => {
3039
+ if (error) {
3040
+ const err = error;
3041
+ if (typeof err.code === "number") {
3042
+ resolve3({ stdout, stderr, exitCode: err.code });
3043
+ return;
3044
+ }
3045
+ reject(error);
3046
+ return;
3047
+ }
3048
+ resolve3({ stdout, stderr, exitCode: 0 });
3049
+ }
3050
+ );
3051
+ });
3052
+ }
3053
+ function runShell(command, options) {
3054
+ return new Promise((resolve3, reject) => {
3055
+ const child = spawn(command, {
3056
+ cwd: options.cwd,
3057
+ shell: true,
3058
+ env: { ...process.env, FORCE_COLOR: "1" },
3059
+ signal: options.signal
3060
+ });
3061
+ let stdout = "";
3062
+ let stderr = "";
3063
+ let timedOut = false;
3064
+ const timer = setTimeout(() => {
3065
+ timedOut = true;
3066
+ child.kill("SIGTERM");
3067
+ setTimeout(() => child.kill("SIGKILL"), 1500).unref();
3068
+ }, options.timeoutMs);
3069
+ child.stdout?.on("data", (chunk) => {
3070
+ stdout += String(chunk);
3071
+ });
3072
+ child.stderr?.on("data", (chunk) => {
3073
+ stderr += String(chunk);
3074
+ });
3075
+ child.on("error", (error) => {
3076
+ clearTimeout(timer);
3077
+ reject(error);
3078
+ });
3079
+ child.on("close", (exitCode) => {
3080
+ clearTimeout(timer);
3081
+ resolve3({ stdout, stderr, exitCode, timedOut });
3082
+ });
3083
+ });
3084
+ }
3011
3085
  var DIRECT_SHELL_COMMAND_PATTERN = /^(?:ls|dir|pwd|date|tree|find|rg|grep|cat|stat|wc)\b/i;
3012
3086
  var DIRECT_UTILITY_PATH_PATTERN = /(?:^|\s)(?:~\/|\.{1,2}\/|\/)[^\s]*/;
3013
3087
  var DIRECT_UTILITY_VERB_PATTERN = /\b(?:list|lista|liste|listar|mostre|mostrar|show|display|open|abrir|abra|read|leia|print|imprima|exiba)\b/i;
3088
+ var PROJECT_DISCOVERY_VERB_PATTERN = /\b(?:list|lista|liste|listar|show|mostre|mostrar|find|busque|buscar|procure|procurar|track|rastreie|rastrear|scan|escanear|discover|descubra)\b/i;
3089
+ var PROJECT_DISCOVERY_NOUN_PATTERN = /\b(?:project|projects|repo|repos|repository|repositories|repositorio|repositorios|projeto|projetos)\b/i;
3014
3090
  var DATE_TIME_QUESTION_PATTERN = /\b(?:que dia e hoje|que dia é hoje|data de hoje|dia de hoje|what day is it|what day is today|today'?s date|current date|que horas sao|que horas são|hora atual|current time|what time is it)\b/i;
3015
3091
  var SIMPLE_SHELL_COMMAND_PATTERN = /^(?:mkdir|touch|rmdir|cp|mv|chmod|chown|echo|ln|git\s+(?:init|clone|add|commit|push|pull|checkout|branch|stash|tag))\b/i;
3016
3092
  var SIMPLE_ACTION_VERB_RE = /^(?:cria|crie|criar|apaga|apague|apagar|deleta|delete|deletar|remove|mova|move|renomeia|renomeie|renomear|create|rename|mkdir|make)\b/;
@@ -3127,6 +3203,11 @@ function parseUtilityRequest(input) {
3127
3203
  if (normalizedInput === "date" || DATE_TIME_QUESTION_PATTERN.test(normalizedInput)) {
3128
3204
  return { kind: "date" };
3129
3205
  }
3206
+ if (PROJECT_DISCOVERY_NOUN_PATTERN.test(normalizedInput) && (PROJECT_DISCOVERY_VERB_PATTERN.test(normalizedInput) || /\bgit\b/i.test(normalizedInput))) {
3207
+ const explicitPathMatch = trimmed.match(/((?:~\/|\.{1,2}\/|\/)[^\s]+)/);
3208
+ const rawPath = explicitPathMatch?.[1]?.trim() || ".";
3209
+ return { kind: "list_projects", path: rawPath, rawPath };
3210
+ }
3130
3211
  const shellListMatch = trimmed.match(/^(?:ls|dir)\s*(.+)?$/i);
3131
3212
  if (shellListMatch) {
3132
3213
  const rawPath = shellListMatch[1]?.trim() || ".";
@@ -3198,7 +3279,9 @@ function utilityDateResponse() {
3198
3279
  }
3199
3280
  function formatUtilityResult(request, result) {
3200
3281
  if (!result.trim()) {
3201
- return request.kind === "list_dir" ? "Diret\xF3rio vazio." : "Sem sa\xEDda.";
3282
+ if (request.kind === "list_dir") return "Diret\xF3rio vazio.";
3283
+ if (request.kind === "list_projects") return "Nenhum projeto encontrado. Quer versionar alguma pasta?";
3284
+ return "Sem sa\xEDda.";
3202
3285
  }
3203
3286
  if (!result.startsWith("Error running ")) {
3204
3287
  return result;
@@ -3254,6 +3337,9 @@ function isDirectUtilityRequest(input, policy) {
3254
3337
  if (DIRECT_UTILITY_PATH_PATTERN.test(input) && DIRECT_UTILITY_VERB_PATTERN.test(normalizedInput)) {
3255
3338
  return true;
3256
3339
  }
3340
+ if (PROJECT_DISCOVERY_NOUN_PATTERN.test(normalizedInput) && (PROJECT_DISCOVERY_VERB_PATTERN.test(normalizedInput) || /\bgit\b/i.test(normalizedInput))) {
3341
+ return true;
3342
+ }
3257
3343
  return DIRECT_UTILITY_VERB_PATTERN.test(normalizedInput) && (normalizedInput.includes(" directory") || normalizedInput.includes(" folder") || normalizedInput.includes(" pasta") || normalizedInput.includes(" diretorio") || normalizedInput.includes(" documents") || normalizedInput.includes(" documentos") || containsConfiguredTerm(normalizedInput, policy.fileExtensions));
3258
3344
  }
3259
3345
  function containsConfiguredTerm(normalizedInput, terms) {
@@ -3310,6 +3396,22 @@ function resolveExecutionTarget(config, session, mode, explicitProvider) {
3310
3396
  }
3311
3397
  return fallback;
3312
3398
  }
3399
+ var PROJECT_MARKER = ".git";
3400
+ var PROJECT_DISCOVERY_SKIP_DIRS = /* @__PURE__ */ new Set([
3401
+ ".git",
3402
+ ".hg",
3403
+ ".svn",
3404
+ "node_modules",
3405
+ "dist",
3406
+ "build",
3407
+ "coverage",
3408
+ ".next",
3409
+ ".turbo",
3410
+ ".cache",
3411
+ "target",
3412
+ "__pycache__",
3413
+ "vendor"
3414
+ ]);
3313
3415
  var Agent = class {
3314
3416
  constructor(providerManager, tools, sessions, config, cache, permissions, pathSecurity, eventBus) {
3315
3417
  this.providerManager = providerManager;
@@ -4013,6 +4115,26 @@ ${assistantText}` : assistantText;
4013
4115
  });
4014
4116
  return output2;
4015
4117
  }
4118
+ if (request.kind === "list_projects") {
4119
+ try {
4120
+ const output2 = await this.discoverProjects(session, request.path ?? ".");
4121
+ this.sessions.addMessage(session.id, {
4122
+ role: "assistant",
4123
+ source: "assistant",
4124
+ content: formatUtilityResult(request, output2)
4125
+ });
4126
+ return formatUtilityResult(request, output2);
4127
+ } catch (error) {
4128
+ const message = error instanceof Error ? error.message : String(error);
4129
+ const output2 = `Nao consegui localizar projetos em ${request.rawPath ?? request.path ?? "."}: ${message}`;
4130
+ this.sessions.addMessage(session.id, {
4131
+ role: "assistant",
4132
+ source: "assistant",
4133
+ content: output2
4134
+ });
4135
+ return output2;
4136
+ }
4137
+ }
4016
4138
  const call = {
4017
4139
  id: createId("toolcall"),
4018
4140
  name: "list_dir",
@@ -4039,6 +4161,71 @@ ${assistantText}` : assistantText;
4039
4161
  });
4040
4162
  return output;
4041
4163
  }
4164
+ async discoverProjects(session, inputPath) {
4165
+ if (!await this.isGitAvailable(session.worktree)) {
4166
+ return "Git nao esta instalado. Quer que eu instale?";
4167
+ }
4168
+ const rootPath = await this.pathSecurity.normalize(inputPath, { enforceAccess: false });
4169
+ await this.permissions.ensure({ operation: "list_projects", kind: "read", path: rootPath });
4170
+ const results = [];
4171
+ await this.walkForProjects(rootPath, 3, results, /* @__PURE__ */ new Set());
4172
+ if (results.length === 0) {
4173
+ return "";
4174
+ }
4175
+ const formatted = results.sort((left, right) => left.path.localeCompare(right.path)).map((match) => {
4176
+ const relative2 = path2.relative(rootPath, match.path) || ".";
4177
+ return `${relative2} [${match.markers.join(", ")}]`;
4178
+ });
4179
+ return formatted.join("\n");
4180
+ }
4181
+ async walkForProjects(directory, depthRemaining, results, seen) {
4182
+ if (seen.has(directory) || results.length >= 200) {
4183
+ return;
4184
+ }
4185
+ seen.add(directory);
4186
+ const entries = await readdir(directory, { withFileTypes: true });
4187
+ const markerSet = new Set(
4188
+ entries.filter((entry) => entry.name === PROJECT_MARKER).map((entry) => entry.name)
4189
+ );
4190
+ if (markerSet.size > 0) {
4191
+ results.push({
4192
+ path: directory,
4193
+ markers: Array.from(markerSet).sort()
4194
+ });
4195
+ }
4196
+ if (depthRemaining <= 0) {
4197
+ return;
4198
+ }
4199
+ for (const entry of entries) {
4200
+ if (!entry.isDirectory()) {
4201
+ continue;
4202
+ }
4203
+ if (PROJECT_DISCOVERY_SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) {
4204
+ continue;
4205
+ }
4206
+ const fullPath = path2.join(directory, entry.name);
4207
+ try {
4208
+ const info = await lstat(fullPath);
4209
+ if (info.isSymbolicLink()) {
4210
+ continue;
4211
+ }
4212
+ await this.walkForProjects(fullPath, depthRemaining - 1, results, seen);
4213
+ } catch {
4214
+ continue;
4215
+ }
4216
+ }
4217
+ }
4218
+ async isGitAvailable(cwd) {
4219
+ try {
4220
+ const result = await execFileAsync("git", ["--version"], {
4221
+ cwd,
4222
+ timeoutMs: 5e3
4223
+ });
4224
+ return result.exitCode === 0;
4225
+ } catch {
4226
+ return false;
4227
+ }
4228
+ }
4042
4229
  resolveTurnStrategy(input, mode) {
4043
4230
  return resolveTurnStrategy(input, mode, this.config.buildTurnPolicy);
4044
4231
  }
@@ -4080,7 +4267,7 @@ var McpClient = class {
4080
4267
  ready;
4081
4268
  nextId = 1;
4082
4269
  pending = /* @__PURE__ */ new Map();
4083
- constructor(command, args, env, spawnProcess = spawn) {
4270
+ constructor(command, args, env, spawnProcess = spawn2) {
4084
4271
  this.process = spawnProcess(command, args, {
4085
4272
  stdio: ["pipe", "pipe", "pipe"],
4086
4273
  env: { ...process.env, ...env }
@@ -4325,7 +4512,7 @@ var ToolCache = class {
4325
4512
  async set(namespace, keyParts, value) {
4326
4513
  if (!this.config.cache.enabled) return;
4327
4514
  const key = cacheKey(namespace, keyParts);
4328
- const dir = path2.join(this.worktree, ".deepcode", "cache");
4515
+ const dir = path22.join(this.worktree, ".deepcode", "cache");
4329
4516
  await mkdir2(dir, { recursive: true });
4330
4517
  const now = Date.now();
4331
4518
  const entry = {
@@ -4338,10 +4525,10 @@ var ToolCache = class {
4338
4525
  `, "utf8");
4339
4526
  }
4340
4527
  async clear() {
4341
- await rm2(path2.join(this.worktree, ".deepcode", "cache"), { recursive: true, force: true });
4528
+ await rm2(path22.join(this.worktree, ".deepcode", "cache"), { recursive: true, force: true });
4342
4529
  }
4343
4530
  filePath(key) {
4344
- return path2.join(this.worktree, ".deepcode", "cache", `${key}.json`);
4531
+ return path22.join(this.worktree, ".deepcode", "cache", `${key}.json`);
4345
4532
  }
4346
4533
  };
4347
4534
  function cacheKey(namespace, keyParts) {
@@ -4349,12 +4536,12 @@ function cacheKey(namespace, keyParts) {
4349
4536
  }
4350
4537
  var ConfigLoader = class {
4351
4538
  resolveConfigPath(options) {
4352
- return options.configPath ? path22.resolve(options.configPath) : path22.join(options.cwd, ".deepcode", "config.json");
4539
+ return options.configPath ? path3.resolve(options.configPath) : path3.join(options.cwd, ".deepcode", "config.json");
4353
4540
  }
4354
4541
  async load(options) {
4355
4542
  const configPath = this.resolveConfigPath(options);
4356
4543
  const rawFile = await this.readOptionalJson(configPath);
4357
- const cwd = path22.dirname(configPath) === path22.join(path22.resolve(options.cwd), ".deepcode") ? path22.resolve(options.cwd) : path22.dirname(configPath);
4544
+ const cwd = path3.dirname(configPath) === path3.join(path3.resolve(options.cwd), ".deepcode") ? path3.resolve(options.cwd) : path3.dirname(configPath);
4358
4545
  const openrouterApiKeyFile = parseOptionalString(process.env.OPENROUTER_API_KEY_FILE) ?? rawFile.providers?.openrouter?.apiKeyFile;
4359
4546
  const anthropicApiKeyFile = parseOptionalString(process.env.ANTHROPIC_API_KEY_FILE) ?? rawFile.providers?.anthropic?.apiKeyFile;
4360
4547
  const openaiApiKeyFile = parseOptionalString(process.env.OPENAI_API_KEY_FILE) ?? rawFile.providers?.openai?.apiKeyFile;
@@ -4436,14 +4623,14 @@ var ConfigLoader = class {
4436
4623
  if (!parsed.success) {
4437
4624
  throw new ConfigError(`Invalid DeepCode config: ${parsed.error.message}`, parsed.error);
4438
4625
  }
4439
- await mkdir22(path22.dirname(configPath), { recursive: true });
4626
+ await mkdir22(path3.dirname(configPath), { recursive: true });
4440
4627
  await writeFileAtomic(configPath, `${JSON.stringify(parsed.data, null, 2)}
4441
4628
  `);
4442
4629
  return configPath;
4443
4630
  }
4444
4631
  async init(cwd) {
4445
- const dir = path22.join(cwd, ".deepcode");
4446
- const configPath = path22.join(dir, "config.json");
4632
+ const dir = path3.join(cwd, ".deepcode");
4633
+ const configPath = path3.join(dir, "config.json");
4447
4634
  await mkdir22(dir, { recursive: true });
4448
4635
  const config = DeepCodeConfigSchema.parse({});
4449
4636
  await writeFileAtomic(configPath, `${JSON.stringify(config, null, 2)}
@@ -4495,7 +4682,7 @@ function normalizeLabel(value) {
4495
4682
  function resolveUserPath(filePath, cwd) {
4496
4683
  if (!filePath) return void 0;
4497
4684
  const expanded = filePath === "~" ? os.homedir() : filePath.replace(/^~(?=\/|\\)/, os.homedir());
4498
- return path22.isAbsolute(expanded) ? expanded : path22.resolve(cwd, expanded);
4685
+ return path3.isAbsolute(expanded) ? expanded : path3.resolve(cwd, expanded);
4499
4686
  }
4500
4687
  function parseOptionalBoolean(value) {
4501
4688
  if (value === void 0) return void 0;
@@ -4548,65 +4735,6 @@ var EventBus = class {
4548
4735
  }
4549
4736
  }
4550
4737
  };
4551
- function execFileAsync(command, args, options) {
4552
- return new Promise((resolve3, reject) => {
4553
- execFile(
4554
- command,
4555
- args,
4556
- {
4557
- cwd: options.cwd,
4558
- timeout: options.timeoutMs,
4559
- signal: options.signal,
4560
- maxBuffer: 20 * 1024 * 1024,
4561
- env: { ...process.env, FORCE_COLOR: "1" }
4562
- },
4563
- (error, stdout, stderr) => {
4564
- if (error) {
4565
- const err = error;
4566
- if (typeof err.code === "number") {
4567
- resolve3({ stdout, stderr, exitCode: err.code });
4568
- return;
4569
- }
4570
- reject(error);
4571
- return;
4572
- }
4573
- resolve3({ stdout, stderr, exitCode: 0 });
4574
- }
4575
- );
4576
- });
4577
- }
4578
- function runShell(command, options) {
4579
- return new Promise((resolve3, reject) => {
4580
- const child = spawn2(command, {
4581
- cwd: options.cwd,
4582
- shell: true,
4583
- env: { ...process.env, FORCE_COLOR: "1" },
4584
- signal: options.signal
4585
- });
4586
- let stdout = "";
4587
- let stderr = "";
4588
- let timedOut = false;
4589
- const timer = setTimeout(() => {
4590
- timedOut = true;
4591
- child.kill("SIGTERM");
4592
- setTimeout(() => child.kill("SIGKILL"), 1500).unref();
4593
- }, options.timeoutMs);
4594
- child.stdout?.on("data", (chunk) => {
4595
- stdout += String(chunk);
4596
- });
4597
- child.stderr?.on("data", (chunk) => {
4598
- stderr += String(chunk);
4599
- });
4600
- child.on("error", (error) => {
4601
- clearTimeout(timer);
4602
- reject(error);
4603
- });
4604
- child.on("close", (exitCode) => {
4605
- clearTimeout(timer);
4606
- resolve3({ stdout, stderr, exitCode, timedOut });
4607
- });
4608
- });
4609
- }
4610
4738
  var GitHubAuthenticatedUserSchema = z3.object({
4611
4739
  login: z3.string(),
4612
4740
  id: z3.number(),
@@ -4730,13 +4858,13 @@ var GitHubClient = class {
4730
4858
  }
4731
4859
  return parseGitHubRemote(result.stdout.trim());
4732
4860
  }
4733
- async request(path12, init = {}) {
4861
+ async request(path13, init = {}) {
4734
4862
  if (!this.options.token) {
4735
4863
  throw new Error(
4736
4864
  "GitHub token is required. Set GITHUB_TOKEN or .deepcode/config.json github.token."
4737
4865
  );
4738
4866
  }
4739
- const response = await fetch(`${this.apiBase}${path12}`, {
4867
+ const response = await fetch(`${this.apiBase}${path13}`, {
4740
4868
  ...init,
4741
4869
  headers: {
4742
4870
  accept: "application/vnd.github+json",
@@ -4752,13 +4880,13 @@ var GitHubClient = class {
4752
4880
  if (response.status === 204) return void 0;
4753
4881
  return await response.json();
4754
4882
  }
4755
- async requestText(path12, init = {}) {
4883
+ async requestText(path13, init = {}) {
4756
4884
  if (!this.options.token) {
4757
4885
  throw new Error(
4758
4886
  "GitHub token is required. Set GITHUB_TOKEN or .deepcode/config.json github.token."
4759
4887
  );
4760
4888
  }
4761
- const response = await fetch(`${this.apiBase}${path12}`, {
4889
+ const response = await fetch(`${this.apiBase}${path13}`, {
4762
4890
  ...init,
4763
4891
  headers: {
4764
4892
  accept: "application/vnd.github+json",
@@ -5099,7 +5227,7 @@ var LspClient = class {
5099
5227
  await this.request("initialize", {
5100
5228
  processId: process.pid,
5101
5229
  rootUri: toFileUri(this.rootPath),
5102
- workspaceFolders: [{ uri: toFileUri(this.rootPath), name: path3.basename(this.rootPath) }],
5230
+ workspaceFolders: [{ uri: toFileUri(this.rootPath), name: path4.basename(this.rootPath) }],
5103
5231
  capabilities: {
5104
5232
  workspace: {
5105
5233
  symbol: {
@@ -5201,7 +5329,7 @@ var LspClient = class {
5201
5329
  }
5202
5330
  };
5203
5331
  function pickLanguageServer(servers, rootPath, queryPath) {
5204
- const extension = path3.extname(queryPath);
5332
+ const extension = path4.extname(queryPath);
5205
5333
  const byExtension = servers.find((server) => server.fileExtensions.includes(extension));
5206
5334
  if (byExtension) return byExtension;
5207
5335
  const projectFiles = [
@@ -5211,14 +5339,14 @@ function pickLanguageServer(servers, rootPath, queryPath) {
5211
5339
  ["Cargo.toml", "rust"],
5212
5340
  ["go.mod", "go"]
5213
5341
  ];
5214
- const detected = projectFiles.find(([file]) => pathExists(path3.join(rootPath, file)))?.[1];
5342
+ const detected = projectFiles.find(([file]) => pathExists(path4.join(rootPath, file)))?.[1];
5215
5343
  return detected ? servers.find((server) => server.languages.includes(detected)) : servers[0];
5216
5344
  }
5217
5345
  function pathExists(filePath) {
5218
5346
  return existsSync(filePath);
5219
5347
  }
5220
5348
  function toFileUri(filePath) {
5221
- return `file://${path3.resolve(filePath).replaceAll(path3.sep, "/")}`;
5349
+ return `file://${path4.resolve(filePath).replaceAll(path4.sep, "/")}`;
5222
5350
  }
5223
5351
  function fromFileUri(uri) {
5224
5352
  return decodeURIComponent(uri.replace(/^file:\/\//, ""));
@@ -5226,12 +5354,12 @@ function fromFileUri(uri) {
5226
5354
  var SECRET_KEY_PATTERN = /(api[_-]?key|token|authorization|secret|password|passwd|credential|private[_-]?key)/i;
5227
5355
  var MIN_SECRET_VALUE_LENGTH = 4;
5228
5356
  function redactSecrets(value, options = {}) {
5229
- const path12 = options.path ?? [];
5357
+ const path13 = options.path ?? [];
5230
5358
  const secretPlaceholder = options.secretPlaceholder ?? "[redacted]";
5231
5359
  const emptySecretPlaceholder = options.emptySecretPlaceholder ?? "[empty]";
5232
5360
  const secretValues = options.secretValues ?? collectSecretValues();
5233
5361
  if (typeof value === "string") {
5234
- if (isSecretPath(path12)) {
5362
+ if (isSecretPath(path13)) {
5235
5363
  return value.length > 0 ? secretPlaceholder : emptySecretPlaceholder;
5236
5364
  }
5237
5365
  return redactText(value, secretValues, secretPlaceholder);
@@ -5240,7 +5368,7 @@ function redactSecrets(value, options = {}) {
5240
5368
  return value.map(
5241
5369
  (item, index) => redactSecrets(item, {
5242
5370
  ...options,
5243
- path: [...path12, String(index)],
5371
+ path: [...path13, String(index)],
5244
5372
  secretValues
5245
5373
  })
5246
5374
  );
@@ -5251,7 +5379,7 @@ function redactSecrets(value, options = {}) {
5251
5379
  key,
5252
5380
  redactSecrets(item, {
5253
5381
  ...options,
5254
- path: [...path12, key],
5382
+ path: [...path13, key],
5255
5383
  secretValues
5256
5384
  })
5257
5385
  ])
@@ -5289,8 +5417,8 @@ function collectSecretValues(config) {
5289
5417
  }
5290
5418
  return [...values].sort((left, right) => right.length - left.length);
5291
5419
  }
5292
- function isSecretPath(path12) {
5293
- const key = path12[path12.length - 1] ?? "";
5420
+ function isSecretPath(path13) {
5421
+ const key = path13[path13.length - 1] ?? "";
5294
5422
  if (/(api[_-]?key|token|secret|credential).*file/i.test(key)) return false;
5295
5423
  return SECRET_KEY_PATTERN.test(key);
5296
5424
  }
@@ -6148,10 +6276,10 @@ var AuditLogger = class {
6148
6276
  }
6149
6277
  worktree;
6150
6278
  async log(entry) {
6151
- const dir = path4.join(this.worktree, ".deepcode");
6279
+ const dir = path5.join(this.worktree, ".deepcode");
6152
6280
  await mkdir3(dir, { recursive: true });
6153
6281
  const payload = redactSecrets({ ...entry, createdAt: entry.createdAt ?? nowIso() });
6154
- await appendFile(path4.join(dir, "audit.log"), `${JSON.stringify(payload)}
6282
+ await appendFile(path5.join(dir, "audit.log"), `${JSON.stringify(payload)}
6155
6283
  `, "utf8");
6156
6284
  }
6157
6285
  };
@@ -6193,7 +6321,7 @@ var PathSecurity = class {
6193
6321
  return resolved;
6194
6322
  }
6195
6323
  classify(targetPath) {
6196
- const candidate = path5.normalize(targetPath);
6324
+ const candidate = path6.normalize(targetPath);
6197
6325
  const blacklisted = this.rules.blacklist.some((rule) => globToRegex(rule).test(candidate));
6198
6326
  if (blacklisted) {
6199
6327
  return "blacklisted";
@@ -6206,8 +6334,8 @@ var PathSecurity = class {
6206
6334
  }
6207
6335
  async resolvePath(inputPath) {
6208
6336
  const expanded = this.expandUserPath(inputPath);
6209
- const absolute = path5.isAbsolute(expanded) ? expanded : path5.resolve(this.worktree, expanded);
6210
- const normalized2 = path5.normalize(absolute);
6337
+ const absolute = path6.isAbsolute(expanded) ? expanded : path6.resolve(this.worktree, expanded);
6338
+ const normalized2 = path6.normalize(absolute);
6211
6339
  const resolved = await this.resolveExistingParent(normalized2);
6212
6340
  return resolved;
6213
6341
  }
@@ -6219,7 +6347,7 @@ var PathSecurity = class {
6219
6347
  const duplicatedHomePrefix = normalizedHome ? `~/${normalizedHome}` : "";
6220
6348
  if (duplicatedHomePrefix && (normalizedInput === duplicatedHomePrefix || normalizedInput.startsWith(`${duplicatedHomePrefix}/`))) {
6221
6349
  const absoluteSuffix = normalizedInput.slice(2);
6222
- return path5.sep === "\\" ? absoluteSuffix.replace(/\//g, "\\") : `/${absoluteSuffix}`;
6350
+ return path6.sep === "\\" ? absoluteSuffix.replace(/\//g, "\\") : `/${absoluteSuffix}`;
6223
6351
  }
6224
6352
  return inputPath.replace(/^~(?=\/|\\)/, this.home);
6225
6353
  }
@@ -6228,16 +6356,16 @@ var PathSecurity = class {
6228
6356
  }
6229
6357
  async resolveExistingParent(targetPath) {
6230
6358
  let cursor = targetPath;
6231
- while (cursor !== path5.dirname(cursor)) {
6359
+ while (cursor !== path6.dirname(cursor)) {
6232
6360
  try {
6233
6361
  await access(cursor);
6234
6362
  const real = await realpath(cursor);
6235
6363
  if (targetPath === cursor) {
6236
6364
  return real;
6237
6365
  }
6238
- return path5.join(real, path5.relative(cursor, targetPath));
6366
+ return path6.join(real, path6.relative(cursor, targetPath));
6239
6367
  } catch {
6240
- cursor = path5.dirname(cursor);
6368
+ cursor = path6.dirname(cursor);
6241
6369
  }
6242
6370
  }
6243
6371
  return targetPath;
@@ -6535,16 +6663,16 @@ function whitelistExampleForPath(targetPath) {
6535
6663
  return "${WORKTREE}/**";
6536
6664
  }
6537
6665
  const home = process.env.HOME ?? os3.homedir();
6538
- const normalizedTarget = path6.resolve(targetPath);
6539
- const normalizedHome = path6.resolve(home);
6666
+ const normalizedTarget = path7.resolve(targetPath);
6667
+ const normalizedHome = path7.resolve(home);
6540
6668
  if (normalizedTarget === normalizedHome) {
6541
6669
  return "${HOME}/**";
6542
6670
  }
6543
- if (normalizedTarget.startsWith(`${normalizedHome}${path6.sep}`)) {
6544
- const relative2 = path6.relative(normalizedHome, normalizedTarget).replaceAll(path6.sep, "/");
6671
+ if (normalizedTarget.startsWith(`${normalizedHome}${path7.sep}`)) {
6672
+ const relative2 = path7.relative(normalizedHome, normalizedTarget).replaceAll(path7.sep, "/");
6545
6673
  return relative2 ? `\${HOME}/${relative2}/**` : "${HOME}/**";
6546
6674
  }
6547
- const normalizedForConfig = normalizedTarget.replaceAll(path6.sep, "/");
6675
+ const normalizedForConfig = normalizedTarget.replaceAll(path7.sep, "/");
6548
6676
  if (normalizedForConfig === "/") {
6549
6677
  return "/**";
6550
6678
  }
@@ -6604,20 +6732,20 @@ var SessionManager = class {
6604
6732
  }
6605
6733
  async persist(sessionId) {
6606
6734
  const session = this.get(sessionId);
6607
- const dir = path7.join(this.worktree, ".deepcode", "sessions");
6735
+ const dir = path8.join(this.worktree, ".deepcode", "sessions");
6608
6736
  await mkdir4(dir, { recursive: true });
6609
- const filePath = path7.join(dir, `${session.id}.json`);
6737
+ const filePath = path8.join(dir, `${session.id}.json`);
6610
6738
  await writeFileAtomic(filePath, `${JSON.stringify(session, null, 2)}
6611
6739
  `);
6612
6740
  return filePath;
6613
6741
  }
6614
6742
  async loadAll() {
6615
- const dir = path7.join(this.worktree, ".deepcode", "sessions");
6743
+ const dir = path8.join(this.worktree, ".deepcode", "sessions");
6616
6744
  try {
6617
- const entries = await readdir(dir);
6745
+ const entries = await readdir2(dir);
6618
6746
  const loaded = [];
6619
6747
  for (const entry of entries.filter((value) => value.endsWith(".json"))) {
6620
- const filePath = path7.join(dir, entry);
6748
+ const filePath = path8.join(dir, entry);
6621
6749
  try {
6622
6750
  const parsed = JSON.parse(await readFile3(filePath, "utf8"));
6623
6751
  const result = SessionSchema.safeParse(parsed);
@@ -6665,7 +6793,7 @@ var analyzeCodeTool = defineTool({
6665
6793
  const declarations = content.split(/\r?\n/).map((line, index) => ({ line: index + 1, text: line.trim() })).filter(({ text }) => /^(export\s+)?(class|interface|type|function|const|let|var|def|func)\s+/.test(text));
6666
6794
  const result = {
6667
6795
  file: filePath,
6668
- extension: path9.extname(filePath),
6796
+ extension: path10.extname(filePath),
6669
6797
  lines: content.split(/\r?\n/).length,
6670
6798
  declarations
6671
6799
  };
@@ -6734,7 +6862,7 @@ var readFileTool = defineTool({
6734
6862
  if (cached.hit && cached.value !== void 0) {
6735
6863
  context.logActivity({
6736
6864
  type: "cache_hit",
6737
- message: `Cache hit read_file ${path10.relative(context.worktree, filePath)}`,
6865
+ message: `Cache hit read_file ${path11.relative(context.worktree, filePath)}`,
6738
6866
  metadata: { path: filePath }
6739
6867
  });
6740
6868
  return cached.value;
@@ -6745,7 +6873,7 @@ var readFileTool = defineTool({
6745
6873
  const end = args.limit ? Math.min(lines.length, start + args.limit) : lines.length;
6746
6874
  context.logActivity({
6747
6875
  type: "file_read",
6748
- message: `Read ${path10.relative(context.worktree, filePath)}`,
6876
+ message: `Read ${path11.relative(context.worktree, filePath)}`,
6749
6877
  metadata: { path: filePath, lines: end - start }
6750
6878
  });
6751
6879
  const output = lines.slice(start, end).map((line, index) => `${String(start + index + 1).padStart(5, " ")} | ${line}`).join("\n");
@@ -6767,11 +6895,11 @@ var writeFileTool = defineTool({
6767
6895
  const filePath = await context.pathSecurity.normalize(args.path, { enforceAccess: false });
6768
6896
  await context.permissions.ensure({ operation: "write_file", kind: "write", path: filePath });
6769
6897
  await context.snapshotForUndo?.(filePath);
6770
- await mkdir6(path10.dirname(filePath), { recursive: true });
6898
+ await mkdir6(path11.dirname(filePath), { recursive: true });
6771
6899
  await writeFile22(filePath, args.content, "utf8");
6772
6900
  context.logActivity({
6773
6901
  type: "file_written",
6774
- message: `Wrote ${path10.relative(context.worktree, filePath)}`,
6902
+ message: `Wrote ${path11.relative(context.worktree, filePath)}`,
6775
6903
  metadata: { path: filePath, bytes: Buffer.byteLength(args.content) }
6776
6904
  });
6777
6905
  return `File written: ${filePath}`;
@@ -6804,7 +6932,7 @@ var editFileTool = defineTool({
6804
6932
  await writeFile22(filePath, next, "utf8");
6805
6933
  context.logActivity({
6806
6934
  type: "file_edited",
6807
- message: `Edited ${path10.relative(context.worktree, filePath)}`,
6935
+ message: `Edited ${path11.relative(context.worktree, filePath)}`,
6808
6936
  metadata: { path: filePath, removedBytes: args.oldString.length, addedBytes: args.newString.length }
6809
6937
  });
6810
6938
  return `File edited: ${filePath}`;
@@ -6822,18 +6950,23 @@ var listDirTool = defineTool({
6822
6950
  try: async () => {
6823
6951
  const dirPath = await context.pathSecurity.normalize(args.path, { enforceAccess: false });
6824
6952
  await context.permissions.ensure({ operation: "list_dir", kind: "read", path: dirPath });
6825
- const entries = await readdir3(dirPath, { withFileTypes: true });
6953
+ const entries = await readdir4(dirPath, { withFileTypes: true });
6826
6954
  const rows = await Promise.all(
6827
6955
  entries.filter((entry) => entry.name !== "node_modules" && entry.name !== ".git").map(async (entry) => {
6828
- const fullPath = path10.join(dirPath, entry.name);
6829
- const info = await stat(fullPath);
6830
- const type = entry.isDirectory() ? "dir " : "file";
6831
- return `${type} ${String(info.size).padStart(9, " ")} ${entry.name}`;
6956
+ const fullPath = path11.join(dirPath, entry.name);
6957
+ try {
6958
+ const info = await lstat2(fullPath);
6959
+ const type = entry.isSymbolicLink() ? "link" : entry.isDirectory() ? "dir " : "file";
6960
+ return `${type} ${String(info.size).padStart(9, " ")} ${entry.name}`;
6961
+ } catch (error) {
6962
+ const message = error instanceof Error ? error.message : String(error);
6963
+ return `unknown ${"?".padStart(9, " ")} ${entry.name} (${message})`;
6964
+ }
6832
6965
  })
6833
6966
  );
6834
6967
  context.logActivity({
6835
6968
  type: "directory_listed",
6836
- message: `Listed ${path10.relative(context.worktree, dirPath) || "."}`,
6969
+ message: `Listed ${path11.relative(context.worktree, dirPath) || "."}`,
6837
6970
  metadata: { path: dirPath, entries: rows.length }
6838
6971
  });
6839
6972
  return rows.join("\n");
@@ -6952,7 +7085,7 @@ var searchTextTool = defineTool({
6952
7085
  if (cached.hit && cached.value !== void 0) {
6953
7086
  context.logActivity({
6954
7087
  type: "cache_hit",
6955
- message: `Cache hit search_text ${path11.relative(context.worktree, searchPath) || "."}`,
7088
+ message: `Cache hit search_text ${path12.relative(context.worktree, searchPath) || "."}`,
6956
7089
  metadata: { pattern: args.pattern }
6957
7090
  });
6958
7091
  return cached.value;
@@ -6977,7 +7110,7 @@ var searchTextTool = defineTool({
6977
7110
  }));
6978
7111
  context.logActivity({
6979
7112
  type: "text_search",
6980
- message: `Searched ${path11.relative(context.worktree, searchPath) || "."}`,
7113
+ message: `Searched ${path12.relative(context.worktree, searchPath) || "."}`,
6981
7114
  metadata: { pattern: args.pattern, matches: matches.length }
6982
7115
  });
6983
7116
  const output = JSON.stringify(matches, null, 2);
@@ -7017,7 +7150,7 @@ var searchFilesTool = defineTool({
7017
7150
  throw new Error(result.stderr || `ripgrep exited with ${result.exitCode}`);
7018
7151
  }
7019
7152
  const needle = args.query.toLowerCase();
7020
- const files = result.stdout.split(/\r?\n/).filter(Boolean).filter((file) => path11.basename(file).toLowerCase().includes(needle)).slice(0, 200);
7153
+ const files = result.stdout.split(/\r?\n/).filter(Boolean).filter((file) => path12.basename(file).toLowerCase().includes(needle)).slice(0, 200);
7021
7154
  context.logActivity({
7022
7155
  type: "file_search",
7023
7156
  message: `Found ${files.length} file(s)`,
@@ -7339,7 +7472,7 @@ function createDefaultToolRegistry() {
7339
7472
  }
7340
7473
 
7341
7474
  // ../../packages/cli/dist/index.js
7342
- import path8 from "path";
7475
+ import path9 from "path";
7343
7476
  import { writeSync } from "fs";
7344
7477
  import { mkdtemp, rm as rm3 } from "fs/promises";
7345
7478
  import { tmpdir } from "os";
@@ -8315,7 +8448,7 @@ var AsyncFzf = class {
8315
8448
  // ../../packages/cli/dist/index.js
8316
8449
  import { execFile as execFile3 } from "child_process";
8317
8450
  import * as nodeFs from "fs";
8318
- import { access as access2, lstat, open, readFile as readFile4, stat as stat2 } from "fs/promises";
8451
+ import { access as access2, lstat as lstat3, open, readFile as readFile4, stat as stat2 } from "fs/promises";
8319
8452
  import * as path42 from "path";
8320
8453
  import { promisify } from "util";
8321
8454
  import { useState as useState2, useRef, useCallback, useMemo } from "react";
@@ -9342,7 +9475,7 @@ import { jsx as jsx45, jsxs as jsxs39 } from "react/jsx-runtime";
9342
9475
  import { jsx as jsx46, jsxs as jsxs40 } from "react/jsx-runtime";
9343
9476
  import { jsx as jsx47 } from "react/jsx-runtime";
9344
9477
  async function createRuntime(options) {
9345
- const worktree = path8.resolve(options.cwd);
9478
+ const worktree = path9.resolve(options.cwd);
9346
9479
  const config = await new ConfigLoader().load({ cwd: worktree, configPath: options.configPath });
9347
9480
  const events = new EventBus();
9348
9481
  const pathSecurity = new PathSecurity(worktree, config.paths);
@@ -10599,7 +10732,7 @@ async function isInTransientGitState(gitRoot) {
10599
10732
  async function countUntrackedLines(absolutePath) {
10600
10733
  let fileStat;
10601
10734
  try {
10602
- fileStat = await lstat(absolutePath);
10735
+ fileStat = await lstat3(absolutePath);
10603
10736
  } catch {
10604
10737
  return { added: 0, isBinary: true, truncated: false };
10605
10738
  }