@xelth/eck-snapshot 5.4.2 → 5.5.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 +18 -11
- package/package.json +1 -1
- package/scripts/mcp-eck-core.js +29 -20
- package/src/cli/commands/updateSnapshot.js +12 -8
- package/src/mcp-server/index.js +12 -22
package/README.md
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
# eck-snapshot
|
|
2
2
|
|
|
3
|
-
A CLI tool that packs your entire Git repository into a single text file optimized for LLMs
|
|
3
|
+
A CLI tool that packs your entire Git repository into a single text file optimized for LLMs. Give any AI full project context in one copy-paste.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install -g @xelth/eck-snapshot
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
## Recommended AI Setup
|
|
10
|
+
|
|
11
|
+
For best results, we recommend splitting roles between models:
|
|
12
|
+
|
|
13
|
+
- **Architect** (large context window): Gemini, Grok Fast, ChatGPT — upload the full snapshot, design the architecture, plan tasks
|
|
14
|
+
- **Coder** (execution): Claude (via Claude Code), GLM (via OpenCode) — receive tasks from the architect, write and fix code
|
|
15
|
+
|
|
16
|
+
eck-snapshot generates tailored instructions (`CLAUDE.md`, `AGENTS.md`) for each role automatically.
|
|
17
|
+
|
|
9
18
|
## Core Workflow
|
|
10
19
|
|
|
11
20
|
### 1. Full Snapshot
|
|
@@ -17,7 +26,7 @@ eck-snapshot
|
|
|
17
26
|
# -> .eck/snapshots/eckMyProject_26-02-15_12-00_abc1234.md
|
|
18
27
|
```
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
Upload the file to your architect AI and start working.
|
|
21
30
|
|
|
22
31
|
### 2. Incremental Update
|
|
23
32
|
|
|
@@ -30,14 +39,6 @@ eck-snapshot update
|
|
|
30
39
|
|
|
31
40
|
This uses a Git anchor (saved automatically during full snapshot) to detect all modified files and includes their full content. No redundant diffs, no wasted tokens.
|
|
32
41
|
|
|
33
|
-
### 3. Lazy Loading
|
|
34
|
-
|
|
35
|
-
If the AI asks to see specific files that weren't in the snapshot, show them instantly:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
eck-snapshot show src/auth.rs src/handlers/sync.rs
|
|
39
|
-
```
|
|
40
|
-
|
|
41
42
|
## Context Profiles
|
|
42
43
|
|
|
43
44
|
Large repositories waste tokens on irrelevant code. Profiles let you partition the codebase so the AI only sees what matters.
|
|
@@ -103,7 +104,7 @@ eck-snapshot setup-mcp --both # Setup for Claude Code + OpenCode
|
|
|
103
104
|
|
|
104
105
|
This gives your AI access to specialized workers: `glm_zai_frontend`, `glm_zai_backend`, `glm_zai_qa`, `glm_zai_refactor`, and the `eck_finish_task` commit tool.
|
|
105
106
|
|
|
106
|
-
## Skeleton Mode
|
|
107
|
+
## Skeleton Mode & Lazy Loading
|
|
107
108
|
|
|
108
109
|
For extremely large projects, skeleton mode strips function bodies and keeps only signatures, types, and structure:
|
|
109
110
|
|
|
@@ -111,6 +112,12 @@ For extremely large projects, skeleton mode strips function bodies and keeps onl
|
|
|
111
112
|
eck-snapshot --skeleton
|
|
112
113
|
```
|
|
113
114
|
|
|
115
|
+
When using skeleton mode, the AI can request full content of specific files on demand:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
eck-snapshot show src/auth.rs src/handlers/sync.rs
|
|
119
|
+
```
|
|
120
|
+
|
|
114
121
|
Useful for initial orientation in massive codebases, but full snapshots with profiles are usually more practical.
|
|
115
122
|
|
|
116
123
|
## Other Commands
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xelth/eck-snapshot",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "A powerful CLI tool to create and restore single-file text snapshots of Git repositories and directories. Optimized for AI context and LLM workflows.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
package/scripts/mcp-eck-core.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* MCP Eck Core -
|
|
3
|
+
* MCP Eck Core - Unified task completion tool for Claude Code and OpenCode
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
9
9
|
import { execa } from "execa";
|
|
10
|
+
import fs from "fs/promises";
|
|
10
11
|
import path from "path";
|
|
11
12
|
import { fileURLToPath } from 'url';
|
|
12
13
|
|
|
@@ -15,7 +16,7 @@ const __dirname = path.dirname(__filename);
|
|
|
15
16
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
16
17
|
|
|
17
18
|
const server = new Server(
|
|
18
|
-
{ name: "eck-core", version: "
|
|
19
|
+
{ name: "eck-core", version: "2.0.0" },
|
|
19
20
|
{ capabilities: { tools: {} } }
|
|
20
21
|
);
|
|
21
22
|
|
|
@@ -24,16 +25,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
24
25
|
tools: [
|
|
25
26
|
{
|
|
26
27
|
name: "eck_finish_task",
|
|
27
|
-
description: "Completes the current coding task. 1) Stages all changes.
|
|
28
|
+
description: "Completes the current coding task. 1) Overwrites AnswerToSA.md with status for the Architect. 2) Stages all changes. 3) Commits with the provided message. 4) Automatically updates the context snapshot. Use this instead of manual git commands.",
|
|
28
29
|
inputSchema: {
|
|
29
30
|
type: "object",
|
|
30
31
|
properties: {
|
|
32
|
+
status: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Status update for the Architect: what was done, what issues remain, what needs review"
|
|
35
|
+
},
|
|
31
36
|
message: {
|
|
32
37
|
type: "string",
|
|
33
38
|
description: "Git commit message (follow Conventional Commits, e.g. 'feat: add login')"
|
|
34
39
|
}
|
|
35
40
|
},
|
|
36
|
-
required: ["message"]
|
|
41
|
+
required: ["status", "message"]
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
]
|
|
@@ -42,20 +47,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
42
47
|
|
|
43
48
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
44
49
|
if (request.params.name === "eck_finish_task") {
|
|
45
|
-
const { message } = request.params.arguments;
|
|
50
|
+
const { status, message } = request.params.arguments;
|
|
51
|
+
const workDir = process.cwd();
|
|
46
52
|
|
|
47
53
|
try {
|
|
48
|
-
// 1.
|
|
49
|
-
|
|
54
|
+
// 1. Write fresh AnswerToSA.md (OVERWRITE, not append)
|
|
55
|
+
const answerDir = path.join(workDir, '.eck', 'lastsnapshot');
|
|
56
|
+
await fs.mkdir(answerDir, { recursive: true });
|
|
57
|
+
await fs.writeFile(
|
|
58
|
+
path.join(answerDir, 'AnswerToSA.md'),
|
|
59
|
+
`# Agent Report\n\n${status}\n`,
|
|
60
|
+
'utf-8'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// 2. Git Add
|
|
64
|
+
await execa("git", ["add", "."], { cwd: workDir, timeout: 30000 });
|
|
50
65
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
await execa("git", ["commit", "--allow-empty", "-m", message], { cwd: process.cwd(), timeout: 30000 });
|
|
66
|
+
// 3. Git Commit
|
|
67
|
+
await execa("git", ["commit", "--allow-empty", "-m", message], { cwd: workDir, timeout: 30000 });
|
|
54
68
|
|
|
55
|
-
//
|
|
56
|
-
// We use the local index.js to ensure we use the current version of the tool
|
|
69
|
+
// 4. Auto Update Snapshot
|
|
57
70
|
const cliPath = path.join(PROJECT_ROOT, "index.js");
|
|
58
|
-
const { stdout } = await execa("node", [cliPath, "update-auto"], { cwd:
|
|
71
|
+
const { stdout } = await execa("node", [cliPath, "update-auto"], { cwd: workDir, timeout: 120000 });
|
|
59
72
|
|
|
60
73
|
let result;
|
|
61
74
|
try {
|
|
@@ -70,22 +83,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
70
83
|
return {
|
|
71
84
|
content: [{
|
|
72
85
|
type: "text",
|
|
73
|
-
text: `✅
|
|
86
|
+
text: `✅ Committed: "${message}"\n📝 AnswerToSA.md updated\n📸 Snapshot: ${result.snapshot_file} (${result.files_count} files)`
|
|
74
87
|
}]
|
|
75
88
|
};
|
|
76
|
-
} else if (result.status === "no_changes") {
|
|
77
|
-
return {
|
|
78
|
-
content: [{ type: "text", text: `✅ Committed: "${message}"\nℹ️ No new changes for snapshot update.` }]
|
|
79
|
-
};
|
|
80
89
|
} else {
|
|
81
90
|
return {
|
|
82
|
-
content: [{ type: "text", text: `✅ Committed: "${message}"\n
|
|
91
|
+
content: [{ type: "text", text: `✅ Committed: "${message}"\nℹ️ Snapshot: ${result.message}` }]
|
|
83
92
|
};
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
} catch (error) {
|
|
87
96
|
return {
|
|
88
|
-
content: [{ type: "text", text: `❌ Error
|
|
97
|
+
content: [{ type: "text", text: `❌ Error: ${error.message}\n${error.stderr || ''}` }],
|
|
89
98
|
isError: true
|
|
90
99
|
};
|
|
91
100
|
}
|
|
@@ -17,15 +17,19 @@ async function generateSnapshotContent(repoPath, changedFiles, anchor, config, g
|
|
|
17
17
|
let includedCount = 0;
|
|
18
18
|
const fileList = [];
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
const
|
|
20
|
+
// Include Agent Report ONLY if it was modified in this commit cycle
|
|
21
|
+
const reportRelPath = '.eck/lastsnapshot/AnswerToSA.md';
|
|
22
|
+
const reportChanged = changedFiles.some(f =>
|
|
23
|
+
f === reportRelPath || f === reportRelPath.replace(/\//g, path.sep)
|
|
24
|
+
);
|
|
25
|
+
|
|
22
26
|
let agentReport = null;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
27
|
+
if (reportChanged) {
|
|
28
|
+
try {
|
|
29
|
+
const reportPath = path.join(repoPath, '.eck', 'lastsnapshot', 'AnswerToSA.md');
|
|
30
|
+
agentReport = await fs.readFile(reportPath, 'utf-8');
|
|
31
|
+
} catch (e) { /* Failed to read */ }
|
|
32
|
+
}
|
|
29
33
|
|
|
30
34
|
for (const filePath of changedFiles) {
|
|
31
35
|
if (config.dirsToIgnore?.some(d => filePath.startsWith(d))) continue;
|
package/src/mcp-server/index.js
CHANGED
|
@@ -56,14 +56,14 @@ class EckSnapshotMCPServer {
|
|
|
56
56
|
properties: {
|
|
57
57
|
status: {
|
|
58
58
|
type: 'string',
|
|
59
|
-
description: 'Status update
|
|
59
|
+
description: 'Status update for the Architect: what was done, what issues remain, what needs review',
|
|
60
60
|
},
|
|
61
|
-
|
|
61
|
+
message: {
|
|
62
62
|
type: 'string',
|
|
63
|
-
description: 'Git commit message
|
|
63
|
+
description: 'Git commit message (follow Conventional Commits, e.g. "feat: add login")',
|
|
64
64
|
},
|
|
65
65
|
},
|
|
66
|
-
required: ['status', '
|
|
66
|
+
required: ['status', 'message'],
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
69
|
],
|
|
@@ -75,10 +75,12 @@ class EckSnapshotMCPServer {
|
|
|
75
75
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
const { status,
|
|
78
|
+
const { status, message } = request.params.arguments;
|
|
79
|
+
// Support legacy 'commitMessage' parameter
|
|
80
|
+
const commitMessage = message || request.params.arguments.commitMessage;
|
|
79
81
|
|
|
80
82
|
if (!status || !commitMessage) {
|
|
81
|
-
throw new Error('Missing required arguments: status and
|
|
83
|
+
throw new Error('Missing required arguments: status and message are required');
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
try {
|
|
@@ -111,23 +113,11 @@ class EckSnapshotMCPServer {
|
|
|
111
113
|
|
|
112
114
|
const steps = [];
|
|
113
115
|
|
|
114
|
-
// Step 1:
|
|
116
|
+
// Step 1: Write AnswerToSA.md (OVERWRITE, not append)
|
|
115
117
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Check if file exists
|
|
120
|
-
try {
|
|
121
|
-
await fs.access(answerFilePath);
|
|
122
|
-
// Append to existing file
|
|
123
|
-
await fs.appendFile(answerFilePath, updateContent);
|
|
124
|
-
steps.push({ step: 'update_answer', success: true, message: 'Updated AnswerToSA.md' });
|
|
125
|
-
} catch {
|
|
126
|
-
// Create directory and file if not exists
|
|
127
|
-
await fs.mkdir(path.dirname(answerFilePath), { recursive: true });
|
|
128
|
-
await fs.writeFile(answerFilePath, `# Task Status\n${updateContent}`);
|
|
129
|
-
steps.push({ step: 'update_answer', success: true, message: 'Created AnswerToSA.md' });
|
|
130
|
-
}
|
|
118
|
+
await fs.mkdir(path.dirname(answerFilePath), { recursive: true });
|
|
119
|
+
await fs.writeFile(answerFilePath, `# Agent Report\n\n${status}\n`, 'utf-8');
|
|
120
|
+
steps.push({ step: 'update_answer', success: true, message: 'Updated AnswerToSA.md' });
|
|
131
121
|
} catch (error) {
|
|
132
122
|
steps.push({ step: 'update_answer', success: false, error: error.message });
|
|
133
123
|
throw new Error(`Failed to update AnswerToSA.md: ${error.message}`);
|