@wrongstack/tools 0.6.0 → 0.6.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/builtin.js +59 -55
- package/dist/builtin.js.map +1 -1
- package/dist/git.js +9 -0
- package/dist/git.js.map +1 -1
- package/dist/index.js +59 -55
- package/dist/index.js.map +1 -1
- package/dist/outdated.js +2 -7
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +59 -55
- package/dist/pack.js.map +1 -1
- package/package.json +2 -2
package/dist/git.js
CHANGED
|
@@ -55,6 +55,15 @@ var gitTool = {
|
|
|
55
55
|
},
|
|
56
56
|
async execute(input, ctx, opts) {
|
|
57
57
|
if (!input?.command) throw new Error("git: command is required");
|
|
58
|
+
if (input.command === "commit" && !input.message) {
|
|
59
|
+
return {
|
|
60
|
+
command: "commit",
|
|
61
|
+
stdout: "",
|
|
62
|
+
stderr: "git commit requires a message (-m flag)",
|
|
63
|
+
exitCode: 1,
|
|
64
|
+
truncated: false
|
|
65
|
+
};
|
|
66
|
+
}
|
|
58
67
|
const gitDir = findGitDir(ctx.cwd, ctx.projectRoot);
|
|
59
68
|
if (!gitDir) {
|
|
60
69
|
return {
|
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\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}\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.',\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 ],\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 },\n required: ['command'],\n },\n async execute(input, ctx, opts) {\n if (!input?.command) throw new Error('git: command is required');\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 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":[],"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\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}\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.',\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 ],\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 },\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 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,4 +1,5 @@
|
|
|
1
1
|
import * as fs4 from 'fs/promises';
|
|
2
|
+
import { stat } from 'fs/promises';
|
|
2
3
|
import * as path from 'path';
|
|
3
4
|
import { dirname } from 'path';
|
|
4
5
|
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, stripAnsi, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan } from '@wrongstack/core';
|
|
@@ -8,12 +9,7 @@ import * as dns from 'dns/promises';
|
|
|
8
9
|
import * as net from 'net';
|
|
9
10
|
import { statSync } from 'fs';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
13
|
-
}) : x)(function(x) {
|
|
14
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
15
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
16
|
-
});
|
|
12
|
+
// src/read.ts
|
|
17
13
|
function resolvePath(input, ctx) {
|
|
18
14
|
return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
|
|
19
15
|
}
|
|
@@ -67,17 +63,17 @@ var readTool = {
|
|
|
67
63
|
async execute(input, ctx) {
|
|
68
64
|
if (!input?.path) throw new Error("read: path is required");
|
|
69
65
|
const absPath = safeResolve(input.path, ctx);
|
|
70
|
-
let
|
|
66
|
+
let stat10;
|
|
71
67
|
try {
|
|
72
|
-
|
|
68
|
+
stat10 = await fs4.stat(absPath);
|
|
73
69
|
} catch (err) {
|
|
74
70
|
const code = err.code;
|
|
75
71
|
if (code === "ENOENT") throw new Error(`read: file not found "${input.path}"`);
|
|
76
72
|
throw new Error(`read: failed to stat "${input.path}": ${err instanceof Error ? err.message : String(err)}`);
|
|
77
73
|
}
|
|
78
|
-
if (!
|
|
79
|
-
if (
|
|
80
|
-
throw new Error(`read: file too large (${
|
|
74
|
+
if (!stat10.isFile()) throw new Error(`read: "${input.path}" is not a regular file`);
|
|
75
|
+
if (stat10.size > MAX_BYTES) {
|
|
76
|
+
throw new Error(`read: file too large (${stat10.size} bytes, limit ${MAX_BYTES})`);
|
|
81
77
|
}
|
|
82
78
|
const buf = await fs4.readFile(absPath);
|
|
83
79
|
if (isBinaryBuffer(buf)) {
|
|
@@ -89,14 +85,14 @@ var readTool = {
|
|
|
89
85
|
const offset = Math.max(1, input.offset ?? 1);
|
|
90
86
|
const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
|
|
91
87
|
if (limit === 0) {
|
|
92
|
-
ctx.recordRead(absPath,
|
|
88
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
93
89
|
return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
|
|
94
90
|
}
|
|
95
91
|
const slice = allLines.slice(offset - 1, offset - 1 + limit);
|
|
96
92
|
const truncated = offset - 1 + slice.length < total;
|
|
97
93
|
const width = String(offset + slice.length - 1).length;
|
|
98
94
|
const numbered = slice.map((line, i) => `${String(offset + i).padStart(width, " ")}\u2192${line}`).join("\n");
|
|
99
|
-
ctx.recordRead(absPath,
|
|
95
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
100
96
|
return {
|
|
101
97
|
text: numbered,
|
|
102
98
|
total_lines: total,
|
|
@@ -128,12 +124,12 @@ var writeTool = {
|
|
|
128
124
|
let existed = false;
|
|
129
125
|
let prev = "";
|
|
130
126
|
try {
|
|
131
|
-
const
|
|
132
|
-
existed =
|
|
127
|
+
const stat11 = await fs4.stat(absPath);
|
|
128
|
+
existed = stat11.isFile();
|
|
133
129
|
if (existed) {
|
|
134
130
|
if (!ctx.hasRead(absPath)) {
|
|
135
131
|
prev = await fs4.readFile(absPath, "utf8");
|
|
136
|
-
ctx.recordRead(absPath,
|
|
132
|
+
ctx.recordRead(absPath, stat11.mtimeMs);
|
|
137
133
|
} else {
|
|
138
134
|
prev = await fs4.readFile(absPath, "utf8");
|
|
139
135
|
}
|
|
@@ -146,8 +142,8 @@ var writeTool = {
|
|
|
146
142
|
await atomicWrite(absPath, input.content);
|
|
147
143
|
const diff = existed ? unifiedDiff(prev, input.content, { fromFile: input.path, toFile: input.path }) : `+++ ${input.path}
|
|
148
144
|
+ (new file, ${input.content.split("\n").length} lines)`;
|
|
149
|
-
const
|
|
150
|
-
ctx.recordRead(absPath,
|
|
145
|
+
const stat10 = await fs4.stat(absPath);
|
|
146
|
+
ctx.recordRead(absPath, stat10.mtimeMs);
|
|
151
147
|
ctx.session.recordFileChange({
|
|
152
148
|
path: absPath,
|
|
153
149
|
action: existed ? "modified" : "created",
|
|
@@ -186,13 +182,13 @@ var editTool = {
|
|
|
186
182
|
if (input.new_string === void 0) throw new Error("edit: new_string is required");
|
|
187
183
|
if (input.old_string === "") throw new Error("edit: old_string cannot be empty");
|
|
188
184
|
const absPath = safeResolve(input.path, ctx);
|
|
189
|
-
const
|
|
185
|
+
const stat10 = await fs4.stat(absPath).catch((err) => {
|
|
190
186
|
if (err.code === "ENOENT") {
|
|
191
187
|
throw new Error(`edit: file "${input.path}" does not exist. Use \`write\` instead.`);
|
|
192
188
|
}
|
|
193
189
|
throw err;
|
|
194
190
|
});
|
|
195
|
-
if (!
|
|
191
|
+
if (!stat10.isFile()) throw new Error(`edit: "${input.path}" is not a regular file`);
|
|
196
192
|
if (!ctx.hasRead(absPath)) {
|
|
197
193
|
throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
|
|
198
194
|
}
|
|
@@ -384,8 +380,8 @@ var replaceTool = {
|
|
|
384
380
|
}
|
|
385
381
|
const rel = path.relative(ctx.projectRoot, realPath);
|
|
386
382
|
if (rel.startsWith("..") || path.isAbsolute(rel)) continue;
|
|
387
|
-
const
|
|
388
|
-
if (!
|
|
383
|
+
const stat10 = await fs4.stat(realPath).catch(() => null);
|
|
384
|
+
if (!stat10 || !stat10.isFile()) continue;
|
|
389
385
|
let content;
|
|
390
386
|
try {
|
|
391
387
|
const buf = await fs4.readFile(realPath);
|
|
@@ -410,7 +406,7 @@ var replaceTool = {
|
|
|
410
406
|
totalReplacements += count;
|
|
411
407
|
if (!dryRun) {
|
|
412
408
|
const newContent = toStyle(newContentLf, style);
|
|
413
|
-
await atomicWrite(realPath, newContent, { mode:
|
|
409
|
+
await atomicWrite(realPath, newContent, { mode: stat10.mode & 511 });
|
|
414
410
|
}
|
|
415
411
|
const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
|
|
416
412
|
fromFile: absPath,
|
|
@@ -440,8 +436,8 @@ async function resolveFiles(filesInput, ctx, extraGlob) {
|
|
|
440
436
|
const resolved = [];
|
|
441
437
|
for (const p of parts) {
|
|
442
438
|
const absPath = safeResolve(p, ctx);
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
439
|
+
const stat10 = await fs4.stat(absPath).catch(() => null);
|
|
440
|
+
if (stat10?.isFile()) {
|
|
445
441
|
resolved.push(absPath);
|
|
446
442
|
}
|
|
447
443
|
}
|
|
@@ -500,8 +496,8 @@ async function globNative(pattern, base, extraGlob) {
|
|
|
500
496
|
if (DEFAULT_IGNORE.includes(e.name)) continue;
|
|
501
497
|
const full = path.join(dir, e.name);
|
|
502
498
|
try {
|
|
503
|
-
const
|
|
504
|
-
if (
|
|
499
|
+
const stat10 = await fs4.lstat(full);
|
|
500
|
+
if (stat10.isSymbolicLink()) continue;
|
|
505
501
|
} catch {
|
|
506
502
|
continue;
|
|
507
503
|
}
|
|
@@ -817,8 +813,8 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
817
813
|
if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;
|
|
818
814
|
if (globRe) globRe.lastIndex = 0;
|
|
819
815
|
try {
|
|
820
|
-
const
|
|
821
|
-
if (
|
|
816
|
+
const stat10 = await fs4.stat(full);
|
|
817
|
+
if (stat10.size > 1e6) continue;
|
|
822
818
|
const head = await fs4.readFile(full);
|
|
823
819
|
if (isBinaryBuffer(head)) continue;
|
|
824
820
|
const text = head.toString("utf8");
|
|
@@ -2405,6 +2401,15 @@ var gitTool = {
|
|
|
2405
2401
|
},
|
|
2406
2402
|
async execute(input, ctx, opts) {
|
|
2407
2403
|
if (!input?.command) throw new Error("git: command is required");
|
|
2404
|
+
if (input.command === "commit" && !input.message) {
|
|
2405
|
+
return {
|
|
2406
|
+
command: "commit",
|
|
2407
|
+
stdout: "",
|
|
2408
|
+
stderr: "git commit requires a message (-m flag)",
|
|
2409
|
+
exitCode: 1,
|
|
2410
|
+
truncated: false
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2408
2413
|
const gitDir = findGitDir(ctx.cwd, ctx.projectRoot);
|
|
2409
2414
|
if (!gitDir) {
|
|
2410
2415
|
return {
|
|
@@ -2424,8 +2429,8 @@ function findGitDir(cwd, projectRoot) {
|
|
|
2424
2429
|
let dir = cwd;
|
|
2425
2430
|
for (let i = 0; i < 20; i++) {
|
|
2426
2431
|
try {
|
|
2427
|
-
const
|
|
2428
|
-
if (
|
|
2432
|
+
const stat10 = statSync(`${dir}/.git`);
|
|
2433
|
+
if (stat10.isDirectory()) return dir;
|
|
2429
2434
|
} catch {
|
|
2430
2435
|
}
|
|
2431
2436
|
if (dir === root) break;
|
|
@@ -2810,8 +2815,8 @@ function findGitDir2(cwd) {
|
|
|
2810
2815
|
let dir = cwd;
|
|
2811
2816
|
for (let i = 0; i < 20; i++) {
|
|
2812
2817
|
try {
|
|
2813
|
-
const
|
|
2814
|
-
if (
|
|
2818
|
+
const stat10 = statSync(path.join(dir, ".git"));
|
|
2819
|
+
if (stat10.isDirectory()) return dir;
|
|
2815
2820
|
} catch {
|
|
2816
2821
|
}
|
|
2817
2822
|
const parent = path.dirname(dir);
|
|
@@ -2850,8 +2855,8 @@ async function fileDiff(input, ctx, signal) {
|
|
|
2850
2855
|
const results = [];
|
|
2851
2856
|
for (const file of files) {
|
|
2852
2857
|
const absPath = safeResolve(file, ctx);
|
|
2853
|
-
const
|
|
2854
|
-
if (!
|
|
2858
|
+
const stat10 = await fs4.stat(absPath).catch(() => null);
|
|
2859
|
+
if (!stat10?.isFile()) continue;
|
|
2855
2860
|
const content = await fs4.readFile(absPath, "utf8");
|
|
2856
2861
|
const lines = content.split(/\r?\n/);
|
|
2857
2862
|
results.push(`--- ${file}
|
|
@@ -3193,11 +3198,11 @@ var lintTool = {
|
|
|
3193
3198
|
}
|
|
3194
3199
|
};
|
|
3195
3200
|
async function detectLinter(cwd) {
|
|
3196
|
-
const { stat:
|
|
3201
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
3197
3202
|
const checks = ["biome.json", ".eslintrc.json", "tslint.json", ".eslintrc.js", "tsconfig.json"];
|
|
3198
3203
|
for (const f of checks) {
|
|
3199
3204
|
try {
|
|
3200
|
-
await
|
|
3205
|
+
await stat10(`${cwd}/${f}`);
|
|
3201
3206
|
if (f.includes("biome")) return "biome";
|
|
3202
3207
|
if (f.includes("eslint")) return "eslint";
|
|
3203
3208
|
if (f.includes("tslint")) return "tslint";
|
|
@@ -3292,13 +3297,13 @@ var formatTool = {
|
|
|
3292
3297
|
}
|
|
3293
3298
|
};
|
|
3294
3299
|
async function detectFixer(cwd) {
|
|
3295
|
-
const { stat:
|
|
3300
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
3296
3301
|
try {
|
|
3297
|
-
await
|
|
3302
|
+
await stat10(`${cwd}/biome.json`);
|
|
3298
3303
|
return "biome";
|
|
3299
3304
|
} catch {
|
|
3300
3305
|
try {
|
|
3301
|
-
await
|
|
3306
|
+
await stat10(`${cwd}/.prettierrc`);
|
|
3302
3307
|
return "prettier";
|
|
3303
3308
|
} catch {
|
|
3304
3309
|
return "biome";
|
|
@@ -3374,11 +3379,11 @@ var typecheckTool = {
|
|
|
3374
3379
|
}
|
|
3375
3380
|
};
|
|
3376
3381
|
async function findTsConfig(cwd) {
|
|
3377
|
-
const { stat:
|
|
3382
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
3378
3383
|
const candidates = ["tsconfig.json", "tsconfig.base.json"];
|
|
3379
3384
|
for (const f of candidates) {
|
|
3380
3385
|
try {
|
|
3381
|
-
const s = await
|
|
3386
|
+
const s = await stat10(path.join(cwd, f));
|
|
3382
3387
|
if (s.isFile()) return path.join(cwd, f);
|
|
3383
3388
|
} catch {
|
|
3384
3389
|
}
|
|
@@ -3455,11 +3460,11 @@ var testTool = {
|
|
|
3455
3460
|
}
|
|
3456
3461
|
};
|
|
3457
3462
|
async function detectRunner(cwd) {
|
|
3458
|
-
const { stat:
|
|
3463
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
3459
3464
|
const candidates = ["vitest.config.ts", "jest.config.js", ".mocharc.json"];
|
|
3460
3465
|
for (const f of candidates) {
|
|
3461
3466
|
try {
|
|
3462
|
-
await
|
|
3467
|
+
await stat10(path.join(cwd, f));
|
|
3463
3468
|
if (f.includes("vitest")) return "vitest";
|
|
3464
3469
|
if (f.includes("jest")) return "jest";
|
|
3465
3470
|
if (f.includes("mocha")) return "mocha";
|
|
@@ -3630,13 +3635,13 @@ var installTool = {
|
|
|
3630
3635
|
}
|
|
3631
3636
|
};
|
|
3632
3637
|
async function detectPackageManager(cwd) {
|
|
3633
|
-
const { stat:
|
|
3638
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
3634
3639
|
try {
|
|
3635
|
-
await
|
|
3640
|
+
await stat10(`${cwd}/pnpm-lock.yaml`);
|
|
3636
3641
|
return "pnpm";
|
|
3637
3642
|
} catch {
|
|
3638
3643
|
try {
|
|
3639
|
-
await
|
|
3644
|
+
await stat10(`${cwd}/yarn.lock`);
|
|
3640
3645
|
return "yarn";
|
|
3641
3646
|
} catch {
|
|
3642
3647
|
return "npm";
|
|
@@ -3695,14 +3700,14 @@ var auditTool = {
|
|
|
3695
3700
|
}
|
|
3696
3701
|
};
|
|
3697
3702
|
async function detectManager(cwd) {
|
|
3698
|
-
const { stat:
|
|
3703
|
+
const { stat: stat10 } = await import('fs/promises');
|
|
3699
3704
|
try {
|
|
3700
|
-
await
|
|
3705
|
+
await stat10(`${cwd}/pnpm-lock.yaml`);
|
|
3701
3706
|
return "pnpm";
|
|
3702
3707
|
} catch {
|
|
3703
3708
|
}
|
|
3704
3709
|
try {
|
|
3705
|
-
await
|
|
3710
|
+
await stat10(`${cwd}/yarn.lock`);
|
|
3706
3711
|
return "yarn";
|
|
3707
3712
|
} catch {
|
|
3708
3713
|
}
|
|
@@ -3790,14 +3795,13 @@ var outdatedTool = {
|
|
|
3790
3795
|
}
|
|
3791
3796
|
};
|
|
3792
3797
|
async function detectManager2(cwd) {
|
|
3793
|
-
const { stat: stat9 } = __require("fs/promises");
|
|
3794
3798
|
try {
|
|
3795
|
-
await
|
|
3799
|
+
await stat(`${cwd}/pnpm-lock.yaml`);
|
|
3796
3800
|
return "pnpm";
|
|
3797
3801
|
} catch {
|
|
3798
3802
|
}
|
|
3799
3803
|
try {
|
|
3800
|
-
await
|
|
3804
|
+
await stat(`${cwd}/yarn.lock`);
|
|
3801
3805
|
return "yarn";
|
|
3802
3806
|
} catch {
|
|
3803
3807
|
}
|
|
@@ -4133,8 +4137,8 @@ async function resolveFiles2(filesInput, cwd) {
|
|
|
4133
4137
|
for (const f of files) {
|
|
4134
4138
|
const absPath = f.trim().startsWith("/") ? f.trim() : `${cwd}/${f.trim()}`;
|
|
4135
4139
|
try {
|
|
4136
|
-
const
|
|
4137
|
-
if (
|
|
4140
|
+
const stat10 = await fs4.stat(absPath);
|
|
4141
|
+
if (stat10.isFile()) resolved.push(absPath);
|
|
4138
4142
|
} catch {
|
|
4139
4143
|
}
|
|
4140
4144
|
}
|