agent-sh 0.12.7 → 0.12.9
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 +2 -0
- package/dist/agent/agent-loop.js +6 -1
- package/dist/agent/tools/glob.js +14 -11
- package/dist/agent/tools/grep.js +22 -14
- package/dist/agent/tools/pwsh.d.ts +7 -0
- package/dist/agent/tools/pwsh.js +90 -0
- package/dist/executor.d.ts +20 -0
- package/dist/executor.js +110 -0
- package/dist/extensions/openrouter.js +1 -1
- package/dist/utils/ripgrep-path.d.ts +7 -0
- package/dist/utils/ripgrep-path.js +18 -0
- package/dist/utils/tool-display.js +2 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -77,6 +77,8 @@ alias ash="agent-sh"
|
|
|
77
77
|
|
|
78
78
|
Requires Node.js 18+. Currently supports **bash** and **zsh**; other shells (fish, nushell, etc.) are not yet wired up.
|
|
79
79
|
|
|
80
|
+
**Windows:** the interactive shell layer is bash/zsh-only. Run agent-sh inside **WSL** for the full experience. Native Windows (cmd.exe / PowerShell) is not supported as the host shell, though headless / library / ACP-bridge usage may work — file an issue if you hit a gap.
|
|
81
|
+
|
|
80
82
|
## Key Features
|
|
81
83
|
|
|
82
84
|
**Real terminal, zero compromise.** Full PTY with your shell config, aliases, and environment. Shell starts instantly — the agent connects asynchronously in the background.
|
package/dist/agent/agent-loop.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { setMaxListeners } from "node:events";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import * as os from "node:os";
|
|
4
5
|
import { computeDiff, computeEditDiff, computeInputDiff } from "../utils/diff.js";
|
|
5
6
|
import { ToolRegistry } from "./tool-registry.js";
|
|
6
7
|
import { ConversationState } from "./conversation-state.js";
|
|
@@ -14,6 +15,7 @@ import { getSettings, updateSettings } from "../settings.js";
|
|
|
14
15
|
import { createToolProtocol } from "./tool-protocol.js";
|
|
15
16
|
// Core tool factories
|
|
16
17
|
import { createBashTool } from "./tools/bash.js";
|
|
18
|
+
import { createPwshTool } from "./tools/pwsh.js";
|
|
17
19
|
import { createReadFileTool } from "./tools/read-file.js";
|
|
18
20
|
import { createWriteFileTool } from "./tools/write-file.js";
|
|
19
21
|
import { createEditFileTool } from "./tools/edit-file.js";
|
|
@@ -590,6 +592,9 @@ export class AgentLoop {
|
|
|
590
592
|
return env;
|
|
591
593
|
};
|
|
592
594
|
this.toolRegistry.register(createBashTool({ getCwd, getEnv, bus: this.bus }));
|
|
595
|
+
if (process.platform === "win32") {
|
|
596
|
+
this.toolRegistry.register(createPwshTool({ getCwd, getEnv, bus: this.bus }));
|
|
597
|
+
}
|
|
593
598
|
this.toolRegistry.register(createReadFileTool(getCwd, this.fileReadCache));
|
|
594
599
|
this.toolRegistry.register(createWriteFileTool(getCwd));
|
|
595
600
|
this.toolRegistry.register(createEditFileTool(getCwd));
|
|
@@ -915,7 +920,7 @@ export class AgentLoop {
|
|
|
915
920
|
permKind = "file-write";
|
|
916
921
|
// Shorten path for display
|
|
917
922
|
const cwd = process.cwd();
|
|
918
|
-
const home = process.env.HOME;
|
|
923
|
+
const home = process.env.HOME ?? os.homedir();
|
|
919
924
|
let displayPath = absPath;
|
|
920
925
|
if (absPath.startsWith(cwd + "/"))
|
|
921
926
|
displayPath = absPath.slice(cwd.length + 1);
|
package/dist/agent/tools/glob.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { executeArgv } from "../../executor.js";
|
|
4
|
+
import { resolveRgPath } from "../../utils/ripgrep-path.js";
|
|
4
5
|
import { expandHome } from "./expand-home.js";
|
|
5
6
|
export function createGlobTool(getCwd) {
|
|
6
7
|
return {
|
|
@@ -15,11 +16,11 @@ export function createGlobTool(getCwd) {
|
|
|
15
16
|
properties: {
|
|
16
17
|
pattern: {
|
|
17
18
|
type: "string",
|
|
18
|
-
description: "Glob pattern (e.g., 'src/**/*.ts', '*.json')",
|
|
19
|
+
description: "Glob pattern (e.g., 'src/**/*.ts', '*.json'). Do NOT put `~` or absolute prefixes here — pass the directory in `path` instead.",
|
|
19
20
|
},
|
|
20
21
|
path: {
|
|
21
22
|
type: "string",
|
|
22
|
-
description: "Base directory to search (default: cwd)",
|
|
23
|
+
description: "Base directory to search (default: cwd). Supports `~` and `~/...` for the home directory.",
|
|
23
24
|
},
|
|
24
25
|
},
|
|
25
26
|
required: ["pattern"],
|
|
@@ -42,18 +43,20 @@ export function createGlobTool(getCwd) {
|
|
|
42
43
|
const pattern = args.pattern;
|
|
43
44
|
const searchPath = expandHome(args.path ?? ".");
|
|
44
45
|
// Use ripgrep for correct glob matching + .gitignore awareness
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
"--glob", shellEsc(pattern),
|
|
49
|
-
shellEsc(searchPath),
|
|
50
|
-
];
|
|
51
|
-
const { session, done } = executeCommand({
|
|
52
|
-
command: parts.join(" "),
|
|
46
|
+
const { session, done } = executeArgv({
|
|
47
|
+
file: resolveRgPath(),
|
|
48
|
+
args: ["--files", "--glob", pattern, searchPath],
|
|
53
49
|
cwd: getCwd(),
|
|
54
50
|
timeout: 10_000,
|
|
55
51
|
});
|
|
56
52
|
await done;
|
|
53
|
+
if (session.spawnFailed) {
|
|
54
|
+
return {
|
|
55
|
+
content: "ripgrep not available — the bundled binary failed to load and `rg` is not on PATH. Reinstall agent-sh, or install ripgrep manually (https://github.com/BurntSushi/ripgrep#installation).",
|
|
56
|
+
exitCode: 1,
|
|
57
|
+
isError: true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
57
60
|
if (!session.output.trim()) {
|
|
58
61
|
return {
|
|
59
62
|
content: "No files matched.",
|
package/dist/agent/tools/grep.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { executeArgv } from "../../executor.js";
|
|
2
|
+
import { resolveRgPath } from "../../utils/ripgrep-path.js";
|
|
2
3
|
import { expandHome } from "./expand-home.js";
|
|
3
4
|
export function createGrepTool(getCwd) {
|
|
4
5
|
return {
|
|
@@ -89,43 +90,50 @@ export function createGrepTool(getCwd) {
|
|
|
89
90
|
const contextAfter = args.context_after;
|
|
90
91
|
const headLimit = args.head_limit;
|
|
91
92
|
const offset = args.offset ?? 0;
|
|
92
|
-
const
|
|
93
|
-
const parts = ["rg", "--color=never"];
|
|
93
|
+
const rgArgs = ["--color=never"];
|
|
94
94
|
// Mode-specific flags
|
|
95
95
|
if (mode === "files_with_matches") {
|
|
96
|
-
|
|
96
|
+
rgArgs.push("--files-with-matches");
|
|
97
97
|
}
|
|
98
98
|
else if (mode === "count") {
|
|
99
|
-
|
|
99
|
+
rgArgs.push("--count");
|
|
100
100
|
}
|
|
101
101
|
else {
|
|
102
102
|
// content mode
|
|
103
|
-
|
|
103
|
+
rgArgs.push("--line-number", "--no-heading");
|
|
104
104
|
if (contextBefore != null && contextBefore > 0) {
|
|
105
|
-
|
|
105
|
+
rgArgs.push(`-B${contextBefore}`);
|
|
106
106
|
}
|
|
107
107
|
if (contextAfter != null && contextAfter > 0) {
|
|
108
|
-
|
|
108
|
+
rgArgs.push(`-A${contextAfter}`);
|
|
109
109
|
}
|
|
110
110
|
// If neither -A nor -B specified, use --max-count to limit per-file
|
|
111
111
|
if (contextBefore == null && contextAfter == null) {
|
|
112
|
-
|
|
112
|
+
rgArgs.push("--max-count=50");
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
if (caseInsensitive) {
|
|
116
|
-
|
|
116
|
+
rgArgs.push("-i");
|
|
117
117
|
}
|
|
118
118
|
if (include) {
|
|
119
|
-
|
|
119
|
+
rgArgs.push("--glob", include);
|
|
120
120
|
}
|
|
121
|
-
|
|
122
|
-
const { session, done } =
|
|
123
|
-
|
|
121
|
+
rgArgs.push("-e", pattern, searchPath);
|
|
122
|
+
const { session, done } = executeArgv({
|
|
123
|
+
file: resolveRgPath(),
|
|
124
|
+
args: rgArgs,
|
|
124
125
|
cwd: getCwd(),
|
|
125
126
|
timeout: 10_000,
|
|
126
127
|
maxOutputBytes: 64 * 1024,
|
|
127
128
|
});
|
|
128
129
|
await done;
|
|
130
|
+
if (session.spawnFailed) {
|
|
131
|
+
return {
|
|
132
|
+
content: "ripgrep not available — the bundled binary failed to load and `rg` is not on PATH. Reinstall agent-sh, or install ripgrep manually (https://github.com/BurntSushi/ripgrep#installation).",
|
|
133
|
+
exitCode: 1,
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
129
137
|
if (session.exitCode === 1 && !session.output.trim()) {
|
|
130
138
|
// If the pattern looks like a filename (e.g. "SKILL.md", "package.json"),
|
|
131
139
|
// the agent probably meant to find files by name, not search inside them.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { executeArgv, killSession } from "../../executor.js";
|
|
2
|
+
// Targets PowerShell 7+ (`pwsh`). Legacy `powershell.exe` is intentionally
|
|
3
|
+
// not auto-fallback — its tool surface diverges enough that compatibility
|
|
4
|
+
// shims aren't worth the maintenance.
|
|
5
|
+
export function createPwshTool(opts) {
|
|
6
|
+
return {
|
|
7
|
+
name: "pwsh",
|
|
8
|
+
description: "Execute a PowerShell command in an isolated subprocess. " +
|
|
9
|
+
"Use this on Windows when the `bash` tool fails (no /bin/bash available). " +
|
|
10
|
+
"Use PowerShell syntax — e.g. `Get-ChildItem`, `Select-String`, `$env:HOME`. " +
|
|
11
|
+
"Does not affect the user's shell state. " +
|
|
12
|
+
"cwd is set to the working directory from the shell context. " +
|
|
13
|
+
"Do NOT use pwsh for file searching — use grep/glob instead. " +
|
|
14
|
+
"Do NOT use pwsh for reading files — use read_file instead.",
|
|
15
|
+
input_schema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
command: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The PowerShell command to execute",
|
|
21
|
+
},
|
|
22
|
+
timeout: {
|
|
23
|
+
type: "number",
|
|
24
|
+
description: "Timeout in seconds (default: 60)",
|
|
25
|
+
},
|
|
26
|
+
description: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Short description of what this command does (e.g., 'Install dependencies', 'Run test suite')",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ["command"],
|
|
32
|
+
},
|
|
33
|
+
showOutput: true,
|
|
34
|
+
modifiesFiles: true,
|
|
35
|
+
requiresPermission: true,
|
|
36
|
+
getDisplayInfo: () => ({
|
|
37
|
+
kind: "execute",
|
|
38
|
+
icon: "▶",
|
|
39
|
+
locations: [],
|
|
40
|
+
}),
|
|
41
|
+
async execute(args, onChunk, ctx) {
|
|
42
|
+
const command = args.command;
|
|
43
|
+
const timeout = (args.timeout ?? 60) * 1000;
|
|
44
|
+
const intercepted = opts.bus.emitPipe("agent:terminal-intercept", {
|
|
45
|
+
command,
|
|
46
|
+
cwd: opts.getCwd(),
|
|
47
|
+
intercepted: false,
|
|
48
|
+
output: "",
|
|
49
|
+
});
|
|
50
|
+
if (intercepted.intercepted) {
|
|
51
|
+
return {
|
|
52
|
+
content: intercepted.output,
|
|
53
|
+
exitCode: 0,
|
|
54
|
+
isError: false,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const { session, done } = executeArgv({
|
|
58
|
+
file: "pwsh",
|
|
59
|
+
args: ["-NoProfile", "-NonInteractive", "-Command", command],
|
|
60
|
+
cwd: opts.getCwd(),
|
|
61
|
+
env: opts.getEnv(),
|
|
62
|
+
timeout,
|
|
63
|
+
onOutput: onChunk,
|
|
64
|
+
});
|
|
65
|
+
const onAbort = () => killSession(session);
|
|
66
|
+
ctx?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
67
|
+
try {
|
|
68
|
+
await done;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
ctx?.signal?.removeEventListener("abort", onAbort);
|
|
72
|
+
}
|
|
73
|
+
if (session.spawnFailed) {
|
|
74
|
+
return {
|
|
75
|
+
content: "PowerShell (pwsh) not found on PATH. Install PowerShell 7: winget install Microsoft.PowerShell.",
|
|
76
|
+
exitCode: 1,
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const content = session.truncated
|
|
81
|
+
? `[output truncated, showing last portion]\n${session.output}`
|
|
82
|
+
: session.output;
|
|
83
|
+
return {
|
|
84
|
+
content: content || "(no output)",
|
|
85
|
+
exitCode: session.exitCode,
|
|
86
|
+
isError: session.exitCode !== 0,
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
package/dist/executor.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export interface ExecutorSession {
|
|
|
6
6
|
exitCode: number | null;
|
|
7
7
|
done: boolean;
|
|
8
8
|
truncated: boolean;
|
|
9
|
+
/** True when the binary couldn't be launched (ENOENT, EACCES). Lets callers
|
|
10
|
+
* distinguish "tool missing" from "tool ran and exited with -1". */
|
|
11
|
+
spawnFailed: boolean;
|
|
9
12
|
process: ChildProcess | null;
|
|
10
13
|
resolve?: () => void;
|
|
11
14
|
}
|
|
@@ -24,6 +27,23 @@ export declare function executeCommand(opts: {
|
|
|
24
27
|
session: ExecutorSession;
|
|
25
28
|
done: Promise<void>;
|
|
26
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Spawn a binary directly (no shell). Use for invoking known tools like `rg`
|
|
32
|
+
* with structured args — avoids shell-quoting bugs and works on platforms
|
|
33
|
+
* without /bin/bash.
|
|
34
|
+
*/
|
|
35
|
+
export declare function executeArgv(opts: {
|
|
36
|
+
file: string;
|
|
37
|
+
args: string[];
|
|
38
|
+
cwd: string;
|
|
39
|
+
env?: Record<string, string>;
|
|
40
|
+
timeout?: number;
|
|
41
|
+
maxOutputBytes?: number;
|
|
42
|
+
onOutput?: (chunk: string) => void;
|
|
43
|
+
}): {
|
|
44
|
+
session: ExecutorSession;
|
|
45
|
+
done: Promise<void>;
|
|
46
|
+
};
|
|
27
47
|
/**
|
|
28
48
|
* Kill a running session's process group: SIGTERM, then SIGKILL after 5s.
|
|
29
49
|
* Returns a cleanup that cancels the pending SIGKILL — callers should invoke
|
package/dist/executor.js
CHANGED
|
@@ -16,6 +16,7 @@ export function executeCommand(opts) {
|
|
|
16
16
|
exitCode: null,
|
|
17
17
|
done: false,
|
|
18
18
|
truncated: false,
|
|
19
|
+
spawnFailed: false,
|
|
19
20
|
process: null,
|
|
20
21
|
};
|
|
21
22
|
const done = new Promise((resolve) => {
|
|
@@ -39,6 +40,7 @@ export function executeCommand(opts) {
|
|
|
39
40
|
}
|
|
40
41
|
catch (err) {
|
|
41
42
|
session.exitCode = -1;
|
|
43
|
+
session.spawnFailed = true;
|
|
42
44
|
session.output = `Failed to spawn: ${err instanceof Error ? err.message : String(err)}`;
|
|
43
45
|
session.done = true;
|
|
44
46
|
session.resolve?.();
|
|
@@ -79,6 +81,103 @@ export function executeCommand(opts) {
|
|
|
79
81
|
cancelKill?.();
|
|
80
82
|
if (!session.done) {
|
|
81
83
|
session.exitCode = -1;
|
|
84
|
+
const code = err.code;
|
|
85
|
+
if (code === "ENOENT" || code === "EACCES")
|
|
86
|
+
session.spawnFailed = true;
|
|
87
|
+
session.output += `\nProcess error: ${err.message}`;
|
|
88
|
+
session.done = true;
|
|
89
|
+
session.process = null;
|
|
90
|
+
session.resolve?.();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
return { session, done };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Spawn a binary directly (no shell). Use for invoking known tools like `rg`
|
|
97
|
+
* with structured args — avoids shell-quoting bugs and works on platforms
|
|
98
|
+
* without /bin/bash.
|
|
99
|
+
*/
|
|
100
|
+
export function executeArgv(opts) {
|
|
101
|
+
const timeout = opts.timeout ?? DEFAULT_TIMEOUT;
|
|
102
|
+
const maxOutput = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT;
|
|
103
|
+
const session = {
|
|
104
|
+
id: "",
|
|
105
|
+
command: `${opts.file} ${opts.args.join(" ")}`,
|
|
106
|
+
output: "",
|
|
107
|
+
exitCode: null,
|
|
108
|
+
done: false,
|
|
109
|
+
truncated: false,
|
|
110
|
+
spawnFailed: false,
|
|
111
|
+
process: null,
|
|
112
|
+
};
|
|
113
|
+
const done = new Promise((resolve) => {
|
|
114
|
+
session.resolve = resolve;
|
|
115
|
+
});
|
|
116
|
+
const env = {};
|
|
117
|
+
const source = opts.env ?? process.env;
|
|
118
|
+
for (const [k, v] of Object.entries(source)) {
|
|
119
|
+
if (v !== undefined)
|
|
120
|
+
env[k] = v;
|
|
121
|
+
}
|
|
122
|
+
let child;
|
|
123
|
+
try {
|
|
124
|
+
child = spawn(opts.file, opts.args, {
|
|
125
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
126
|
+
cwd: opts.cwd,
|
|
127
|
+
env,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
session.exitCode = -1;
|
|
132
|
+
session.spawnFailed = true;
|
|
133
|
+
session.output = `Failed to spawn ${opts.file}: ${err instanceof Error ? err.message : String(err)}`;
|
|
134
|
+
session.done = true;
|
|
135
|
+
session.resolve?.();
|
|
136
|
+
return { session, done };
|
|
137
|
+
}
|
|
138
|
+
session.process = child;
|
|
139
|
+
const handleData = (data) => {
|
|
140
|
+
const raw = data.toString("utf-8");
|
|
141
|
+
const clean = stripAnsi(raw);
|
|
142
|
+
session.output += clean;
|
|
143
|
+
if (session.output.length > maxOutput) {
|
|
144
|
+
session.output = session.output.slice(-maxOutput);
|
|
145
|
+
session.truncated = true;
|
|
146
|
+
}
|
|
147
|
+
opts.onOutput?.(raw);
|
|
148
|
+
};
|
|
149
|
+
child.stdout?.on("data", handleData);
|
|
150
|
+
child.stderr?.on("data", handleData);
|
|
151
|
+
const timer = setTimeout(() => {
|
|
152
|
+
if (!session.done && session.process) {
|
|
153
|
+
try {
|
|
154
|
+
session.process.kill("SIGTERM");
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
setTimeout(() => {
|
|
158
|
+
if (!session.done && session.process) {
|
|
159
|
+
try {
|
|
160
|
+
session.process.kill("SIGKILL");
|
|
161
|
+
}
|
|
162
|
+
catch { }
|
|
163
|
+
}
|
|
164
|
+
}, 5000).unref();
|
|
165
|
+
}
|
|
166
|
+
}, timeout);
|
|
167
|
+
child.on("exit", (code, signal) => {
|
|
168
|
+
clearTimeout(timer);
|
|
169
|
+
session.exitCode = code ?? (signal ? -1 : null);
|
|
170
|
+
session.done = true;
|
|
171
|
+
session.process = null;
|
|
172
|
+
session.resolve?.();
|
|
173
|
+
});
|
|
174
|
+
child.on("error", (err) => {
|
|
175
|
+
clearTimeout(timer);
|
|
176
|
+
if (!session.done) {
|
|
177
|
+
session.exitCode = -1;
|
|
178
|
+
const code = err.code;
|
|
179
|
+
if (code === "ENOENT" || code === "EACCES")
|
|
180
|
+
session.spawnFailed = true;
|
|
82
181
|
session.output += `\nProcess error: ${err.message}`;
|
|
83
182
|
session.done = true;
|
|
84
183
|
session.process = null;
|
|
@@ -96,10 +195,17 @@ export function killSession(session) {
|
|
|
96
195
|
const proc = session.process;
|
|
97
196
|
if (!proc || !proc.pid)
|
|
98
197
|
return () => { };
|
|
198
|
+
// Try process-group kill first (works for executeCommand's detached bash
|
|
199
|
+
// children); fall back to direct kill (executeArgv's non-detached spawn,
|
|
200
|
+
// and Windows where negative pids aren't supported).
|
|
99
201
|
try {
|
|
100
202
|
process.kill(-proc.pid, "SIGTERM");
|
|
101
203
|
}
|
|
102
204
|
catch { }
|
|
205
|
+
try {
|
|
206
|
+
proc.kill("SIGTERM");
|
|
207
|
+
}
|
|
208
|
+
catch { }
|
|
103
209
|
let settled = false;
|
|
104
210
|
const fallback = setTimeout(() => {
|
|
105
211
|
if (!settled && !session.done && proc.pid) {
|
|
@@ -107,6 +213,10 @@ export function killSession(session) {
|
|
|
107
213
|
process.kill(-proc.pid, "SIGKILL");
|
|
108
214
|
}
|
|
109
215
|
catch { }
|
|
216
|
+
try {
|
|
217
|
+
proc.kill("SIGKILL");
|
|
218
|
+
}
|
|
219
|
+
catch { }
|
|
110
220
|
}
|
|
111
221
|
}, 5000);
|
|
112
222
|
fallback.unref();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getSettings } from "../settings.js";
|
|
2
2
|
const BASE_URL = "https://openrouter.ai/api/v1";
|
|
3
|
-
const DEFAULT_MODELS = ["
|
|
3
|
+
const DEFAULT_MODELS = ["deepseek/deepseek-v4-flash"];
|
|
4
4
|
// Built-in defaults for models requiring reasoning_content echoed back
|
|
5
5
|
// (server 400s without it). Extend or override in settings.json:
|
|
6
6
|
// providers.openrouter.echoReasoningPatterns = ["deepseek", "..."]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the ripgrep binary path. Prefers the version bundled via
|
|
3
|
+
* @vscode/ripgrep (downloaded by its postinstall hook). Falls back to plain
|
|
4
|
+
* "rg" so users with rg on PATH still work even if the postinstall failed
|
|
5
|
+
* (offline install, blocked egress, etc.).
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveRgPath(): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { rgPath as bundledRgPath } from "@vscode/ripgrep";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the ripgrep binary path. Prefers the version bundled via
|
|
5
|
+
* @vscode/ripgrep (downloaded by its postinstall hook). Falls back to plain
|
|
6
|
+
* "rg" so users with rg on PATH still work even if the postinstall failed
|
|
7
|
+
* (offline install, blocked egress, etc.).
|
|
8
|
+
*/
|
|
9
|
+
export function resolveRgPath() {
|
|
10
|
+
try {
|
|
11
|
+
if (bundledRgPath && fs.existsSync(bundledRgPath))
|
|
12
|
+
return bundledRgPath;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// fall through
|
|
16
|
+
}
|
|
17
|
+
return "rg";
|
|
18
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
1
2
|
import { visibleLen } from "./ansi.js";
|
|
2
3
|
import { palette as p } from "./palette.js";
|
|
3
4
|
// ── Quiet command detection ──────────────────────────────────────
|
|
@@ -231,7 +232,7 @@ function shortenPath(p, cwd) {
|
|
|
231
232
|
return p.slice(cwd.length + 1);
|
|
232
233
|
if (p.startsWith(cwd))
|
|
233
234
|
return p.slice(cwd.length) || ".";
|
|
234
|
-
const home = process.env.HOME;
|
|
235
|
+
const home = process.env.HOME ?? os.homedir();
|
|
235
236
|
if (home && p.startsWith(home + "/"))
|
|
236
237
|
return "~/" + p.slice(home.length + 1);
|
|
237
238
|
return p;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-sh",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.9",
|
|
4
4
|
"description": "A shell-first terminal where AI is one keystroke away",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/core.js",
|
|
@@ -127,6 +127,7 @@
|
|
|
127
127
|
"node": ">=18"
|
|
128
128
|
},
|
|
129
129
|
"dependencies": {
|
|
130
|
+
"@vscode/ripgrep": "^1.17.1",
|
|
130
131
|
"@xterm/addon-serialize": "^0.13.0",
|
|
131
132
|
"@xterm/headless": "^5.5.0",
|
|
132
133
|
"cli-highlight": "^2.1.11",
|