agent-sh 0.2.0 → 0.3.1
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 +21 -0
- package/dist/acp-client.d.ts +24 -0
- package/dist/acp-client.js +155 -33
- package/dist/context-manager.d.ts +5 -3
- package/dist/context-manager.js +62 -31
- package/dist/core.js +10 -0
- package/dist/event-bus.d.ts +26 -0
- package/dist/event-bus.js +10 -0
- package/dist/extension-loader.js +3 -14
- package/dist/extensions/shell-exec.js +27 -22
- package/dist/extensions/tui-renderer.d.ts +1 -1
- package/dist/extensions/tui-renderer.js +369 -126
- package/dist/index.js +184 -37
- package/dist/input-handler.d.ts +10 -0
- package/dist/input-handler.js +169 -10
- package/dist/mcp-server.js +37 -8
- package/dist/settings.d.ts +44 -0
- package/dist/settings.js +61 -0
- package/dist/shell.d.ts +1 -0
- package/dist/shell.js +44 -4
- package/dist/types.d.ts +17 -0
- package/dist/utils/ansi.d.ts +4 -1
- package/dist/utils/ansi.js +60 -2
- package/dist/utils/box-frame.js +2 -1
- package/dist/utils/diff-renderer.js +1 -1
- package/dist/utils/frame-renderer.d.ts +26 -0
- package/dist/utils/frame-renderer.js +76 -0
- package/dist/utils/handler-registry.d.ts +41 -0
- package/dist/utils/handler-registry.js +52 -0
- package/dist/utils/line-editor.d.ts +21 -1
- package/dist/utils/line-editor.js +193 -99
- package/dist/utils/markdown.d.ts +15 -6
- package/dist/utils/markdown.js +106 -67
- package/dist/utils/output-writer.d.ts +22 -0
- package/dist/utils/output-writer.js +29 -0
- package/dist/utils/stream-transform.d.ts +70 -0
- package/dist/utils/stream-transform.js +229 -0
- package/dist/utils/tool-display.d.ts +11 -8
- package/dist/utils/tool-display.js +69 -46
- package/examples/extensions/latex-images.ts +142 -0
- package/examples/pi-agent-sh.ts +166 -0
- package/package.json +10 -2
package/dist/event-bus.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface ShellEvents {
|
|
|
18
18
|
"shell:foreground-busy": {
|
|
19
19
|
busy: boolean;
|
|
20
20
|
};
|
|
21
|
+
"shell:agent-exec-start": Record<string, never>;
|
|
22
|
+
"shell:agent-exec-done": Record<string, never>;
|
|
21
23
|
"agent:submit": {
|
|
22
24
|
query: string;
|
|
23
25
|
};
|
|
@@ -30,6 +32,7 @@ export interface ShellEvents {
|
|
|
30
32
|
};
|
|
31
33
|
"agent:response-chunk": {
|
|
32
34
|
text: string;
|
|
35
|
+
blocks?: ContentBlock[];
|
|
33
36
|
};
|
|
34
37
|
"agent:response-done": {
|
|
35
38
|
response: string;
|
|
@@ -114,6 +117,8 @@ export interface ShellEvents {
|
|
|
114
117
|
}[];
|
|
115
118
|
}[];
|
|
116
119
|
};
|
|
120
|
+
"config:changed": Record<string, never>;
|
|
121
|
+
"config:cycle": Record<string, never>;
|
|
117
122
|
"autocomplete:request": {
|
|
118
123
|
buffer: string;
|
|
119
124
|
items: {
|
|
@@ -122,6 +127,20 @@ export interface ShellEvents {
|
|
|
122
127
|
}[];
|
|
123
128
|
};
|
|
124
129
|
}
|
|
130
|
+
export type ContentBlock = {
|
|
131
|
+
type: "text";
|
|
132
|
+
text: string;
|
|
133
|
+
} | {
|
|
134
|
+
type: "code-block";
|
|
135
|
+
language: string;
|
|
136
|
+
code: string;
|
|
137
|
+
} | {
|
|
138
|
+
type: "image";
|
|
139
|
+
data: Buffer;
|
|
140
|
+
} | {
|
|
141
|
+
type: "raw";
|
|
142
|
+
escape: string;
|
|
143
|
+
};
|
|
125
144
|
type Listener<T> = (payload: T) => void;
|
|
126
145
|
type PipeListener<T> = (payload: T) => T;
|
|
127
146
|
type AsyncPipeListener<T> = (payload: T) => T | Promise<T>;
|
|
@@ -141,6 +160,13 @@ export declare class EventBus {
|
|
|
141
160
|
off<K extends keyof ShellEvents>(event: K, fn: Listener<ShellEvents[K]>): void;
|
|
142
161
|
/** Emit a fire-and-forget event. */
|
|
143
162
|
emit<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): void;
|
|
163
|
+
/**
|
|
164
|
+
* Transform-then-notify: run the payload through any registered pipe
|
|
165
|
+
* listeners (transforms), then emit the final result to regular `on`
|
|
166
|
+
* listeners (renderers). This enables content pipelines where extensions
|
|
167
|
+
* modify data (e.g. render LaTeX → terminal image) before renderers see it.
|
|
168
|
+
*/
|
|
169
|
+
emitTransform<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): void;
|
|
144
170
|
/** Register a transform listener for a pipeline event. */
|
|
145
171
|
onPipe<K extends keyof ShellEvents>(event: K, fn: PipeListener<ShellEvents[K]>): void;
|
|
146
172
|
/**
|
package/dist/event-bus.js
CHANGED
|
@@ -21,6 +21,16 @@ export class EventBus {
|
|
|
21
21
|
emit(event, payload) {
|
|
22
22
|
this.emitter.emit(event, payload);
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Transform-then-notify: run the payload through any registered pipe
|
|
26
|
+
* listeners (transforms), then emit the final result to regular `on`
|
|
27
|
+
* listeners (renderers). This enables content pipelines where extensions
|
|
28
|
+
* modify data (e.g. render LaTeX → terminal image) before renderers see it.
|
|
29
|
+
*/
|
|
30
|
+
emitTransform(event, payload) {
|
|
31
|
+
const transformed = this.emitPipe(event, payload);
|
|
32
|
+
this.emitter.emit(event, transformed);
|
|
33
|
+
}
|
|
24
34
|
/** Register a transform listener for a pipeline event. */
|
|
25
35
|
onPipe(event, fn) {
|
|
26
36
|
let listeners = this.pipeListeners.get(event);
|
package/dist/extension-loader.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import
|
|
4
|
-
const CONFIG_DIR = path.join(os.homedir(), ".agent-sh");
|
|
3
|
+
import { CONFIG_DIR, getSettings } from "./settings.js";
|
|
5
4
|
const EXT_DIR = path.join(CONFIG_DIR, "extensions");
|
|
6
|
-
const SETTINGS_PATH = path.join(CONFIG_DIR, "settings.json");
|
|
7
5
|
const TS_EXTS = [".ts", ".tsx", ".mts"];
|
|
8
6
|
const SCRIPT_EXTS = [".js", ".mjs", ".ts", ".tsx", ".mts"];
|
|
9
7
|
let tsRegistered = false;
|
|
@@ -19,15 +17,6 @@ async function ensureTsSupport() {
|
|
|
19
17
|
// tsx not available — TS extensions will fail with a clear error
|
|
20
18
|
}
|
|
21
19
|
}
|
|
22
|
-
async function loadSettings() {
|
|
23
|
-
try {
|
|
24
|
-
const raw = await fs.readFile(SETTINGS_PATH, "utf-8");
|
|
25
|
-
return JSON.parse(raw);
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
20
|
/**
|
|
32
21
|
* Load extensions from three sources (merged, deduplicated):
|
|
33
22
|
*
|
|
@@ -49,8 +38,8 @@ export async function loadExtensions(ctx, cliExtensions) {
|
|
|
49
38
|
specifiers.push(...cliExtensions);
|
|
50
39
|
}
|
|
51
40
|
// 2. settings.json
|
|
52
|
-
const settings =
|
|
53
|
-
if (settings.extensions) {
|
|
41
|
+
const settings = getSettings();
|
|
42
|
+
if (settings.extensions.length > 0) {
|
|
54
43
|
specifiers.push(...settings.extensions);
|
|
55
44
|
}
|
|
56
45
|
// 3. ~/.agent-sh/extensions/ directory
|
|
@@ -22,25 +22,30 @@ import * as net from "node:net";
|
|
|
22
22
|
import * as fs from "node:fs";
|
|
23
23
|
import * as path from "node:path";
|
|
24
24
|
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { getSettings } from "../settings.js";
|
|
25
26
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
26
27
|
export default function activate({ bus, contextManager }, opts) {
|
|
27
28
|
const { socketPath } = opts;
|
|
28
|
-
// Register MCP server so ACP agents discover the
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
// Register MCP server so ACP agents discover the bridge tools.
|
|
30
|
+
// Agents that don't support MCP (e.g. pi-acp) simply ignore it.
|
|
31
|
+
// Can be disabled via settings.json if not needed.
|
|
32
|
+
if (getSettings().enableMcp) {
|
|
33
|
+
bus.onPipe("session:configure", (payload) => {
|
|
34
|
+
return {
|
|
35
|
+
...payload,
|
|
36
|
+
mcpServers: [
|
|
37
|
+
...payload.mcpServers,
|
|
38
|
+
{
|
|
39
|
+
name: "agent-sh",
|
|
40
|
+
command: process.execPath,
|
|
41
|
+
args: [path.join(__dirname, "..", "mcp-server.js")],
|
|
42
|
+
env: [{ name: "AGENT_SH_SOCKET", value: socketPath }],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Set AGENT_SH_SOCKET for agent extensions that connect directly
|
|
44
49
|
process.env.AGENT_SH_SOCKET = socketPath;
|
|
45
50
|
// Serialize shell/exec requests — only one PTY command at a time
|
|
46
51
|
let execPending = Promise.resolve();
|
|
@@ -56,21 +61,19 @@ export default function activate({ bus, contextManager }, opts) {
|
|
|
56
61
|
return new Promise((resolve, reject) => {
|
|
57
62
|
execPending = execPending.then(async () => {
|
|
58
63
|
try {
|
|
64
|
+
bus.emit("shell:agent-exec-start", {});
|
|
59
65
|
const result = await bus.emitPipeAsync("shell:exec-request", {
|
|
60
66
|
command,
|
|
61
67
|
output: "",
|
|
62
68
|
cwd: "",
|
|
63
69
|
done: false,
|
|
64
70
|
});
|
|
65
|
-
|
|
66
|
-
if (result.output) {
|
|
67
|
-
bus.emit("agent:tool-output-chunk", { chunk: result.output });
|
|
68
|
-
}
|
|
71
|
+
bus.emit("shell:agent-exec-done", {});
|
|
69
72
|
resolve({ output: result.output, cwd: result.cwd });
|
|
70
73
|
}
|
|
71
74
|
catch (err) {
|
|
75
|
+
bus.emit("shell:agent-exec-done", {});
|
|
72
76
|
const message = err instanceof Error ? err.message : String(err);
|
|
73
|
-
bus.emit("agent:tool-output-chunk", { chunk: `Error: ${message}` });
|
|
74
77
|
reject(rpcError(-32000, message));
|
|
75
78
|
}
|
|
76
79
|
});
|
|
@@ -98,7 +101,9 @@ export default function activate({ bus, contextManager }, opts) {
|
|
|
98
101
|
if (!Array.isArray(ids) || ids.length === 0) {
|
|
99
102
|
throw rpcError(-32602, "Missing required parameter: ids (array of numbers)");
|
|
100
103
|
}
|
|
101
|
-
|
|
104
|
+
const start = typeof params?.start === "number" ? params.start : undefined;
|
|
105
|
+
const end = typeof params?.end === "number" ? params.end : undefined;
|
|
106
|
+
return { result: contextManager.expand(ids.map(Number), start, end) };
|
|
102
107
|
}
|
|
103
108
|
case "browse":
|
|
104
109
|
return { result: contextManager.getRecentSummary() };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ExtensionContext } from "../types.js";
|
|
2
|
-
export default function activate(
|
|
2
|
+
export default function activate(ctx: ExtensionContext): void;
|