@gemdoq/codi 0.1.1 → 0.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/README.md +83 -0
- package/dist/cli.js +377 -42
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -330,10 +330,12 @@ import chalk13 from "chalk";
|
|
|
330
330
|
// src/setup-wizard.ts
|
|
331
331
|
init_esm_shims();
|
|
332
332
|
import * as fs from "fs";
|
|
333
|
+
import * as os from "os";
|
|
333
334
|
import * as path2 from "path";
|
|
334
335
|
import chalk from "chalk";
|
|
335
336
|
import * as readline from "readline/promises";
|
|
336
337
|
import { stdin as input, stdout as output } from "process";
|
|
338
|
+
var isWindows = os.platform() === "win32";
|
|
337
339
|
var SETTINGS_DIR = path2.join(
|
|
338
340
|
process.env["HOME"] || process.env["USERPROFILE"] || "~",
|
|
339
341
|
".codi"
|
|
@@ -425,7 +427,8 @@ async function runSetupWizard() {
|
|
|
425
427
|
rl.close();
|
|
426
428
|
if (!apiKey.trim()) {
|
|
427
429
|
console.log(chalk.yellow("\n No API key provided. Setup cancelled."));
|
|
428
|
-
|
|
430
|
+
const laterCmd = isWindows ? `$env:${envVarName}="your-key"` : `export ${envVarName}=your-key`;
|
|
431
|
+
console.log(chalk.dim(` You can set it later: ${laterCmd}
|
|
429
432
|
`));
|
|
430
433
|
return null;
|
|
431
434
|
}
|
|
@@ -441,11 +444,20 @@ async function runSetupWizard() {
|
|
|
441
444
|
const trimmedKey = apiKey.trim();
|
|
442
445
|
if (saveChoice.trim() === "2") {
|
|
443
446
|
console.log("");
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
if (isWindows) {
|
|
448
|
+
console.log(chalk.bold(" Run this command in PowerShell (admin) to set permanently:"));
|
|
449
|
+
console.log("");
|
|
450
|
+
console.log(chalk.cyan(` [System.Environment]::SetEnvironmentVariable('${envVarName}', '${trimmedKey}', 'User')`));
|
|
451
|
+
console.log("");
|
|
452
|
+
console.log(chalk.dim(" Or set temporarily for this session:"));
|
|
453
|
+
console.log(chalk.cyan(` $env:${envVarName}="${trimmedKey}"`));
|
|
454
|
+
} else {
|
|
455
|
+
console.log(chalk.bold(" Add this to your shell profile (~/.zshrc or ~/.bashrc):"));
|
|
456
|
+
console.log("");
|
|
457
|
+
console.log(chalk.cyan(` export ${envVarName}=${trimmedKey}`));
|
|
458
|
+
console.log("");
|
|
459
|
+
console.log(chalk.dim(" Then restart your terminal or run: source ~/.zshrc"));
|
|
460
|
+
}
|
|
449
461
|
console.log("");
|
|
450
462
|
return { apiKey: trimmedKey, provider };
|
|
451
463
|
}
|
|
@@ -481,7 +493,8 @@ async function runSetupWizard() {
|
|
|
481
493
|
} catch (err) {
|
|
482
494
|
console.log(chalk.red(`
|
|
483
495
|
Failed to save settings: ${err}`));
|
|
484
|
-
|
|
496
|
+
const manualCmd = isWindows ? `$env:${envVarName}="${trimmedKey}"` : `export ${envVarName}=${trimmedKey}`;
|
|
497
|
+
console.log(chalk.dim(` Set manually: ${manualCmd}
|
|
485
498
|
`));
|
|
486
499
|
}
|
|
487
500
|
return { apiKey: trimmedKey, provider };
|
|
@@ -622,6 +635,7 @@ var configManager = new ConfigManager();
|
|
|
622
635
|
init_esm_shims();
|
|
623
636
|
import * as readline2 from "readline/promises";
|
|
624
637
|
import { stdin as input2, stdout as output2 } from "process";
|
|
638
|
+
import * as os2 from "os";
|
|
625
639
|
import chalk4 from "chalk";
|
|
626
640
|
import { execSync } from "child_process";
|
|
627
641
|
import { edit } from "external-editor";
|
|
@@ -886,10 +900,12 @@ var Repl = class {
|
|
|
886
900
|
const cmd = input3.slice(1).trim();
|
|
887
901
|
if (!cmd) return;
|
|
888
902
|
try {
|
|
903
|
+
const shell = os2.platform() === "win32" ? "powershell.exe" : void 0;
|
|
889
904
|
const result = execSync(cmd, {
|
|
890
905
|
encoding: "utf-8",
|
|
891
906
|
stdio: ["inherit", "pipe", "pipe"],
|
|
892
|
-
timeout: 3e4
|
|
907
|
+
timeout: 3e4,
|
|
908
|
+
shell
|
|
893
909
|
});
|
|
894
910
|
console.log(result);
|
|
895
911
|
} catch (err) {
|
|
@@ -905,8 +921,8 @@ var Repl = class {
|
|
|
905
921
|
for (const match of atMatches) {
|
|
906
922
|
const filePath = match.slice(1);
|
|
907
923
|
try {
|
|
908
|
-
const { readFileSync:
|
|
909
|
-
const content =
|
|
924
|
+
const { readFileSync: readFileSync13 } = await import("fs");
|
|
925
|
+
const content = readFileSync13(filePath, "utf-8");
|
|
910
926
|
message = message.replace(match, `
|
|
911
927
|
[File: ${filePath}]
|
|
912
928
|
\`\`\`
|
|
@@ -1438,7 +1454,7 @@ function sleep(ms) {
|
|
|
1438
1454
|
|
|
1439
1455
|
// src/agent/system-prompt.ts
|
|
1440
1456
|
init_esm_shims();
|
|
1441
|
-
import * as
|
|
1457
|
+
import * as os3 from "os";
|
|
1442
1458
|
import { execSync as execSync2 } from "child_process";
|
|
1443
1459
|
var ROLE_DEFINITION = `You are Codi (\uCF54\uB514), a terminal-based AI coding agent. You help users with software engineering tasks including writing code, debugging, refactoring, and explaining code. You have access to tools for file manipulation, code search, shell execution, and more.`;
|
|
1444
1460
|
var TOOL_HIERARCHY = `# Tool Usage Rules
|
|
@@ -1450,6 +1466,21 @@ var TOOL_HIERARCHY = `# Tool Usage Rules
|
|
|
1450
1466
|
- Reserve bash for system commands that have no dedicated tool
|
|
1451
1467
|
- Use sub_agent for complex multi-step exploration tasks
|
|
1452
1468
|
- Call multiple tools in parallel when they are independent`;
|
|
1469
|
+
var WINDOWS_RULES = `# Windows Shell Rules
|
|
1470
|
+
You are running on Windows. The shell is PowerShell. Follow these rules:
|
|
1471
|
+
- Use PowerShell syntax, NOT bash/sh syntax
|
|
1472
|
+
- Path separators: use \\\\ or / (PowerShell accepts both)
|
|
1473
|
+
- mkdir works without -p flag (PowerShell creates parent directories automatically)
|
|
1474
|
+
- Use gradlew.bat instead of ./gradlew for Gradle projects
|
|
1475
|
+
- Use mvnw.cmd instead of ./mvnw for Maven projects
|
|
1476
|
+
- Do NOT use chmod (not available on Windows)
|
|
1477
|
+
- Do NOT use HEREDOC (cat <<EOF) \u2014 use write_file tool instead
|
|
1478
|
+
- Use Remove-Item instead of rm -rf
|
|
1479
|
+
- Use Get-ChildItem instead of ls -la
|
|
1480
|
+
- Use Invoke-WebRequest or curl.exe instead of curl
|
|
1481
|
+
- Environment variables: use $env:VAR_NAME instead of $VAR_NAME
|
|
1482
|
+
- Use semicolons (;) or separate commands instead of && for chaining
|
|
1483
|
+
- Scripts: use .ps1 files instead of .sh files`;
|
|
1453
1484
|
var CODE_RULES = `# Code Modification Rules
|
|
1454
1485
|
- ALWAYS read a file before editing it
|
|
1455
1486
|
- Prefer edit_file over write_file for existing files
|
|
@@ -1485,6 +1516,9 @@ function buildSystemPrompt(context) {
|
|
|
1485
1516
|
fragments.push(ROLE_DEFINITION);
|
|
1486
1517
|
fragments.push(buildEnvironmentInfo(context));
|
|
1487
1518
|
fragments.push(TOOL_HIERARCHY);
|
|
1519
|
+
if (os3.platform() === "win32") {
|
|
1520
|
+
fragments.push(WINDOWS_RULES);
|
|
1521
|
+
}
|
|
1488
1522
|
fragments.push(CODE_RULES);
|
|
1489
1523
|
fragments.push(GIT_SAFETY);
|
|
1490
1524
|
fragments.push(RESPONSE_STYLE);
|
|
@@ -1513,8 +1547,8 @@ function buildEnvironmentInfo(context) {
|
|
|
1513
1547
|
const lines = [
|
|
1514
1548
|
"# Environment",
|
|
1515
1549
|
`- Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
1516
|
-
`- OS: ${
|
|
1517
|
-
`- Shell: ${process.env["SHELL"] || "unknown"}`,
|
|
1550
|
+
`- OS: ${os3.platform()} ${os3.release()}`,
|
|
1551
|
+
`- Shell: ${process.env["SHELL"] || process.env["COMSPEC"] || "unknown"}`,
|
|
1518
1552
|
`- Working Directory: ${context.cwd}`,
|
|
1519
1553
|
`- Model: ${context.model}`,
|
|
1520
1554
|
`- Provider: ${context.provider}`
|
|
@@ -2075,6 +2109,13 @@ async function promptUser(tool, input3) {
|
|
|
2075
2109
|
// src/hooks/hook-manager.ts
|
|
2076
2110
|
init_esm_shims();
|
|
2077
2111
|
import { exec } from "child_process";
|
|
2112
|
+
import * as os4 from "os";
|
|
2113
|
+
function getDefaultShell() {
|
|
2114
|
+
if (os4.platform() === "win32") {
|
|
2115
|
+
return "powershell.exe";
|
|
2116
|
+
}
|
|
2117
|
+
return void 0;
|
|
2118
|
+
}
|
|
2078
2119
|
var HookManager = class {
|
|
2079
2120
|
async runHooks(event, context) {
|
|
2080
2121
|
const config = configManager.get();
|
|
@@ -2110,7 +2151,8 @@ var HookManager = class {
|
|
|
2110
2151
|
const proc = exec(command, {
|
|
2111
2152
|
timeout: timeout || 5e3,
|
|
2112
2153
|
cwd: process.cwd(),
|
|
2113
|
-
env: { ...process.env }
|
|
2154
|
+
env: { ...process.env },
|
|
2155
|
+
shell: getDefaultShell()
|
|
2114
2156
|
}, (err, stdout, stderr) => {
|
|
2115
2157
|
if (err) {
|
|
2116
2158
|
if (err.code === 2) {
|
|
@@ -2371,6 +2413,7 @@ var subAgentTool = {
|
|
|
2371
2413
|
// src/config/slash-commands.ts
|
|
2372
2414
|
init_esm_shims();
|
|
2373
2415
|
import * as fs8 from "fs";
|
|
2416
|
+
import * as os5 from "os";
|
|
2374
2417
|
import * as path9 from "path";
|
|
2375
2418
|
import chalk11 from "chalk";
|
|
2376
2419
|
function createBuiltinCommands() {
|
|
@@ -2719,6 +2762,146 @@ ${content}
|
|
|
2719
2762
|
return true;
|
|
2720
2763
|
}
|
|
2721
2764
|
},
|
|
2765
|
+
{
|
|
2766
|
+
name: "/commit",
|
|
2767
|
+
description: "Generate commit message and commit with AI",
|
|
2768
|
+
handler: async (_args, ctx) => {
|
|
2769
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
2770
|
+
try {
|
|
2771
|
+
const staged = execSync5("git diff --cached", { encoding: "utf-8", cwd: process.cwd() });
|
|
2772
|
+
const unstaged = execSync5("git diff", { encoding: "utf-8", cwd: process.cwd() });
|
|
2773
|
+
const diff = staged + unstaged;
|
|
2774
|
+
if (!diff.trim()) {
|
|
2775
|
+
console.log(chalk11.dim("\nNo changes to commit.\n"));
|
|
2776
|
+
return true;
|
|
2777
|
+
}
|
|
2778
|
+
ctx.conversation.addUserMessage(
|
|
2779
|
+
`\uB2E4\uC74C git diff\uB97C \uBD84\uC11D\uD574\uC11C \uC801\uC808\uD55C \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB97C \uC0DD\uC131\uD558\uACE0, git \uB3C4\uAD6C\uB85C \uBCC0\uACBD\uB41C \uD30C\uC77C\uC744 add\uD558\uACE0 \uCEE4\uBC0B\uD574\uC918.
|
|
2780
|
+
|
|
2781
|
+
\`\`\`diff
|
|
2782
|
+
${diff}
|
|
2783
|
+
\`\`\``
|
|
2784
|
+
);
|
|
2785
|
+
return false;
|
|
2786
|
+
} catch {
|
|
2787
|
+
console.log(chalk11.yellow("Not a git repository or git not available."));
|
|
2788
|
+
return true;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
},
|
|
2792
|
+
{
|
|
2793
|
+
name: "/review",
|
|
2794
|
+
description: "AI code review of current changes",
|
|
2795
|
+
handler: async (_args, ctx) => {
|
|
2796
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
2797
|
+
try {
|
|
2798
|
+
const staged = execSync5("git diff --cached", { encoding: "utf-8", cwd: process.cwd() });
|
|
2799
|
+
const unstaged = execSync5("git diff", { encoding: "utf-8", cwd: process.cwd() });
|
|
2800
|
+
const diff = staged + unstaged;
|
|
2801
|
+
if (!diff.trim()) {
|
|
2802
|
+
console.log(chalk11.dim("\nNo changes to review.\n"));
|
|
2803
|
+
return true;
|
|
2804
|
+
}
|
|
2805
|
+
ctx.conversation.addUserMessage(
|
|
2806
|
+
`\uB2E4\uC74C git diff\uB97C \uCF54\uB4DC \uB9AC\uBDF0\uD574\uC918. \uBCF4\uC548 \uCDE8\uC57D\uC810, \uBC84\uADF8, \uC131\uB2A5, \uCF54\uB4DC \uC2A4\uD0C0\uC77C \uAD00\uC810\uC5D0\uC11C \uBD84\uC11D\uD558\uACE0 \uAC1C\uC120 \uC0AC\uD56D\uC744 \uC54C\uB824\uC918.
|
|
2807
|
+
|
|
2808
|
+
\`\`\`diff
|
|
2809
|
+
${diff}
|
|
2810
|
+
\`\`\``
|
|
2811
|
+
);
|
|
2812
|
+
return false;
|
|
2813
|
+
} catch {
|
|
2814
|
+
console.log(chalk11.yellow("Not a git repository or git not available."));
|
|
2815
|
+
return true;
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
name: "/search",
|
|
2821
|
+
description: "Search past conversation sessions",
|
|
2822
|
+
handler: async (args) => {
|
|
2823
|
+
if (!args) {
|
|
2824
|
+
console.log(chalk11.yellow("Usage: /search <keyword>"));
|
|
2825
|
+
return true;
|
|
2826
|
+
}
|
|
2827
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2828
|
+
const sessionsDir = path9.join(home, ".codi", "sessions");
|
|
2829
|
+
if (!fs8.existsSync(sessionsDir)) {
|
|
2830
|
+
console.log(chalk11.dim("\nNo sessions found.\n"));
|
|
2831
|
+
return true;
|
|
2832
|
+
}
|
|
2833
|
+
const files = fs8.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl"));
|
|
2834
|
+
const results = [];
|
|
2835
|
+
const keyword = args.toLowerCase();
|
|
2836
|
+
for (const file of files) {
|
|
2837
|
+
if (results.length >= 10) break;
|
|
2838
|
+
const filePath = path9.join(sessionsDir, file);
|
|
2839
|
+
const lines = fs8.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
2840
|
+
for (const line of lines) {
|
|
2841
|
+
if (results.length >= 10) break;
|
|
2842
|
+
if (line.toLowerCase().includes(keyword)) {
|
|
2843
|
+
const sessionId = file.replace(".jsonl", "");
|
|
2844
|
+
const stat = fs8.statSync(filePath);
|
|
2845
|
+
const date = stat.mtime.toISOString().split("T")[0];
|
|
2846
|
+
const preview = line.length > 100 ? line.slice(0, 100) + "..." : line;
|
|
2847
|
+
results.push({ sessionId, date, preview });
|
|
2848
|
+
break;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
if (results.length === 0) {
|
|
2853
|
+
console.log(chalk11.dim(`
|
|
2854
|
+
No results for "${args}".
|
|
2855
|
+
`));
|
|
2856
|
+
} else {
|
|
2857
|
+
console.log(chalk11.bold(`
|
|
2858
|
+
Search results for "${args}":
|
|
2859
|
+
`));
|
|
2860
|
+
for (const r of results) {
|
|
2861
|
+
console.log(` ${chalk11.cyan(r.sessionId)} ${chalk11.dim(r.date)}`);
|
|
2862
|
+
console.log(` ${chalk11.dim(r.preview)}`);
|
|
2863
|
+
}
|
|
2864
|
+
console.log("");
|
|
2865
|
+
}
|
|
2866
|
+
return true;
|
|
2867
|
+
}
|
|
2868
|
+
},
|
|
2869
|
+
{
|
|
2870
|
+
name: "/fix",
|
|
2871
|
+
description: "Run a command and auto-fix errors (e.g., /fix npm run build)",
|
|
2872
|
+
handler: async (args, ctx) => {
|
|
2873
|
+
if (!args) {
|
|
2874
|
+
console.log(chalk11.yellow("Usage: /fix <command>"));
|
|
2875
|
+
return true;
|
|
2876
|
+
}
|
|
2877
|
+
const { execSync: execSync5 } = await import("child_process");
|
|
2878
|
+
try {
|
|
2879
|
+
const shell = os5.platform() === "win32" ? "powershell.exe" : void 0;
|
|
2880
|
+
const output3 = execSync5(args, { encoding: "utf-8", cwd: process.cwd(), stdio: "pipe", shell });
|
|
2881
|
+
console.log(chalk11.green(`
|
|
2882
|
+
\u2713 Command succeeded. No errors to fix.
|
|
2883
|
+
`));
|
|
2884
|
+
if (output3.trim()) console.log(chalk11.dim(output3));
|
|
2885
|
+
return true;
|
|
2886
|
+
} catch (err) {
|
|
2887
|
+
const error = err;
|
|
2888
|
+
const errorOutput = (error.stderr || "") + (error.stdout || "");
|
|
2889
|
+
console.log(chalk11.red(`
|
|
2890
|
+
Command failed: ${args}
|
|
2891
|
+
`));
|
|
2892
|
+
ctx.conversation.addUserMessage(
|
|
2893
|
+
`\uB2E4\uC74C \uBA85\uB839\uC5B4\uB97C \uC2E4\uD589\uD588\uB354\uB2C8 \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD588\uC5B4. \uC5D0\uB7EC\uB97C \uBD84\uC11D\uD558\uACE0 \uCF54\uB4DC\uB97C \uC218\uC815\uD574\uC918.
|
|
2894
|
+
|
|
2895
|
+
Command: ${args}
|
|
2896
|
+
|
|
2897
|
+
\`\`\`
|
|
2898
|
+
${errorOutput}
|
|
2899
|
+
\`\`\``
|
|
2900
|
+
);
|
|
2901
|
+
return false;
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
},
|
|
2722
2905
|
{
|
|
2723
2906
|
name: "/mcp",
|
|
2724
2907
|
description: "Show MCP server status",
|
|
@@ -2872,7 +3055,8 @@ var fileReadTool = {
|
|
|
2872
3055
|
}
|
|
2873
3056
|
}
|
|
2874
3057
|
try {
|
|
2875
|
-
const
|
|
3058
|
+
const raw = fs9.readFileSync(resolved, "utf-8");
|
|
3059
|
+
const content = raw.replace(/\r\n/g, "\n");
|
|
2876
3060
|
const lines = content.split("\n");
|
|
2877
3061
|
const totalLines = lines.length;
|
|
2878
3062
|
const startLine = Math.max(1, offset || 1);
|
|
@@ -2967,7 +3151,9 @@ var fileEditTool = {
|
|
|
2967
3151
|
return makeToolError(`File not found: ${resolved}`);
|
|
2968
3152
|
}
|
|
2969
3153
|
try {
|
|
2970
|
-
|
|
3154
|
+
const raw = fs11.readFileSync(resolved, "utf-8");
|
|
3155
|
+
const hasCrlf = raw.includes("\r\n");
|
|
3156
|
+
let content = hasCrlf ? raw.replace(/\r\n/g, "\n") : raw;
|
|
2971
3157
|
if (oldString === newString) {
|
|
2972
3158
|
return makeToolError("old_string and new_string are identical. No changes needed.");
|
|
2973
3159
|
}
|
|
@@ -2999,7 +3185,8 @@ Did you mean this line?
|
|
|
2999
3185
|
const idx = content.indexOf(oldString);
|
|
3000
3186
|
content = content.slice(0, idx) + newString + content.slice(idx + oldString.length);
|
|
3001
3187
|
}
|
|
3002
|
-
|
|
3188
|
+
const output3 = hasCrlf ? content.replace(/\n/g, "\r\n") : content;
|
|
3189
|
+
fs11.writeFileSync(resolved, output3, "utf-8");
|
|
3003
3190
|
const linesChanged = Math.max(
|
|
3004
3191
|
oldString.split("\n").length,
|
|
3005
3192
|
newString.split("\n").length
|
|
@@ -3054,7 +3241,9 @@ var fileMultiEditTool = {
|
|
|
3054
3241
|
return makeToolError("edits must be a non-empty array of {old_string, new_string} objects");
|
|
3055
3242
|
}
|
|
3056
3243
|
try {
|
|
3057
|
-
|
|
3244
|
+
const raw = fs12.readFileSync(resolved, "utf-8");
|
|
3245
|
+
const hasCrlf = raw.includes("\r\n");
|
|
3246
|
+
let content = hasCrlf ? raw.replace(/\r\n/g, "\n") : raw;
|
|
3058
3247
|
for (let i = 0; i < edits.length; i++) {
|
|
3059
3248
|
const edit2 = edits[i];
|
|
3060
3249
|
if (!content.includes(edit2.old_string)) {
|
|
@@ -3076,7 +3265,8 @@ Searching for: ${edit2.old_string.slice(0, 100)}...`
|
|
|
3076
3265
|
edit2.new_string.split("\n").length
|
|
3077
3266
|
);
|
|
3078
3267
|
}
|
|
3079
|
-
|
|
3268
|
+
const output3 = hasCrlf ? content.replace(/\n/g, "\r\n") : content;
|
|
3269
|
+
fs12.writeFileSync(resolved, output3, "utf-8");
|
|
3080
3270
|
return makeToolResult(`Applied ${edits.length} edits to ${resolved}`, {
|
|
3081
3271
|
filePath: resolved,
|
|
3082
3272
|
linesChanged: totalLinesChanged
|
|
@@ -3145,10 +3335,11 @@ ${result.join("\n")}`
|
|
|
3145
3335
|
init_esm_shims();
|
|
3146
3336
|
init_tool();
|
|
3147
3337
|
import { execFile } from "child_process";
|
|
3338
|
+
import * as fs14 from "fs";
|
|
3148
3339
|
import * as path15 from "path";
|
|
3149
3340
|
var grepTool = {
|
|
3150
3341
|
name: "grep",
|
|
3151
|
-
description: `Search file contents using regex patterns. Uses ripgrep (rg) if available, falls back to grep. Supports context lines, file type filters, and multiple output modes.`,
|
|
3342
|
+
description: `Search file contents using regex patterns. Uses ripgrep (rg) if available, falls back to grep, then to a built-in Node.js search. Supports context lines, file type filters, and multiple output modes.`,
|
|
3152
3343
|
inputSchema: {
|
|
3153
3344
|
type: "object",
|
|
3154
3345
|
properties: {
|
|
@@ -3178,16 +3369,20 @@ var grepTool = {
|
|
|
3178
3369
|
const searchPath = path15.resolve(input3["path"] ? String(input3["path"]) : process.cwd());
|
|
3179
3370
|
const outputMode = input3["output_mode"] || "files_with_matches";
|
|
3180
3371
|
const headLimit = input3["head_limit"] || 0;
|
|
3372
|
+
const hasRg = await hasCommand("rg");
|
|
3373
|
+
const hasGrep = !hasRg && await hasCommand("grep");
|
|
3374
|
+
if (!hasRg && !hasGrep) {
|
|
3375
|
+
return builtinSearch(pattern, searchPath, input3, outputMode, headLimit);
|
|
3376
|
+
}
|
|
3377
|
+
const cmd = hasRg ? "rg" : "grep";
|
|
3181
3378
|
const args = [];
|
|
3182
|
-
|
|
3183
|
-
const cmd = useRg ? "rg" : "grep";
|
|
3184
|
-
if (!useRg) {
|
|
3379
|
+
if (!hasRg) {
|
|
3185
3380
|
args.push("-r");
|
|
3186
3381
|
}
|
|
3187
3382
|
if (outputMode === "files_with_matches") {
|
|
3188
|
-
args.push(
|
|
3383
|
+
args.push("-l");
|
|
3189
3384
|
} else if (outputMode === "count") {
|
|
3190
|
-
args.push(
|
|
3385
|
+
args.push("-c");
|
|
3191
3386
|
}
|
|
3192
3387
|
if (input3["-i"]) args.push("-i");
|
|
3193
3388
|
if (input3["-n"] !== false && outputMode === "content") {
|
|
@@ -3196,16 +3391,16 @@ var grepTool = {
|
|
|
3196
3391
|
if (input3["-C"]) args.push("-C", String(input3["-C"]));
|
|
3197
3392
|
else if (input3["-A"]) args.push("-A", String(input3["-A"]));
|
|
3198
3393
|
if (input3["-B"]) args.push("-B", String(input3["-B"]));
|
|
3199
|
-
if (input3["multiline"] &&
|
|
3394
|
+
if (input3["multiline"] && hasRg) {
|
|
3200
3395
|
args.push("-U", "--multiline-dotall");
|
|
3201
3396
|
}
|
|
3202
|
-
if (input3["type"] &&
|
|
3397
|
+
if (input3["type"] && hasRg) {
|
|
3203
3398
|
args.push("--type", String(input3["type"]));
|
|
3204
3399
|
}
|
|
3205
|
-
if (input3["glob"] &&
|
|
3400
|
+
if (input3["glob"] && hasRg) {
|
|
3206
3401
|
args.push("--glob", String(input3["glob"]));
|
|
3207
3402
|
}
|
|
3208
|
-
if (
|
|
3403
|
+
if (hasRg) {
|
|
3209
3404
|
args.push("--no-ignore-vcs");
|
|
3210
3405
|
args.push("-g", "!node_modules");
|
|
3211
3406
|
args.push("-g", "!.git");
|
|
@@ -3235,8 +3430,9 @@ var grepTool = {
|
|
|
3235
3430
|
}
|
|
3236
3431
|
};
|
|
3237
3432
|
function hasCommand(cmd) {
|
|
3433
|
+
const checkCmd = process.platform === "win32" ? "where" : "which";
|
|
3238
3434
|
return new Promise((resolve10) => {
|
|
3239
|
-
execFile(
|
|
3435
|
+
execFile(checkCmd, [cmd], (err) => resolve10(!err));
|
|
3240
3436
|
});
|
|
3241
3437
|
}
|
|
3242
3438
|
function runCommand(cmd, args) {
|
|
@@ -3251,16 +3447,155 @@ function runCommand(cmd, args) {
|
|
|
3251
3447
|
});
|
|
3252
3448
|
});
|
|
3253
3449
|
}
|
|
3450
|
+
var IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "__pycache__", ".cache", "coverage"]);
|
|
3451
|
+
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".ttf", ".eot", ".pdf", ".zip", ".tar", ".gz", ".exe", ".dll", ".so", ".dylib"]);
|
|
3452
|
+
var TYPE_EXTENSIONS = {
|
|
3453
|
+
ts: [".ts", ".tsx"],
|
|
3454
|
+
js: [".js", ".jsx", ".mjs", ".cjs"],
|
|
3455
|
+
py: [".py"],
|
|
3456
|
+
java: [".java"],
|
|
3457
|
+
kt: [".kt", ".kts"],
|
|
3458
|
+
go: [".go"],
|
|
3459
|
+
rs: [".rs"],
|
|
3460
|
+
rb: [".rb"],
|
|
3461
|
+
css: [".css", ".scss", ".less"],
|
|
3462
|
+
html: [".html", ".htm"],
|
|
3463
|
+
json: [".json"],
|
|
3464
|
+
yaml: [".yaml", ".yml"],
|
|
3465
|
+
md: [".md"],
|
|
3466
|
+
xml: [".xml"]
|
|
3467
|
+
};
|
|
3468
|
+
async function builtinSearch(pattern, searchPath, input3, outputMode, headLimit) {
|
|
3469
|
+
const caseInsensitive = input3["-i"] === true;
|
|
3470
|
+
const showLineNumbers = input3["-n"] !== false && outputMode === "content";
|
|
3471
|
+
const contextBefore = Number(input3["-C"] || input3["-B"] || 0);
|
|
3472
|
+
const contextAfter = Number(input3["-C"] || input3["-A"] || 0);
|
|
3473
|
+
const typeFilter = input3["type"] ? String(input3["type"]) : void 0;
|
|
3474
|
+
const globFilter = input3["glob"] ? String(input3["glob"]) : void 0;
|
|
3475
|
+
let regex;
|
|
3476
|
+
try {
|
|
3477
|
+
regex = new RegExp(pattern, caseInsensitive ? "gi" : "g");
|
|
3478
|
+
} catch {
|
|
3479
|
+
return makeToolError(`Invalid regex pattern: ${pattern}`);
|
|
3480
|
+
}
|
|
3481
|
+
const files = collectFiles(searchPath, typeFilter, globFilter);
|
|
3482
|
+
const results = [];
|
|
3483
|
+
const fileCounts = /* @__PURE__ */ new Map();
|
|
3484
|
+
let entryCount = 0;
|
|
3485
|
+
for (const filePath of files) {
|
|
3486
|
+
if (headLimit > 0 && entryCount >= headLimit) break;
|
|
3487
|
+
let content;
|
|
3488
|
+
try {
|
|
3489
|
+
content = fs14.readFileSync(filePath, "utf-8");
|
|
3490
|
+
} catch {
|
|
3491
|
+
continue;
|
|
3492
|
+
}
|
|
3493
|
+
const lines = content.split("\n");
|
|
3494
|
+
const matchedLineIndices = /* @__PURE__ */ new Set();
|
|
3495
|
+
let fileMatchCount = 0;
|
|
3496
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3497
|
+
if (regex.test(lines[i])) {
|
|
3498
|
+
matchedLineIndices.add(i);
|
|
3499
|
+
fileMatchCount++;
|
|
3500
|
+
}
|
|
3501
|
+
regex.lastIndex = 0;
|
|
3502
|
+
}
|
|
3503
|
+
if (fileMatchCount === 0) continue;
|
|
3504
|
+
if (outputMode === "files_with_matches") {
|
|
3505
|
+
results.push(filePath);
|
|
3506
|
+
entryCount++;
|
|
3507
|
+
} else if (outputMode === "count") {
|
|
3508
|
+
fileCounts.set(filePath, fileMatchCount);
|
|
3509
|
+
entryCount++;
|
|
3510
|
+
} else {
|
|
3511
|
+
const outputLines = /* @__PURE__ */ new Set();
|
|
3512
|
+
for (const idx of matchedLineIndices) {
|
|
3513
|
+
for (let j = Math.max(0, idx - contextBefore); j <= Math.min(lines.length - 1, idx + contextAfter); j++) {
|
|
3514
|
+
outputLines.add(j);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
const sortedIndices = [...outputLines].sort((a, b) => a - b);
|
|
3518
|
+
let lastIdx = -2;
|
|
3519
|
+
for (const idx of sortedIndices) {
|
|
3520
|
+
if (headLimit > 0 && entryCount >= headLimit) break;
|
|
3521
|
+
if (idx > lastIdx + 1 && lastIdx >= 0) {
|
|
3522
|
+
results.push("--");
|
|
3523
|
+
}
|
|
3524
|
+
const prefix = showLineNumbers ? `${filePath}:${idx + 1}:` : `${filePath}:`;
|
|
3525
|
+
results.push(`${prefix}${lines[idx]}`);
|
|
3526
|
+
entryCount++;
|
|
3527
|
+
lastIdx = idx;
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
if (outputMode === "count") {
|
|
3532
|
+
for (const [file, count] of fileCounts) {
|
|
3533
|
+
results.push(`${file}:${count}`);
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
if (results.length === 0) {
|
|
3537
|
+
return makeToolResult(`No matches found for pattern: ${pattern}`);
|
|
3538
|
+
}
|
|
3539
|
+
return makeToolResult(results.join("\n"));
|
|
3540
|
+
}
|
|
3541
|
+
function collectFiles(dirPath, typeFilter, globFilter) {
|
|
3542
|
+
const files = [];
|
|
3543
|
+
const allowedExtensions = typeFilter && TYPE_EXTENSIONS[typeFilter] ? new Set(TYPE_EXTENSIONS[typeFilter]) : null;
|
|
3544
|
+
let globRegex = null;
|
|
3545
|
+
if (globFilter) {
|
|
3546
|
+
const escaped = globFilter.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
3547
|
+
globRegex = new RegExp(`^${escaped}$`);
|
|
3548
|
+
}
|
|
3549
|
+
function walk(dir) {
|
|
3550
|
+
let entries;
|
|
3551
|
+
try {
|
|
3552
|
+
entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
3553
|
+
} catch {
|
|
3554
|
+
return;
|
|
3555
|
+
}
|
|
3556
|
+
for (const entry of entries) {
|
|
3557
|
+
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
3558
|
+
if (entry.name.startsWith(".") && entry.name !== ".") continue;
|
|
3559
|
+
const fullPath = path15.join(dir, entry.name);
|
|
3560
|
+
if (entry.isDirectory()) {
|
|
3561
|
+
walk(fullPath);
|
|
3562
|
+
} else if (entry.isFile()) {
|
|
3563
|
+
const ext = path15.extname(entry.name).toLowerCase();
|
|
3564
|
+
if (BINARY_EXTENSIONS.has(ext)) continue;
|
|
3565
|
+
if (allowedExtensions && !allowedExtensions.has(ext)) continue;
|
|
3566
|
+
if (globRegex && !globRegex.test(entry.name)) continue;
|
|
3567
|
+
files.push(fullPath);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
try {
|
|
3572
|
+
const stat = fs14.statSync(dirPath);
|
|
3573
|
+
if (stat.isFile()) {
|
|
3574
|
+
return [dirPath];
|
|
3575
|
+
}
|
|
3576
|
+
} catch {
|
|
3577
|
+
return [];
|
|
3578
|
+
}
|
|
3579
|
+
walk(dirPath);
|
|
3580
|
+
return files;
|
|
3581
|
+
}
|
|
3254
3582
|
|
|
3255
3583
|
// src/tools/bash.ts
|
|
3256
3584
|
init_esm_shims();
|
|
3257
3585
|
init_tool();
|
|
3258
3586
|
import { exec as exec2, spawn } from "child_process";
|
|
3587
|
+
import * as os6 from "os";
|
|
3588
|
+
function getDefaultShell2() {
|
|
3589
|
+
if (os6.platform() === "win32") {
|
|
3590
|
+
return "powershell.exe";
|
|
3591
|
+
}
|
|
3592
|
+
return process.env["SHELL"] || "/bin/bash";
|
|
3593
|
+
}
|
|
3259
3594
|
var backgroundTasks = /* @__PURE__ */ new Map();
|
|
3260
3595
|
var taskCounter = 0;
|
|
3261
3596
|
var bashTool = {
|
|
3262
3597
|
name: "bash",
|
|
3263
|
-
description: `Execute a
|
|
3598
|
+
description: `Execute a shell command. Supports timeout (max 600s, default 120s) and background execution. The working directory persists between calls. Uses the platform default shell (bash on Unix, cmd.exe on Windows).`,
|
|
3264
3599
|
inputSchema: {
|
|
3265
3600
|
type: "object",
|
|
3266
3601
|
properties: {
|
|
@@ -3287,7 +3622,7 @@ var bashTool = {
|
|
|
3287
3622
|
exec2(command, {
|
|
3288
3623
|
timeout,
|
|
3289
3624
|
maxBuffer: 10 * 1024 * 1024,
|
|
3290
|
-
shell:
|
|
3625
|
+
shell: getDefaultShell2(),
|
|
3291
3626
|
cwd: process.cwd(),
|
|
3292
3627
|
env: { ...process.env }
|
|
3293
3628
|
}, (err, stdout, stderr) => {
|
|
@@ -3321,7 +3656,7 @@ ${stderr}` : ""
|
|
|
3321
3656
|
function runBackgroundTask(command) {
|
|
3322
3657
|
const taskId = `bg_${++taskCounter}`;
|
|
3323
3658
|
const proc = spawn(command, {
|
|
3324
|
-
shell:
|
|
3659
|
+
shell: getDefaultShell2(),
|
|
3325
3660
|
cwd: process.cwd(),
|
|
3326
3661
|
env: { ...process.env },
|
|
3327
3662
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -3351,7 +3686,7 @@ Use task_output tool to check results.`);
|
|
|
3351
3686
|
// src/tools/list-dir.ts
|
|
3352
3687
|
init_esm_shims();
|
|
3353
3688
|
init_tool();
|
|
3354
|
-
import * as
|
|
3689
|
+
import * as fs15 from "fs";
|
|
3355
3690
|
import * as path16 from "path";
|
|
3356
3691
|
var listDirTool = {
|
|
3357
3692
|
name: "list_dir",
|
|
@@ -3367,15 +3702,15 @@ var listDirTool = {
|
|
|
3367
3702
|
readOnly: true,
|
|
3368
3703
|
async execute(input3) {
|
|
3369
3704
|
const dirPath = path16.resolve(input3["path"] ? String(input3["path"]) : process.cwd());
|
|
3370
|
-
if (!
|
|
3705
|
+
if (!fs15.existsSync(dirPath)) {
|
|
3371
3706
|
return makeToolError(`Directory not found: ${dirPath}`);
|
|
3372
3707
|
}
|
|
3373
|
-
const stat =
|
|
3708
|
+
const stat = fs15.statSync(dirPath);
|
|
3374
3709
|
if (!stat.isDirectory()) {
|
|
3375
3710
|
return makeToolError(`Not a directory: ${dirPath}`);
|
|
3376
3711
|
}
|
|
3377
3712
|
try {
|
|
3378
|
-
const entries =
|
|
3713
|
+
const entries = fs15.readdirSync(dirPath, { withFileTypes: true });
|
|
3379
3714
|
const IGNORE = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "__pycache__", ".next", "dist", "build"]);
|
|
3380
3715
|
const lines = [];
|
|
3381
3716
|
const dirs = [];
|
|
@@ -3386,7 +3721,7 @@ var listDirTool = {
|
|
|
3386
3721
|
dirs.push(`${entry.name}/`);
|
|
3387
3722
|
} else if (entry.isSymbolicLink()) {
|
|
3388
3723
|
try {
|
|
3389
|
-
const target =
|
|
3724
|
+
const target = fs15.readlinkSync(path16.join(dirPath, entry.name));
|
|
3390
3725
|
files.push(`${entry.name} -> ${target}`);
|
|
3391
3726
|
} catch {
|
|
3392
3727
|
files.push(`${entry.name} -> (broken link)`);
|
|
@@ -3598,7 +3933,7 @@ ${formatted}`);
|
|
|
3598
3933
|
// src/tools/notebook-edit.ts
|
|
3599
3934
|
init_esm_shims();
|
|
3600
3935
|
init_tool();
|
|
3601
|
-
import * as
|
|
3936
|
+
import * as fs16 from "fs";
|
|
3602
3937
|
import * as path17 from "path";
|
|
3603
3938
|
var notebookEditTool = {
|
|
3604
3939
|
name: "notebook_edit",
|
|
@@ -3622,11 +3957,11 @@ var notebookEditTool = {
|
|
|
3622
3957
|
const newSource = String(input3["new_source"]);
|
|
3623
3958
|
const cellType = input3["cell_type"] || "code";
|
|
3624
3959
|
const editMode = input3["edit_mode"] || "replace";
|
|
3625
|
-
if (!
|
|
3960
|
+
if (!fs16.existsSync(nbPath)) {
|
|
3626
3961
|
return makeToolError(`Notebook not found: ${nbPath}`);
|
|
3627
3962
|
}
|
|
3628
3963
|
try {
|
|
3629
|
-
const content =
|
|
3964
|
+
const content = fs16.readFileSync(nbPath, "utf-8");
|
|
3630
3965
|
const nb = JSON.parse(content);
|
|
3631
3966
|
if (!nb.cells || !Array.isArray(nb.cells)) {
|
|
3632
3967
|
return makeToolError("Invalid notebook format: no cells array");
|
|
@@ -3671,7 +4006,7 @@ var notebookEditTool = {
|
|
|
3671
4006
|
default:
|
|
3672
4007
|
return makeToolError(`Unknown edit_mode: ${editMode}`);
|
|
3673
4008
|
}
|
|
3674
|
-
|
|
4009
|
+
fs16.writeFileSync(nbPath, JSON.stringify(nb, null, 1), "utf-8");
|
|
3675
4010
|
return makeToolResult(`Notebook ${editMode}d cell in ${nbPath} (${nb.cells.length} cells total)`);
|
|
3676
4011
|
} catch (err) {
|
|
3677
4012
|
return makeToolError(`Notebook edit failed: ${err instanceof Error ? err.message : String(err)}`);
|