context-mcp-server 1.0.6 → 1.0.8
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 +98 -359
- package/codegraph/server.py +37 -46
- package/package.json +4 -3
- package/pyproject.toml +1 -1
- package/src/assests/main.png +0 -0
- package/src/cli.js +69 -57
- package/src/db.js +946 -798
- package/src/guard.js +9 -3
- package/src/migrator.js +124 -0
- package/src/server.js +7 -6
- package/src/templates/AGENTS.md +6 -13
- package/src/templates/CLAUDE.md +6 -12
- package/src/templates/GEMINI.md +6 -13
- package/src/templates/commands/context-resume.md +1 -1
- package/src/templates/commands/save-context.md +1 -1
- package/src/templates/cursor-rules.mdc +3 -3
- package/src/templates/skills/SKILL.md +9 -16
- package/src/templates/windsurf-rules.md +3 -3
- package/src/tools/codegraph.js +8 -77
- package/src/tools/context.js +23 -11
- package/src/tools/gitTools.js +1 -3
- package/src/tools/plan.js +130 -0
- package/uv.lock +1 -1
- package/codegraph/extractors/audio_extractor.py +0 -8
- package/codegraph/extractors/doc_extractor.py +0 -34
- package/codegraph/extractors/image_extractor.py +0 -26
- package/src/tools/discussion.js +0 -123
package/src/guard.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { resolve, sep } from 'node:path';
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
// Follow symlinks to get the true path; fall back to resolve() if path doesn't exist yet.
|
|
5
|
+
function safeStat(p) {
|
|
6
|
+
try { return realpathSync(p); } catch { return resolve(p); }
|
|
7
|
+
}
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
|
-
* Resolves `inputPath` and asserts it falls within `allowedRoot`.
|
|
10
|
+
* Resolves `inputPath` (following symlinks) and asserts it falls within `allowedRoot`.
|
|
5
11
|
* Throws if no root is configured or if the path escapes the root.
|
|
6
12
|
* Returns the resolved absolute path on success.
|
|
7
13
|
*/
|
|
@@ -11,8 +17,8 @@ export function guardPath(inputPath, allowedRoot) {
|
|
|
11
17
|
'Project root not configured. Call context with action:"resume" and include rootPath to enable file/git access.'
|
|
12
18
|
);
|
|
13
19
|
}
|
|
14
|
-
const resolved =
|
|
15
|
-
const root =
|
|
20
|
+
const resolved = safeStat(inputPath);
|
|
21
|
+
const root = safeStat(allowedRoot);
|
|
16
22
|
if (resolved !== root && !resolved.startsWith(root + sep)) {
|
|
17
23
|
throw new Error(`Access denied: "${resolved}" is outside the project root "${root}"`);
|
|
18
24
|
}
|
package/src/migrator.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* migrator.js — one-time migration from flat JSON files to per-project directory structure.
|
|
3
|
+
*
|
|
4
|
+
* Old layout:
|
|
5
|
+
* ~/.context-mcp/contexts.json all entries flat
|
|
6
|
+
* ~/.context-mcp/discussions.json all discussions flat
|
|
7
|
+
* ~/.context-mcp/graphs.json all graph build records flat
|
|
8
|
+
*
|
|
9
|
+
* New layout:
|
|
10
|
+
* ~/.context-mcp/projects/<slug>/context.json
|
|
11
|
+
* ~/.context-mcp/projects/<slug>/graph.json { build, entries[] }
|
|
12
|
+
* ~/.context-mcp/projects/<slug>/summary.json
|
|
13
|
+
* ~/.context-mcp/projects/<slug>/discussions.json
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
|
|
19
|
+
function normPath(p) {
|
|
20
|
+
return p ? p.toLowerCase().replace(/\\/g, '/').replace(/\/$/, '') : '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function treeFor(entry) {
|
|
24
|
+
if (entry.type === 'compaction') return 'summary';
|
|
25
|
+
return 'context';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readArr(filePath, key) {
|
|
29
|
+
if (!existsSync(filePath)) return [];
|
|
30
|
+
try {
|
|
31
|
+
const d = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
32
|
+
return Array.isArray(d[key]) ? d[key] : (Array.isArray(d) ? d : []);
|
|
33
|
+
} catch { return []; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run migration if legacy flat files are present.
|
|
38
|
+
* @param {object} opts
|
|
39
|
+
* @param {string} opts.dataDir - base data dir (~/.context-mcp)
|
|
40
|
+
* @param {string} opts.projectsDir - projects sub-dir
|
|
41
|
+
* @param {string} opts.projectsPath - path to projects.json
|
|
42
|
+
* @param {Function} opts.slugify - name → filesystem slug
|
|
43
|
+
* @param {Function} opts.flushFile - (filePath, content) atomic write
|
|
44
|
+
* @param {Array} opts.projectsIndex - current projects.json array (mutated in place)
|
|
45
|
+
*/
|
|
46
|
+
export function runMigration({ dataDir, projectsDir, projectsPath, slugify, flushFile, projectsIndex }) {
|
|
47
|
+
const legacyContexts = join(dataDir, 'contexts.json');
|
|
48
|
+
const legacyDiscussions = join(dataDir, 'discussions.json');
|
|
49
|
+
const legacyGraphs = join(dataDir, 'graphs.json');
|
|
50
|
+
|
|
51
|
+
const hasLegacy = existsSync(legacyContexts)
|
|
52
|
+
|| existsSync(legacyDiscussions)
|
|
53
|
+
|| existsSync(legacyGraphs);
|
|
54
|
+
|
|
55
|
+
if (!hasLegacy) return false;
|
|
56
|
+
|
|
57
|
+
const oldContexts = readArr(legacyContexts, 'contexts');
|
|
58
|
+
const oldDiscussions = readArr(legacyDiscussions, 'discussions');
|
|
59
|
+
const oldGraphs = readArr(legacyGraphs, 'graphs');
|
|
60
|
+
|
|
61
|
+
// Remap legacy type names to current 4-type schema
|
|
62
|
+
const TYPE_MAP = { architecture: 'note', code: 'note', error: 'bug', summary: 'compaction' };
|
|
63
|
+
const remapType = entry => {
|
|
64
|
+
if (TYPE_MAP[entry.type]) entry.type = TYPE_MAP[entry.type];
|
|
65
|
+
return entry;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Group everything by project name
|
|
69
|
+
const byProject = {};
|
|
70
|
+
const ensure = name => {
|
|
71
|
+
if (!byProject[name]) byProject[name] = {
|
|
72
|
+
context: [], graph: { build: null }, summary: [], discussions: [],
|
|
73
|
+
};
|
|
74
|
+
return byProject[name];
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
for (const entry of oldContexts) {
|
|
78
|
+
remapType(entry);
|
|
79
|
+
const p = entry.project || 'global';
|
|
80
|
+
const d = ensure(p);
|
|
81
|
+
if (treeFor(entry) === 'summary') d.summary.push(entry);
|
|
82
|
+
else d.context.push(entry);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const disc of oldDiscussions) {
|
|
86
|
+
ensure(disc.project || 'global').discussions.push(disc);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Match graph build records to projects via rootPath
|
|
90
|
+
for (const graph of oldGraphs) {
|
|
91
|
+
const proj = projectsIndex.find(p => normPath(p.rootPath) === normPath(graph.path));
|
|
92
|
+
const name = proj ? proj.name : 'global';
|
|
93
|
+
const d = ensure(name);
|
|
94
|
+
d.graph.build = {
|
|
95
|
+
path: graph.path, nodes: graph.nodes, edges: graph.edges,
|
|
96
|
+
communities: graph.communities, cached: graph.cached || 0,
|
|
97
|
+
changed: graph.changed || 0, time_ms: graph.time_ms || 0,
|
|
98
|
+
summary: graph.summary || '', builtAt: graph.builtAt,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Write per-project files
|
|
103
|
+
for (const [name, data] of Object.entries(byProject)) {
|
|
104
|
+
const dir = join(projectsDir, slugify(name));
|
|
105
|
+
mkdirSync(dir, { recursive: true });
|
|
106
|
+
flushFile(join(dir, 'context.json'), { entries: data.context });
|
|
107
|
+
flushFile(join(dir, 'graph.json'), data.graph);
|
|
108
|
+
flushFile(join(dir, 'summary.json'), { entries: data.summary });
|
|
109
|
+
flushFile(join(dir, 'discussions.json'), { discussions: data.discussions });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Stamp dataDir onto each project record
|
|
113
|
+
for (const proj of projectsIndex) {
|
|
114
|
+
if (!proj.dataDir) proj.dataDir = `projects/${slugify(proj.name)}`;
|
|
115
|
+
}
|
|
116
|
+
flushFile(projectsPath, { projects: projectsIndex });
|
|
117
|
+
|
|
118
|
+
// Remove legacy flat files
|
|
119
|
+
try { unlinkSync(legacyContexts); } catch {}
|
|
120
|
+
try { unlinkSync(legacyDiscussions); } catch {}
|
|
121
|
+
try { unlinkSync(legacyGraphs); } catch {}
|
|
122
|
+
|
|
123
|
+
return true;
|
|
124
|
+
}
|
package/src/server.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getConfig } from './config.js';
|
|
|
5
5
|
|
|
6
6
|
import * as contextTool from './tools/context.js';
|
|
7
7
|
import * as searchTool from './tools/search.js';
|
|
8
|
-
import * as
|
|
8
|
+
import * as planTool from './tools/plan.js';
|
|
9
9
|
import * as errorCheckTool from './tools/errorCheck.js';
|
|
10
10
|
import * as fileTool from './tools/fileTools.js';
|
|
11
11
|
import * as gitTool from './tools/gitTools.js';
|
|
@@ -17,8 +17,9 @@ const CODEGRAPH_TOOL_NAMES = codegraphTool.TOOL_NAMES;
|
|
|
17
17
|
|
|
18
18
|
export function createServer({ enableFileTools = false, enableGitTools = getConfig().access_git === true } = {}) {
|
|
19
19
|
const state = {
|
|
20
|
-
sessionProject:
|
|
21
|
-
discussionId:
|
|
20
|
+
sessionProject: null,
|
|
21
|
+
discussionId: null,
|
|
22
|
+
projectRootPath: null,
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const server = new Server(
|
|
@@ -30,7 +31,7 @@ export function createServer({ enableFileTools = false, enableGitTools = getConf
|
|
|
30
31
|
const tools = [
|
|
31
32
|
contextTool.definition,
|
|
32
33
|
searchTool.definition,
|
|
33
|
-
|
|
34
|
+
planTool.definition,
|
|
34
35
|
errorCheckTool.definition,
|
|
35
36
|
];
|
|
36
37
|
if (enableFileTools) tools.push(...fileTool.definitions);
|
|
@@ -56,8 +57,8 @@ export function createServer({ enableFileTools = false, enableGitTools = getConf
|
|
|
56
57
|
result = await contextTool.handle(args, state);
|
|
57
58
|
} else if (name === searchTool.definition.name) {
|
|
58
59
|
result = await searchTool.handle(args, state);
|
|
59
|
-
} else if (name ===
|
|
60
|
-
result = await
|
|
60
|
+
} else if (name === planTool.definition.name) {
|
|
61
|
+
result = await planTool.handle(args, state);
|
|
61
62
|
} else if (name === errorCheckTool.definition.name) {
|
|
62
63
|
result = await errorCheckTool.handle(args, state);
|
|
63
64
|
} else if (FILE_TOOL_NAMES.has(name)) {
|
package/src/templates/AGENTS.md
CHANGED
|
@@ -11,7 +11,7 @@ Call the `context` MCP tool with `action: "resume"`, `project: "<project-name>"`
|
|
|
11
11
|
|
|
12
12
|
Returns:
|
|
13
13
|
- `recentEntries` — decisions, bugs, notes from previous conversations
|
|
14
|
-
- `
|
|
14
|
+
- `activePlans` — active AI-created plans for this project
|
|
15
15
|
- `codegraph` — `{ built: true/false, nodes, edges, communities }`
|
|
16
16
|
|
|
17
17
|
Then:
|
|
@@ -24,7 +24,7 @@ Then:
|
|
|
24
24
|
|
|
25
25
|
**After graph build or rebuild** — every time `codegraph_build` completes:
|
|
26
26
|
```
|
|
27
|
-
context.save type: "
|
|
27
|
+
context.save type: "note" title: "ContextGraph built — <project>"
|
|
28
28
|
content: "nodes: X | edges: Y | communities: Z"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -35,18 +35,12 @@ content: "nodes: X | edges: Y | communities: Z"
|
|
|
35
35
|
| What happened | Type |
|
|
36
36
|
|--------------|------|
|
|
37
37
|
| Approach / library / pattern decided | `decision` |
|
|
38
|
-
| Bug found
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
| Config / env var / secret key discovered | `config` |
|
|
42
|
-
| External API or service integration learned | `note` |
|
|
43
|
-
| Performance insight (why something is slow/fast) | `note` |
|
|
44
|
-
| How to run tests / test pattern discovered | `note` |
|
|
45
|
-
| Deploy / release step discovered | `note` |
|
|
46
|
-
| Milestone / feature / task completed | `note` |
|
|
38
|
+
| Bug found, root cause known, or fixed | `bug` |
|
|
39
|
+
| Gotcha, constraint, discovery, structure understood | `note` |
|
|
40
|
+
| Config / env var / secret / deploy step | `config` |
|
|
47
41
|
|
|
48
42
|
Do NOT save: routine reads, search results, temporary debugging dead-ends.
|
|
49
|
-
|
|
43
|
+
**Making any kind of plan** → call `plan.save` immediately with the plan summary and `planDir` pointing to your platform's plans folder.
|
|
50
44
|
Need past info → `search` before asking. Always pass `project`.
|
|
51
45
|
|
|
52
46
|
---
|
|
@@ -63,7 +57,6 @@ codegraph_build(path) → AST graph: functions, classes, imports, edges
|
|
|
63
57
|
### Step 2 — Query (free, instant)
|
|
64
58
|
```
|
|
65
59
|
codegraph_query(path, question) → fetch any details about the codebase
|
|
66
|
-
codegraph_explain(path, node) → single node: type, file, connections
|
|
67
60
|
codegraph_path(path, from, to) → shortest path
|
|
68
61
|
codegraph_nodes(path, type) → list nodes by type
|
|
69
62
|
codegraph_report(path) → full graph analysis
|
package/src/templates/CLAUDE.md
CHANGED
|
@@ -24,7 +24,7 @@ Both fields are required: `project` names the memory bucket, `rootPath` enables
|
|
|
24
24
|
|
|
25
25
|
Returns:
|
|
26
26
|
- `recentEntries` — decisions, bugs, notes from previous conversations
|
|
27
|
-
- `
|
|
27
|
+
- `activePlans` — active AI-created plans for this project
|
|
28
28
|
- `codegraph` — `{ built: true/false, nodes, edges, communities }`
|
|
29
29
|
|
|
30
30
|
Then:
|
|
@@ -39,7 +39,7 @@ Then:
|
|
|
39
39
|
|
|
40
40
|
**After graph build or rebuild** — every time `codegraph_build` completes:
|
|
41
41
|
```
|
|
42
|
-
context.save project: "<project>" type: "
|
|
42
|
+
context.save project: "<project>" type: "note" title: "ContextGraph built — <project>"
|
|
43
43
|
content: "nodes: X | edges: Y | communities: Z"
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -50,19 +50,13 @@ content: "nodes: X | edges: Y | communities: Z"
|
|
|
50
50
|
| What happened | Type |
|
|
51
51
|
|--------------|------|
|
|
52
52
|
| Approach / library / pattern decided | `decision` |
|
|
53
|
-
| Bug found
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
| Config / env var / secret key discovered | `config` |
|
|
57
|
-
| External API or service integration learned | `note` |
|
|
58
|
-
| Performance insight (why something is slow/fast) | `note` |
|
|
59
|
-
| How to run tests / test pattern discovered | `note` |
|
|
60
|
-
| Deploy / release step discovered | `note` |
|
|
61
|
-
| Milestone / feature / task completed | `note` |
|
|
53
|
+
| Bug found, root cause known, or fixed | `bug` |
|
|
54
|
+
| Gotcha, constraint, discovery, structure understood | `note` |
|
|
55
|
+
| Config / env var / secret / deploy step | `config` |
|
|
62
56
|
|
|
63
57
|
Do NOT save: routine reads, search results, temporary debugging dead-ends.
|
|
64
58
|
|
|
65
|
-
|
|
59
|
+
**Making any kind of plan** → call `plan.save` immediately with the plan summary and `planDir` pointing to your platform's plans folder.
|
|
66
60
|
Need past info → `search` before asking user.
|
|
67
61
|
Always pass `project`. Auto-compact fires at >20 entries.
|
|
68
62
|
|
package/src/templates/GEMINI.md
CHANGED
|
@@ -11,7 +11,7 @@ Call the `context` MCP tool with `action: "resume"`, `project: "<project-name>"`
|
|
|
11
11
|
|
|
12
12
|
Returns:
|
|
13
13
|
- `recentEntries` — decisions, bugs, notes from previous conversations
|
|
14
|
-
- `
|
|
14
|
+
- `activePlans` — active AI-created plans for this project
|
|
15
15
|
- `codegraph` — `{ built: true/false, nodes, edges, communities }`
|
|
16
16
|
|
|
17
17
|
Then:
|
|
@@ -24,7 +24,7 @@ Then:
|
|
|
24
24
|
|
|
25
25
|
**After graph build or rebuild** — every time `codegraph_build` completes:
|
|
26
26
|
```
|
|
27
|
-
context.save type: "
|
|
27
|
+
context.save type: "note" title: "ContextGraph built — <project>"
|
|
28
28
|
content: "nodes: X | edges: Y | communities: Z"
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -35,18 +35,12 @@ content: "nodes: X | edges: Y | communities: Z"
|
|
|
35
35
|
| What happened | Type |
|
|
36
36
|
|--------------|------|
|
|
37
37
|
| Approach / library / pattern decided | `decision` |
|
|
38
|
-
| Bug found
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
| Config / env var / secret key discovered | `config` |
|
|
42
|
-
| External API or service integration learned | `note` |
|
|
43
|
-
| Performance insight (why something is slow/fast) | `note` |
|
|
44
|
-
| How to run tests / test pattern discovered | `note` |
|
|
45
|
-
| Deploy / release step discovered | `note` |
|
|
46
|
-
| Milestone / feature / task completed | `note` |
|
|
38
|
+
| Bug found, root cause known, or fixed | `bug` |
|
|
39
|
+
| Gotcha, constraint, discovery, structure understood | `note` |
|
|
40
|
+
| Config / env var / secret / deploy step | `config` |
|
|
47
41
|
|
|
48
42
|
Do NOT save: routine reads, search results, temporary debugging dead-ends.
|
|
49
|
-
|
|
43
|
+
**Making any kind of plan** → call `plan.save` immediately with the plan summary and `planDir` pointing to your platform's plans folder.
|
|
50
44
|
Need past info → `search` before asking. Always pass `project`.
|
|
51
45
|
|
|
52
46
|
---
|
|
@@ -63,7 +57,6 @@ codegraph_build(path) → AST graph: functions, classes, imports, edges
|
|
|
63
57
|
### Step 2 — Query (free, instant)
|
|
64
58
|
```
|
|
65
59
|
codegraph_query(path, question) → fetch any details about the codebase
|
|
66
|
-
codegraph_explain(path, node) → single node: type, file, connections
|
|
67
60
|
codegraph_path(path, from, to) → shortest path
|
|
68
61
|
codegraph_nodes(path, type) → list nodes by type
|
|
69
62
|
codegraph_report(path) → full graph analysis
|
|
@@ -4,7 +4,7 @@ Both `project` and `rootPath` are required: `project` names the memory bucket, `
|
|
|
4
4
|
|
|
5
5
|
This loads:
|
|
6
6
|
- Recent decisions, bugs, and notes from past sessions
|
|
7
|
-
- Active
|
|
7
|
+
- Active plans
|
|
8
8
|
- ContextGraph status (built or not)
|
|
9
9
|
|
|
10
10
|
If `codegraph.built` is false in the response, immediately call `codegraph_build` on the project path before proceeding.
|
|
@@ -4,6 +4,6 @@ Parse `$ARGUMENTS` to determine:
|
|
|
4
4
|
- `project`: infer from current working directory name if not specified
|
|
5
5
|
- `title`: first sentence or phrase from the argument
|
|
6
6
|
- `content`: full argument text
|
|
7
|
-
- `type`: auto-detect — if mentions a bug/fix → `"bug"`, decision/chose/decided → `"decision"`,
|
|
7
|
+
- `type`: auto-detect — if mentions a bug/fix → `"bug"`, decision/chose/decided → `"decision"`, config/env/secret/deploy → `"config"`, otherwise `"note"`
|
|
8
8
|
|
|
9
9
|
Confirm to the user what was saved (title, type, project).
|
|
@@ -13,7 +13,7 @@ Every conversation starts with `context.resume`. Every structural question uses
|
|
|
13
13
|
|
|
14
14
|
Call `context` tool, `action: "resume"`, `project: "<project-name>"` before anything else.
|
|
15
15
|
|
|
16
|
-
Returns: `recentEntries`, `
|
|
16
|
+
Returns: `recentEntries`, `activePlans`, `codegraph { built, nodes, edges }`.
|
|
17
17
|
|
|
18
18
|
- `codegraph.built: true` → use `codegraph_query` before reading files
|
|
19
19
|
- `codegraph.built: false` → run `codegraph_build(path)` first
|
|
@@ -24,8 +24,8 @@ Returns: `recentEntries`, `activeDiscussions`, `codegraph { built, nodes, edges
|
|
|
24
24
|
|-----------|--------|
|
|
25
25
|
| Decision made | `context.save` type: `"decision"` |
|
|
26
26
|
| Bug found/fixed | `context.save` type: `"bug"` |
|
|
27
|
-
|
|
|
28
|
-
|
|
|
27
|
+
| Discovery / structure understood | `context.save` type: `"note"` |
|
|
28
|
+
| Making any plan | `plan.save` + `planDir` |
|
|
29
29
|
|
|
30
30
|
## 3. CodeGraph
|
|
31
31
|
|
|
@@ -27,7 +27,7 @@ Both fields are required: `project` names the memory bucket, `rootPath` enables
|
|
|
27
27
|
|
|
28
28
|
Returns:
|
|
29
29
|
- `recentEntries` — decisions, bugs, notes from past sessions
|
|
30
|
-
- `
|
|
30
|
+
- `activePlans` — active AI-created plans for this project
|
|
31
31
|
- `codegraph` — `{ built: true/false, nodes, edges, communities }`
|
|
32
32
|
|
|
33
33
|
Then:
|
|
@@ -43,7 +43,7 @@ Then:
|
|
|
43
43
|
**1. After graph build or rebuild**
|
|
44
44
|
Every time `codegraph_build` completes successfully, immediately call:
|
|
45
45
|
```
|
|
46
|
-
context.save project: "<project>" type: "
|
|
46
|
+
context.save project: "<project>" type: "note" title: "ContextGraph built — <project>"
|
|
47
47
|
content: "nodes: X | edges: Y | communities: Z | built: <timestamp>"
|
|
48
48
|
```
|
|
49
49
|
|
|
@@ -63,17 +63,11 @@ Do NOT save for: routine file reads, search results, explanations of existing co
|
|
|
63
63
|
| What happened | Type |
|
|
64
64
|
|--------------|------|
|
|
65
65
|
| Approach / library / pattern decided | `decision` |
|
|
66
|
-
| Bug found
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
| Performance insight (why something is slow/fast) | `note` |
|
|
72
|
-
| How to run tests / test pattern discovered | `note` |
|
|
73
|
-
| Deploy / release step discovered | `note` |
|
|
74
|
-
| Milestone / feature / task completed | `note` |
|
|
75
|
-
|
|
76
|
-
Always pass `project`. Feature spans multiple sessions → `discussion.create` or `discussion.update`. Need past info → `search` before asking user. Auto-compact fires at >20 entries.
|
|
66
|
+
| Bug found, root cause known, or fixed | `bug` |
|
|
67
|
+
| Gotcha, constraint, discovery, structure understood | `note` |
|
|
68
|
+
| Config / env var / secret / deploy step | `config` |
|
|
69
|
+
|
|
70
|
+
Always pass `project`. **Making any plan** → `plan.save` immediately (planDir = your platform's plans folder). Need past info → `search` before asking user. Auto-compact fires at >20 entries.
|
|
77
71
|
|
|
78
72
|
---
|
|
79
73
|
|
|
@@ -89,9 +83,8 @@ Parses codebase into AST graph via tree-sitter. Extracts functions, classes, imp
|
|
|
89
83
|
|
|
90
84
|
### Query (free, instant, forever)
|
|
91
85
|
```
|
|
92
|
-
codegraph_query(path, question)
|
|
93
|
-
|
|
94
|
-
codegraph_path(path, from, to) → shortest path between two concepts
|
|
86
|
+
codegraph_query(path, question?, node?) → structural question OR single-node lookup (or both in one call)
|
|
87
|
+
codegraph_path(path, from, to) → shortest path between two concepts
|
|
95
88
|
codegraph_nodes(path, type) → list all nodes of a type
|
|
96
89
|
codegraph_report(path) → god nodes, clusters, surprises
|
|
97
90
|
```
|
|
@@ -7,7 +7,7 @@ Every conversation starts with `context.resume`. Every structural question uses
|
|
|
7
7
|
|
|
8
8
|
Call `context` tool, `action: "resume"`, `project: "<project-name>"` before anything else.
|
|
9
9
|
|
|
10
|
-
Returns: `recentEntries`, `
|
|
10
|
+
Returns: `recentEntries`, `activePlans`, `codegraph { built, nodes, edges }`.
|
|
11
11
|
|
|
12
12
|
- `codegraph.built: true` → use `codegraph_query` before reading files
|
|
13
13
|
- `codegraph.built: false` → run `codegraph_build(path)` first
|
|
@@ -18,8 +18,8 @@ Returns: `recentEntries`, `activeDiscussions`, `codegraph { built, nodes, edges
|
|
|
18
18
|
|-----------|--------|
|
|
19
19
|
| Decision made | `context.save` type: `"decision"` |
|
|
20
20
|
| Bug found/fixed | `context.save` type: `"bug"` |
|
|
21
|
-
|
|
|
22
|
-
|
|
|
21
|
+
| Discovery / structure understood | `context.save` type: `"note"` |
|
|
22
|
+
| Making any plan | `plan.save` + `planDir` |
|
|
23
23
|
|
|
24
24
|
## 3. CodeGraph
|
|
25
25
|
|
package/src/tools/codegraph.js
CHANGED
|
@@ -40,92 +40,23 @@ export const definitions = [
|
|
|
40
40
|
required: ['path'],
|
|
41
41
|
},
|
|
42
42
|
},
|
|
43
|
-
{
|
|
44
|
-
name: 'codegraph_extract',
|
|
45
|
-
description:
|
|
46
|
-
'Return raw content of changed code and doc/PDF files so the AI can write descriptions. ' +
|
|
47
|
-
'Code files: lists existing AST nodes — AI writes a description for each. ' +
|
|
48
|
-
'Doc files: AI extracts new concept nodes. ' +
|
|
49
|
-
'Call after codegraph_build, then call codegraph_add_nodes with results. ' +
|
|
50
|
-
'Pass force:true to re-enrich all files (not just changed ones).',
|
|
51
|
-
inputSchema: {
|
|
52
|
-
type: 'object',
|
|
53
|
-
properties: {
|
|
54
|
-
path: { type: 'string', description: 'Project root (same as codegraph_build)' },
|
|
55
|
-
limit: { type: 'integer', description: 'Max files to return per call (default 10)' },
|
|
56
|
-
force: { type: 'boolean', description: 'Return all files, not just changed (for re-enrichment)' },
|
|
57
|
-
},
|
|
58
|
-
required: ['path'],
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: 'codegraph_add_nodes',
|
|
63
|
-
description:
|
|
64
|
-
'Add concept nodes extracted by the AI into the graph. ' +
|
|
65
|
-
'Call after reading codegraph_extract output. ' +
|
|
66
|
-
'Each node: name, type, file, and optionally description and relations.',
|
|
67
|
-
inputSchema: {
|
|
68
|
-
type: 'object',
|
|
69
|
-
properties: {
|
|
70
|
-
path: { type: 'string', description: 'Project root' },
|
|
71
|
-
nodes: {
|
|
72
|
-
type: 'array',
|
|
73
|
-
description: 'Concept nodes to add',
|
|
74
|
-
items: {
|
|
75
|
-
type: 'object',
|
|
76
|
-
properties: {
|
|
77
|
-
name: { type: 'string' },
|
|
78
|
-
type: { type: 'string', description: 'class|function|concept|service|decision|requirement' },
|
|
79
|
-
file: { type: 'string', description: 'Relative file path this concept came from' },
|
|
80
|
-
description: { type: 'string' },
|
|
81
|
-
relations: {
|
|
82
|
-
type: 'array',
|
|
83
|
-
items: {
|
|
84
|
-
type: 'object',
|
|
85
|
-
properties: {
|
|
86
|
-
name: { type: 'string' },
|
|
87
|
-
relation: { type: 'string', description: 'depends-on|uses|implements|defines|documents' },
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
required: ['name', 'type', 'file'],
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
required: ['path', 'nodes'],
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
43
|
{
|
|
100
44
|
name: 'codegraph_query',
|
|
101
45
|
description:
|
|
102
|
-
'Ask a structural
|
|
103
|
-
'
|
|
104
|
-
'
|
|
105
|
-
'
|
|
46
|
+
'Ask a structural question about the codebase OR look up a specific node by name — or both in one call. ' +
|
|
47
|
+
'Pass `question` for natural-language traversal: "what does module X depend on?", "what calls function Y?". ' +
|
|
48
|
+
'Pass `node` for fast single-node lookup: returns type, file, depends_on, used_by. ' +
|
|
49
|
+
'Pass both to get node detail + surrounding graph context together. ' +
|
|
50
|
+
'Returns structured text within token_budget. Use before reading any files.',
|
|
106
51
|
inputSchema: {
|
|
107
52
|
type: 'object',
|
|
108
53
|
properties: {
|
|
109
54
|
path: { type: 'string', description: 'Project root' },
|
|
110
|
-
question: { type: 'string', description: 'Natural language question' },
|
|
55
|
+
question: { type: 'string', description: 'Natural language question about the codebase' },
|
|
56
|
+
node: { type: 'string', description: 'Node name or partial name to look up (type, file, deps, callers)' },
|
|
111
57
|
token_budget: { type: 'integer', description: 'Max tokens in response (default 2000)' },
|
|
112
58
|
},
|
|
113
|
-
required: ['path'
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: 'codegraph_explain',
|
|
118
|
-
description:
|
|
119
|
-
'Look up a node by name — returns description, type, file, and direct neighbors (depends_on + used_by). ' +
|
|
120
|
-
'Use to understand what a specific function/class/module does and how it connects. ' +
|
|
121
|
-
'Descriptions are AI-written via codegraph_add_nodes.',
|
|
122
|
-
inputSchema: {
|
|
123
|
-
type: 'object',
|
|
124
|
-
properties: {
|
|
125
|
-
path: { type: 'string', description: 'Project root' },
|
|
126
|
-
node: { type: 'string', description: 'Node name or partial name' },
|
|
127
|
-
},
|
|
128
|
-
required: ['path', 'node'],
|
|
59
|
+
required: ['path'],
|
|
129
60
|
},
|
|
130
61
|
},
|
|
131
62
|
{
|
package/src/tools/context.js
CHANGED
|
@@ -28,9 +28,9 @@ export const definition = {
|
|
|
28
28
|
`Factual memory — record what happened, what was decided, what broke, what was built.\n` +
|
|
29
29
|
`• "resume" — START HERE every conversation. Loads recent context, active discussions, and graph status for a project.\n` +
|
|
30
30
|
`• "save" — Store a note, decision, bug, or code snippet. Auto-deduplicates.\n` +
|
|
31
|
-
`• "get" — Load
|
|
31
|
+
`• "get" — Load entries. Pass id/ids to fetch specific ones, or project/tags/limit for recent.\n` +
|
|
32
32
|
`• "update" — Edit an existing entry by id (any field).\n` +
|
|
33
|
-
`• "delete" — Remove
|
|
33
|
+
`• "delete" — Remove one entry (id) or multiple at once (ids: [...]).\n` +
|
|
34
34
|
`• "list_projects"— Show all projects and entry counts.`,
|
|
35
35
|
inputSchema: {
|
|
36
36
|
type: 'object',
|
|
@@ -40,17 +40,17 @@ export const definition = {
|
|
|
40
40
|
title: { type: 'string' },
|
|
41
41
|
project: { type: 'string' },
|
|
42
42
|
rootPath: { type: 'string', description: 'Absolute path to the project root directory. Stored on first call and used to sandbox file/git tool access.' },
|
|
43
|
-
type: { type: 'string', enum: ['decision', '
|
|
43
|
+
type: { type: 'string', enum: ['decision', 'bug', 'note', 'config'] },
|
|
44
44
|
status: { type: 'string', enum: ['active', 'archived'] },
|
|
45
45
|
tags: { type: 'array', items: { type: 'string' } },
|
|
46
46
|
source: { type: 'string', enum: ['user', 'ai-summary', 'file', 'web', 'cli', 'auto'] },
|
|
47
47
|
files: { type: 'array', items: { type: 'object' } },
|
|
48
48
|
codeRefs: { type: 'array', items: { type: 'object' } },
|
|
49
|
-
relations: { type: 'array', items: { type: 'object' } },
|
|
50
49
|
expiresAt: { type: 'string' },
|
|
51
50
|
limit: { type: 'number' },
|
|
52
51
|
includeArchived: { type: 'boolean' },
|
|
53
|
-
id: { type: 'string' },
|
|
52
|
+
id: { type: 'string', description: 'Single entry ID (get/update/delete)' },
|
|
53
|
+
ids: { type: 'array', items: { type: 'string' }, description: 'Multiple entry IDs — fetch or delete several at once' },
|
|
54
54
|
},
|
|
55
55
|
required: ['action'],
|
|
56
56
|
},
|
|
@@ -116,7 +116,7 @@ export async function handle(args, state) {
|
|
|
116
116
|
|
|
117
117
|
return {
|
|
118
118
|
recentEntries: entries,
|
|
119
|
-
|
|
119
|
+
activePlans: discussions,
|
|
120
120
|
restoredDiscussion: discussions.length === 1 ? { id: discussions[0].id, name: discussions[0].name } : null,
|
|
121
121
|
codegraph: graphStatus,
|
|
122
122
|
digest: digest || undefined,
|
|
@@ -164,13 +164,12 @@ export async function handle(args, state) {
|
|
|
164
164
|
type: args.type || dupe.type, status: args.status || dupe.status,
|
|
165
165
|
expiresAt: args.expiresAt !== undefined ? args.expiresAt : dupe.expiresAt,
|
|
166
166
|
files: args.files || dupe.files, codeRefs: args.codeRefs || dupe.codeRefs,
|
|
167
|
-
relations: args.relations || dupe.relations,
|
|
168
167
|
});
|
|
169
168
|
fireAutoLink(updated.id, state);
|
|
170
169
|
return { success: true, id: updated.id, deduplicated: true,
|
|
171
170
|
message: `Updated existing entry "${updated.title || updated.id}" (auto-dedup).` };
|
|
172
171
|
}
|
|
173
|
-
const entry = saveContext({ ...args });
|
|
172
|
+
const entry = saveContext({ ...args, rootPath: state.projectRootPath || undefined });
|
|
174
173
|
fireAutoLink(entry.id, state);
|
|
175
174
|
|
|
176
175
|
// Auto-compact when too many entries accumulate
|
|
@@ -189,8 +188,20 @@ export async function handle(args, state) {
|
|
|
189
188
|
|
|
190
189
|
case 'get': {
|
|
191
190
|
if (!args.project && state.sessionProject) args = { ...args, project: state.sessionProject };
|
|
192
|
-
archiveExpired(args.project);
|
|
193
191
|
const includeArchived = args.includeArchived === true;
|
|
192
|
+
|
|
193
|
+
// Fetch by specific ID(s) — bypass project/tag/limit filters
|
|
194
|
+
const ids = args.ids || (args.id ? [args.id] : null);
|
|
195
|
+
if (ids) {
|
|
196
|
+
const entries = getContext({ ids, compact: false })
|
|
197
|
+
.filter(e => includeArchived || e.status !== 'archived');
|
|
198
|
+
return {
|
|
199
|
+
entries, count: entries.length,
|
|
200
|
+
message: entries.length ? `Found ${entries.length} entries.` : 'No entries found for given IDs.',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
archiveExpired(args.project);
|
|
194
205
|
let entries = getContext({ project: args.project, tags: args.tags, limit: args.limit, compact: true });
|
|
195
206
|
if (!includeArchived) entries = entries.filter(e => e.status !== 'archived');
|
|
196
207
|
const fullEntries = entries.length > 10
|
|
@@ -216,8 +227,9 @@ export async function handle(args, state) {
|
|
|
216
227
|
}
|
|
217
228
|
|
|
218
229
|
case 'delete': {
|
|
219
|
-
if (!args.id) throw new Error('id is required for delete');
|
|
220
|
-
|
|
230
|
+
if (!args.id && !args.ids) throw new Error('id or ids is required for delete');
|
|
231
|
+
const result = deleteContext(args);
|
|
232
|
+
return { ...result, message: `Deleted ${result.deleted} entr${result.deleted === 1 ? 'y' : 'ies'}.` };
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
case 'list_projects': {
|
package/src/tools/gitTools.js
CHANGED
|
@@ -26,10 +26,8 @@ function autoDetectRoot(fromDir) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function resolveCwd(args, state) {
|
|
29
|
-
// Auto-detect project root on first use if not already configured.
|
|
30
29
|
if (!state.projectRootPath) {
|
|
31
|
-
|
|
32
|
-
state.projectRootPath = detected || process.cwd();
|
|
30
|
+
throw new Error('No project root configured. Call context.resume with rootPath before using git tools.');
|
|
33
31
|
}
|
|
34
32
|
const raw = args.cwd ? pathResolve(args.cwd) : state.projectRootPath;
|
|
35
33
|
return guardPath(raw, state.projectRootPath);
|