klypix-mcp 1.53.0 → 1.54.0
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/bin/klypix-worker.mjs +14 -3
- package/package.json +1 -1
- package/src/project-graph.mjs +41 -6
package/README.md
CHANGED
|
@@ -193,6 +193,27 @@ excludes the owning IDE/chat application's RAM, redacts command-line secrets, an
|
|
|
193
193
|
brain or terminates a process. Multiple processes under one host are reported as parallel sessions,
|
|
194
194
|
not called duplicates without an authoritative logical-session receipt.
|
|
195
195
|
|
|
196
|
+
### Project Map: current structure beside project understanding
|
|
197
|
+
|
|
198
|
+
If the project contains a compatible NetworkX node-link `graph.json`, agents can ask for bounded
|
|
199
|
+
code-structure evidence and current brain context in one read-only call:
|
|
200
|
+
|
|
201
|
+
```jsonc
|
|
202
|
+
project_map_context {
|
|
203
|
+
"question": "what owns refresh-token rotation?",
|
|
204
|
+
"graph_path": "graphify-out/graph.json"
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Use `compare_to` with another project-relative graph artifact to add exact total node/edge deltas
|
|
209
|
+
and additions/removals from the two bounded query neighborhoods. Both paths are confined to the
|
|
210
|
+
declared project root; unsafe source paths are withheld; large or unsupported artifacts are
|
|
211
|
+
rejected. The tool never writes graph facts into `brain.klypix`.
|
|
212
|
+
|
|
213
|
+
Graphify is the first compatible producer. KLYPIX reads artifacts that users generate separately;
|
|
214
|
+
it does not bundle, install, or run Graphify and does not imply a partnership. A compatible generic
|
|
215
|
+
`graph.json` works through the same provider-neutral boundary.
|
|
216
|
+
|
|
196
217
|
---
|
|
197
218
|
|
|
198
219
|
## Supported hosts and their integration level
|
package/bin/klypix-worker.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
opBrainInsights, opBrainConnect, opBrainReconcile, opBrainGarden, opCreateCanvas, opAddToCanvas, opBrainNote, opBrainMessage, opBrainAsk, opBrainChallenge, opCanvasView, opBrainLens,
|
|
31
31
|
opBrainTaskContext,
|
|
32
32
|
} from '../src/klypix-core.mjs';
|
|
33
|
-
import { projectGraphContextMarkdown, queryProjectGraph } from '../src/project-graph.mjs';
|
|
33
|
+
import { compareProjectGraphResults, projectGraphContextMarkdown, queryProjectGraph } from '../src/project-graph.mjs';
|
|
34
34
|
import { auditProject, compactAgentsBrief, linkProject, mcpServerEntry } from '../src/agent-rules.mjs';
|
|
35
35
|
import { createMcpPresence, KLYPIX_MCP_INSTRUCTIONS } from '../src/mcp-presence.mjs';
|
|
36
36
|
import {
|
|
@@ -256,12 +256,13 @@ server.registerTool('project_map_context', {
|
|
|
256
256
|
question: z.string().min(1).describe('Question or code concept to ground in both current structure and project memory.'),
|
|
257
257
|
project: z.string().optional().describe('Absolute project root. Defaults to this MCP connection\'s configured project/vault.'),
|
|
258
258
|
graph_path: z.string().optional().describe('Optional project-relative graph JSON path. Defaults to graphify-out/graph.json and may not escape the project root.'),
|
|
259
|
+
compare_to: z.string().optional().describe('Optional project-relative prior graph JSON path. Adds exact total deltas plus bounded query-neighborhood changes; it may not escape the project root.'),
|
|
259
260
|
depth: z.number().optional().describe('Relationship hops around the best code matches (0-3, default 1).'),
|
|
260
261
|
max_nodes: z.number().optional().describe('Maximum code nodes returned (default 60, capped 200).'),
|
|
261
262
|
k: z.number().optional().describe('Maximum brain cards returned (default 8; fast mode caps 8, deep history caps 20).'),
|
|
262
263
|
deep_history: z.boolean().optional().describe('false (default) uses the sub-second lexical-fast correction-aware path; true opts into whole-brain semantic/history retrieval, which may cold-load the local model.'),
|
|
263
264
|
},
|
|
264
|
-
}, async ({ question, project, graph_path, depth, max_nodes, k, deep_history }) => {
|
|
265
|
+
}, async ({ question, project, graph_path, compare_to, depth, max_nodes, k, deep_history }) => {
|
|
265
266
|
let graphResult;
|
|
266
267
|
let graphMarkdown;
|
|
267
268
|
try {
|
|
@@ -272,9 +273,19 @@ server.registerTool('project_map_context', {
|
|
|
272
273
|
depth,
|
|
273
274
|
maxNodes: max_nodes,
|
|
274
275
|
});
|
|
276
|
+
if (compare_to) {
|
|
277
|
+
const previousGraphResult = queryProjectGraph({
|
|
278
|
+
project: project || mcpPresence.vault,
|
|
279
|
+
graphPath: compare_to,
|
|
280
|
+
query: question,
|
|
281
|
+
depth,
|
|
282
|
+
maxNodes: max_nodes,
|
|
283
|
+
});
|
|
284
|
+
graphResult.change = compareProjectGraphResults(graphResult, previousGraphResult);
|
|
285
|
+
}
|
|
275
286
|
graphMarkdown = projectGraphContextMarkdown(graphResult);
|
|
276
287
|
} catch (error) {
|
|
277
|
-
graphResult = { schemaVersion:
|
|
288
|
+
graphResult = { schemaVersion: 2, status: 'invalid', error: error?.message || String(error) };
|
|
278
289
|
graphMarkdown = `# Project Map\n\nThe generated graph could not be read safely: ${graphResult.error}`;
|
|
279
290
|
}
|
|
280
291
|
const graphFiles = Array.isArray(graphResult?.nodes)
|
package/package.json
CHANGED
package/src/project-graph.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
|
|
10
|
-
export const PROJECT_GRAPH_SCHEMA_VERSION =
|
|
10
|
+
export const PROJECT_GRAPH_SCHEMA_VERSION = 2;
|
|
11
11
|
export const DEFAULT_PROJECT_GRAPH = 'graphify-out/graph.json';
|
|
12
12
|
export const DEFAULT_PROJECT_GRAPH_HTML = 'graphify-out/graph.html';
|
|
13
13
|
export const DEFAULT_PROJECT_GRAPH_REPORT = 'graphify-out/GRAPH_REPORT.md';
|
|
@@ -148,6 +148,11 @@ function normalizeGraph(raw, projectRoot, artifact) {
|
|
|
148
148
|
return {
|
|
149
149
|
schemaVersion: PROJECT_GRAPH_SCHEMA_VERSION,
|
|
150
150
|
provider: 'graphify',
|
|
151
|
+
format: {
|
|
152
|
+
family: 'networkx-node-link',
|
|
153
|
+
sourceSchemaVersion: shortString(raw.schema_version ?? raw.schemaVersion, 80) || null,
|
|
154
|
+
providerVersion: shortString(raw?.metadata?.version ?? raw?.meta?.version ?? raw.generator_version, 80) || null,
|
|
155
|
+
},
|
|
151
156
|
artifact,
|
|
152
157
|
nodes,
|
|
153
158
|
edges,
|
|
@@ -294,6 +299,7 @@ export function queryProjectGraph({ project, graphPath, query = '', depth = 1, m
|
|
|
294
299
|
return {
|
|
295
300
|
schemaVersion: PROJECT_GRAPH_SCHEMA_VERSION,
|
|
296
301
|
provider: graph.provider,
|
|
302
|
+
format: graph.format,
|
|
297
303
|
status: 'ready',
|
|
298
304
|
query: String(query || ''),
|
|
299
305
|
depth: hopLimit,
|
|
@@ -311,23 +317,52 @@ export function queryProjectGraph({ project, graphPath, query = '', depth = 1, m
|
|
|
311
317
|
};
|
|
312
318
|
}
|
|
313
319
|
|
|
320
|
+
function edgeIdentity(edge) {
|
|
321
|
+
return `${edge.source}\u0000${edge.relation}\u0000${edge.target}\u0000${edge.confidence}`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Compare two already-bounded query results. Artifact totals are exact; named
|
|
326
|
+
* node/edge changes describe only the returned neighborhoods and say so.
|
|
327
|
+
*/
|
|
328
|
+
export function compareProjectGraphResults(current, previous) {
|
|
329
|
+
if (!current || !previous || current.status !== 'ready' || previous.status !== 'ready') return null;
|
|
330
|
+
const oldNodes = new Map(previous.nodes.map(node => [node.id, node]));
|
|
331
|
+
const newNodes = new Map(current.nodes.map(node => [node.id, node]));
|
|
332
|
+
const oldEdges = new Map(previous.edges.map(edge => [edgeIdentity(edge), edge]));
|
|
333
|
+
const newEdges = new Map(current.edges.map(edge => [edgeIdentity(edge), edge]));
|
|
334
|
+
return {
|
|
335
|
+
graphNodesDelta: current.counts.graphNodes - previous.counts.graphNodes,
|
|
336
|
+
graphEdgesDelta: current.counts.graphEdges - previous.counts.graphEdges,
|
|
337
|
+
addedNodes: current.nodes.filter(node => !oldNodes.has(node.id)).slice(0, 80),
|
|
338
|
+
removedNodes: previous.nodes.filter(node => !newNodes.has(node.id)).slice(0, 80),
|
|
339
|
+
addedEdges: current.edges.filter(edge => !oldEdges.has(edgeIdentity(edge))).slice(0, 160),
|
|
340
|
+
removedEdges: previous.edges.filter(edge => !newEdges.has(edgeIdentity(edge))).slice(0, 160),
|
|
341
|
+
coverage: 'bounded-query-neighborhoods',
|
|
342
|
+
comparedArtifact: previous.artifact?.graphJson || null,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
314
346
|
export function projectGraphContextMarkdown(result) {
|
|
315
347
|
if (result.status === 'missing') {
|
|
316
348
|
return `# Project Map\n\nNo supported project graph was found at \`${result.artifact.graphJson}\`. KLYPIX did not install or run a provider. Generate the artifact with Graphify, then retry.`;
|
|
317
349
|
}
|
|
318
350
|
const lines = result.nodes.map(node => {
|
|
319
|
-
const where = node.sourceFile ? `
|
|
320
|
-
return `- **${node.label || node.id}** (${node.kind || 'symbol'})${where}
|
|
351
|
+
const where = node.sourceFile ? ` in \`${node.sourceFile}${node.sourceLocation ? `:${node.sourceLocation}` : ''}\`` : '';
|
|
352
|
+
return `- **${node.label || node.id}** (${node.kind || 'symbol'})${where}; id \`${node.id}\``;
|
|
321
353
|
});
|
|
322
354
|
const edgeLines = result.edges.slice(0, 80).map(edge =>
|
|
323
|
-
`- \`${edge.source}\`
|
|
355
|
+
`- \`${edge.source}\` --${edge.relation}--> \`${edge.target}\` [${edge.confidence}]`);
|
|
324
356
|
const warnings = [];
|
|
325
357
|
if (result.diagnostics?.unsafeSourcePaths) warnings.push(`${result.diagnostics.unsafeSourcePaths} unsafe/out-of-root source path(s) were withheld`);
|
|
326
358
|
if (result.diagnostics?.danglingEdges) warnings.push(`${result.diagnostics.danglingEdges} dangling edge(s) were ignored`);
|
|
327
359
|
return [
|
|
328
|
-
'# Project Map
|
|
329
|
-
`Provider artifact: \`${result.artifact.graphJson}
|
|
360
|
+
'# Project Map: code evidence',
|
|
361
|
+
`Provider artifact: \`${result.artifact.graphJson}\`; ${result.counts.graphNodes.toLocaleString()} nodes; ${result.counts.graphEdges.toLocaleString()} edges; query \`${result.query || '(overview)'}\`.`,
|
|
330
362
|
warnings.length ? `Safety note: ${warnings.join('; ')}.` : '',
|
|
363
|
+
result.change ? `## Change from \`${result.change.comparedArtifact || 'comparison artifact'}\`\nExact total deltas: ${result.change.graphNodesDelta >= 0 ? '+' : ''}${result.change.graphNodesDelta} nodes; ${result.change.graphEdgesDelta >= 0 ? '+' : ''}${result.change.graphEdgesDelta} edges. Named additions and removals are limited to the two bounded query neighborhoods.` : '',
|
|
364
|
+
result.change?.addedNodes?.length ? `Added in bounded result: ${result.change.addedNodes.slice(0, 20).map(node => `\`${node.label || node.id}\``).join(', ')}.` : '',
|
|
365
|
+
result.change?.removedNodes?.length ? `Removed from bounded result: ${result.change.removedNodes.slice(0, 20).map(node => `\`${node.label || node.id}\``).join(', ')}.` : '',
|
|
331
366
|
'## Relevant code nodes',
|
|
332
367
|
lines.join('\n') || '_No matching code nodes._',
|
|
333
368
|
edgeLines.length ? '## Relationships\n' + edgeLines.join('\n') : '',
|