@daniel156161/prism 0.2.92 → 0.2.97
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/LICENSE +21 -0
- package/README.md +386 -181
- package/dist/pi/pi-context-files.d.ts +6 -0
- package/dist/pi/pi-context-files.js +17 -0
- package/dist/pi/pi-context-files.js.map +1 -0
- package/dist/pi/pi-extensions.d.ts +0 -1
- package/dist/pi/pi-extensions.js +7 -22
- package/dist/pi/pi-extensions.js.map +1 -1
- package/dist/pi/pi-npm-extension-entry.d.ts +2 -0
- package/dist/pi/pi-npm-extension-entry.js +74 -0
- package/dist/pi/pi-npm-extension-entry.js.map +1 -0
- package/dist/pi/pi-package.js +4 -0
- package/dist/pi/pi-package.js.map +1 -1
- package/dist/pi/pi-patch-context-files.d.ts +12 -0
- package/dist/pi/pi-patch-context-files.js +46 -0
- package/dist/pi/pi-patch-context-files.js.map +1 -0
- package/dist/pi/pi-patch-interactive.js +5 -1
- package/dist/pi/pi-patch-interactive.js.map +1 -1
- package/dist/pi/plan-mode-tools.d.ts +13 -0
- package/dist/pi/plan-mode-tools.js +54 -0
- package/dist/pi/plan-mode-tools.js.map +1 -0
- package/dist/prism-extensions/core/mex-anchor.d.ts +9 -0
- package/dist/prism-extensions/core/mex-anchor.js +29 -0
- package/dist/prism-extensions/core/mex-anchor.js.map +1 -0
- package/dist/prism-extensions/core/mex-cli.d.ts +68 -0
- package/dist/prism-extensions/core/mex-cli.js +183 -0
- package/dist/prism-extensions/core/mex-cli.js.map +1 -0
- package/dist/prism-extensions/core/mex-graph-db.d.ts +26 -0
- package/dist/prism-extensions/core/mex-graph-db.js +69 -0
- package/dist/prism-extensions/core/mex-graph-db.js.map +1 -0
- package/dist/prism-extensions/integrations/ai-memory-system.js +27 -6
- package/dist/prism-extensions/integrations/ai-memory-system.js.map +1 -1
- package/dist/prism-extensions/integrations/mex-memory.d.ts +21 -0
- package/dist/prism-extensions/integrations/mex-memory.js +231 -0
- package/dist/prism-extensions/integrations/mex-memory.js.map +1 -0
- package/dist/prism-extensions/tools/builtin-tools.js +2 -0
- package/dist/prism-extensions/tools/builtin-tools.js.map +1 -1
- package/dist/prism-extensions/tools/mex-tools.d.ts +2 -0
- package/dist/prism-extensions/tools/mex-tools.js +78 -0
- package/dist/prism-extensions/tools/mex-tools.js.map +1 -0
- package/dist/prism-extensions/tools/prism-diagnostic-tools.js +10 -10
- package/dist/prism-extensions/tools/prism-diagnostic-tools.js.map +1 -1
- package/dist/prism-extensions/tools/toolbox.js +1 -1
- package/dist/prism-extensions/tools/toolbox.js.map +1 -1
- package/dist/prism-extensions/ui/toolbar.d.ts +2 -0
- package/dist/prism-extensions/ui/toolbar.js +10 -1
- package/dist/prism-extensions/ui/toolbar.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +15 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +6 -6
- package/package.json +17 -4
- package/src/prism-extensions/README.md +10 -1
- package/src/prism-extensions/core/mex-anchor.ts +29 -0
- package/src/prism-extensions/core/mex-cli.ts +217 -0
- package/src/prism-extensions/core/mex-graph-db.ts +76 -0
- package/src/prism-extensions/integrations/ai-memory-system.ts +30 -6
- package/src/prism-extensions/integrations/mex-memory.ts +264 -0
- package/src/prism-extensions/tools/builtin-tools.ts +2 -0
- package/src/prism-extensions/tools/mex-tools.ts +93 -0
- package/src/prism-extensions/tools/prism-diagnostic-tools.ts +10 -10
- package/src/prism-extensions/tools/toolbox.ts +1 -1
- package/src/prism-extensions/ui/toolbar.ts +10 -1
- package/src/prism-extensions/commands/voice-command.ts +0 -49
- package/src/prism-extensions/core/voice-runtime.ts +0 -322
- package/src/prism-extensions/core/voicebox-client.ts +0 -205
- package/src/prism-extensions/core/voicebox-service.ts +0 -137
- package/src/prism-extensions/integrations/honcho-memory.ts +0 -299
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Type } from "typebox"
|
|
2
|
+
import {
|
|
3
|
+
buildCheckArgs,
|
|
4
|
+
buildGetArgs,
|
|
5
|
+
buildGraphArgs,
|
|
6
|
+
buildImpactArgs,
|
|
7
|
+
buildLogArgs,
|
|
8
|
+
buildQueryArgs,
|
|
9
|
+
buildScopeArgs,
|
|
10
|
+
buildTimelineArgs,
|
|
11
|
+
hasMexScaffold,
|
|
12
|
+
isMexAvailable,
|
|
13
|
+
MEX_LOG_TYPES,
|
|
14
|
+
MEX_RELATIONS,
|
|
15
|
+
runMex,
|
|
16
|
+
type MexRunOptions,
|
|
17
|
+
} from "../core/mex-cli.js"
|
|
18
|
+
import {
|
|
19
|
+
describeMexGraphEnsure,
|
|
20
|
+
ensureMexGraphDb,
|
|
21
|
+
hasMexGraphDb,
|
|
22
|
+
isMexAutoGraphEnabled,
|
|
23
|
+
needsMexGraphAction,
|
|
24
|
+
type MexGraphEnsureResult,
|
|
25
|
+
} from "../core/mex-graph-db.js"
|
|
26
|
+
import { textToolResult } from "../tools/toolbox-utils.js"
|
|
27
|
+
import { formatBracketedToolCall, renderSingleLineToolCall, type ThemeLike } from "../ui/tool-call-rendering.js"
|
|
28
|
+
|
|
29
|
+
type ExtensionAPI = any
|
|
30
|
+
|
|
31
|
+
const MEX_STATUS_KEY = "mex"
|
|
32
|
+
const MEX_STATUS_LABEL = "🕸 mex"
|
|
33
|
+
const MEX_STATUS_BUILDING_LABEL = "🕸 mex ⟳"
|
|
34
|
+
|
|
35
|
+
const RETRIEVAL_PARAMS = {
|
|
36
|
+
detail: Type.Optional(Type.String({ description: "minimal | standard | source. Default minimal." })),
|
|
37
|
+
maxNodes: Type.Optional(Type.Number({ description: "Maximum nodes to return." })),
|
|
38
|
+
maxOutputTokens: Type.Optional(Type.Number({ description: "Hard output token ceiling." })),
|
|
39
|
+
maxSourceLines: Type.Optional(Type.Number({ description: "Per-node source line cap (detail=source)." })),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function formatMexOutput(action: string, result: { ok: boolean; exitCode: number; output: string }) {
|
|
43
|
+
const text = result.output || (result.ok ? `mex ${action} produced no output.` : `mex ${action} failed.`)
|
|
44
|
+
return textToolResult(text, { action, ok: result.ok, exitCode: result.exitCode })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Registers the mex code-graph tools. Exported for tests. */
|
|
48
|
+
export function registerMexTools(pi: ExtensionAPI, options: MexRunOptions = {}): string[] {
|
|
49
|
+
const registered: string[] = []
|
|
50
|
+
const exec = async (action: string, args: string[]) => {
|
|
51
|
+
// For code-graph actions we must have a `.mex/` scaffold.
|
|
52
|
+
// Otherwise `mex` itself may auto-create the scaffold/DB, which we explicitly want to avoid.
|
|
53
|
+
if (needsMexGraphAction(action)) {
|
|
54
|
+
const ensured = await ensureMexGraphDb(graphEnsureOptions(options))
|
|
55
|
+
if (ensured.status === "no-scaffold" || ensured.status === "disabled") {
|
|
56
|
+
return formatMexOutput(action, {
|
|
57
|
+
ok: false,
|
|
58
|
+
exitCode: 1,
|
|
59
|
+
output: `mex ${action} disabled: missing .mex scaffold (needed for graph.db). Run npx mex-agent setup in the repo root, or disable mex via PRISM_DISABLE_MEX=1.`,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return formatMexOutput(action, await runMex(args, options))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function register(tool: any): void {
|
|
67
|
+
registered.push(tool.name)
|
|
68
|
+
pi.registerTool(tool)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
register({
|
|
72
|
+
name: "mex_scope",
|
|
73
|
+
label: "Mex Scope",
|
|
74
|
+
description: "Retrieve a compact, task-relevant code neighborhood from the local mex code graph (JSONL: symbols, signatures, relations, node ids).",
|
|
75
|
+
promptSnippet: "Use mex_scope first for codebase questions to get a compact set of relevant symbols instead of scanning files.",
|
|
76
|
+
promptGuidelines: [
|
|
77
|
+
"Describe the task in natural language, e.g. 'trace the authentication flow'.",
|
|
78
|
+
"Scope matching is word-based, not semantic. If results look weak, rephrase once, then fall back to grep/read.",
|
|
79
|
+
"Expand promising node ids with mex_get instead of reading whole files.",
|
|
80
|
+
],
|
|
81
|
+
parameters: Type.Object({
|
|
82
|
+
task: Type.String({ description: "Task description, e.g. 'trace the authentication flow'." }),
|
|
83
|
+
...RETRIEVAL_PARAMS,
|
|
84
|
+
fingerprint: Type.Optional(Type.Boolean({ description: "Attach node fingerprints (grounding workflow)." })),
|
|
85
|
+
}),
|
|
86
|
+
async execute(_id: string, params: any) {
|
|
87
|
+
return exec("scope", buildScopeArgs(params.task, params))
|
|
88
|
+
},
|
|
89
|
+
renderCall(args: any, theme: ThemeLike, context: any) {
|
|
90
|
+
return renderSingleLineToolCall(formatBracketedToolCall("mex scope", args?.task, theme), context)
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
register({
|
|
95
|
+
name: "mex_get",
|
|
96
|
+
label: "Mex Get",
|
|
97
|
+
description: "Expand source code for specific mex code-graph node ids returned by mex_scope, mex_query, or mex_impact.",
|
|
98
|
+
promptSnippet: "Expand mex node ids to source instead of reading whole files.",
|
|
99
|
+
parameters: Type.Object({
|
|
100
|
+
ids: Type.Array(Type.String({ description: "Node id, e.g. function:a3f8...c21" })),
|
|
101
|
+
maxSourceLines: Type.Optional(Type.Number({ description: "Per-node source line cap." })),
|
|
102
|
+
maxOutputTokens: Type.Optional(Type.Number({ description: "Hard output token ceiling." })),
|
|
103
|
+
}),
|
|
104
|
+
async execute(_id: string, params: any) {
|
|
105
|
+
return exec("get", buildGetArgs(params.ids, params))
|
|
106
|
+
},
|
|
107
|
+
renderCall(args: any, theme: ThemeLike, context: any) {
|
|
108
|
+
const ids = Array.isArray(args?.ids) ? args.ids : []
|
|
109
|
+
return renderSingleLineToolCall(formatBracketedToolCall("mex get", ids.length > 1 ? `${ids[0]} +${ids.length - 1}` : ids[0], theme), context)
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
register({
|
|
114
|
+
name: "mex_query",
|
|
115
|
+
label: "Mex Query",
|
|
116
|
+
description: `Query structural code relations in the mex graph: ${MEX_RELATIONS.join(", ")}.`,
|
|
117
|
+
promptSnippet: "Use mex_query for precise structural questions like who calls a function or where a symbol is defined.",
|
|
118
|
+
parameters: Type.Object({
|
|
119
|
+
relation: Type.String({ description: MEX_RELATIONS.join(" | ") }),
|
|
120
|
+
target: Type.String({ description: "Symbol or file name." }),
|
|
121
|
+
...RETRIEVAL_PARAMS,
|
|
122
|
+
}),
|
|
123
|
+
async execute(_id: string, params: any) {
|
|
124
|
+
return exec("query", buildQueryArgs(params.relation, params.target, params))
|
|
125
|
+
},
|
|
126
|
+
renderCall(args: any, theme: ThemeLike, context: any) {
|
|
127
|
+
return renderSingleLineToolCall(formatBracketedToolCall("mex query", [args?.relation, args?.target].filter(Boolean).join(" "), theme), context)
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
register({
|
|
132
|
+
name: "mex_impact",
|
|
133
|
+
label: "Mex Impact",
|
|
134
|
+
description: "Show the transitive code and wiki blast radius for a symbol or file before refactoring.",
|
|
135
|
+
promptSnippet: "Use mex_impact before refactors to see what a symbol or file change touches.",
|
|
136
|
+
parameters: Type.Object({
|
|
137
|
+
target: Type.String({ description: "Symbol name or file path." }),
|
|
138
|
+
depth: Type.Optional(Type.Number({ description: "Transitive caller depth. Default 2." })),
|
|
139
|
+
...RETRIEVAL_PARAMS,
|
|
140
|
+
}),
|
|
141
|
+
async execute(_id: string, params: any) {
|
|
142
|
+
return exec("impact", buildImpactArgs(params.target, params))
|
|
143
|
+
},
|
|
144
|
+
renderCall(args: any, theme: ThemeLike, context: any) {
|
|
145
|
+
return renderSingleLineToolCall(formatBracketedToolCall("mex impact", args?.target, theme), context)
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
register({
|
|
150
|
+
name: "mex_log",
|
|
151
|
+
label: "Mex Log",
|
|
152
|
+
description: `Append a ${MEX_LOG_TYPES.join("/")} entry to the repo-local mex event log (.mex/events).`,
|
|
153
|
+
promptSnippet: "Log durable repo decisions, risks, or todos into the mex event log.",
|
|
154
|
+
promptGuidelines: [
|
|
155
|
+
"Log decisions only when they are actually settled in this conversation.",
|
|
156
|
+
"Keep entries short and repo-specific; no secrets, credentials, or private hostnames.",
|
|
157
|
+
],
|
|
158
|
+
parameters: Type.Object({
|
|
159
|
+
message: Type.String({ description: "Short entry text." }),
|
|
160
|
+
type: Type.Optional(Type.String({ description: `${MEX_LOG_TYPES.join(" | ")}. Default note.` })),
|
|
161
|
+
files: Type.Optional(Type.Array(Type.String({ description: "Related file path." }))),
|
|
162
|
+
status: Type.Optional(Type.String({ description: "Lifecycle status, e.g. decided or implemented." })),
|
|
163
|
+
}),
|
|
164
|
+
async execute(_id: string, params: any) {
|
|
165
|
+
return exec("log", buildLogArgs(params.message, params))
|
|
166
|
+
},
|
|
167
|
+
renderCall(args: any, theme: ThemeLike, context: any) {
|
|
168
|
+
return renderSingleLineToolCall(formatBracketedToolCall(`mex log ${String(args?.type ?? "note")}`, args?.message, theme), context)
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
return registered
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default function mexMemoryExtension(pi: ExtensionAPI): void {
|
|
176
|
+
const cwd = process.cwd()
|
|
177
|
+
if (!isMexAvailable(process.env, cwd)) return
|
|
178
|
+
|
|
179
|
+
registerMexTools(pi, { cwd })
|
|
180
|
+
|
|
181
|
+
pi.on?.("session_start", (_event: any, ctx: any) => {
|
|
182
|
+
ctx?.ui?.setStatus?.(MEX_STATUS_KEY, mexStatusLabel(cwd))
|
|
183
|
+
void autoBuildMissingGraph(cwd, ctx)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
pi.registerCommand("mex", {
|
|
187
|
+
description: "Mex code graph: /mex [status] | graph | check | timeline | scope <task>",
|
|
188
|
+
handler: async (args: string, ctx: any) => {
|
|
189
|
+
const [sub, ...rest] = (args || "").trim().split(/\s+/)
|
|
190
|
+
const argument = rest.join(" ").trim()
|
|
191
|
+
const action = (sub || "status").toLowerCase()
|
|
192
|
+
try {
|
|
193
|
+
if (action === "status") {
|
|
194
|
+
const result = await runMex(buildCheckArgs({ quiet: true }), { cwd })
|
|
195
|
+
const lines = [
|
|
196
|
+
`mex scaffold: ${hasMexScaffold(cwd) ? mexPathLabel(cwd) : "missing"}`,
|
|
197
|
+
`mex graph.db: ${hasMexGraphDb(cwd) ? "present" : isMexAutoGraphEnabled() ? "missing (auto-build on demand)" : "missing (auto-build disabled)"}`,
|
|
198
|
+
result.output,
|
|
199
|
+
]
|
|
200
|
+
ctx.ui.notify(lines.filter(Boolean).join("\n"), result.ok ? "info" : "warning")
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
if (action === "graph") {
|
|
204
|
+
// Never let `mex graph` create a scaffold/DB in `~/.mex` when we're running outside a repo scaffold.
|
|
205
|
+
// Don't rebuild when graph.db already exists; just skip to avoid touching ~/.mex.
|
|
206
|
+
const ensured = await ensureMexGraphDb({ cwd, env: process.env, runner: undefined, timeoutMs: 300_000 })
|
|
207
|
+
const message = describeMexGraphEnsure(ensured)
|
|
208
|
+
if (message) ctx.ui.notify(message, ensured.status === "built" ? "info" : "warning")
|
|
209
|
+
else ctx.ui.notify("mex graph skipped.", ensured.status === "built" ? "info" : "warning")
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
if (action === "check") {
|
|
213
|
+
const result = await runMex(buildCheckArgs({}), { cwd, timeoutMs: 300_000 })
|
|
214
|
+
ctx.ui.notify(result.output || "No drift output.", result.ok ? "info" : "warning")
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
if (action === "timeline") {
|
|
218
|
+
const result = await runMex(buildTimelineArgs({ limit: 20 }), { cwd })
|
|
219
|
+
ctx.ui.notify(result.output || "No mex events.", "info")
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
if (action === "scope") {
|
|
223
|
+
if (!argument) return ctx.ui.notify("Usage: /mex scope <task>", "warning")
|
|
224
|
+
const result = await runMex(buildScopeArgs(argument, {}), { cwd })
|
|
225
|
+
ctx.ui.notify(result.output || "No scope results.", result.ok ? "info" : "warning")
|
|
226
|
+
return
|
|
227
|
+
}
|
|
228
|
+
ctx.ui.notify("Usage: /mex [status] | graph | check | timeline | scope <task>", "warning")
|
|
229
|
+
} catch (error) {
|
|
230
|
+
ctx.ui.notify(`mex command failed:\n${error instanceof Error ? error.message : String(error)}`, "error")
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function mexPathLabel(cwd: string): string {
|
|
237
|
+
return `${cwd.replace(/\/+$/, "")}/.mex`
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function graphEnsureOptions(options: MexRunOptions): MexRunOptions {
|
|
241
|
+
return { cwd: options.cwd, env: options.env, runner: options.runner }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Only a scaffold with a usable code graph counts as "mex is working"; otherwise the segment stays hidden. */
|
|
245
|
+
export function mexStatusLabel(cwd: string = process.cwd()): string | undefined {
|
|
246
|
+
return hasMexScaffold(cwd) && hasMexGraphDb(cwd) ? MEX_STATUS_LABEL : undefined
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Builds a missing code graph in the background so the first graph tool call is not blocked. */
|
|
250
|
+
async function autoBuildMissingGraph(cwd: string, ctx: any): Promise<MexGraphEnsureResult | undefined> {
|
|
251
|
+
if (!hasMexScaffold(cwd) || hasMexGraphDb(cwd) || !isMexAutoGraphEnabled()) return undefined
|
|
252
|
+
ctx?.ui?.setStatus?.(MEX_STATUS_KEY, MEX_STATUS_BUILDING_LABEL)
|
|
253
|
+
try {
|
|
254
|
+
const result = await ensureMexGraphDb({ cwd })
|
|
255
|
+
const message = describeMexGraphEnsure(result)
|
|
256
|
+
if (message) ctx?.ui?.notify?.(message, result.status === "built" ? "info" : "warning")
|
|
257
|
+
return result
|
|
258
|
+
} catch (error) {
|
|
259
|
+
ctx?.ui?.notify?.(`mex graph build failed:\n${error instanceof Error ? error.message : String(error)}`, "warning")
|
|
260
|
+
return undefined
|
|
261
|
+
} finally {
|
|
262
|
+
ctx?.ui?.setStatus?.(MEX_STATUS_KEY, mexStatusLabel(cwd))
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { ToolboxToolDefinition } from "./toolbox.js"
|
|
|
2
2
|
import { createDiagnosticToolboxTools } from "./diagnostic-tools.js"
|
|
3
3
|
import { createPrismDiagnosticToolboxTools } from "./prism-diagnostic-tools.js"
|
|
4
4
|
import { createSshConfigTool } from "./ssh-config-tool.js"
|
|
5
|
+
import { createMexToolboxTools } from "./mex-tools.js"
|
|
5
6
|
|
|
6
7
|
export function defineToolboxTool(definition: ToolboxToolDefinition): ToolboxToolDefinition {
|
|
7
8
|
return { source: "toolbox/builtin", ...definition }
|
|
@@ -11,6 +12,7 @@ export const builtinToolboxTools: ToolboxToolDefinition[] = [
|
|
|
11
12
|
createSshConfigTool(),
|
|
12
13
|
...createDiagnosticToolboxTools(),
|
|
13
14
|
...createPrismDiagnosticToolboxTools(),
|
|
15
|
+
...createMexToolboxTools(),
|
|
14
16
|
]
|
|
15
17
|
|
|
16
18
|
export function createBuiltinToolboxTools(): ToolboxToolDefinition[] {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Type } from "typebox"
|
|
2
|
+
import {
|
|
3
|
+
buildCheckArgs,
|
|
4
|
+
buildGraphArgs,
|
|
5
|
+
buildTimelineArgs,
|
|
6
|
+
hasMexScaffold,
|
|
7
|
+
isMexCliInstalled,
|
|
8
|
+
MEX_LOG_TYPES,
|
|
9
|
+
runMex,
|
|
10
|
+
type MexRunOptions,
|
|
11
|
+
} from "../core/mex-cli.js"
|
|
12
|
+
import type { ToolboxToolDefinition } from "./toolbox.js"
|
|
13
|
+
import { textToolResult, timeoutMs, type ToolInput } from "./toolbox-utils.js"
|
|
14
|
+
|
|
15
|
+
const MEX_TAGS = ["mex", "codegraph", "graph", "wiki", "scaffold", "drift", "documentation", "memory"]
|
|
16
|
+
|
|
17
|
+
function runOptions(params: ToolInput, fallbackTimeout: number): MexRunOptions {
|
|
18
|
+
return {
|
|
19
|
+
cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : process.cwd(),
|
|
20
|
+
timeoutMs: timeoutMs(params.timeoutMs, fallbackTimeout),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function guardScaffold(cwd: string): void {
|
|
25
|
+
if (!isMexCliInstalled()) throw new Error("mex CLI not found. Install it with: npm install -g mex-agent")
|
|
26
|
+
if (!hasMexScaffold(cwd)) throw new Error(`No .mex scaffold in ${cwd}. Run 'npx mex-agent setup' first.`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function execute(action: string, args: string[], options: MexRunOptions) {
|
|
30
|
+
guardScaffold(options.cwd ?? process.cwd())
|
|
31
|
+
const result = await runMex(args, options)
|
|
32
|
+
return textToolResult(result.output || `mex ${action} produced no output.`, { action, ok: result.ok, exitCode: result.exitCode, cwd: options.cwd })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createMexGraphBuildTool(): ToolboxToolDefinition {
|
|
36
|
+
return {
|
|
37
|
+
name: "mex_graph_build",
|
|
38
|
+
label: "Mex Graph Build",
|
|
39
|
+
description: "Rebuild the local mex code knowledge graph (.mex/graph.db) so scope/query/impact results are current.",
|
|
40
|
+
tags: [...MEX_TAGS, "build", "index", "rebuild"],
|
|
41
|
+
parameters: Type.Object({
|
|
42
|
+
cwd: Type.Optional(Type.String({ description: "Repository root. Defaults to the current working directory." })),
|
|
43
|
+
json: Type.Optional(Type.Boolean({ description: "Return the build summary as JSON." })),
|
|
44
|
+
timeoutMs: Type.Optional(Type.Number({ description: "Command timeout in milliseconds. Default 120000." })),
|
|
45
|
+
}),
|
|
46
|
+
async execute(_id: string, params: ToolInput) {
|
|
47
|
+
const options = runOptions(params, 120_000)
|
|
48
|
+
return execute("graph", buildGraphArgs({ json: params.json, root: params.cwd }), options)
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createMexCheckTool(): ToolboxToolDefinition {
|
|
54
|
+
return {
|
|
55
|
+
name: "mex_check",
|
|
56
|
+
label: "Mex Check",
|
|
57
|
+
description: "Run mex drift detection between the .mex wiki and the codebase (paths, commands, dependencies, links, grounded symbols).",
|
|
58
|
+
tags: [...MEX_TAGS, "check", "health", "stale", "validate"],
|
|
59
|
+
parameters: Type.Object({
|
|
60
|
+
cwd: Type.Optional(Type.String({ description: "Repository root. Defaults to the current working directory." })),
|
|
61
|
+
json: Type.Optional(Type.Boolean({ description: "Return the full drift report as JSON." })),
|
|
62
|
+
quiet: Type.Optional(Type.Boolean({ description: "Single-line summary only." })),
|
|
63
|
+
timeoutMs: Type.Optional(Type.Number({ description: "Command timeout in milliseconds. Default 120000." })),
|
|
64
|
+
}),
|
|
65
|
+
async execute(_id: string, params: ToolInput) {
|
|
66
|
+
return execute("check", buildCheckArgs({ json: params.json, quiet: params.quiet }), runOptions(params, 120_000))
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createMexTimelineTool(): ToolboxToolDefinition {
|
|
72
|
+
return {
|
|
73
|
+
name: "mex_timeline",
|
|
74
|
+
label: "Mex Timeline",
|
|
75
|
+
description: `Read recent entries from the mex event log (${MEX_LOG_TYPES.join("/")}) for repo history and past decisions.`,
|
|
76
|
+
tags: [...MEX_TAGS, "timeline", "events", "decisions", "history", "log"],
|
|
77
|
+
parameters: Type.Object({
|
|
78
|
+
cwd: Type.Optional(Type.String({ description: "Repository root. Defaults to the current working directory." })),
|
|
79
|
+
since: Type.Optional(Type.String({ description: "YYYY-MM-DD or relative like 30d." })),
|
|
80
|
+
type: Type.Optional(Type.String({ description: MEX_LOG_TYPES.join(" | ") })),
|
|
81
|
+
limit: Type.Optional(Type.Number({ description: "Maximum entries." })),
|
|
82
|
+
json: Type.Optional(Type.Boolean({ description: "Return events as JSON." })),
|
|
83
|
+
timeoutMs: Type.Optional(Type.Number({ description: "Command timeout in milliseconds. Default 30000." })),
|
|
84
|
+
}),
|
|
85
|
+
async execute(_id: string, params: ToolInput) {
|
|
86
|
+
return execute("timeline", buildTimelineArgs(params), runOptions(params, 30_000))
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createMexToolboxTools(): ToolboxToolDefinition[] {
|
|
92
|
+
return [createMexGraphBuildTool(), createMexCheckTool(), createMexTimelineTool()]
|
|
93
|
+
}
|
|
@@ -66,21 +66,21 @@ function createPrismRuntimeStatusTool(): ToolboxToolDefinition {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function
|
|
69
|
+
function createNpmPackageVersionTool(): ToolboxToolDefinition {
|
|
70
70
|
return {
|
|
71
|
-
name: "
|
|
72
|
-
label: "
|
|
73
|
-
description: "Check
|
|
74
|
-
tags: ["
|
|
71
|
+
name: "npm_package_version",
|
|
72
|
+
label: "Npm Package Version",
|
|
73
|
+
description: "Check an npm-compatible registry metadata endpoint for the latest published version.",
|
|
74
|
+
tags: ["package", "npm", "version", "registry", "prism", "deploy"],
|
|
75
75
|
source: "toolbox/prism-diagnostics",
|
|
76
76
|
parameters: Type.Object({
|
|
77
|
-
registry: Type.Optional(Type.String({ description: "Registry base URL. Default https://
|
|
78
|
-
packageName: Type.Optional(Type.String({ description: "Package name. Default @
|
|
77
|
+
registry: Type.Optional(Type.String({ description: "Registry base URL. Default https://registry.npmjs.org" })),
|
|
78
|
+
packageName: Type.Optional(Type.String({ description: "Package name. Default @daniel156161/prism" })),
|
|
79
79
|
timeoutMs: Type.Optional(Type.Number({ description: "Request timeout in milliseconds. Default 5000." })),
|
|
80
80
|
}),
|
|
81
81
|
async execute(_id: string, params: ToolInput) {
|
|
82
|
-
const registry = String(params.registry || "https://
|
|
83
|
-
const packageName = String(params.packageName || "@
|
|
82
|
+
const registry = String(params.registry || "https://registry.npmjs.org").replace(/\/+$/, "")
|
|
83
|
+
const packageName = String(params.packageName || "@daniel156161/prism")
|
|
84
84
|
const encodedName = packageName.startsWith("@") ? `@${encodeURIComponent(packageName.slice(1))}` : encodeURIComponent(packageName)
|
|
85
85
|
const url = `${registry}/${encodedName}`
|
|
86
86
|
const started = Date.now()
|
|
@@ -153,7 +153,7 @@ function createPrismDeployCheckTool(): ToolboxToolDefinition {
|
|
|
153
153
|
export function createPrismDiagnosticToolboxTools(): ToolboxToolDefinition[] {
|
|
154
154
|
return [
|
|
155
155
|
createPrismRuntimeStatusTool(),
|
|
156
|
-
|
|
156
|
+
createNpmPackageVersionTool(),
|
|
157
157
|
createPortainerWebhookTool(),
|
|
158
158
|
createPrismDeployCheckTool(),
|
|
159
159
|
]
|
|
@@ -89,7 +89,7 @@ function formatResults(results: ToolboxSearchResult[]): string {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const AUTO_SUGGEST_DOMAIN_TERMS = new Set([
|
|
92
|
-
"backup", "borg", "container", "deploy", "diagnostic", "docker", "
|
|
92
|
+
"backup", "borg", "container", "deploy", "diagnostic", "docker", "health", "http", "logs", "netbird", "npm", "opnsense", "package", "pfsense", "port", "portainer", "process", "prism", "registry", "redeploy", "router", "runtime", "service", "services", "ssh", "stack", "status", "systemd", "vpn", "webhook",
|
|
93
93
|
])
|
|
94
94
|
|
|
95
95
|
const AUTO_SUGGEST_STOPWORDS = new Set([
|
|
@@ -72,6 +72,13 @@ export function toolbarAccent(theme: ThemeLike, text: string): string {
|
|
|
72
72
|
return fg(theme, "accent", text)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/** Role-play status is only worth a toolbar slot while a role-play is actually running. */
|
|
76
|
+
export function rpLabel(status: string | undefined): string | undefined {
|
|
77
|
+
const value = status?.trim()
|
|
78
|
+
if (!value || /^RP:\s*off$/i.test(value)) return undefined
|
|
79
|
+
return value
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
export function cavemanColor(mode: string): number {
|
|
76
83
|
const normalized = mode.toLowerCase()
|
|
77
84
|
if (normalized === "lite") return 226
|
|
@@ -185,8 +192,10 @@ class ToolbarFooter {
|
|
|
185
192
|
const obsidian = statuses?.get("obsidian-memory")
|
|
186
193
|
const logseq = statuses?.get("logseq-memory")
|
|
187
194
|
const aiMemory = statuses?.get("ai-memory")
|
|
195
|
+
const mex = statuses?.get("mex")
|
|
196
|
+
const rp = rpLabel(statuses?.get("rp"))
|
|
188
197
|
|
|
189
|
-
return [joinDistributed([mode ? fg(this.theme, "success", mode) : undefined, model, fg(this.theme, "accent", `🔧 ${toolCalls}`), fg(this.theme, "dim", `↑${formatTokens(input)}`), fg(this.theme, "dim", `↓${formatTokens(output)}`), fg(this.theme, "success", `$${cost.toFixed(4)}`), context, caveman, obsidian, logseq, aiMemory ? `AI Memory: ${aiMemory}` : undefined, thinking].filter(Boolean) as string[], Math.max(0, width))]
|
|
198
|
+
return [joinDistributed([mode ? fg(this.theme, "success", mode) : undefined, model, fg(this.theme, "accent", `🔧 ${toolCalls}`), fg(this.theme, "dim", `↑${formatTokens(input)}`), fg(this.theme, "dim", `↓${formatTokens(output)}`), fg(this.theme, "success", `$${cost.toFixed(4)}`), context, caveman, obsidian, logseq, aiMemory ? `AI Memory: ${aiMemory}` : undefined, mex, rp, thinking].filter(Boolean) as string[], Math.max(0, width))]
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
201
|
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { LocalVoiceRuntime, parseVoiceMode, voiceStatus, type VoiceMode } from "../core/voice-runtime.js"
|
|
2
|
-
|
|
3
|
-
type ExtensionAPI = any
|
|
4
|
-
|
|
5
|
-
export default function voiceCommandExtension(pi: ExtensionAPI): void {
|
|
6
|
-
let runtime: LocalVoiceRuntime | undefined
|
|
7
|
-
let mode: VoiceMode = "off"
|
|
8
|
-
|
|
9
|
-
function ensureRuntime(ctx: any): LocalVoiceRuntime {
|
|
10
|
-
runtime ??= new LocalVoiceRuntime({
|
|
11
|
-
sendUserMessage: (message) => pi.sendUserMessage(message),
|
|
12
|
-
notify: (message, level) => ctx?.ui?.notify?.(message, level),
|
|
13
|
-
setStatus: (value) => ctx?.ui?.setStatus?.("voice", value),
|
|
14
|
-
})
|
|
15
|
-
return runtime
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
pi.registerCommand("voice", {
|
|
19
|
-
description: "Local voice mode: /voice on | output | input | off",
|
|
20
|
-
handler: async (args: string, ctx: any) => {
|
|
21
|
-
const next = parseVoiceMode(args)
|
|
22
|
-
if (!next) {
|
|
23
|
-
ctx.ui.notify(`Voice mode: ${mode}. Use /voice on, /voice output, /voice input, or /voice off.`, "info")
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
mode = next
|
|
27
|
-
const voice = ensureRuntime(ctx)
|
|
28
|
-
voice.setMode(mode)
|
|
29
|
-
ctx.ui.setStatus("voice", voiceStatus(mode))
|
|
30
|
-
if (mode === "output" || mode === "on") {
|
|
31
|
-
const ok = await voice.ensureVoicebox()
|
|
32
|
-
if (voice.isVoiceboxEnabled() && !ok) {
|
|
33
|
-
ctx.ui.notify("Voicebox is not reachable and could not be started. Check voicebox settings/PRISM_VOICEBOX_URL.", "warning")
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
ctx.ui.notify(mode === "off" ? "Voice off" : `Voice ${mode}`, "info")
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
pi.on("session_start", (_event: any, ctx: any) => {
|
|
41
|
-
if (mode !== "off") ensureRuntime(ctx).setMode(mode)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
pi.on("message_end", async (event: any, ctx: any) => {
|
|
45
|
-
if (mode === "on" || mode === "output") await ensureRuntime(ctx).speakAssistantMessage(event.message)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
pi.on?.("session_shutdown", () => runtime?.shutdown())
|
|
49
|
-
}
|