context-mcp-server 1.0.1 → 1.0.3
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 +12 -16
- package/codegraph/__pycache__/config.cpython-313.pyc +0 -0
- package/codegraph/__pycache__/scanner.cpython-313.pyc +0 -0
- package/codegraph/__pycache__/server.cpython-313.pyc +0 -0
- package/codegraph/config.py +139 -22
- package/codegraph/extractors/__pycache__/ast_extractor.cpython-313.pyc +0 -0
- package/codegraph/extractors/__pycache__/build_extractor.cpython-313.pyc +0 -0
- package/codegraph/extractors/ast_extractor.py +392 -176
- package/codegraph/extractors/build_extractor.py +68 -0
- package/codegraph/scanner.py +5 -21
- package/codegraph/server.py +32 -229
- package/package.json +2 -1
- package/src/cli.js +65 -4
- package/src/templates/AGENTS.md +8 -15
- package/src/templates/CLAUDE.md +15 -27
- package/src/templates/GEMINI.md +7 -14
- package/src/tools/context.js +39 -3
- package/src/tools/fileTools.js +8 -6
- package/src/tools/gitTools.js +28 -12
package/src/templates/CLAUDE.md
CHANGED
|
@@ -9,7 +9,7 @@ description: >
|
|
|
9
9
|
# Context-MCP — Claude Usage Guide
|
|
10
10
|
|
|
11
11
|
Persistent memory + codebase knowledge graph for Claude.
|
|
12
|
-
Every conversation starts with `context.resume`. Every
|
|
12
|
+
Every conversation starts with `context.resume`. Every codebase question uses `codegraph_query`. Files only read for bugs/logic.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -45,42 +45,31 @@ Always pass `project`. Auto-compact fires at >50 entries — oldest summarized a
|
|
|
45
45
|
|
|
46
46
|
## 3. CodeGraph Pipeline
|
|
47
47
|
|
|
48
|
-
### Step 1 — Build (once per project,
|
|
48
|
+
### Step 1 — Build (once per project, fast, local)
|
|
49
49
|
```
|
|
50
50
|
codegraph_build(path)
|
|
51
|
-
→ AST: code files → functions, classes, imports, edges
|
|
52
|
-
→ Config: .yaml .toml .sql → schema nodes
|
|
53
|
-
→ Docs: .md .txt .pdf → pending (no content yet)
|
|
54
51
|
```
|
|
55
|
-
|
|
52
|
+
- Parses codebase into AST graph using tree-sitter (regex fallback for unsupported languages)
|
|
53
|
+
- Extracts functions, classes, imports, call edges for all code files
|
|
54
|
+
- Build files (package.json, pyproject.toml, go.mod, Dockerfile, etc.) get a single metadata node
|
|
55
|
+
- Saves graph to `~/.context-mcp/graphs.json`. Visible on `context.resume`.
|
|
56
56
|
|
|
57
|
-
### Step 2 —
|
|
57
|
+
### Step 2 — Query (free, instant, forever)
|
|
58
58
|
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
codegraph_add_nodes(path, nodes)
|
|
66
|
-
→ stores descriptions in semantic cache (never overwritten by rebuild)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Step 3 — Query (free, instant forever)
|
|
70
|
-
```
|
|
71
|
-
codegraph_query(path, question) → NODE/EDGE subgraph, token_budget param (default 2000)
|
|
72
|
-
codegraph_explain(path, node) → one node: description + depends_on + used_by
|
|
73
|
-
codegraph_path(path, from, to) → shortest path between two concepts
|
|
74
|
-
codegraph_nodes(path, type) → list all nodes of a type
|
|
75
|
-
codegraph_report(path) → god nodes, clusters, surprises
|
|
59
|
+
codegraph_query(path, question) → fetch any details about the codebase
|
|
60
|
+
codegraph_explain(path, node) → one node: type, file, depends_on, used_by
|
|
61
|
+
codegraph_path(path, from, to) → shortest path between two concepts
|
|
62
|
+
codegraph_nodes(path, type) → list all nodes of a type
|
|
63
|
+
codegraph_report(path) → god nodes, clusters, surprises
|
|
76
64
|
```
|
|
77
65
|
|
|
78
66
|
---
|
|
79
67
|
|
|
80
68
|
## 4. Graph vs File
|
|
81
69
|
|
|
82
|
-
**Graph** —
|
|
83
|
-
|
|
70
|
+
**Graph** — use for any question about what exists in the codebase: finding functions, classes, files, understanding what a module contains, dependencies, callers, imports, paths between concepts.
|
|
71
|
+
|
|
72
|
+
**File** — use for bugs, logic inside a specific function, tracing unexpected behavior.
|
|
84
73
|
|
|
85
74
|
---
|
|
86
75
|
|
|
@@ -91,4 +80,3 @@ codegraph_report(path) → god nodes, clusters, surprises
|
|
|
91
80
|
3. **`search` before asking** — if user references past work, find it first
|
|
92
81
|
4. **`codegraph_query` before reading files** — graph is faster and cheaper
|
|
93
82
|
5. **Read files for bugs/logic** — graph is structure only, not behavior
|
|
94
|
-
6. **Enrich once** — run extract → add_nodes once per project; descriptions persist forever
|
package/src/templates/GEMINI.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Context-MCP — Gemini CLI Usage Guide
|
|
2
2
|
|
|
3
3
|
Persistent memory + codebase knowledge graph.
|
|
4
|
-
Every conversation starts with `context.resume`. Every
|
|
4
|
+
Every conversation starts with `context.resume`. Every codebase question uses `codegraph_query`. Files only read for bugs/logic.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -37,21 +37,15 @@ Always pass `project`. Auto-compact fires at >50 entries.
|
|
|
37
37
|
|
|
38
38
|
## 3. CodeGraph Pipeline
|
|
39
39
|
|
|
40
|
-
### Step 1 — Build (once,
|
|
40
|
+
### Step 1 — Build (once, fast, local)
|
|
41
41
|
```
|
|
42
42
|
codegraph_build(path) → AST graph: functions, classes, imports, edges
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
### Step 2 —
|
|
45
|
+
### Step 2 — Query (free, instant)
|
|
46
46
|
```
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Step 3 — Query (free, instant)
|
|
52
|
-
```
|
|
53
|
-
codegraph_query(path, question) → NODE/EDGE subgraph (token_budget default 2000)
|
|
54
|
-
codegraph_explain(path, node) → single node + neighbors
|
|
47
|
+
codegraph_query(path, question) → fetch any details about the codebase
|
|
48
|
+
codegraph_explain(path, node) → single node: type, file, connections
|
|
55
49
|
codegraph_path(path, from, to) → shortest path
|
|
56
50
|
codegraph_nodes(path, type) → list nodes by type
|
|
57
51
|
codegraph_report(path) → full graph analysis
|
|
@@ -61,8 +55,8 @@ codegraph_report(path) → full graph analysis
|
|
|
61
55
|
|
|
62
56
|
## 4. Graph vs File
|
|
63
57
|
|
|
64
|
-
**Graph** —
|
|
65
|
-
**File** — bugs, logic, tracing behavior.
|
|
58
|
+
**Graph** — use for any question about what exists: finding functions, classes, files, dependencies, callers, imports, paths between concepts.
|
|
59
|
+
**File** — bugs, logic, tracing unexpected behavior.
|
|
66
60
|
|
|
67
61
|
---
|
|
68
62
|
|
|
@@ -73,4 +67,3 @@ codegraph_report(path) → full graph analysis
|
|
|
73
67
|
3. **`search` before asking** — if user references past work, find it first
|
|
74
68
|
4. **`codegraph_query` before reading files** — graph is faster and cheaper
|
|
75
69
|
5. **Read files for bugs/logic** — graph is structure only, not behavior
|
|
76
|
-
6. **Enrich once** — descriptions persist forever
|
package/src/tools/context.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { guardPath } from '../guard.js';
|
|
1
3
|
import {
|
|
2
4
|
saveContext, updateContext, getContext, deleteContext,
|
|
3
5
|
listProjects, findDuplicate, archiveExpired, linkContextToDiscussion,
|
|
4
6
|
listDiscussions, listGraphs, countContext, shouldCompact, compactProject,
|
|
5
7
|
ensureProject, getProjectRoot,
|
|
6
8
|
} from '../db.js';
|
|
9
|
+
|
|
10
|
+
function detectGitRoot() {
|
|
11
|
+
try {
|
|
12
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
13
|
+
cwd: process.cwd(), encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
|
+
}).trim();
|
|
15
|
+
} catch { return null; }
|
|
16
|
+
}
|
|
7
17
|
import { summarizeEntries } from '../summarizer.js';
|
|
8
18
|
import { fireAutoLink } from '../hooks/autoLink.js';
|
|
9
19
|
|
|
@@ -71,10 +81,12 @@ export async function handle(args, state) {
|
|
|
71
81
|
// Set project on state so autoLink works for subsequent saves
|
|
72
82
|
if (proj) state.sessionProject = proj;
|
|
73
83
|
|
|
74
|
-
// Store rootPath with project (first time only) and load it onto session state
|
|
75
|
-
if
|
|
84
|
+
// Store rootPath with project (first time only) and load it onto session state.
|
|
85
|
+
// Auto-detect from git if neither provided nor previously stored.
|
|
76
86
|
const storedRoot = proj ? getProjectRoot(proj) : null;
|
|
77
|
-
|
|
87
|
+
const resolvedRoot = args.rootPath || storedRoot || detectGitRoot() || null;
|
|
88
|
+
if (proj) ensureProject(proj, resolvedRoot || undefined);
|
|
89
|
+
state.projectRootPath = resolvedRoot;
|
|
78
90
|
|
|
79
91
|
const entries = getContext({ project: proj, limit: 15, compact: true })
|
|
80
92
|
.filter(e => e.status !== 'archived');
|
|
@@ -105,6 +117,9 @@ export async function handle(args, state) {
|
|
|
105
117
|
stats: { totalEntries, projects: listProjects().length },
|
|
106
118
|
message: `Loaded ${totalEntries} entries for project "${proj || 'global'}".${discussions.length === 1 ? ` Auto-linked to discussion "${discussions[0].name}".` : ''}`,
|
|
107
119
|
rootPath: state.projectRootPath || undefined,
|
|
120
|
+
sandbox: state.projectRootPath
|
|
121
|
+
? `All file and git operations are sandboxed to: ${state.projectRootPath} — do not use paths outside this root.`
|
|
122
|
+
: 'No project root configured — pass rootPath to restrict file/git access to a directory.',
|
|
108
123
|
hint: graphStatus.built
|
|
109
124
|
? `Graph ready (${graphStatus.nodes} nodes). Use codegraph_query for structural questions.`
|
|
110
125
|
: 'No graph built yet. Call codegraph_build on the project root to enable graph queries.',
|
|
@@ -114,6 +129,27 @@ export async function handle(args, state) {
|
|
|
114
129
|
case 'save': {
|
|
115
130
|
if (!args.content) throw new Error('content is required for save');
|
|
116
131
|
if (!args.project && state.sessionProject) args = { ...args, project: state.sessionProject };
|
|
132
|
+
// Auto-detect and store project root if not yet configured
|
|
133
|
+
if (args.project) {
|
|
134
|
+
const existing = getProjectRoot(args.project);
|
|
135
|
+
if (!existing) {
|
|
136
|
+
const detected = state.projectRootPath || detectGitRoot();
|
|
137
|
+
if (detected) {
|
|
138
|
+
ensureProject(args.project, detected);
|
|
139
|
+
if (!state.projectRootPath) state.projectRootPath = detected;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Validate file paths in files[] and codeRefs[] stay within project root
|
|
144
|
+
if (state.projectRootPath) {
|
|
145
|
+
if (Array.isArray(args.files)) {
|
|
146
|
+
args.files.forEach(f => { if (f.path) guardPath(f.path, state.projectRootPath); });
|
|
147
|
+
}
|
|
148
|
+
if (Array.isArray(args.codeRefs)) {
|
|
149
|
+
args.codeRefs.forEach(r => { if (r.file) guardPath(r.file, state.projectRootPath); });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
117
153
|
const dupe = findDuplicate(args.content, args.project);
|
|
118
154
|
if (dupe) {
|
|
119
155
|
const updated = updateContext({
|
package/src/tools/fileTools.js
CHANGED
|
@@ -20,28 +20,30 @@ function atomicWrite(filePath, data) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const ROOT_NOTE = ' Sandboxed to project root — paths outside root are denied.';
|
|
24
|
+
|
|
23
25
|
export const definitions = [
|
|
24
26
|
{
|
|
25
27
|
name: 'create_dir',
|
|
26
|
-
description: 'Create a directory (and any missing parent directories). Safe to call if already exists.',
|
|
28
|
+
description: 'Create a directory (and any missing parent directories). Safe to call if already exists.' + ROOT_NOTE,
|
|
27
29
|
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
28
30
|
outputSchema: { type: 'object', properties: { path: { type: 'string' }, existed: { type: 'boolean' }, message: { type: 'string' } } },
|
|
29
31
|
},
|
|
30
32
|
{
|
|
31
33
|
name: 'list_dir',
|
|
32
|
-
description: 'List the contents of a local directory.',
|
|
34
|
+
description: 'List the contents of a local directory.' + ROOT_NOTE,
|
|
33
35
|
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
34
36
|
outputSchema: { type: 'object', properties: { directory: { type: 'string' }, items: { type: 'array' } } },
|
|
35
37
|
},
|
|
36
38
|
{
|
|
37
39
|
name: 'read_file',
|
|
38
|
-
description: 'Read the text contents of a local file.',
|
|
40
|
+
description: 'Read the text contents of a local file.' + ROOT_NOTE,
|
|
39
41
|
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
40
42
|
outputSchema: { type: 'object', properties: { file: { type: 'string' }, content: { type: 'string' } } },
|
|
41
43
|
},
|
|
42
44
|
{
|
|
43
45
|
name: 'write_file',
|
|
44
|
-
description: 'Create a new file or overwrite an existing file.',
|
|
46
|
+
description: 'Create a new file or overwrite an existing file.' + ROOT_NOTE,
|
|
45
47
|
inputSchema: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] },
|
|
46
48
|
outputSchema: { type: 'object', properties: { file: { type: 'string' }, message: { type: 'string' } } },
|
|
47
49
|
},
|
|
@@ -51,7 +53,7 @@ export const definitions = [
|
|
|
51
53
|
`Apply targeted string replacement(s) to a file.\n` +
|
|
52
54
|
`Single edit: pass old_str + new_str.\n` +
|
|
53
55
|
`Multi edit: pass edits:[{old_str, new_str, description?}] — atomic.\n` +
|
|
54
|
-
`Use dry_run:true to validate without writing
|
|
56
|
+
`Use dry_run:true to validate without writing.` + ROOT_NOTE,
|
|
55
57
|
inputSchema: {
|
|
56
58
|
type: 'object',
|
|
57
59
|
properties: {
|
|
@@ -68,7 +70,7 @@ export const definitions = [
|
|
|
68
70
|
},
|
|
69
71
|
{
|
|
70
72
|
name: 'delete_file',
|
|
71
|
-
description: 'Delete a local file. Pass recursive:true to delete a directory and its contents.',
|
|
73
|
+
description: 'Delete a local file. Pass recursive:true to delete a directory and its contents.' + ROOT_NOTE,
|
|
72
74
|
inputSchema: { type: 'object', properties: { path: { type: 'string' }, recursive: { type: 'boolean', description: 'Required to delete a directory. Defaults to false.' } }, required: ['path'] },
|
|
73
75
|
outputSchema: { type: 'object', properties: { path: { type: 'string' }, message: { type: 'string' } } },
|
|
74
76
|
},
|
package/src/tools/gitTools.js
CHANGED
|
@@ -15,69 +15,85 @@ function runGit(argArr, cwd) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function autoDetectRoot(fromDir) {
|
|
19
|
+
try {
|
|
20
|
+
return execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
21
|
+
cwd: fromDir, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
22
|
+
}).trim();
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
function resolveCwd(args, state) {
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
// Auto-detect project root on first use if not already configured.
|
|
30
|
+
if (!state.projectRootPath) {
|
|
31
|
+
const detected = autoDetectRoot(args.cwd ? pathResolve(args.cwd) : process.cwd());
|
|
32
|
+
if (detected) state.projectRootPath = detected;
|
|
33
|
+
}
|
|
34
|
+
const raw = args.cwd ? pathResolve(args.cwd) : (state.projectRootPath || process.cwd());
|
|
21
35
|
if (state.projectRootPath) return guardPath(raw, state.projectRootPath);
|
|
22
36
|
return raw;
|
|
23
37
|
}
|
|
24
38
|
|
|
39
|
+
const ROOT_NOTE = ' All paths must be within the project root (sandboxed — access outside root is denied).';
|
|
40
|
+
|
|
25
41
|
export const definitions = [
|
|
26
42
|
{
|
|
27
43
|
name: 'git_status',
|
|
28
|
-
description: 'Show working tree status — current branch, staged, unstaged, and untracked files.',
|
|
44
|
+
description: 'Show working tree status — current branch, staged, unstaged, and untracked files.' + ROOT_NOTE,
|
|
29
45
|
inputSchema: { type: 'object', properties: { cwd: { type: 'string' } } },
|
|
30
46
|
outputSchema: { type: 'object', properties: { branch: { type: 'string' }, clean: { type: 'boolean' }, staged: { type: 'array' }, unstaged: { type: 'array' }, untracked: { type: 'array' } } },
|
|
31
47
|
},
|
|
32
48
|
{
|
|
33
49
|
name: 'git_diff',
|
|
34
|
-
description: 'Show file changes. Use staged:true for cached diff. Optionally scope to a path.',
|
|
50
|
+
description: 'Show file changes. Use staged:true for cached diff. Optionally scope to a path.' + ROOT_NOTE,
|
|
35
51
|
inputSchema: { type: 'object', properties: { staged: { type: 'boolean' }, path: { type: 'string' }, cwd: { type: 'string' } } },
|
|
36
52
|
outputSchema: { type: 'object', properties: { diff: { type: 'string' }, staged: { type: 'boolean' } } },
|
|
37
53
|
},
|
|
38
54
|
{
|
|
39
55
|
name: 'git_log',
|
|
40
|
-
description: 'Show recent commit history — hash, author, date, message.',
|
|
56
|
+
description: 'Show recent commit history — hash, author, date, message.' + ROOT_NOTE,
|
|
41
57
|
inputSchema: { type: 'object', properties: { limit: { type: 'number' }, path: { type: 'string' }, cwd: { type: 'string' } } },
|
|
42
58
|
outputSchema: { type: 'object', properties: { commits: { type: 'array' }, count: { type: 'number' } } },
|
|
43
59
|
},
|
|
44
60
|
{
|
|
45
61
|
name: 'git_add',
|
|
46
|
-
description: 'Stage files for commit. Pass paths:["."] to stage everything.',
|
|
62
|
+
description: 'Stage files for commit. Pass paths:["."] to stage everything.' + ROOT_NOTE,
|
|
47
63
|
inputSchema: { type: 'object', properties: { paths: { type: 'array', items: { type: 'string' } }, cwd: { type: 'string' } }, required: ['paths'] },
|
|
48
64
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, staged: { type: 'array' }, message: { type: 'string' } } },
|
|
49
65
|
},
|
|
50
66
|
{
|
|
51
67
|
name: 'git_commit',
|
|
52
|
-
description: 'Commit staged changes. Set all:true to auto-stage tracked modified files first. Auto-saves context entry.',
|
|
68
|
+
description: 'Commit staged changes. Set all:true to auto-stage tracked modified files first. Auto-saves context entry.' + ROOT_NOTE,
|
|
53
69
|
inputSchema: { type: 'object', properties: { message: { type: 'string' }, all: { type: 'boolean' }, cwd: { type: 'string' } }, required: ['message'] },
|
|
54
70
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, hash: { type: 'string' }, branch: { type: 'string' }, message: { type: 'string' }, files: { type: 'array' } } },
|
|
55
71
|
},
|
|
56
72
|
{
|
|
57
73
|
name: 'git_push',
|
|
58
|
-
description: 'Push current branch to remote.',
|
|
74
|
+
description: 'Push current branch to remote.' + ROOT_NOTE,
|
|
59
75
|
inputSchema: { type: 'object', properties: { remote: { type: 'string' }, branch: { type: 'string' }, cwd: { type: 'string' } } },
|
|
60
76
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, remote: { type: 'string' }, branch: { type: 'string' }, output: { type: 'string' } } },
|
|
61
77
|
},
|
|
62
78
|
{
|
|
63
79
|
name: 'git_pull',
|
|
64
|
-
description: 'Pull from remote and merge into current branch.',
|
|
80
|
+
description: 'Pull from remote and merge into current branch.' + ROOT_NOTE,
|
|
65
81
|
inputSchema: { type: 'object', properties: { remote: { type: 'string' }, branch: { type: 'string' }, cwd: { type: 'string' } } },
|
|
66
82
|
outputSchema: { type: 'object', properties: { success: { type: 'boolean' }, remote: { type: 'string' }, output: { type: 'string' } } },
|
|
67
83
|
},
|
|
68
84
|
{
|
|
69
85
|
name: 'git_branch',
|
|
70
|
-
description: 'List, create, or checkout branches.',
|
|
86
|
+
description: 'List, create, or checkout branches.' + ROOT_NOTE,
|
|
71
87
|
inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['list', 'create', 'checkout'] }, name: { type: 'string' }, cwd: { type: 'string' } } },
|
|
72
88
|
},
|
|
73
89
|
{
|
|
74
90
|
name: 'git_stash',
|
|
75
|
-
description: 'Stash or restore work-in-progress changes.',
|
|
91
|
+
description: 'Stash or restore work-in-progress changes.' + ROOT_NOTE,
|
|
76
92
|
inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['save', 'pop', 'list', 'drop'] }, message: { type: 'string' }, ref: { type: 'string' }, cwd: { type: 'string' } } },
|
|
77
93
|
},
|
|
78
94
|
{
|
|
79
95
|
name: 'git_reset',
|
|
80
|
-
description: 'Unstage files or reset HEAD. Use mode:file + path to restore a single file.',
|
|
96
|
+
description: 'Unstage files or reset HEAD. Use mode:file + path to restore a single file.' + ROOT_NOTE,
|
|
81
97
|
inputSchema: { type: 'object', properties: { mode: { type: 'string', enum: ['soft', 'mixed', 'hard', 'file'] }, path: { type: 'string' }, ref: { type: 'string' }, cwd: { type: 'string' } } },
|
|
82
98
|
},
|
|
83
99
|
{
|