@wrongstack/tools 0.8.4 → 0.8.6

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/git.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/git.ts"],"names":[],"mappings":"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","file":"git.js","sourcesContent":["import { spawn } from 'node:child_process';\nimport { statSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { buildChildEnv } from '@wrongstack/core';\nimport type { Tool } from '@wrongstack/core';\n\ntype GitSubcommand =\n | 'status'\n | 'log'\n | 'diff'\n | 'commit'\n | 'branch'\n | 'checkout'\n | 'stash'\n | 'push'\n | 'pull'\n | 'fetch'\n | 'reset'\n | 'worktree';\n\ninterface GitInput {\n command: GitSubcommand;\n files?: string | string[];\n dry_run?: boolean;\n /** commit message for `commit` subcommand */\n message?: string;\n /** branch name for `checkout` / `branch` */\n branch?: string;\n /** pass --graph, --oneline, --stat for `log` */\n format?: 'short' | 'oneline' | 'stat' | 'graph';\n /** limit for `log` */\n limit?: number;\n /** worktree action: list, add, remove, prune */\n worktreeAction?: 'list' | 'add' | 'remove' | 'prune';\n /** path for worktree add/remove (e.g. \"../wt-feature-xyz\") */\n worktreePath?: string;\n /** create new branch when adding worktree */\n newBranch?: boolean;\n /** force operation (e.g. worktree remove --force) */\n force?: boolean;\n}\n\ninterface GitOutput {\n command: GitSubcommand;\n stdout: string;\n stderr: string;\n exitCode: number;\n truncated: boolean;\n}\n\nconst TIMEOUT_MS = 30_000;\nconst MAX_OUTPUT = 100_000;\n\nexport const gitTool: Tool<GitInput, GitOutput> = {\n name: 'git',\n category: 'Git',\n description:\n 'Run git commands. Wraps common operations: status, log, diff, commit, branch, checkout, stash, push, pull, fetch, reset, worktree.',\n usageHint:\n 'Prefer built-in subcommands over raw args. `command` is required. `message` for commits. `branch` for checkout/branch. `files` for status/diff. `format` for log.',\n permission: 'confirm',\n // Conservative: any of these may mutate. The non-mutating commands\n // (status/log/diff/branch/fetch) are still gated on `permission: 'confirm'`\n // and `MUTATING_SUBCOMMANDS` is consulted at runtime for per-call checks.\n mutating: true,\n timeoutMs: TIMEOUT_MS,\n inputSchema: {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n enum: [\n 'status',\n 'log',\n 'diff',\n 'commit',\n 'branch',\n 'checkout',\n 'stash',\n 'push',\n 'pull',\n 'fetch',\n 'reset',\n 'worktree',\n ],\n description: 'Git subcommand',\n },\n files: {\n type: 'string',\n description:\n 'File(s) for status/diff: single path, comma-separated list, or \"**/*.ts\" glob',\n },\n message: { type: 'string', description: 'Commit message (required for commit)' },\n branch: { type: 'string', description: 'Branch name for checkout/branch' },\n format: {\n type: 'string',\n enum: ['short', 'oneline', 'stat', 'graph'],\n description: 'Log format (default: short)',\n },\n limit: { type: 'integer', description: 'Limit for log (default: 20)' },\n dry_run: { type: 'boolean', description: 'For commit: show what would be committed' },\n worktreeAction: {\n type: 'string',\n enum: ['list', 'add', 'remove', 'prune'],\n description: 'Worktree action: list, add, remove, prune',\n },\n worktreePath: {\n type: 'string',\n description: 'Path for worktree add/remove (e.g. \"../wt-feature-xyz\")',\n },\n newBranch: {\n type: 'boolean',\n description: 'Create new branch when adding worktree',\n },\n force: {\n type: 'boolean',\n description: 'Force operation (e.g. worktree remove --force)',\n },\n },\n required: ['command'],\n },\n async execute(input, ctx, opts) {\n if (!input?.command) throw new Error('git: command is required');\n\n if (input.command === 'commit' && !input.message) {\n return {\n command: 'commit',\n stdout: '',\n stderr: 'git commit requires a message (-m flag)',\n exitCode: 1,\n truncated: false,\n };\n }\n\n // Bound the search at projectRoot so a non-git project doesn't drift\n // into a parent repo (e.g. ~/repos/.git) and operate on the wrong tree.\n const gitDir = findGitDir(ctx.cwd, ctx.projectRoot);\n if (!gitDir) {\n return {\n command: input.command,\n stdout: '',\n stderr: 'Not in a git repository (within project root)',\n exitCode: 128,\n truncated: false,\n };\n }\n\n const args = buildArgs(input);\n return await runGit(args, gitDir, opts.signal);\n },\n};\n\nfunction findGitDir(cwd: string, projectRoot: string): string | null {\n const root = projectRoot;\n let dir = cwd;\n for (let i = 0; i < 20; i++) {\n try {\n const stat = statSync(`${dir}/.git`);\n if (stat.isDirectory()) return dir;\n } catch {\n // continue\n }\n if (dir === root) break;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction buildArgs(input: GitInput): string[] {\n const limit = input.limit ?? 20;\n const files = input.files\n ? (Array.isArray(input.files) ? input.files : input.files.split(','))\n .map((s: string) => s.trim())\n .filter(Boolean)\n : [];\n\n switch (input.command) {\n case 'status':\n return ['status', ...(files.length ? ['--', ...files] : [])];\n case 'log':\n return [\n 'log',\n `--max-count=${limit}`,\n ...(input.format === 'oneline' ? ['--oneline'] : []),\n ...(input.format === 'stat' ? ['--stat'] : []),\n ...(input.format === 'graph' ? ['--oneline', '--graph', '--decorate'] : []),\n ...(input.format === 'short' || !input.format ? [] : []),\n ];\n case 'diff':\n return ['diff', '--no-color', ...(files.length ? ['--', ...files] : [])];\n case 'commit':\n return [\n 'commit',\n ...(input.dry_run ? ['--dry-run', '--porcelain'] : []),\n ...(input.message ? ['-m', input.message] : []),\n ...(files.length ? ['--', ...files] : []),\n ];\n case 'branch':\n // Validate branch name: reject names starting with '-' (flag injection).\n return input.branch\n ? ['branch', ...(input.branch.startsWith('-') ? [] : [input.branch])]\n : ['branch'];\n case 'checkout':\n return [\n 'checkout',\n ...(input.branch ? ['--', input.branch] : []),\n ...(files.length ? ['--', ...files] : []),\n ];\n case 'stash':\n return input.message ? ['stash', 'push', '-m', input.message] : ['stash', 'push'];\n case 'push':\n return ['push'];\n case 'pull':\n return ['pull'];\n case 'fetch':\n return ['fetch', ...(input.branch ? [input.branch] : ['--all'])];\n case 'reset':\n return ['reset'];\n case 'worktree':\n switch (input.worktreeAction) {\n case 'list':\n return ['worktree', 'list'];\n case 'add':\n return [\n 'worktree',\n 'add',\n ...(input.newBranch ? ['-b'] : []),\n ...(input.branch ? [input.branch] : []),\n input.worktreePath ?? '',\n ].filter(Boolean);\n case 'remove':\n return [\n 'worktree',\n 'remove',\n ...(input.force ? ['--force'] : []),\n input.worktreePath ?? '',\n ].filter(Boolean);\n case 'prune':\n return ['worktree', 'prune'];\n default:\n return ['worktree', 'list'];\n }\n default:\n return [input.command];\n }\n}\n\nfunction runGit(args: string[], cwd: string, signal: AbortSignal): Promise<GitOutput> {\n return new Promise((resolve) => {\n let stdout = '';\n let stderr = '';\n\n const child = spawn('git', args, {\n cwd,\n signal,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n child.stdout?.on('data', (chunk: Buffer) => {\n if (stdout.length < MAX_OUTPUT) {\n stdout += chunk.toString();\n }\n });\n\n child.stderr?.on('data', (chunk: Buffer) => {\n if (stderr.length < MAX_OUTPUT) {\n stderr += chunk.toString();\n }\n });\n\n child.on('error', (err) => {\n resolve({\n command: args[0] as GitSubcommand,\n stdout,\n stderr: err.message,\n exitCode: 1,\n truncated: stdout.length >= MAX_OUTPUT,\n });\n });\n\n child.on('close', (code) => {\n resolve({\n command: args[0] as GitSubcommand,\n stdout: stdout.slice(0, MAX_OUTPUT),\n stderr: stderr.slice(0, MAX_OUTPUT),\n exitCode: code ?? 1,\n truncated: stdout.length >= MAX_OUTPUT || stderr.length >= MAX_OUTPUT,\n });\n });\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/git.ts"],"names":["resolve"],"mappings":"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","file":"git.js","sourcesContent":["import { spawn } from 'node:child_process';\nimport { statSync } from 'node:fs';\nimport { dirname, resolve, sep } from 'node:path';\nimport { buildChildEnv } from '@wrongstack/core';\nimport type { Tool } from '@wrongstack/core';\n\ntype GitSubcommand =\n | 'status'\n | 'log'\n | 'diff'\n | 'commit'\n | 'branch'\n | 'checkout'\n | 'stash'\n | 'push'\n | 'pull'\n | 'fetch'\n | 'reset'\n | 'worktree';\n\ninterface GitInput {\n command: GitSubcommand;\n files?: string | string[];\n dry_run?: boolean;\n /** commit message for `commit` subcommand */\n message?: string;\n /** branch name for `checkout` / `branch` */\n branch?: string;\n /** pass --graph, --oneline, --stat for `log` */\n format?: 'short' | 'oneline' | 'stat' | 'graph';\n /** limit for `log` */\n limit?: number;\n /** worktree action: list, add, remove, prune */\n worktreeAction?: 'list' | 'add' | 'remove' | 'prune';\n /** path for worktree add/remove (e.g. \"../wt-feature-xyz\") */\n worktreePath?: string;\n /** create new branch when adding worktree */\n newBranch?: boolean;\n /** force operation (e.g. worktree remove --force) */\n force?: boolean;\n}\n\ninterface GitOutput {\n command: GitSubcommand;\n stdout: string;\n stderr: string;\n exitCode: number;\n truncated: boolean;\n}\n\nconst TIMEOUT_MS = 30_000;\nconst MAX_OUTPUT = 100_000;\n\nexport const gitTool: Tool<GitInput, GitOutput> = {\n name: 'git',\n category: 'Git',\n description:\n 'Run git commands. Wraps common operations: status, log, diff, commit, branch, checkout, stash, push, pull, fetch, reset, worktree.',\n usageHint:\n 'Prefer built-in subcommands over raw args. `command` is required. `message` for commits. `branch` for checkout/branch. `files` for status/diff. `format` for log.',\n permission: 'confirm',\n // Conservative: any of these may mutate. The non-mutating commands\n // (status/log/diff/branch/fetch) are still gated on `permission: 'confirm'`\n // and `MUTATING_SUBCOMMANDS` is consulted at runtime for per-call checks.\n mutating: true,\n timeoutMs: TIMEOUT_MS,\n inputSchema: {\n type: 'object',\n properties: {\n command: {\n type: 'string',\n enum: [\n 'status',\n 'log',\n 'diff',\n 'commit',\n 'branch',\n 'checkout',\n 'stash',\n 'push',\n 'pull',\n 'fetch',\n 'reset',\n 'worktree',\n ],\n description: 'Git subcommand',\n },\n files: {\n type: 'string',\n description:\n 'File(s) for status/diff: single path, comma-separated list, or \"**/*.ts\" glob',\n },\n message: { type: 'string', description: 'Commit message (required for commit)' },\n branch: { type: 'string', description: 'Branch name for checkout/branch' },\n format: {\n type: 'string',\n enum: ['short', 'oneline', 'stat', 'graph'],\n description: 'Log format (default: short)',\n },\n limit: { type: 'integer', description: 'Limit for log (default: 20)' },\n dry_run: { type: 'boolean', description: 'For commit: show what would be committed' },\n worktreeAction: {\n type: 'string',\n enum: ['list', 'add', 'remove', 'prune'],\n description: 'Worktree action: list, add, remove, prune',\n },\n worktreePath: {\n type: 'string',\n description: 'Path for worktree add/remove (e.g. \"../wt-feature-xyz\")',\n },\n newBranch: {\n type: 'boolean',\n description: 'Create new branch when adding worktree',\n },\n force: {\n type: 'boolean',\n description: 'Force operation (e.g. worktree remove --force)',\n },\n },\n required: ['command'],\n },\n async execute(input, ctx, opts) {\n if (!input?.command) throw new Error('git: command is required');\n\n if (input.command === 'commit' && !input.message) {\n return {\n command: 'commit',\n stdout: '',\n stderr: 'git commit requires a message (-m flag)',\n exitCode: 1,\n truncated: false,\n };\n }\n\n // Validate worktree paths/branches before touching the filesystem: reject\n // flag injection and any path that escapes the project root.\n if (input.command === 'worktree') {\n const guard = validateWorktreeInput(input, ctx.projectRoot);\n if (guard) return guard;\n }\n\n // Bound the search at projectRoot so a non-git project doesn't drift\n // into a parent repo (e.g. ~/repos/.git) and operate on the wrong tree.\n const gitDir = findGitDir(ctx.cwd, ctx.projectRoot);\n if (!gitDir) {\n return {\n command: input.command,\n stdout: '',\n stderr: 'Not in a git repository (within project root)',\n exitCode: 128,\n truncated: false,\n };\n }\n\n const args = buildArgs(input);\n return await runGit(args, gitDir, opts.signal);\n },\n};\n\n/**\n * Reject worktree inputs that could inject git flags or escape the project\n * root. Returns a `GitOutput` describing the rejection, or `null` if safe.\n */\nfunction validateWorktreeInput(input: GitInput, projectRoot: string): GitOutput | null {\n const reject = (stderr: string): GitOutput => ({\n command: 'worktree',\n stdout: '',\n stderr,\n exitCode: 1,\n truncated: false,\n });\n\n // Flag injection: a leading '-' would be parsed as a git option.\n if (input.branch?.startsWith('-')) return reject(`unsafe branch name: ${input.branch}`);\n if (input.worktreePath?.startsWith('-')) {\n return reject(`unsafe worktree path: ${input.worktreePath}`);\n }\n\n // Path escape: add/remove targets must resolve inside the project root.\n if (\n (input.worktreeAction === 'add' || input.worktreeAction === 'remove') &&\n input.worktreePath\n ) {\n const root = resolve(projectRoot);\n const abs = resolve(root, input.worktreePath);\n if (abs !== root && !abs.startsWith(root + sep)) {\n return reject(`unsafe worktree path (escapes project root): ${input.worktreePath}`);\n }\n }\n\n return null;\n}\n\nfunction findGitDir(cwd: string, projectRoot: string): string | null {\n const root = projectRoot;\n let dir = cwd;\n for (let i = 0; i < 20; i++) {\n try {\n const stat = statSync(`${dir}/.git`);\n // A normal repo has a `.git` directory; a linked worktree has a `.git`\n // *file* (gitlink pointing at the main repo). Accept both so the tool\n // operates inside a worktree when a subagent's cwd is a worktree dir.\n if (stat.isDirectory() || stat.isFile()) return dir;\n } catch {\n // continue\n }\n if (dir === root) break;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction buildArgs(input: GitInput): string[] {\n const limit = input.limit ?? 20;\n const files = input.files\n ? (Array.isArray(input.files) ? input.files : input.files.split(','))\n .map((s: string) => s.trim())\n .filter(Boolean)\n : [];\n\n switch (input.command) {\n case 'status':\n return ['status', ...(files.length ? ['--', ...files] : [])];\n case 'log':\n return [\n 'log',\n `--max-count=${limit}`,\n ...(input.format === 'oneline' ? ['--oneline'] : []),\n ...(input.format === 'stat' ? ['--stat'] : []),\n ...(input.format === 'graph' ? ['--oneline', '--graph', '--decorate'] : []),\n ...(input.format === 'short' || !input.format ? [] : []),\n ];\n case 'diff':\n return ['diff', '--no-color', ...(files.length ? ['--', ...files] : [])];\n case 'commit':\n return [\n 'commit',\n ...(input.dry_run ? ['--dry-run', '--porcelain'] : []),\n ...(input.message ? ['-m', input.message] : []),\n ...(files.length ? ['--', ...files] : []),\n ];\n case 'branch':\n // Validate branch name: reject names starting with '-' (flag injection).\n return input.branch\n ? ['branch', ...(input.branch.startsWith('-') ? [] : [input.branch])]\n : ['branch'];\n case 'checkout':\n return [\n 'checkout',\n ...(input.branch ? ['--', input.branch] : []),\n ...(files.length ? ['--', ...files] : []),\n ];\n case 'stash':\n return input.message ? ['stash', 'push', '-m', input.message] : ['stash', 'push'];\n case 'push':\n return ['push'];\n case 'pull':\n return ['pull'];\n case 'fetch':\n return ['fetch', ...(input.branch ? [input.branch] : ['--all'])];\n case 'reset':\n return ['reset'];\n case 'worktree':\n switch (input.worktreeAction) {\n case 'list':\n return ['worktree', 'list'];\n case 'add': {\n // git worktree add [-b <new-branch>] <path> [<commit-ish>]\n // The path comes BEFORE the branch/commit-ish. With --newBranch the\n // branch is the name to create (`-b <branch> <path>`); without it the\n // branch is an existing branch/commit to check out (`<path> <branch>`).\n if (!input.worktreePath) return ['worktree', 'list'];\n const add = ['worktree', 'add'];\n if (input.newBranch && input.branch) add.push('-b', input.branch);\n add.push(input.worktreePath);\n if (!input.newBranch && input.branch) add.push(input.branch);\n return add;\n }\n case 'remove':\n return [\n 'worktree',\n 'remove',\n ...(input.force ? ['--force'] : []),\n input.worktreePath ?? '',\n ].filter(Boolean);\n case 'prune':\n return ['worktree', 'prune'];\n default:\n return ['worktree', 'list'];\n }\n default:\n return [input.command];\n }\n}\n\nfunction runGit(args: string[], cwd: string, signal: AbortSignal): Promise<GitOutput> {\n return new Promise((resolve) => {\n let stdout = '';\n let stderr = '';\n\n const child = spawn('git', args, {\n cwd,\n signal,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n child.stdout?.on('data', (chunk: Buffer) => {\n if (stdout.length < MAX_OUTPUT) {\n stdout += chunk.toString();\n }\n });\n\n child.stderr?.on('data', (chunk: Buffer) => {\n if (stderr.length < MAX_OUTPUT) {\n stderr += chunk.toString();\n }\n });\n\n child.on('error', (err) => {\n resolve({\n command: args[0] as GitSubcommand,\n stdout,\n stderr: err.message,\n exitCode: 1,\n truncated: stdout.length >= MAX_OUTPUT,\n });\n });\n\n child.on('close', (code) => {\n resolve({\n command: args[0] as GitSubcommand,\n stdout: stdout.slice(0, MAX_OUTPUT),\n stderr: stderr.slice(0, MAX_OUTPUT),\n exitCode: code ?? 1,\n truncated: stdout.length >= MAX_OUTPUT || stderr.length >= MAX_OUTPUT,\n });\n });\n });\n}\n"]}
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import * as fs4 from 'node:fs/promises';
2
2
  import { stat } from 'node:fs/promises';
3
3
  import * as path from 'node:path';
4
- import { dirname } from 'node:path';
4
+ import { resolve, sep, dirname } from 'node:path';
5
5
  import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, stripAnsi, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan } from '@wrongstack/core';
6
- import { spawn, execSync, spawnSync } from 'node:child_process';
6
+ import { spawn, execFileSync, spawnSync } from 'node:child_process';
7
7
  import * as os from 'node:os';
8
8
  import * as dns from 'node:dns/promises';
9
9
  import * as net from 'node:net';
10
+ import { Agent } from 'undici';
10
11
  import * as fs13 from 'node:fs';
11
12
  import { statSync, mkdirSync, writeFileSync } from 'node:fs';
12
13
  import { createRequire } from 'node:module';
@@ -371,6 +372,7 @@ var replaceTool = {
371
372
  const dryRun = input.dry_run ?? false;
372
373
  const filesInput = Array.isArray(input.files) ? input.files.join(",") : input.files;
373
374
  const fileList = await resolveFiles(filesInput, ctx, globRe);
375
+ const realRoot = await fs4.realpath(ctx.projectRoot).catch(() => ctx.projectRoot);
374
376
  const results = [];
375
377
  let totalReplacements = 0;
376
378
  for (const absPath of fileList) {
@@ -386,7 +388,7 @@ var replaceTool = {
386
388
  } catch {
387
389
  continue;
388
390
  }
389
- const rel = path.relative(ctx.projectRoot, realPath);
391
+ const rel = path.relative(realRoot, realPath);
390
392
  if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
391
393
  const stat11 = await fs4.stat(realPath).catch(() => null);
392
394
  if (!stat11 || !stat11.isFile()) continue;
@@ -464,13 +466,13 @@ async function globFiles(pattern, base, extraGlob) {
464
466
  return await globNative(pattern, base, extraGlob);
465
467
  }
466
468
  function checkRg() {
467
- return new Promise((resolve6) => {
469
+ return new Promise((resolve7) => {
468
470
  try {
469
471
  const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore" });
470
- p.on("error", () => resolve6(false));
471
- p.on("close", (code) => resolve6(code === 0));
472
+ p.on("error", () => resolve7(false));
473
+ p.on("close", (code) => resolve7(code === 0));
472
474
  } catch {
473
- resolve6(false);
475
+ resolve7(false);
474
476
  }
475
477
  });
476
478
  }
@@ -482,10 +484,10 @@ function spawnRgFind(pattern, base) {
482
484
  buf += chunk.toString();
483
485
  });
484
486
  return {
485
- promise: new Promise((resolve6, reject) => {
487
+ promise: new Promise((resolve7, reject) => {
486
488
  child.on("error", reject);
487
489
  child.on("close", () => {
488
- resolve6(buf.split("\n").filter(Boolean));
490
+ resolve7(buf.split("\n").filter(Boolean));
489
491
  });
490
492
  })
491
493
  };
@@ -654,13 +656,13 @@ var grepTool = {
654
656
  }
655
657
  };
656
658
  async function detectRg(signal) {
657
- return new Promise((resolve6) => {
659
+ return new Promise((resolve7) => {
658
660
  try {
659
661
  const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", signal });
660
- p.on("error", () => resolve6(false));
661
- p.on("close", (code) => resolve6(code === 0));
662
+ p.on("error", () => resolve7(false));
663
+ p.on("close", (code) => resolve7(code === 0));
662
664
  } catch {
663
- resolve6(false);
665
+ resolve7(false);
664
666
  }
665
667
  });
666
668
  }
@@ -1371,10 +1373,10 @@ var bashTool = {
1371
1373
  queue.push(c);
1372
1374
  }
1373
1375
  };
1374
- const next = () => new Promise((resolve6) => {
1376
+ const next = () => new Promise((resolve7) => {
1375
1377
  const c = queue.shift();
1376
- if (c) resolve6(c);
1377
- else resolveNext = resolve6;
1378
+ if (c) resolve7(c);
1379
+ else resolveNext = resolve7;
1378
1380
  });
1379
1381
  let lastFlush = Date.now();
1380
1382
  const flush = () => {
@@ -1488,8 +1490,20 @@ var BLOCKED_ARG_PATTERNS = {
1488
1490
  // python -c/--command executes arbitrary code; python -m runs modules
1489
1491
  python: [/-c$/, /^--command$/, /^-m$/, /^--module$/],
1490
1492
  // git --exec=<cmd> runs arbitrary commands via upload-pack/receive-pack;
1491
- // -C <dir> changes working directory, bypassing cwd sandbox
1492
- git: [/^--exec=/, /^--upload-pack=/, /^--receive-pack=/, /^-C$/],
1493
+ // -C <dir> changes working directory, bypassing cwd sandbox;
1494
+ // -c/--config <k>=<v> injects config that runs commands
1495
+ // (e.g. core.sshCommand, core.pager, http.proxy, alias.x=!cmd).
1496
+ git: [
1497
+ /^--exec=/,
1498
+ /^--upload-pack=/,
1499
+ /^--receive-pack=/,
1500
+ /^-C$/,
1501
+ /^-c$/,
1502
+ /^--config$/,
1503
+ /^-c=/,
1504
+ /^--config=/,
1505
+ /^--config-env=/
1506
+ ],
1493
1507
  // node -r/--require preloads arbitrary modules; --eval executes code
1494
1508
  node: [/^-r$/, /^--require$/, /^-e$/, /^--eval$/, /^--prof-process$/],
1495
1509
  // go run could execute arbitrary .go files; -ldflags could inject build-time code
@@ -1612,7 +1626,7 @@ var execTool = {
1612
1626
  }
1613
1627
  };
1614
1628
  function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
1615
- return new Promise((resolve6) => {
1629
+ return new Promise((resolve7) => {
1616
1630
  let stdout = "";
1617
1631
  let stderr = "";
1618
1632
  let killed = false;
@@ -1646,7 +1660,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
1646
1660
  const durationMs = Date.now() - startedAt;
1647
1661
  const exitCode = killed ? 124 : code ?? 1;
1648
1662
  registry.afterCall(durationMs, exitCode !== 0);
1649
- resolve6({
1663
+ resolve7({
1650
1664
  command: cmd,
1651
1665
  args,
1652
1666
  stdout: stdout.slice(0, MAX_OUTPUT2),
@@ -1660,7 +1674,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
1660
1674
  clearTimeout(timer);
1661
1675
  if (typeof pid === "number") registry.unregister(pid);
1662
1676
  registry.afterCall(Date.now() - startedAt, true);
1663
- resolve6({
1677
+ resolve7({
1664
1678
  command: cmd,
1665
1679
  args,
1666
1680
  stdout: stdout.slice(0, MAX_OUTPUT2),
@@ -1675,6 +1689,48 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
1675
1689
  var MAX_BYTES2 = 131072;
1676
1690
  var TIMEOUT_MS2 = 2e4;
1677
1691
  var ALLOW_PRIVATE = process.env["WRONGSTACK_FETCH_ALLOW_PRIVATE"] === "1";
1692
+ function guardedLookup(hostname, options, callback) {
1693
+ dns.lookup(hostname, { all: true }).then((records) => {
1694
+ const family = options?.family;
1695
+ const byFamily = family === 4 || family === 6 ? records.filter((r) => r.family === family) : records;
1696
+ const list = byFamily.length > 0 ? byFamily : records;
1697
+ if (!ALLOW_PRIVATE) {
1698
+ for (const r of list) {
1699
+ const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);
1700
+ if (bad) {
1701
+ callback(
1702
+ Object.assign(new Error(`fetch: resolved to private address ${r.address}`), {
1703
+ code: "EAI_FAIL"
1704
+ })
1705
+ );
1706
+ return;
1707
+ }
1708
+ }
1709
+ }
1710
+ if (options?.all) {
1711
+ callback(
1712
+ null,
1713
+ list.map((r) => ({ address: r.address, family: r.family }))
1714
+ );
1715
+ return;
1716
+ }
1717
+ const first = list[0];
1718
+ if (!first) {
1719
+ callback(
1720
+ Object.assign(new Error(`fetch: no address for ${hostname}`), { code: "ENOTFOUND" })
1721
+ );
1722
+ return;
1723
+ }
1724
+ callback(null, first.address, first.family);
1725
+ }).catch((err) => callback(err));
1726
+ }
1727
+ var pinnedAgent;
1728
+ function getPinnedDispatcher() {
1729
+ if (!pinnedAgent) {
1730
+ pinnedAgent = new Agent({ connect: { lookup: guardedLookup } });
1731
+ }
1732
+ return pinnedAgent;
1733
+ }
1678
1734
  async function fetchWithRedirectLimit(url, maxRedirects, signal) {
1679
1735
  const headers = {
1680
1736
  "user-agent": "WrongStack/1.0 (+https://wrongstack.com)",
@@ -1691,11 +1747,13 @@ async function fetchWithRedirectLimit(url, maxRedirects, signal) {
1691
1747
  throw new Error("fetch: redirect to http:// blocked (HTTPS required by default)");
1692
1748
  }
1693
1749
  await assertNotPrivate(parsed.hostname);
1694
- const res = await fetch(currentUrl, {
1750
+ const init = {
1695
1751
  redirect: "manual",
1696
1752
  signal,
1697
- headers
1698
- });
1753
+ headers,
1754
+ dispatcher: getPinnedDispatcher()
1755
+ };
1756
+ const res = await fetch(currentUrl, init);
1699
1757
  if (res.status < 300 || res.status > 399) {
1700
1758
  return res;
1701
1759
  }
@@ -2440,6 +2498,10 @@ var gitTool = {
2440
2498
  truncated: false
2441
2499
  };
2442
2500
  }
2501
+ if (input.command === "worktree") {
2502
+ const guard = validateWorktreeInput(input, ctx.projectRoot);
2503
+ if (guard) return guard;
2504
+ }
2443
2505
  const gitDir = findGitDir(ctx.cwd, ctx.projectRoot);
2444
2506
  if (!gitDir) {
2445
2507
  return {
@@ -2454,13 +2516,34 @@ var gitTool = {
2454
2516
  return await runGit(args, gitDir, opts.signal);
2455
2517
  }
2456
2518
  };
2519
+ function validateWorktreeInput(input, projectRoot) {
2520
+ const reject = (stderr) => ({
2521
+ command: "worktree",
2522
+ stdout: "",
2523
+ stderr,
2524
+ exitCode: 1,
2525
+ truncated: false
2526
+ });
2527
+ if (input.branch?.startsWith("-")) return reject(`unsafe branch name: ${input.branch}`);
2528
+ if (input.worktreePath?.startsWith("-")) {
2529
+ return reject(`unsafe worktree path: ${input.worktreePath}`);
2530
+ }
2531
+ if ((input.worktreeAction === "add" || input.worktreeAction === "remove") && input.worktreePath) {
2532
+ const root = resolve(projectRoot);
2533
+ const abs = resolve(root, input.worktreePath);
2534
+ if (abs !== root && !abs.startsWith(root + sep)) {
2535
+ return reject(`unsafe worktree path (escapes project root): ${input.worktreePath}`);
2536
+ }
2537
+ }
2538
+ return null;
2539
+ }
2457
2540
  function findGitDir(cwd, projectRoot) {
2458
2541
  const root = projectRoot;
2459
2542
  let dir = cwd;
2460
2543
  for (let i = 0; i < 20; i++) {
2461
2544
  try {
2462
2545
  const stat11 = statSync(`${dir}/.git`);
2463
- if (stat11.isDirectory()) return dir;
2546
+ if (stat11.isDirectory() || stat11.isFile()) return dir;
2464
2547
  } catch {
2465
2548
  }
2466
2549
  if (dir === root) break;
@@ -2516,14 +2599,14 @@ function buildArgs(input) {
2516
2599
  switch (input.worktreeAction) {
2517
2600
  case "list":
2518
2601
  return ["worktree", "list"];
2519
- case "add":
2520
- return [
2521
- "worktree",
2522
- "add",
2523
- ...input.newBranch ? ["-b"] : [],
2524
- ...input.branch ? [input.branch] : [],
2525
- input.worktreePath ?? ""
2526
- ].filter(Boolean);
2602
+ case "add": {
2603
+ if (!input.worktreePath) return ["worktree", "list"];
2604
+ const add = ["worktree", "add"];
2605
+ if (input.newBranch && input.branch) add.push("-b", input.branch);
2606
+ add.push(input.worktreePath);
2607
+ if (!input.newBranch && input.branch) add.push(input.branch);
2608
+ return add;
2609
+ }
2527
2610
  case "remove":
2528
2611
  return [
2529
2612
  "worktree",
@@ -2541,7 +2624,7 @@ function buildArgs(input) {
2541
2624
  }
2542
2625
  }
2543
2626
  function runGit(args, cwd, signal) {
2544
- return new Promise((resolve6) => {
2627
+ return new Promise((resolve7) => {
2545
2628
  let stdout = "";
2546
2629
  let stderr = "";
2547
2630
  const child = spawn("git", args, {
@@ -2561,7 +2644,7 @@ function runGit(args, cwd, signal) {
2561
2644
  }
2562
2645
  });
2563
2646
  child.on("error", (err) => {
2564
- resolve6({
2647
+ resolve7({
2565
2648
  command: args[0],
2566
2649
  stdout,
2567
2650
  stderr: err.message,
@@ -2570,7 +2653,7 @@ function runGit(args, cwd, signal) {
2570
2653
  });
2571
2654
  });
2572
2655
  child.on("close", (code) => {
2573
- resolve6({
2656
+ resolve7({
2574
2657
  command: args[0],
2575
2658
  stdout: stdout.slice(0, MAX_OUTPUT3),
2576
2659
  stderr: stderr.slice(0, MAX_OUTPUT3),
@@ -2666,7 +2749,7 @@ function stripPathComponents(p, strip) {
2666
2749
  return parts.slice(strip).join("/");
2667
2750
  }
2668
2751
  function runPatch(args, cwd, signal) {
2669
- return new Promise((resolve6) => {
2752
+ return new Promise((resolve7) => {
2670
2753
  let stdout = "";
2671
2754
  let stderr = "";
2672
2755
  const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
@@ -2677,8 +2760,8 @@ function runPatch(args, cwd, signal) {
2677
2760
  child.stderr?.on("data", (c) => {
2678
2761
  stderr += c.toString();
2679
2762
  });
2680
- child.on("close", (code) => resolve6({ exitCode: code ?? 1, stdout, stderr }));
2681
- child.on("error", (e) => resolve6({ exitCode: 1, stdout: "", stderr: e.message }));
2763
+ child.on("close", (code) => resolve7({ exitCode: code ?? 1, stdout, stderr }));
2764
+ child.on("error", (e) => resolve7({ exitCode: 1, stdout: "", stderr: e.message }));
2682
2765
  });
2683
2766
  }
2684
2767
  function extractPatchedFiles(output) {
@@ -2880,7 +2963,7 @@ function findGitDir2(cwd) {
2880
2963
  return null;
2881
2964
  }
2882
2965
  function runGit2(args, cwd, signal) {
2883
- return new Promise((resolve6) => {
2966
+ return new Promise((resolve7) => {
2884
2967
  let stdout = "";
2885
2968
  let stderr = "";
2886
2969
  const child = spawn("git", args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"] });
@@ -2890,8 +2973,8 @@ function runGit2(args, cwd, signal) {
2890
2973
  child.stderr?.on("data", (c) => {
2891
2974
  stderr += c.toString();
2892
2975
  });
2893
- child.on("close", (code) => resolve6({ stdout, stderr, exitCode: code ?? 0 }));
2894
- child.on("error", (e) => resolve6({ stdout: "", stderr: e.message, exitCode: 1 }));
2976
+ child.on("close", (code) => resolve7({ stdout, stderr, exitCode: code ?? 0 }));
2977
+ child.on("error", (e) => resolve7({ stdout: "", stderr: e.message, exitCode: 1 }));
2895
2978
  });
2896
2979
  }
2897
2980
  async function fileDiff(input, ctx, signal) {
@@ -3142,8 +3225,8 @@ async function* spawnStream(opts) {
3142
3225
  let spawnFailed = false;
3143
3226
  for (; ; ) {
3144
3227
  while (queue.length === 0) {
3145
- await new Promise((resolve6) => {
3146
- waiter = resolve6;
3228
+ await new Promise((resolve7) => {
3229
+ waiter = resolve7;
3147
3230
  });
3148
3231
  }
3149
3232
  const chunk = queue.shift();
@@ -3862,7 +3945,7 @@ async function detectManager2(cwd) {
3862
3945
  return "npm";
3863
3946
  }
3864
3947
  function runOutdated(manager, args, cwd, signal) {
3865
- return new Promise((resolve6) => {
3948
+ return new Promise((resolve7) => {
3866
3949
  let stdout = "";
3867
3950
  let stderr = "";
3868
3951
  const MAX = 1e5;
@@ -3875,10 +3958,10 @@ function runOutdated(manager, args, cwd, signal) {
3875
3958
  });
3876
3959
  child.on("close", (code) => {
3877
3960
  const result = parseOutdatedOutput(stdout, code ?? 0);
3878
- resolve6(result);
3961
+ resolve7(result);
3879
3962
  });
3880
3963
  child.on("error", (e) => {
3881
- resolve6({
3964
+ resolve7({
3882
3965
  exit_code: 1,
3883
3966
  packages: [],
3884
3967
  total: 0,
@@ -4002,21 +4085,39 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
4002
4085
  };
4003
4086
  }
4004
4087
  args.push("--timestamps", service);
4005
- return new Promise((resolve6) => {
4088
+ return new Promise((resolve7) => {
4006
4089
  let stdout = "";
4007
4090
  let stderr = "";
4008
4091
  const MAX = 2e5;
4092
+ let settled = false;
4093
+ const empty = () => ({
4094
+ source: `docker:${service}`,
4095
+ entries: [],
4096
+ total: 0,
4097
+ truncated: false,
4098
+ stream_mode: false
4099
+ });
4100
+ const finish = (result) => {
4101
+ if (settled) return;
4102
+ settled = true;
4103
+ clearTimeout(timer);
4104
+ resolve7(result);
4105
+ };
4009
4106
  const child = spawn("docker", args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"] });
4107
+ const timer = setTimeout(() => {
4108
+ child.kill("SIGTERM");
4109
+ finish(empty());
4110
+ }, DOCKER_LOGS_TIMEOUT_MS);
4010
4111
  child.stdout?.on("data", (c) => {
4011
4112
  if (stdout.length < MAX) stdout += c.toString();
4012
4113
  });
4013
4114
  child.stderr?.on("data", (c) => {
4014
4115
  if (stderr.length < MAX) stderr += c.toString();
4015
4116
  });
4016
- child.on("close", (code) => {
4117
+ child.on("close", () => {
4017
4118
  const output = stdout + stderr;
4018
4119
  const entries = parseLogLines(output, filterRe);
4019
- resolve6({
4120
+ finish({
4020
4121
  source: `docker:${service}`,
4021
4122
  entries,
4022
4123
  total: entries.length,
@@ -4024,17 +4125,10 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
4024
4125
  stream_mode: false
4025
4126
  });
4026
4127
  });
4027
- child.on("error", (e) => {
4028
- resolve6({
4029
- source: `docker:${service}`,
4030
- entries: [],
4031
- total: 0,
4032
- truncated: false,
4033
- stream_mode: false
4034
- });
4035
- });
4128
+ child.on("error", () => finish(empty()));
4036
4129
  });
4037
4130
  }
4131
+ var DOCKER_LOGS_TIMEOUT_MS = 3e3;
4038
4132
  var MAX_TAIL_LINES = 1e5;
4039
4133
  async function fileLogs(path18, lines, filterRe, stream) {
4040
4134
  const { createInterface } = await import('node:readline');
@@ -5747,7 +5841,7 @@ function syncGoParse(filePath, content, lang) {
5747
5841
  mkdirSync(tmpDir, { recursive: true });
5748
5842
  const scriptPath = path.join(tmpDir, "parse.go");
5749
5843
  writeFileSync(scriptPath, GO_PARSE_SCRIPT, "utf8");
5750
- const stdout = execSync(`go run "${scriptPath}"`, {
5844
+ const stdout = execFileSync("go", ["run", scriptPath], {
5751
5845
  input: content,
5752
5846
  timeout: 15e3,
5753
5847
  encoding: "utf8",
@@ -5993,7 +6087,7 @@ function syncPyParse(filePath, lang) {
5993
6087
  mkdirSync(tmpDir, { recursive: true });
5994
6088
  const scriptPath = path.join(tmpDir, "parse.py");
5995
6089
  writeFileSync(scriptPath, PY_PARSE_SCRIPT, "utf8");
5996
- const stdout = execSync(`python "${scriptPath}" "${filePath}"`, {
6090
+ const stdout = execFileSync("python", [scriptPath, filePath], {
5997
6091
  timeout: 15e3,
5998
6092
  encoding: "utf8",
5999
6093
  windowsHide: true
@@ -6031,11 +6125,19 @@ function parseSymbols4(opts) {
6031
6125
  }
6032
6126
  function checkNativeParser() {
6033
6127
  try {
6034
- execSync("rustc --version", { stdio: "pipe" });
6128
+ execFileSync("rustc", ["--version"], { stdio: "pipe" });
6035
6129
  const toolsDir = path.join(process.cwd(), "tools");
6036
6130
  try {
6037
- execSync(
6038
- "cargo metadata --no-deps --format-version 1 --manifest-path " + path.join(toolsDir, "Cargo.toml"),
6131
+ execFileSync(
6132
+ "cargo",
6133
+ [
6134
+ "metadata",
6135
+ "--no-deps",
6136
+ "--format-version",
6137
+ "1",
6138
+ "--manifest-path",
6139
+ path.join(toolsDir, "Cargo.toml")
6140
+ ],
6039
6141
  { stdio: "pipe" }
6040
6142
  );
6041
6143
  return true;