mono-pilot 0.2.9 → 0.2.12
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 +270 -7
- package/dist/src/agents-paths.js +36 -0
- package/dist/src/brief/blocks.js +83 -0
- package/dist/src/brief/defaults.js +60 -0
- package/dist/src/brief/frontmatter.js +53 -0
- package/dist/src/brief/paths.js +10 -0
- package/dist/src/brief/reflection.js +27 -0
- package/dist/src/cli.js +62 -5
- package/dist/src/cluster/bus.js +102 -0
- package/dist/src/cluster/follower.js +137 -0
- package/dist/src/cluster/init.js +182 -0
- package/dist/src/cluster/leader.js +97 -0
- package/dist/src/cluster/log.js +49 -0
- package/dist/src/cluster/protocol.js +34 -0
- package/dist/src/cluster/services/bus.js +243 -0
- package/dist/src/cluster/services/embedding.js +12 -0
- package/dist/src/cluster/socket.js +86 -0
- package/dist/src/cluster/test-bus.js +175 -0
- package/dist/src/cluster_v2/connection-lifecycle.js +31 -0
- package/dist/src/cluster_v2/connection-lifecycle.test.js +24 -0
- package/dist/src/cluster_v2/connection.js +159 -0
- package/dist/src/cluster_v2/connection.test.js +55 -0
- package/dist/src/cluster_v2/events.js +102 -0
- package/dist/src/cluster_v2/index.js +2 -0
- package/dist/src/cluster_v2/observability.js +99 -0
- package/dist/src/cluster_v2/observability.test.js +46 -0
- package/dist/src/cluster_v2/rpc.js +389 -0
- package/dist/src/cluster_v2/rpc.test.js +110 -0
- package/dist/src/cluster_v2/runtime.failover.integration.test.js +156 -0
- package/dist/src/cluster_v2/runtime.js +531 -0
- package/dist/src/cluster_v2/runtime.lease-compromise.integration.test.js +91 -0
- package/dist/src/cluster_v2/runtime.lifecycle.integration.test.js +225 -0
- package/dist/src/cluster_v2/services/bus.integration.test.js +140 -0
- package/dist/src/cluster_v2/services/bus.js +450 -0
- package/dist/src/cluster_v2/services/discord/auth-store.js +82 -0
- package/dist/src/cluster_v2/services/discord/collector.js +569 -0
- package/dist/src/cluster_v2/services/discord/index.js +1 -0
- package/dist/src/cluster_v2/services/discord/oauth.js +87 -0
- package/dist/src/cluster_v2/services/discord/rpc-client.js +325 -0
- package/dist/src/cluster_v2/services/embedding.js +66 -0
- package/dist/src/cluster_v2/services/registry-cache.js +107 -0
- package/dist/src/cluster_v2/services/registry-cache.test.js +66 -0
- package/dist/src/cluster_v2/services/registry.js +36 -0
- package/dist/src/cluster_v2/services/twitter/collector.js +1055 -0
- package/dist/src/cluster_v2/services/twitter/index.js +1 -0
- package/dist/src/config/digest.js +78 -0
- package/dist/src/config/discord.js +143 -0
- package/dist/src/config/image-gen.js +48 -0
- package/dist/src/config/mono-pilot.js +31 -0
- package/dist/src/config/twitter.js +100 -0
- package/dist/src/extensions/cluster.js +311 -0
- package/dist/src/extensions/commands/build-memory.js +76 -0
- package/dist/src/extensions/commands/digest/backfill.js +779 -0
- package/dist/src/extensions/commands/digest/index.js +1133 -0
- package/dist/src/extensions/commands/image-model.js +214 -0
- package/dist/src/extensions/game/bus-injection.js +47 -0
- package/dist/src/extensions/game/identity.js +83 -0
- package/dist/src/extensions/game/mailbox.js +61 -0
- package/dist/src/extensions/game/system-prompt.js +134 -0
- package/dist/src/extensions/game/tools.js +28 -0
- package/dist/src/extensions/lifecycle.js +337 -0
- package/dist/src/extensions/mode-runtime.js +26 -2
- package/dist/src/extensions/mono-game.js +66 -0
- package/dist/src/extensions/mono-pilot.js +100 -18
- package/dist/src/extensions/nvim.js +47 -0
- package/dist/src/extensions/session-hints.js +60 -35
- package/dist/src/extensions/sftp.js +897 -0
- package/dist/src/extensions/status.js +676 -0
- package/dist/src/extensions/system-events.js +478 -0
- package/dist/src/extensions/system-prompt.js +24 -14
- package/dist/src/extensions/user-message.js +94 -50
- package/dist/src/lsp/client.js +235 -0
- package/dist/src/lsp/index.js +165 -0
- package/dist/src/lsp/runtime.js +67 -0
- package/dist/src/lsp/server.js +242 -0
- package/dist/src/mcp/config.js +112 -0
- package/dist/src/{utils/mcp-client.js → mcp/protocol.js} +1 -100
- package/dist/src/mcp/servers.js +90 -0
- package/dist/src/memory/build-memory.js +103 -0
- package/dist/src/memory/config/defaults.js +55 -0
- package/dist/src/memory/config/loader.js +29 -0
- package/dist/src/memory/config/paths.js +9 -0
- package/dist/src/memory/config/resolve.js +90 -0
- package/dist/src/memory/config/types.js +1 -0
- package/dist/src/memory/embeddings/batch-runner.js +39 -0
- package/dist/src/memory/embeddings/cache.js +47 -0
- package/dist/src/memory/embeddings/chunk-limits.js +26 -0
- package/dist/src/memory/embeddings/input-limits.js +48 -0
- package/dist/src/memory/embeddings/local.js +108 -0
- package/dist/src/memory/embeddings/types.js +1 -0
- package/dist/src/memory/index-manager.js +552 -0
- package/dist/src/memory/indexing/embeddings.js +67 -0
- package/dist/src/memory/indexing/files.js +180 -0
- package/dist/src/memory/indexing/index-file.js +105 -0
- package/dist/src/memory/log.js +38 -0
- package/dist/src/memory/paths.js +15 -0
- package/dist/src/memory/runtime/index.js +299 -0
- package/dist/src/memory/runtime/thread.js +116 -0
- package/dist/src/memory/search/fts.js +57 -0
- package/dist/src/memory/search/hybrid.js +50 -0
- package/dist/src/memory/search/text.js +30 -0
- package/dist/src/memory/search/vector.js +43 -0
- package/dist/src/memory/session/content-hash.js +7 -0
- package/dist/src/memory/session/entry.js +33 -0
- package/dist/src/memory/session/flush-policy.js +34 -0
- package/dist/src/memory/session/hook.js +191 -0
- package/dist/src/memory/session/paths.js +15 -0
- package/dist/src/memory/session/session-reader.js +88 -0
- package/dist/src/memory/session/transcript/content-hash.js +7 -0
- package/dist/src/memory/session/transcript/entry.js +28 -0
- package/dist/src/memory/session/transcript/flush.js +56 -0
- package/dist/src/memory/session/transcript/paths.js +28 -0
- package/dist/src/memory/session/transcript/reader.js +112 -0
- package/dist/src/memory/session/transcript/state.js +31 -0
- package/dist/src/memory/store/schema.js +89 -0
- package/dist/src/memory/store/sqlite.js +89 -0
- package/dist/src/memory/types.js +1 -0
- package/dist/src/memory/warm.js +25 -0
- package/dist/src/rules/discovery.js +41 -0
- package/dist/{tools → src/tools}/README.md +29 -3
- package/dist/{tools → src/tools}/apply-patch-description.md +8 -2
- package/dist/{tools → src/tools}/apply-patch.js +174 -104
- package/dist/{tools → src/tools}/apply-patch.test.js +52 -1
- package/dist/{tools/ask-question.js → src/tools/ask-user-question.js} +3 -3
- package/dist/src/tools/ast-grep.js +357 -0
- package/dist/src/tools/brief-write.js +122 -0
- package/dist/src/tools/bus-send.js +100 -0
- package/dist/{tools → src/tools}/call-mcp-tool.js +40 -124
- package/dist/src/tools/codex-apply-patch-description.md +52 -0
- package/dist/src/tools/codex-apply-patch.js +540 -0
- package/dist/{tools → src/tools}/delete.js +24 -0
- package/dist/src/tools/exit-plan-mode.js +83 -0
- package/dist/{tools → src/tools}/fetch-mcp-resource.js +56 -100
- package/dist/src/tools/generate-image.js +567 -0
- package/dist/{tools → src/tools}/glob.js +55 -1
- package/dist/{tools → src/tools}/list-mcp-resources.js +46 -57
- package/dist/{tools → src/tools}/list-mcp-tools.js +52 -63
- package/dist/src/tools/ls.js +48 -0
- package/dist/src/tools/lsp-diagnostics.js +67 -0
- package/dist/src/tools/lsp-symbols.js +54 -0
- package/dist/src/tools/mailbox.js +85 -0
- package/dist/src/tools/memory-get.js +90 -0
- package/dist/src/tools/memory-search.js +180 -0
- package/dist/{tools → src/tools}/plan-mode-reminder.md +3 -4
- package/dist/{tools → src/tools}/read-file.js +8 -19
- package/dist/{tools → src/tools}/rg.js +10 -20
- package/dist/{tools → src/tools}/shell.js +19 -42
- package/dist/{tools → src/tools}/subagent.js +255 -6
- package/dist/{tools → src/tools}/switch-mode.js +37 -6
- package/dist/{tools → src/tools}/web-fetch.js +105 -7
- package/dist/{tools → src/tools}/web-search.js +29 -1
- package/package.json +21 -9
- /package/dist/{tools → src/tools}/ask-mode-reminder.md +0 -0
- /package/dist/{tools → src/tools}/rg.test.js +0 -0
- /package/dist/{tools → src/tools}/semantic-search-description.md +0 -0
- /package/dist/{tools → src/tools}/semantic-search.js +0 -0
- /package/dist/{tools → src/tools}/shell-description.md +0 -0
- /package/dist/{tools → src/tools}/subagent-description.md +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { keyHint } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
3
3
|
import { Type } from "@sinclair/typebox";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveTargetServers } from "../mcp/servers.js";
|
|
5
|
+
import { createRpcRequestId, formatJsonRpcError, initializeMcpSession, postJsonRpcRequest } from "../mcp/protocol.js";
|
|
6
|
+
import { formatErrorMessage, isRecord, toNonEmptyString } from "../mcp/config.js";
|
|
5
7
|
const DESCRIPTION = `List available resources from configured MCP servers. Each returned resource will include all standard MCP resource fields plus a 'server' field indicating which server the resource belongs to. MCP resources are _not_ the same as tools, so don't call this function to discover MCP tools.`;
|
|
6
8
|
const listMcpResourcesSchema = Type.Object({
|
|
7
9
|
server: Type.Optional(Type.String({
|
|
@@ -49,12 +51,9 @@ async function listRemoteMcpResources(options) {
|
|
|
49
51
|
mimeType: toNonEmptyString(raw.mimeType),
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
|
-
return {
|
|
53
|
-
resources,
|
|
54
|
-
nextCursor: toNonEmptyString(resourcesBody.result.nextCursor),
|
|
55
|
-
};
|
|
54
|
+
return { resources, nextCursor: toNonEmptyString(resourcesBody.result.nextCursor) };
|
|
56
55
|
}
|
|
57
|
-
function
|
|
56
|
+
function normalizeOptionalString(value) {
|
|
58
57
|
if (typeof value !== "string")
|
|
59
58
|
return undefined;
|
|
60
59
|
const normalized = value.trim();
|
|
@@ -66,62 +65,50 @@ export default function listMcpResourcesExtension(pi) {
|
|
|
66
65
|
label: "ListMcpResources",
|
|
67
66
|
description: DESCRIPTION,
|
|
68
67
|
parameters: listMcpResourcesSchema,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
renderCall(args, theme) {
|
|
69
|
+
const input = args;
|
|
70
|
+
const server = typeof input.server === "string" && input.server.trim().length > 0 ? input.server : undefined;
|
|
71
|
+
let text = theme.fg("toolTitle", theme.bold("ListMcpResources"));
|
|
72
|
+
if (server)
|
|
73
|
+
text += ` ${theme.fg("toolOutput", server)}`;
|
|
74
|
+
return new Text(text, 0, 0);
|
|
75
|
+
},
|
|
76
|
+
renderResult(result, { expanded, isPartial }, theme) {
|
|
77
|
+
if (isPartial) {
|
|
78
|
+
return new Text(theme.fg("muted", "Listing resources..."), 0, 0);
|
|
73
79
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return
|
|
77
|
-
content: [{ type: "text", text: message }],
|
|
78
|
-
details: { error: message },
|
|
79
|
-
isError: true,
|
|
80
|
-
};
|
|
80
|
+
const textBlock = result.content.find((entry) => entry.type === "text" && typeof entry.text === "string");
|
|
81
|
+
if (!textBlock) {
|
|
82
|
+
return new Text(theme.fg("error", "No text result returned."), 0, 0);
|
|
81
83
|
}
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
return
|
|
88
|
-
content: [{ type: "text", text: message }],
|
|
89
|
-
details: { error: message },
|
|
90
|
-
isError: true,
|
|
91
|
-
};
|
|
84
|
+
const fullText = textBlock.text;
|
|
85
|
+
const details = result.details;
|
|
86
|
+
const count = details?.total_resources ?? 0;
|
|
87
|
+
if (!expanded) {
|
|
88
|
+
const summary = `${count} resources (click or ${keyHint("expandTools", "to expand")})`;
|
|
89
|
+
return new Text(theme.fg("muted", summary), 0, 0);
|
|
92
90
|
}
|
|
93
|
-
let
|
|
91
|
+
let text = fullText.split("\n").map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
92
|
+
text += theme.fg("muted", `\n(click or ${keyHint("expandTools", "to collapse")})`);
|
|
93
|
+
return new Text(text, 0, 0);
|
|
94
|
+
},
|
|
95
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
96
|
+
const serverFilter = normalizeOptionalString(params.server);
|
|
97
|
+
let targetServers;
|
|
98
|
+
let sources;
|
|
94
99
|
try {
|
|
95
|
-
|
|
100
|
+
const result = await resolveTargetServers(ctx.cwd, serverFilter);
|
|
101
|
+
targetServers = result.servers;
|
|
102
|
+
sources = result.sources;
|
|
96
103
|
}
|
|
97
104
|
catch (error) {
|
|
98
105
|
const message = formatErrorMessage(error);
|
|
99
106
|
return {
|
|
100
107
|
content: [{ type: "text", text: message }],
|
|
101
|
-
details: {
|
|
102
|
-
config_path: configPath,
|
|
103
|
-
error: message,
|
|
104
|
-
},
|
|
108
|
+
details: { error: message },
|
|
105
109
|
isError: true,
|
|
106
110
|
};
|
|
107
111
|
}
|
|
108
|
-
const targetServers = [];
|
|
109
|
-
for (const [serverName, serverConfig] of Object.entries(servers)) {
|
|
110
|
-
if (serverFilter && serverName !== serverFilter)
|
|
111
|
-
continue;
|
|
112
|
-
if (!isServerEnabled(serverConfig))
|
|
113
|
-
continue;
|
|
114
|
-
if (inferTransport(serverConfig) !== "remote")
|
|
115
|
-
continue;
|
|
116
|
-
const serverUrl = toNonEmptyString(serverConfig.url);
|
|
117
|
-
if (!serverUrl)
|
|
118
|
-
continue;
|
|
119
|
-
targetServers.push({
|
|
120
|
-
name: serverName,
|
|
121
|
-
url: serverUrl,
|
|
122
|
-
headers: extractStringHeaders(serverConfig.headers),
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
112
|
if (targetServers.length === 0) {
|
|
126
113
|
const message = serverFilter
|
|
127
114
|
? `No active remote MCP server found matching '${serverFilter}'.`
|
|
@@ -129,13 +116,16 @@ export default function listMcpResourcesExtension(pi) {
|
|
|
129
116
|
return {
|
|
130
117
|
content: [{ type: "text", text: message }],
|
|
131
118
|
details: {
|
|
132
|
-
|
|
119
|
+
config_paths: sources.map((s) => s.path),
|
|
133
120
|
servers_matched: 0,
|
|
134
121
|
},
|
|
135
122
|
};
|
|
136
123
|
}
|
|
137
124
|
const lines = [];
|
|
138
|
-
lines.push(
|
|
125
|
+
lines.push("MCP config:");
|
|
126
|
+
for (const source of sources) {
|
|
127
|
+
lines.push(`- ${source.scope}: ${source.path}`);
|
|
128
|
+
}
|
|
139
129
|
lines.push(`Servers matched: ${targetServers.length}`);
|
|
140
130
|
if (serverFilter)
|
|
141
131
|
lines.push(`Server filter: ${serverFilter}`);
|
|
@@ -162,8 +152,7 @@ export default function listMcpResourcesExtension(pi) {
|
|
|
162
152
|
if (resource.mimeType)
|
|
163
153
|
lines.push(` mimeType: ${resource.mimeType}`);
|
|
164
154
|
if (resource.description) {
|
|
165
|
-
const
|
|
166
|
-
for (const descLine of descLines) {
|
|
155
|
+
for (const descLine of resource.description.split("\n")) {
|
|
167
156
|
lines.push(` ${descLine}`);
|
|
168
157
|
}
|
|
169
158
|
}
|
|
@@ -180,7 +169,7 @@ export default function listMcpResourcesExtension(pi) {
|
|
|
180
169
|
return {
|
|
181
170
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
182
171
|
details: {
|
|
183
|
-
|
|
172
|
+
config_paths: sources.map((s) => s.path),
|
|
184
173
|
servers_matched: targetServers.length,
|
|
185
174
|
servers_queried: targetServers.length,
|
|
186
175
|
servers_failed: serversFailed,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { keyHint } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
3
3
|
import { Type } from "@sinclair/typebox";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveTargetServers } from "../mcp/servers.js";
|
|
5
|
+
import { createRpcRequestId, formatJsonRpcError, initializeMcpSession, postJsonRpcRequest } from "../mcp/protocol.js";
|
|
6
|
+
import { formatErrorMessage, isRecord, toNonEmptyString } from "../mcp/config.js";
|
|
5
7
|
const DESCRIPTION = `List available MCP tools from configured MCP servers. Each returned tool includes server metadata. If server is provided, results are limited to that server. If toolName is provided, returns full documentation and input JSON schema for matching tools.`;
|
|
6
8
|
const listMcpToolsSchema = Type.Object({
|
|
7
9
|
server: Type.Optional(Type.String({
|
|
@@ -44,16 +46,9 @@ async function listRemoteMcpTools(options) {
|
|
|
44
46
|
const name = toNonEmptyString(raw.name);
|
|
45
47
|
if (!name)
|
|
46
48
|
continue;
|
|
47
|
-
tools.push({
|
|
48
|
-
name,
|
|
49
|
-
description: toNonEmptyString(raw.description),
|
|
50
|
-
inputSchema: raw.inputSchema,
|
|
51
|
-
});
|
|
49
|
+
tools.push({ name, description: toNonEmptyString(raw.description), inputSchema: raw.inputSchema });
|
|
52
50
|
}
|
|
53
|
-
return {
|
|
54
|
-
tools,
|
|
55
|
-
nextCursor: toNonEmptyString(body.result.nextCursor),
|
|
56
|
-
};
|
|
51
|
+
return { tools, nextCursor: toNonEmptyString(body.result.nextCursor) };
|
|
57
52
|
}
|
|
58
53
|
function normalizeOptionalString(value) {
|
|
59
54
|
if (typeof value !== "string")
|
|
@@ -67,64 +62,57 @@ export default function listMcpToolsExtension(pi) {
|
|
|
67
62
|
label: "ListMcpTools",
|
|
68
63
|
description: DESCRIPTION,
|
|
69
64
|
parameters: listMcpToolsSchema,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
toolNameFilter = normalizeOptionalString(params.toolName);
|
|
65
|
+
renderCall(args, theme) {
|
|
66
|
+
const input = args;
|
|
67
|
+
const commandArgs = [];
|
|
68
|
+
if (typeof input.server === "string" && input.server.trim()) {
|
|
69
|
+
commandArgs.push("--server", input.server.trim());
|
|
76
70
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
content: [{ type: "text", text: message }],
|
|
81
|
-
details: { error: message },
|
|
82
|
-
isError: true,
|
|
83
|
-
};
|
|
71
|
+
if (typeof input.toolName === "string" && input.toolName.trim()) {
|
|
72
|
+
commandArgs.push("--toolName", input.toolName.trim());
|
|
84
73
|
}
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
isError: true,
|
|
94
|
-
};
|
|
74
|
+
let text = theme.fg("toolTitle", theme.bold("ListMcpTools"));
|
|
75
|
+
if (commandArgs.length > 0)
|
|
76
|
+
text += ` ${theme.fg("toolOutput", commandArgs.join(" "))}`;
|
|
77
|
+
return new Text(text, 0, 0);
|
|
78
|
+
},
|
|
79
|
+
renderResult(result, { expanded, isPartial }, theme) {
|
|
80
|
+
if (isPartial) {
|
|
81
|
+
return new Text(theme.fg("muted", "Listing tools..."), 0, 0);
|
|
95
82
|
}
|
|
96
|
-
|
|
83
|
+
const textBlock = result.content.find((entry) => entry.type === "text" && typeof entry.text === "string");
|
|
84
|
+
if (!textBlock) {
|
|
85
|
+
return new Text(theme.fg("error", "No text result returned."), 0, 0);
|
|
86
|
+
}
|
|
87
|
+
const fullText = textBlock.text;
|
|
88
|
+
const details = result.details;
|
|
89
|
+
const count = details?.total_tools ?? 0;
|
|
90
|
+
if (!expanded) {
|
|
91
|
+
const summary = `${count} tools (click or ${keyHint("expandTools", "to expand")})`;
|
|
92
|
+
return new Text(theme.fg("muted", summary), 0, 0);
|
|
93
|
+
}
|
|
94
|
+
let text = fullText.split("\n").map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
95
|
+
text += theme.fg("muted", `\n(click or ${keyHint("expandTools", "to collapse")})`);
|
|
96
|
+
return new Text(text, 0, 0);
|
|
97
|
+
},
|
|
98
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
99
|
+
const serverFilter = normalizeOptionalString(params.server);
|
|
100
|
+
const toolNameFilter = normalizeOptionalString(params.toolName);
|
|
101
|
+
let targetServers;
|
|
102
|
+
let sources;
|
|
97
103
|
try {
|
|
98
|
-
|
|
104
|
+
const result = await resolveTargetServers(ctx.cwd, serverFilter);
|
|
105
|
+
targetServers = result.servers;
|
|
106
|
+
sources = result.sources;
|
|
99
107
|
}
|
|
100
108
|
catch (error) {
|
|
101
109
|
const message = formatErrorMessage(error);
|
|
102
110
|
return {
|
|
103
111
|
content: [{ type: "text", text: message }],
|
|
104
|
-
details: {
|
|
105
|
-
config_path: configPath,
|
|
106
|
-
error: message,
|
|
107
|
-
},
|
|
112
|
+
details: { error: message },
|
|
108
113
|
isError: true,
|
|
109
114
|
};
|
|
110
115
|
}
|
|
111
|
-
const targetServers = [];
|
|
112
|
-
for (const [serverName, serverConfig] of Object.entries(servers)) {
|
|
113
|
-
if (serverFilter && serverName !== serverFilter)
|
|
114
|
-
continue;
|
|
115
|
-
if (!isServerEnabled(serverConfig))
|
|
116
|
-
continue;
|
|
117
|
-
if (inferTransport(serverConfig) !== "remote")
|
|
118
|
-
continue;
|
|
119
|
-
const serverUrl = toNonEmptyString(serverConfig.url);
|
|
120
|
-
if (!serverUrl)
|
|
121
|
-
continue;
|
|
122
|
-
targetServers.push({
|
|
123
|
-
name: serverName,
|
|
124
|
-
url: serverUrl,
|
|
125
|
-
headers: extractStringHeaders(serverConfig.headers),
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
116
|
if (targetServers.length === 0) {
|
|
129
117
|
const message = serverFilter
|
|
130
118
|
? `No active remote MCP server found matching '${serverFilter}'.`
|
|
@@ -132,13 +120,16 @@ export default function listMcpToolsExtension(pi) {
|
|
|
132
120
|
return {
|
|
133
121
|
content: [{ type: "text", text: message }],
|
|
134
122
|
details: {
|
|
135
|
-
|
|
123
|
+
config_paths: sources.map((s) => s.path),
|
|
136
124
|
servers_matched: 0,
|
|
137
125
|
},
|
|
138
126
|
};
|
|
139
127
|
}
|
|
140
128
|
const lines = [];
|
|
141
|
-
lines.push(
|
|
129
|
+
lines.push("MCP config:");
|
|
130
|
+
for (const source of sources) {
|
|
131
|
+
lines.push(`- ${source.scope}: ${source.path}`);
|
|
132
|
+
}
|
|
142
133
|
lines.push(`Servers matched: ${targetServers.length}`);
|
|
143
134
|
if (serverFilter)
|
|
144
135
|
lines.push(`Server filter: ${serverFilter}`);
|
|
@@ -167,7 +158,6 @@ export default function listMcpToolsExtension(pi) {
|
|
|
167
158
|
continue;
|
|
168
159
|
}
|
|
169
160
|
if (toolNameFilter) {
|
|
170
|
-
// Detailed mode
|
|
171
161
|
for (const tool of matchedTools) {
|
|
172
162
|
totalTools++;
|
|
173
163
|
lines.push(`## [${target.name}] ${tool.name}`);
|
|
@@ -187,7 +177,6 @@ export default function listMcpToolsExtension(pi) {
|
|
|
187
177
|
}
|
|
188
178
|
}
|
|
189
179
|
else {
|
|
190
|
-
// Summary mode
|
|
191
180
|
lines.push(`Tools returned: ${matchedTools.length}`);
|
|
192
181
|
lines.push("");
|
|
193
182
|
for (const tool of matchedTools) {
|
|
@@ -218,7 +207,7 @@ export default function listMcpToolsExtension(pi) {
|
|
|
218
207
|
return {
|
|
219
208
|
content: [{ type: "text", text: lines.join("\n").trim() }],
|
|
220
209
|
details: {
|
|
221
|
-
|
|
210
|
+
config_paths: sources.map((s) => s.path),
|
|
222
211
|
servers_matched: targetServers.length,
|
|
223
212
|
servers_queried: targetServers.length,
|
|
224
213
|
servers_failed: serversFailed,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { keyHint } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { createLsTool } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
4
|
+
// Grab schema, description, label from the builtin ls tool
|
|
5
|
+
const builtinLs = createLsTool(process.cwd());
|
|
6
|
+
export default function (pi) {
|
|
7
|
+
pi.registerTool({
|
|
8
|
+
name: builtinLs.name,
|
|
9
|
+
label: builtinLs.label,
|
|
10
|
+
description: builtinLs.description,
|
|
11
|
+
parameters: builtinLs.parameters,
|
|
12
|
+
renderCall(args, theme) {
|
|
13
|
+
const input = args;
|
|
14
|
+
const dir = typeof input.path === "string" && input.path.trim().length > 0
|
|
15
|
+
? input.path
|
|
16
|
+
: ".";
|
|
17
|
+
let text = theme.fg("toolTitle", theme.bold("ls"));
|
|
18
|
+
text += ` ${theme.fg("toolOutput", dir)}`;
|
|
19
|
+
return new Text(text, 0, 0);
|
|
20
|
+
},
|
|
21
|
+
renderResult(result, { expanded, isPartial }, theme) {
|
|
22
|
+
if (isPartial) {
|
|
23
|
+
return new Text(theme.fg("muted", "Listing..."), 0, 0);
|
|
24
|
+
}
|
|
25
|
+
const textBlock = result.content.find((entry) => entry.type === "text");
|
|
26
|
+
if (!textBlock) {
|
|
27
|
+
return new Text(theme.fg("error", "No output."), 0, 0);
|
|
28
|
+
}
|
|
29
|
+
const fullText = textBlock.text;
|
|
30
|
+
const entryCount = fullText.split("\n").filter(Boolean).length;
|
|
31
|
+
if (!expanded) {
|
|
32
|
+
const summary = `${entryCount} entries (click or ${keyHint("expandTools", "to expand")})`;
|
|
33
|
+
return new Text(theme.fg("muted", summary), 0, 0);
|
|
34
|
+
}
|
|
35
|
+
let text = fullText
|
|
36
|
+
.split("\n")
|
|
37
|
+
.map((line) => theme.fg("toolOutput", line))
|
|
38
|
+
.join("\n");
|
|
39
|
+
text += theme.fg("muted", `\n(click or ${keyHint("expandTools", "to collapse")})`);
|
|
40
|
+
return new Text(text, 0, 0);
|
|
41
|
+
},
|
|
42
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
43
|
+
// Delegate to the built-in ls tool, constructed with the runtime cwd
|
|
44
|
+
const delegate = createLsTool(ctx.cwd);
|
|
45
|
+
return delegate.execute(toolCallId, params, signal, onUpdate);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { keyHint } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
3
|
+
import { Type } from "@sinclair/typebox";
|
|
4
|
+
import { LSP } from "../lsp/index.js";
|
|
5
|
+
const DESCRIPTION = `Get LSP diagnostics (type errors, warnings, hints) for a specific file.
|
|
6
|
+
|
|
7
|
+
- Run after editing a file to verify no errors were introduced
|
|
8
|
+
- Provide the absolute path to the file
|
|
9
|
+
- Results formatted as: SEVERITY [line:col] message
|
|
10
|
+
- Returns "No diagnostics." if the file has no issues
|
|
11
|
+
- Language server is started automatically on first use
|
|
12
|
+
- Supported languages: TypeScript/JavaScript, Python (Pyright), Go (gopls), Rust (rust-analyzer), Swift (sourcekit-lsp)`;
|
|
13
|
+
const schema = Type.Object({
|
|
14
|
+
file: Type.String({
|
|
15
|
+
description: "Absolute path to the file to check for diagnostics.",
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
export default function (pi) {
|
|
19
|
+
pi.registerTool({
|
|
20
|
+
name: "LspDiagnostics",
|
|
21
|
+
label: "LspDiagnostics",
|
|
22
|
+
description: DESCRIPTION,
|
|
23
|
+
parameters: schema,
|
|
24
|
+
renderCall(args, theme) {
|
|
25
|
+
const input = args;
|
|
26
|
+
const file = typeof input.file === "string" && input.file.trim().length > 0
|
|
27
|
+
? input.file
|
|
28
|
+
: "(missing file)";
|
|
29
|
+
let text = theme.fg("toolTitle", theme.bold("LspDiagnostics"));
|
|
30
|
+
text += ` ${theme.fg("toolOutput", file)}`;
|
|
31
|
+
return new Text(text, 0, 0);
|
|
32
|
+
},
|
|
33
|
+
renderResult(result, { expanded, isPartial }, theme) {
|
|
34
|
+
if (isPartial) {
|
|
35
|
+
return new Text(theme.fg("muted", "Checking..."), 0, 0);
|
|
36
|
+
}
|
|
37
|
+
const textBlock = result.content.find((entry) => entry.type === "text" && typeof entry.text === "string");
|
|
38
|
+
if (!textBlock) {
|
|
39
|
+
return new Text(theme.fg("error", "No text result returned."), 0, 0);
|
|
40
|
+
}
|
|
41
|
+
const fullText = textBlock.text;
|
|
42
|
+
const lineCount = fullText.split("\n").length;
|
|
43
|
+
const isClean = fullText === "No diagnostics.";
|
|
44
|
+
if (!expanded) {
|
|
45
|
+
const summary = isClean
|
|
46
|
+
? theme.fg("muted", "No diagnostics.")
|
|
47
|
+
: `${theme.fg("error", `${lineCount} diagnostics`)} ${theme.fg("muted", `(click or ${keyHint("expandTools", "to expand")})`)}`;
|
|
48
|
+
return new Text(summary, 0, 0);
|
|
49
|
+
}
|
|
50
|
+
let text = fullText
|
|
51
|
+
.split("\n")
|
|
52
|
+
.map((line) => (isClean ? theme.fg("muted", line) : theme.fg("toolOutput", line)))
|
|
53
|
+
.join("\n");
|
|
54
|
+
text += theme.fg("muted", `\n(click or ${keyHint("expandTools", "to collapse")})`);
|
|
55
|
+
return new Text(text, 0, 0);
|
|
56
|
+
},
|
|
57
|
+
async execute(_id, params, _signal, _onUpdate, _ctx) {
|
|
58
|
+
await LSP.touchFile(params.file, true);
|
|
59
|
+
const all = await LSP.diagnostics();
|
|
60
|
+
const diags = all[params.file] ?? [];
|
|
61
|
+
const text = diags.length === 0
|
|
62
|
+
? "No diagnostics."
|
|
63
|
+
: diags.map((d) => LSP.Diagnostic.pretty(d)).join("\n");
|
|
64
|
+
return { content: [{ type: "text", text }], details: { file: params.file, count: diags.length } };
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { LSP } from "../lsp/index.js";
|
|
3
|
+
import { LspState } from "../lsp/runtime.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
const DESCRIPTION = `Search for symbols (functions, classes, interfaces, etc.) across the entire workspace using LSP.
|
|
7
|
+
|
|
8
|
+
- More semantic than text search; understands code structure and filters to meaningful symbols only
|
|
9
|
+
- Provide a partial or full symbol name to search for
|
|
10
|
+
- Returns up to 10 matching symbols with their kind and file location
|
|
11
|
+
- Results formatted as: name (kind) relative/path/to/file.ts:line
|
|
12
|
+
- Supported languages: TypeScript/JavaScript, Python (Pyright), Go (gopls), Rust (rust-analyzer), Swift (sourcekit-lsp)`;
|
|
13
|
+
const KIND_NAMES = {
|
|
14
|
+
5: "class",
|
|
15
|
+
6: "method",
|
|
16
|
+
10: "enum",
|
|
17
|
+
11: "interface",
|
|
18
|
+
12: "function",
|
|
19
|
+
13: "variable",
|
|
20
|
+
14: "constant",
|
|
21
|
+
23: "struct",
|
|
22
|
+
};
|
|
23
|
+
const schema = Type.Object({
|
|
24
|
+
query: Type.String({
|
|
25
|
+
description: "Symbol name or partial name to search for.",
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
export default function (pi) {
|
|
29
|
+
pi.registerTool({
|
|
30
|
+
name: "LspSymbols",
|
|
31
|
+
label: "LspSymbols",
|
|
32
|
+
description: DESCRIPTION,
|
|
33
|
+
parameters: schema,
|
|
34
|
+
async execute(_id, params, _signal, _onUpdate, _ctx) {
|
|
35
|
+
const symbols = await LSP.workspaceSymbol(params.query);
|
|
36
|
+
if (symbols.length === 0) {
|
|
37
|
+
return { content: [{ type: "text", text: "No symbols found." }], details: undefined };
|
|
38
|
+
}
|
|
39
|
+
const lines = symbols.map((s) => {
|
|
40
|
+
const kind = KIND_NAMES[s.kind] ?? `kind:${s.kind}`;
|
|
41
|
+
const uri = s.location?.uri ?? "";
|
|
42
|
+
const line = (s.location?.range?.start?.line ?? 0) + 1;
|
|
43
|
+
const file = uri.startsWith("file://")
|
|
44
|
+
? path.relative(LspState.directory, fileURLToPath(uri))
|
|
45
|
+
: uri;
|
|
46
|
+
return `${s.name} (${kind}) ${file}:${line}`;
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
50
|
+
details: undefined,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MailBox tool — lets a game agent read queued bus messages.
|
|
3
|
+
*/
|
|
4
|
+
import { keyHint } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
6
|
+
import { Type } from "@sinclair/typebox";
|
|
7
|
+
import { isMailBoxConnected, readMailBox, getMailBoxCount } from "../extensions/game/mailbox.js";
|
|
8
|
+
const mailboxSchema = Type.Object({
|
|
9
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, description: "Max messages to return." })),
|
|
10
|
+
clear: Type.Optional(Type.Boolean({ description: "Clear returned messages (default: true)." })),
|
|
11
|
+
});
|
|
12
|
+
const mailboxExtension = (pi) => {
|
|
13
|
+
pi.registerTool({
|
|
14
|
+
name: "MailBox",
|
|
15
|
+
label: "MailBox",
|
|
16
|
+
description: "Read queued message-bus items that were not injected into the conversation. " +
|
|
17
|
+
"Use this to check public or private messages in game mode.",
|
|
18
|
+
parameters: mailboxSchema,
|
|
19
|
+
renderCall(args, theme) {
|
|
20
|
+
const input = args;
|
|
21
|
+
const limit = input.limit ? `limit=${input.limit}` : "all";
|
|
22
|
+
return new Text(`${theme.fg("toolTitle", theme.bold("mailbox"))} ${theme.fg("toolOutput", limit)}`, 0, 0);
|
|
23
|
+
},
|
|
24
|
+
renderResult(result, { expanded, isPartial }, theme) {
|
|
25
|
+
if (isPartial)
|
|
26
|
+
return new Text(theme.fg("muted", "Loading..."), 0, 0);
|
|
27
|
+
const text = result.content.find((e) => e.type === "text")?.text ?? "";
|
|
28
|
+
const details = result.details;
|
|
29
|
+
const count = details?.count;
|
|
30
|
+
const remaining = details?.remaining ?? 0;
|
|
31
|
+
if (!expanded && typeof count === "number") {
|
|
32
|
+
const extra = remaining > 0 ? `, ${remaining} unread` : "";
|
|
33
|
+
const summary = `${count} messages${extra} (click or ${keyHint("expandTools", "to expand")})`;
|
|
34
|
+
return new Text(theme.fg("muted", summary), 0, 0);
|
|
35
|
+
}
|
|
36
|
+
let body = text
|
|
37
|
+
.split("\n")
|
|
38
|
+
.map((line) => theme.fg("toolOutput", line))
|
|
39
|
+
.join("\n");
|
|
40
|
+
if (typeof count === "number") {
|
|
41
|
+
body += theme.fg("muted", `\n(click or ${keyHint("expandTools", "to collapse")})`);
|
|
42
|
+
}
|
|
43
|
+
return new Text(body, 0, 0);
|
|
44
|
+
},
|
|
45
|
+
async execute(_toolCallId, params) {
|
|
46
|
+
if (!isMailBoxConnected()) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: "text", text: "MailBox not connected." }],
|
|
49
|
+
details: "not_connected",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const items = readMailBox({ limit: params.limit, clear: params.clear });
|
|
53
|
+
if (items.length === 0) {
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: "MailBox empty." }],
|
|
56
|
+
details: "empty",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const lines = items.map((msg) => formatMessage(msg));
|
|
60
|
+
const remaining = getMailBoxCount();
|
|
61
|
+
const header = `MailBox (${items.length}):`;
|
|
62
|
+
const footer = remaining > 0 ? `\n(${remaining} more unread)` : "";
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: "text", text: `${header}\n${lines.join("\n")}${footer}` }],
|
|
65
|
+
details: { count: items.length, remaining },
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
function formatMessage(msg) {
|
|
71
|
+
const text = typeof msg.payload === "object" && msg.payload !== null && "text" in msg.payload
|
|
72
|
+
? msg.payload.text
|
|
73
|
+
: JSON.stringify(msg.payload);
|
|
74
|
+
const sender = msg.fromName ?? msg.from;
|
|
75
|
+
const channel = msg.channel ?? "public";
|
|
76
|
+
const channelLabel = channel.startsWith("private:") ? "私信" : channel;
|
|
77
|
+
return [
|
|
78
|
+
`- from: ${sender}`,
|
|
79
|
+
` seq: ${msg.seq}`,
|
|
80
|
+
` channel: ${channelLabel}`,
|
|
81
|
+
` message: ${text}`,
|
|
82
|
+
"",
|
|
83
|
+
].join("\n");
|
|
84
|
+
}
|
|
85
|
+
export default mailboxExtension;
|