@xelth/eck-snapshot 5.9.0 → 6.0.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 CHANGED
@@ -1,190 +1,71 @@
1
- # eck-snapshot
1
+ # 📸 eckSnapshot v6.0
2
2
 
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.
3
+ A specialized, AI-native CLI tool designed to create and restore single-file text snapshots of Git repositories. Optimized for providing full project context to Large Language Models (LLMs) and serving as the coordination hub for Multi-Agent AI Architectures.
4
4
 
5
- ```bash
6
- npm install -g @xelth/eck-snapshot
7
- ```
8
-
9
- ## Recommended AI Setup
10
-
11
- For best results, we recommend splitting roles between models:
5
+ ## 🌟 Key Features
12
6
 
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
7
+ * **🧠 Multi-Agent Protocol (Royal Court):** Built-in support for the "Royal Court" architecture. Delegate tasks from a Senior Architect (Claude/Gemini) to Junior Managers, who orchestrate a swarm of specialized GLM-4.7 workers.
8
+ * **☠️ Skeleton Mode:** Uses `Tree-sitter` and `Babel` to strip function bodies, drastically reducing token count while preserving structural context. Supports JS/TS, Rust, Go, Python, C, Java, and Kotlin.
9
+ * **🔄 Smart Delta Updates:** Tracks incremental changes via Git anchors with sequential numbering. Now accurately tracks and reports deleted files to prevent LLM hallucinations.
10
+ * **🛡️ Security (SecretScanner):** Automatically redacts API keys and credentials before sending context to LLMs. Features both Regex matching and **Shannon Entropy** analysis for catching non-standard hardcoded secrets.
11
+ * **📊 Telemetry Hub:** Integrated with a Rust-based microservice (`eck-telemetry`) for tracking agent execution metrics, auto-syncing token estimation weights via linear regression, and in-memory caching.
12
+ * **🔌 Native MCP Integration:** Instantly spins up Model Context Protocol (MCP) servers (`eck-core` for context sync and `glm-zai` for worker swarms) for Claude Code and OpenCode.
15
13
 
16
- eck-snapshot generates tailored instructions (`CLAUDE.md`, `AGENTS.md`) for each role automatically.
17
-
18
- ## Core Workflow
19
-
20
- ### 1. Full Snapshot
21
-
22
- Run `eck-snapshot` in your project root. It scans every tracked file, filters out noise (lock files, build artifacts, secrets), and produces a single `.md` file ready for an AI chat.
14
+ ## 🚀 Quick Start
23
15
 
16
+ ### Installation
24
17
  ```bash
25
- eck-snapshot
26
- # -> .eck/snapshots/eckMyProject_26-02-15_12-00_abc1234.md
27
- ```
28
-
29
- Upload the file to your architect AI and start working.
30
-
31
- ### 2. Incremental Update
32
-
33
- After you make changes, don't re-send the entire project. Send only what changed since the last full snapshot:
34
-
35
- ```bash
36
- eck-snapshot update
37
- # -> .eck/snapshots/eckMyProject_26-02-15_14-30_abc1234_up1_42kb.md
38
- ```
39
-
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.
41
-
42
- Notice the `_42kb` suffix in the filename — this is the **Anti-Truncation Guard** (see below).
43
-
44
- ## Context Profiles
45
-
46
- Large repositories waste tokens on irrelevant code. Profiles let you partition the codebase so the AI only sees what matters.
47
-
48
- ### Auto-Detection
49
-
50
- Let AI scan your directory tree and generate profiles automatically:
51
-
52
- ```bash
53
- eck-snapshot profile-detect
54
- # -> Saves profiles to .eck/profiles.json
55
- ```
56
-
57
- ### Manual Guide
58
-
59
- For very large repos where auto-detection is too slow, generate a prompt guide, paste it into a powerful Web LLM (Gemini, ChatGPT), and save the resulting JSON:
60
-
61
- ```bash
62
- eck-snapshot generate-profile-guide
63
- # -> .eck/profile_generation_guide.md (paste into AI, get profiles back)
64
- ```
65
-
66
- ### Using Profiles
67
-
68
- ```bash
69
- eck-snapshot --profile # List all available profiles
70
- eck-snapshot --profile backend # Use a named profile
71
- eck-snapshot --profile backend --skeleton # Profile + skeleton mode
72
- eck-snapshot --profile "src/**/*.rs,-**/test_*" # Ad-hoc glob filtering
73
- ```
74
-
75
- Profiles work with both full snapshots and incremental updates.
76
-
77
- ## Anti-Truncation Guard
78
-
79
- Web AI interfaces (Gemini, ChatGPT) sometimes silently drop the content of large uploaded files, replacing them with a small JSON metadata stub like `{"fileName": "...", "contentFetchId": "..."}`. The AI then hallucinates code it never actually received.
80
-
81
- eck-snapshot counters this with two mechanisms:
82
-
83
- **1. Size-stamped filenames.** Every snapshot — full and incremental — embeds its exact size in kilobytes directly in the filename:
84
-
85
- ```
86
- eckMyProject_26-02-15_12-00_abc1234_1250kb.md ← full snapshot
87
- eckMyProject_26-02-15_14-30_abc1234_up1_42kb.md ← incremental update
18
+ npm install -g @xelth/eck-snapshot
88
19
  ```
89
20
 
90
- **2. AI instructions baked into the snapshot.** The snapshot header instructs the AI to cross-check the `kb` value in the filename against the actual payload it received. If there is a mismatch, the AI is required to stop and alert you instead of hallucinating:
91
-
92
- > 🚨 **System Error:** The web interface truncated the file `eckMyProject_..._1250kb.md`. I only received the metadata/JSON stub, not the actual 1250 kb of code. Please split the snapshot or paste the text directly.
93
-
94
- This protection works on any web interface that embeds the filename (Gemini, ChatGPT, Grok, etc.).
95
-
96
- ## Smart Filtering
97
-
98
- eck-snapshot automatically detects your project type (Rust, Node.js, Android, Python, etc.) and excludes language-specific noise:
99
-
100
- - **Rust**: `Cargo.lock`, `target/`
101
- - **Node.js**: `package-lock.json`, `node_modules/`
102
- - **Android**: build artifacts, generated code
103
- - **All projects**: `.git/`, IDE configs, binary files
104
-
105
- The built-in `SecretScanner` also redacts API keys, tokens, and credentials before they reach the AI.
106
-
107
- ## Multi-Agent Architecture
108
-
109
- eck-snapshot generates tailored `CLAUDE.md` instructions for different AI agent roles:
110
-
21
+ ### Basic Usage
111
22
  ```bash
112
- eck-snapshot --jas # Junior Architect Sonnet - fast, standard features
113
- eck-snapshot --jao # Junior Architect Opus - deep, critical architecture
114
- eck-snapshot --jag # Junior Architect Gemini - massive context tasks
115
- ```
116
-
117
- ### Chinese Delegation (`--zh`)
23
+ # Create a standard full snapshot
24
+ eck-snapshot snapshot
118
25
 
119
- For GLM Z.AI workers (trained on Chinese data), the `--zh` flag instructs the architect to formulate all worker tasks in Chinese, improving output quality:
26
+ # Create a highly compressed skeleton snapshot
27
+ eck-snapshot snapshot --skeleton
120
28
 
121
- ```bash
122
- eck-snapshot --jas --zh # Claude Code: delegate to GLM workers in Chinese
123
- eck-snapshot --zh # OpenCode/GLM: generate AGENTS.md with Chinese protocol
29
+ # Create an incremental update (only changed/deleted files)
30
+ eck-snapshot update
124
31
  ```
125
32
 
126
- The architect still communicates with you in your language. Only the `instruction` parameter sent to GLM workers switches to Chinese. Code, variable names, and commit messages stay in English.
33
+ ## 🤖 AI Swarm Setup (GLM Z.AI)
127
34
 
128
- ### MCP Server Integration
35
+ eckSnapshot v6 acts as the bridge between your primary AI IDE (Claude Code or OpenCode) and a cost-effective GLM-4.7 worker swarm.
129
36
 
130
- Delegate coding tasks to the GLM Z.AI Worker Fleet via MCP:
37
+ 1. **Get an API Key:** Register at [Z.AI](https://z.ai) and set `export ZAI_API_KEY="your-key"`.
38
+ 2. **Setup MCP Servers:**
39
+ ```bash
40
+ eck-snapshot setup-mcp --both
41
+ ```
42
+ 3. **Initialize Project Manifests:**
43
+ ```bash
44
+ # Generates smart instructions (CLAUDE.md / AGENTS.md)
45
+ eck-snapshot snapshot --jas # For Claude Code (Sonnet 4.5)
46
+ eck-snapshot snapshot --jaz # For OpenCode (GLM-4.7)
47
+ ```
131
48
 
132
- ```bash
133
- export ZAI_API_KEY="your-key"
134
- eck-snapshot setup-mcp --both # Setup for Claude Code + OpenCode
135
- ```
49
+ ## 📁 The `.eck/` Manifest Directory
136
50
 
137
- 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.
51
+ eckSnapshot automatically maintains a `.eck/` directory in your project to provide deep context to AI agents:
52
+ - `CONTEXT.md` - High-level architecture (Auto-generated)
53
+ - `ENVIRONMENT.md` - Runtime specifics (Auto-generated)
54
+ - `ROADMAP.md` & `TECH_DEBT.md` - Strategic planning
55
+ - `RUNTIME_STATE.md` - Live port/process status
138
56
 
139
- ## Skeleton Mode & Lazy Loading
57
+ *Note: The tool automatically filters confidential files like `SERVER_ACCESS.md` from snapshots to ensure security.*
140
58
 
141
- For extremely large projects, skeleton mode strips function bodies and keeps only signatures, types, and structure:
59
+ ## 📈 Token Estimation
142
60
 
61
+ Train the local estimator to perfectly predict token counts for your specific project:
143
62
  ```bash
144
- eck-snapshot --skeleton
145
- ```
146
-
147
- When using skeleton mode, the AI can request full content of specific files on demand:
148
-
149
- ```bash
150
- eck-snapshot show src/auth.rs src/handlers/sync.rs
151
- ```
152
-
153
- Useful for initial orientation in massive codebases, but full snapshots with profiles are usually more practical.
154
-
155
- ## Other Commands
63
+ # Manually push agent telemetry
64
+ eck-snapshot telemetry push
156
65
 
157
- ```bash
158
- eck-snapshot restore <snapshot> # Restore files from a snapshot to disk
159
- eck-snapshot prune <snapshot> # AI-powered snapshot size reduction
160
- eck-snapshot doctor # Check project health
161
- eck-snapshot env push # Encrypt and sync .eck/ config between machines
162
- eck-snapshot env pull # Restore .eck/ config on another machine
66
+ # Sync global token weights from the Telemetry Hub
67
+ eck-snapshot telemetry sync-weights
163
68
  ```
164
69
 
165
- ## Changelog
166
-
167
- ### v5.8.6
168
- - **Anti-Truncation Guard:** Every snapshot filename now includes its size in KB (e.g., `_1250kb.md`). The snapshot header instructs the AI to verify the payload size matches the filename and alert the user if the web UI truncated the file instead of hallucinating missing code.
169
- - **Incremental snapshot filtering parity:** `eck-snapshot update` now applies the same file guards as full snapshots — hidden paths (`.idea/`, `.vscode/`), binary files, and glob patterns in `filesToIgnore` are all properly filtered out, preventing IDE config files or committed binaries from bloating update snapshots.
170
-
171
- ### v5.8.5
172
- - Re-publish to fix missing README on npmjs.com.
173
-
174
- ### v5.8.4
175
- - Fixed directory filtering in incremental snapshots. Paths like `web/build/app.js` are now correctly ignored when `build/` is in the ignore list.
176
-
177
- ### v5.8.3
178
- - Optimized agent report formatting with clean Markdown to improve token efficiency.
179
- - Fixed report injection order so it appears correctly after system instructions.
180
-
181
- ### v5.8.2
182
- - Fixed agent report injection in incremental snapshots. The `AnswerToSA.md` file is now preserved on disk for manual debugging and uses an internal `[SYSTEM: EMBEDDED]` marker to prevent duplicate injections into future snapshots.
183
-
184
- ### v5.8.1
185
- - Improved Android project parsing by ignoring boilerplate and vector graphics.
186
- - Removed duplicate `ecksnapshot` MCP server and fixed JSON parsing in `update-auto`.
187
-
188
70
  ## License
189
-
190
- MIT
71
+ MIT © xelth-com
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xelth/eck-snapshot",
3
- "version": "5.9.0",
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.",
3
+ "version": "6.0.0",
4
+ "description": "A powerful CLI tool to create and restore single-file text snapshots of Git repositories. Optimized for AI context, LLM workflows, and multi-agent Swarm coordination.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -24,8 +24,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
24
24
  return {
25
25
  tools: [
26
26
  {
27
- name: "eck_finish_task",
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.",
27
+ name: "eck_fail_task",
28
+ description: "Use this if you are stuck, blocked, or unable to complete the task. It saves your report to AnswerToSA.md and generates an emergency snapshot WITHOUT committing broken code. Do NOT use this if tests pass.",
29
+ inputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ status: {
33
+ type: "string",
34
+ description: "Detailed explanation of why you are blocked, what you tried, and what the Architect should know."
35
+ }
36
+ },
37
+ required: ["status"]
38
+ }
39
+ },
40
+ {
41
+ name: "eck_finish_task",
42
+ 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. WARNING: USE ONLY ONCE PER TASK WHEN 100% FINISHED. Do NOT use this for intermediate saves or testing during your debugging loop.",
29
43
  inputSchema: {
30
44
  type: "object",
31
45
  properties: {
@@ -46,6 +60,40 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
46
60
  });
47
61
 
48
62
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
63
+ if (request.params.name === "eck_fail_task") {
64
+ const { status } = request.params.arguments;
65
+ const workDir = process.cwd();
66
+
67
+ try {
68
+ const answerDir = path.join(workDir, '.eck', 'lastsnapshot');
69
+ await fs.mkdir(answerDir, { recursive: true });
70
+ await fs.writeFile(
71
+ path.join(answerDir, 'AnswerToSA.md'),
72
+ `# Agent Report (BLOCKED/FAILED)\n\n${status}\n`,
73
+ 'utf-8'
74
+ );
75
+
76
+ const cliPath = path.join(PROJECT_ROOT, "index.js");
77
+ const { stdout } = await execa("node", [cliPath, "update-auto", "--fail"], { cwd: workDir, timeout: 120000 });
78
+
79
+ let result;
80
+ try {
81
+ result = JSON.parse(stdout);
82
+ } catch (e) {
83
+ return { content: [{ type: "text", text: `⚠️ Task aborted, but snapshot update returned invalid JSON: ${stdout}` }] };
84
+ }
85
+
86
+ return {
87
+ content: [{
88
+ type: "text",
89
+ text: `🚨 Task marked as FAILED.\n📝 AnswerToSA.md updated\n📸 Emergency Snapshot: ${result.snapshot_file} (${result.files_count} files)`
90
+ }]
91
+ };
92
+ } catch (error) {
93
+ return { content: [{ type: "text", text: `❌ Error: ${error.message}\n${error.stderr || ''}` }], isError: true };
94
+ }
95
+ }
96
+
49
97
  if (request.params.name === "eck_finish_task") {
50
98
  const { status, message } = request.params.arguments;
51
99
  const workDir = process.cwd();
@@ -106,14 +154,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
106
154
  };
107
155
  });
108
156
 
109
- const transport = new StdioServerTransport();
110
- await server.connect(transport);
111
-
112
- // --- Graceful Shutdown Handler ---
113
- process.stdout.on('error', (err) => {
114
- if (err.code === 'EPIPE') {
115
- process.exit(0);
116
- }
117
- });
118
- process.on('SIGINT', () => process.exit(0));
119
- process.on('SIGTERM', () => process.exit(0));
157
+ const transport = new StdioServerTransport();
158
+ await server.connect(transport);
159
+
160
+ // --- Graceful Shutdown Handler ---
161
+ process.stdout.on('error', (err) => {
162
+ if (err.code === 'EPIPE') {
163
+ process.exit(0);
164
+ }
165
+ });
166
+ process.on('SIGINT', () => process.exit(0));
167
+ process.on('SIGTERM', () => process.exit(0));
package/src/cli/cli.js CHANGED
@@ -205,6 +205,7 @@ Quick --profile Examples:
205
205
  .description('Create a delta snapshot of changed files since the last full snapshot')
206
206
  .argument('[repoPath]', 'Path to the repository', process.cwd())
207
207
  .option('--config <path>', 'Configuration file path')
208
+ .option('-f, --fail', 'Create an emergency snapshot without git commit')
208
209
  .action(updateSnapshot);
209
210
 
210
211
  // Auto/Silent Update command for Agents
@@ -212,6 +213,7 @@ Quick --profile Examples:
212
213
  .command('update-auto')
213
214
  .description('Silent update for AI agents (JSON output)')
214
215
  .argument('[repoPath]', 'Path to the repository', process.cwd())
216
+ .option('-f, --fail', 'Create an emergency snapshot without git commit')
215
217
  .action(updateSnapshotJson);
216
218
 
217
219
  // Restore command
@@ -1,15 +1,16 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import ora from 'ora';
4
- import chalk from 'chalk';
5
- import isBinaryPath from 'is-binary-path';
6
- import { getGitAnchor, getChangedFiles } from '../../utils/gitUtils.js';
7
- import { loadSetupConfig } from '../../config.js';
8
- import { readFileWithSizeCheck, parseSize, formatSize, matchesPattern, loadGitignore, generateTimestamp, getShortRepoName } from '../../utils/fileUtils.js';
9
- import { detectProjectType, getProjectSpecificFiltering } from '../../utils/projectDetector.js';
10
- import { execa } from 'execa';
11
- import { fileURLToPath } from 'url';
12
- import { pushTelemetry } from '../../utils/telemetry.js';
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import isBinaryPath from 'is-binary-path';
6
+ import { getGitAnchor, getChangedFiles } from '../../utils/gitUtils.js';
7
+ import { loadSetupConfig } from '../../config.js';
8
+ import { readFileWithSizeCheck, parseSize, formatSize, matchesPattern, loadGitignore, generateTimestamp, getShortRepoName } from '../../utils/fileUtils.js';
9
+ import { detectProjectType, getProjectSpecificFiltering } from '../../utils/projectDetector.js';
10
+ import { execa } from 'execa';
11
+ import { fileURLToPath } from 'url';
12
+ import { pushTelemetry } from '../../utils/telemetry.js';
13
+ import { syncTokenWeights } from '../../utils/tokenEstimator.js';
13
14
 
14
15
  // Mirror the same hidden-path guard used in createSnapshot.js
15
16
  function isHiddenPath(filePath) {
@@ -42,52 +43,95 @@ async function generateSnapshotContent(repoPath, changedFiles, anchor, config, g
42
43
  let includedCount = 0;
43
44
  const fileList = [];
44
45
 
45
- // Include Agent Report if it exists and hasn't been embedded yet
46
- let agentReport = null;
47
- const reportPath = path.join(repoPath, '.eck', 'lastsnapshot', 'AnswerToSA.md');
48
- try {
49
- const reportContent = await fs.readFile(reportPath, 'utf-8');
50
- if (!reportContent.includes('[SYSTEM: EMBEDDED]')) {
51
- agentReport = reportContent;
52
- await fs.appendFile(reportPath, '\n\n[SYSTEM: EMBEDDED]\n', 'utf-8');
53
- }
54
- } catch (e) { /* File not found or unreadable */ }
46
+ // Include Agent Report if it exists and hasn't been embedded yet
47
+ let agentReport = null;
48
+ const reportPath = path.join(repoPath, '.eck', 'lastsnapshot', 'AnswerToSA.md');
49
+ const lockPath = path.join(repoPath, '.eck', 'lastsnapshot', 'AnswerToSA.lock');
50
+ try {
51
+ // Use atomic directory creation as a lock to prevent race conditions
52
+ await fs.mkdir(lockPath);
53
+ const reportContent = await fs.readFile(reportPath, 'utf-8');
54
+
55
+ if (!reportContent.includes('[SYSTEM: EMBEDDED]')) {
56
+ agentReport = reportContent;
57
+
58
+ // Immediately mark as embedded to release the race window
59
+ await fs.appendFile(reportPath, '\n\n[SYSTEM: EMBEDDED]\n', 'utf-8');
60
+
61
+ // Auto-Journaling: prepend agent report to JOURNAL.md
62
+ const journalPath = path.join(repoPath, '.eck', 'JOURNAL.md');
63
+ try {
64
+ const dateStr = new Date().toISOString().split('T')[0];
65
+ const journalEntry = `## ${dateStr} — Agent Report\n\n${reportContent.trim()}\n`;
66
+
67
+ let existingJournal = '';
68
+ try {
69
+ existingJournal = await fs.readFile(journalPath, 'utf-8');
70
+ } catch (e) { /* might not exist */ }
71
+
72
+ const insertPos = existingJournal.indexOf('\n## ');
73
+ if (insertPos !== -1) {
74
+ const newJournal = existingJournal.slice(0, insertPos) + '\n\n' + journalEntry + existingJournal.slice(insertPos);
75
+ await fs.writeFile(journalPath, newJournal, 'utf-8');
76
+ } else {
77
+ await fs.writeFile(journalPath, (existingJournal ? existingJournal + '\n\n' : '') + journalEntry + '\n', 'utf-8');
78
+ }
79
+ } catch (je) {
80
+ console.warn('Could not auto-update JOURNAL.md', je.message);
81
+ }
82
+ }
83
+ await fs.rmdir(lockPath);
84
+ } catch (e) {
85
+ // File not found or locked by another process
86
+ try { await fs.rmdir(lockPath); } catch (_) {}
87
+ }
55
88
 
56
89
  const cleanDirsToIgnore = (config.dirsToIgnore || []).map(d => d.replace(/\/$/, ''));
57
90
 
58
- for (const filePath of changedFiles) {
59
- const normalizedPath = filePath.replace(/\\/g, '/');
60
-
61
- // Skip hidden paths (.idea/, .vscode/, etc.) — mirrors createSnapshot.js
62
- if (isHiddenPath(normalizedPath)) continue;
63
-
64
- // Skip binary files — mirrors createSnapshot.js
65
- if (isBinaryPath(filePath)) continue;
66
-
67
- const pathParts = normalizedPath.split('/');
68
- let isIgnoredDir = false;
69
- for (let i = 0; i < pathParts.length - 1; i++) {
70
- if (cleanDirsToIgnore.includes(pathParts[i])) {
71
- isIgnoredDir = true;
72
- break;
73
- }
74
- }
75
- if (isIgnoredDir) continue;
76
-
77
- const fileExt = path.extname(filePath);
78
- // Use matchesPattern (glob support) instead of exact includes() — mirrors createSnapshot.js
79
- if (config.filesToIgnore && matchesPattern(normalizedPath, config.filesToIgnore)) continue;
80
- if (fileExt && config.extensionsToIgnore?.includes(fileExt)) continue;
81
- if (gitignore.ignores(normalizedPath)) continue;
82
-
83
- try {
84
- const fullPath = path.join(repoPath, filePath);
85
- const content = await readFileWithSizeCheck(fullPath, parseSize(config.maxFileSize));
86
- contentOutput += `--- File: /${normalizedPath} ---\n\n${content}\n\n`;
87
- fileList.push(`- ${normalizedPath}`);
88
- includedCount++;
89
- } catch (e) { /* Skip */ }
90
- }
91
+ for (const filePath of changedFiles) {
92
+ const normalizedPath = filePath.replace(/\\/g, '/');
93
+
94
+ // Skip hidden paths (.idea/, .vscode/, etc.) — mirrors createSnapshot.js
95
+ if (isHiddenPath(normalizedPath)) continue;
96
+
97
+ // Skip binary files — mirrors createSnapshot.js
98
+ if (isBinaryPath(filePath)) continue;
99
+
100
+ const pathParts = normalizedPath.split('/');
101
+ let isIgnoredDir = false;
102
+ for (let i = 0; i < pathParts.length - 1; i++) {
103
+ if (cleanDirsToIgnore.includes(pathParts[i])) {
104
+ isIgnoredDir = true;
105
+ break;
106
+ }
107
+ }
108
+ if (isIgnoredDir) continue;
109
+
110
+ const fileExt = path.extname(filePath);
111
+ // Use matchesPattern (glob support) instead of exact includes() — mirrors createSnapshot.js
112
+ if (config.filesToIgnore && matchesPattern(normalizedPath, config.filesToIgnore)) continue;
113
+ if (fileExt && config.extensionsToIgnore?.includes(fileExt)) continue;
114
+ if (gitignore.ignores(normalizedPath)) continue;
115
+
116
+ try {
117
+ const fullPath = path.join(repoPath, filePath);
118
+
119
+ // Explicitly check if file was deleted
120
+ try {
121
+ await fs.access(fullPath);
122
+ } catch (accessErr) {
123
+ contentOutput += `--- File: /${normalizedPath} ---\n\n[FILE DELETED]\n\n`;
124
+ fileList.push(`- ${normalizedPath} (Deleted)`);
125
+ includedCount++;
126
+ continue;
127
+ }
128
+
129
+ const content = await readFileWithSizeCheck(fullPath, parseSize(config.maxFileSize));
130
+ contentOutput += `--- File: /${normalizedPath} ---\n\n${content}\n\n`;
131
+ fileList.push(`- ${normalizedPath} (Modified/Added)`);
132
+ includedCount++;
133
+ } catch (e) { /* Skip */ }
134
+ }
91
135
 
92
136
  // Load Template
93
137
  const templatePath = path.join(__dirname, '../../templates/update-prompt.template.md');
@@ -122,17 +166,22 @@ export async function updateSnapshot(repoPath, options) {
122
166
  }
123
167
 
124
168
  // Auto-commit any uncommitted changes so they appear in the diff
125
- const didCommit = await autoCommit(repoPath);
126
- if (didCommit) {
127
- spinner.info('Auto-committed uncommitted changes.');
128
- spinner.start('Generating update snapshot...');
169
+ let didCommit = false;
170
+ if (!options.fail) {
171
+ didCommit = await autoCommit(repoPath);
172
+ if (didCommit) {
173
+ spinner.info('Auto-committed uncommitted changes.');
174
+ }
175
+ } else {
176
+ spinner.info('Fail flag passed: skipping auto-commit.');
129
177
  }
178
+ spinner.start('Generating update snapshot...');
130
179
 
131
- const changedFiles = await getChangedFiles(repoPath, anchor);
132
- if (changedFiles.length === 0) {
133
- spinner.succeed('No changes detected since last full snapshot.');
134
- return;
135
- }
180
+ const changedFiles = await getChangedFiles(repoPath, anchor, options.fail);
181
+ if (changedFiles.length === 0) {
182
+ spinner.succeed('No changes detected since last full snapshot.');
183
+ return;
184
+ }
136
185
 
137
186
  const setupConfig = await loadSetupConfig();
138
187
  let config = { ...setupConfig.fileFiltering, ...setupConfig.performance, ...options };
@@ -216,7 +265,7 @@ export async function updateSnapshot(repoPath, options) {
216
265
  }
217
266
 
218
267
  // New Silent/JSON command for Agents
219
- export async function updateSnapshotJson(repoPath) {
268
+ export async function updateSnapshotJson(repoPath, options = {}) {
220
269
  try {
221
270
  const anchor = await getGitAnchor(repoPath);
222
271
  if (!anchor) {
@@ -225,9 +274,11 @@ export async function updateSnapshotJson(repoPath) {
225
274
  }
226
275
 
227
276
  // Auto-commit any uncommitted changes
228
- await autoCommit(repoPath);
277
+ if (!options.fail) {
278
+ await autoCommit(repoPath);
279
+ }
229
280
 
230
- const changedFiles = await getChangedFiles(repoPath, anchor);
281
+ const changedFiles = await getChangedFiles(repoPath, anchor, !!options.fail);
231
282
  if (changedFiles.length === 0) {
232
283
  console.log(JSON.stringify({ status: "no_changes", message: "No changes detected" }));
233
284
  return;
@@ -294,15 +345,16 @@ export async function updateSnapshotJson(repoPath) {
294
345
  }
295
346
  // --------------------------------------------
296
347
 
297
- console.log(JSON.stringify({
298
- status: "success",
299
- snapshot_file: `.eck/snapshots/${outputFilename}`,
300
- files_count: includedCount,
301
- timestamp: timestamp
302
- }));
303
-
304
- // Auto-push telemetry (fire and forget so it doesn't break JSON output)
305
- pushTelemetry(repoPath, true).catch(() => {});
348
+ console.log(JSON.stringify({
349
+ status: "success",
350
+ snapshot_file: `.eck/snapshots/${outputFilename}`,
351
+ files_count: includedCount,
352
+ timestamp: timestamp
353
+ }));
354
+
355
+ // Auto-push telemetry and sync weights (fire and forget so it doesn't break JSON output)
356
+ pushTelemetry(repoPath, true).catch(() => {});
357
+ syncTokenWeights(true).catch(() => {});
306
358
 
307
359
  } catch (error) {
308
360
  console.log(JSON.stringify({ status: "error", message: error.message }));
@@ -4,18 +4,25 @@
4
4
  You are an Expert Developer. The architecture is already decided. Your job is to **execute**, **fix**, and **polish**.
5
5
 
6
6
  ## DEFINITION OF DONE (CRITICAL)
7
- When the task is complete:
8
- 1. **WRITE** your report to \`.eck/lastsnapshot/AnswerToSA.md\` (overwrite, not append). Use this exact format:
9
- ```markdown
10
- # Report: [Task Name]
11
- **Executor:** [Your Exact Model Name, e.g., GLM-4.7 (OpenCode)]
12
- **Status:** [SUCCESS / BLOCKED / FAILED]
13
- **Changes:**
14
- - Modified X
15
- ```
16
- 2. **Use the \`eck_finish_task\` tool** to commit and sync context.
17
- - This tool automatically creates a git commit and generates a delta snapshot
18
- 3. **DO NOT** use raw git commands for the final commit.
7
+ When task is complete, you must report back and sync context.
8
+
9
+ **OPTION A: Using MCP Tool (Recommended)**
10
+ Call the \`eck_finish_task\` tool. Pass your detailed markdown report into the \`status\` argument.
11
+ - The tool will automatically write the report to \`AnswerToSA.md\`, commit, and generate a snapshot.
12
+ - **DO NOT** manually write to \`AnswerToSA.md\` with your file editing tools (it will fail safety checks).
13
+ - **WARNING: USE ONLY ONCE.** Do not use \`eck_finish_task\` for intermediate testing. It spams snapshot history.
14
+
15
+ **OPTION B: Manual CLI (Fallback)**
16
+ If the MCP tool is unavailable:
17
+ 1. **READ** \`.eck/lastsnapshot/AnswerToSA.md\` using your \`Read\` tool (REQUIRED by safety rules before overwriting).
18
+ 2. **WRITE** your report to that file.
19
+ 3. Run \`eck-snapshot update\` in terminal.
20
+
21
+ ## PROJECT CONTEXT (.eck DIRECTORY)
22
+ The `.eck/` directory contains critical project documentation. **Before starting your task, you MUST:**
23
+ 1. List the files in the `.eck/` directory.
24
+ 2. Read any files that might be relevant to your task based on their names (e.g., `CONTEXT.md`, `TECH_DEBT.md`, `OPERATIONS.md`).
25
+ 3. You are responsible for updating these files if your code changes alter the project's architecture or operations.
19
26
 
20
27
  ## CONTEXT
21
28
  - The GLM ZAI swarm might have struggled or produced code that needs refinement.
@@ -23,7 +30,9 @@ When the task is complete:
23
30
  - You have full permission to edit files directly.
24
31
 
25
32
  ## WORKFLOW
26
- 1. Read the code.
27
- 2. Fix the bugs / Implement the feature.
28
- 3. Verify functionality (Run tests!).
29
- 4. **Loop:** If verification fails, fix it immediately. Do not ask for permission.
33
+ 1. Check the `.eck/RUNTIME_STATE.md` and verify actual running processes.
34
+ 2. Read the code. If the Architect's hypothesis is wrong, discard it and find the real bug.
35
+ 3. Fix the bugs / Implement the feature.
36
+ 4. Verify functionality manually via browser/curl/logs/DB checks.
37
+ 5. **Loop:** If verification fails, fix it immediately. Do not ask for permission.
38
+ 6. **Blocked?** Use the `eck_fail_task` tool to abort safely without committing broken code.