@xelth/eck-snapshot 6.5.1 โ†’ 6.7.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,4 +1,4 @@
1
- # ๐Ÿ“ธ eckSnapshot v6.5.0 (AI-Native Edition)
1
+ # ๐Ÿ“ธ eckSnapshot v6.7.0 (AI-Native Edition)
2
2
 
3
3
  A specialized, AI-native CLI tool that creates single-file text snapshots of entire Git repositories and feeds them directly into LLM context windows. Instead of letting AI agents guess which files to read, eckSnapshot force-feeds the complete project into the model's context โ€” giving it a "university degree" in your codebase from the very first prompt.
4
4
 
@@ -200,6 +200,7 @@ By analyzing Claude Code's internal architecture, eckSnapshot replaces the old m
200
200
  * **๐Ÿ“ The `.eck/` Manifest:** Automatically maintains project context files (`CONTEXT.md`, `ROADMAP.md`, `TECH_DEBT.md`). Dynamic scanning โ€” any `.md` file you add to `.eck/` is automatically included in snapshots.
201
201
  * **โ˜ ๏ธ Skeleton Mode:** Uses Tree-sitter and Babel to strip function bodies, drastically reducing token count for huge codebases.
202
202
  * **๐Ÿ“š NotebookLM Export:** Semantic chunking for Google's NotebookLM with "Brain + Body" architecture (see below).
203
+ * **๐Ÿงช ML Model Compatibility:** Smart metadata extraction for `.safetensors`, `.onnx`, `.pt`, `.pth`, `.h5`, `.pb`, `.bin`, `.ckpt`, `.gguf` โ€” reads the first 4KB header instead of loading multi-GB weights into memory.
203
204
  * **๐Ÿง  Multi-Agent Protocol:** Junior Architect delegation system for multi-agent coding workflows (see below).
204
205
 
205
206
  ### ๐Ÿค– Autonomous AI Protocols
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xelth/eck-snapshot",
3
- "version": "6.5.1",
3
+ "version": "6.7.0",
4
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
  "keywords": [
6
6
  "ai",
package/setup.json CHANGED
@@ -164,6 +164,19 @@
164
164
  ],
165
165
  "priority": 7
166
166
  },
167
+ "esp-idf": {
168
+ "files": [
169
+ "sdkconfig.defaults",
170
+ "partitions.csv"
171
+ ],
172
+ "directories": [
173
+ "managed_components"
174
+ ],
175
+ "manifestFiles": [
176
+ "idf_component.yml"
177
+ ],
178
+ "priority": 8
179
+ },
167
180
  "c": {
168
181
  "files": [
169
182
  "Makefile",
@@ -518,6 +531,27 @@
518
531
  "packages/"
519
532
  ]
520
533
  },
534
+ "esp-idf": {
535
+ "filesToIgnore": [
536
+ "sdkconfig.old",
537
+ "dependencies.lock"
538
+ ],
539
+ "dirsToIgnore": [
540
+ "managed_components/",
541
+ "build/",
542
+ "managed_components/*/examples/",
543
+ "managed_components/*/test/",
544
+ "managed_components/*/test_apps/",
545
+ "managed_components/*/docs/",
546
+ "managed_components/*/hw/"
547
+ ],
548
+ "extensionsToIgnore": [
549
+ ".bin",
550
+ ".elf",
551
+ ".map",
552
+ ".flash"
553
+ ]
554
+ },
521
555
  "c": {
522
556
  "filesToIgnore": [
523
557
  "*.o",
package/src/cli/cli.js CHANGED
@@ -6,6 +6,7 @@ import chalk from 'chalk';
6
6
  import { createRequire } from 'module';
7
7
  import os from 'os';
8
8
  import crypto from 'crypto';
9
+ import { ensureSnapshotsInGitignore } from '../utils/fileUtils.js';
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
@@ -48,17 +49,17 @@ const LEGACY_COMMANDS = {
48
49
  const base = baseIdx !== -1 && args[baseIdx + 1] ? args[baseIdx + 1] : undefined;
49
50
  return { name: 'eck_update_auto', arguments: { fail: args.includes('--fail') || args.includes('-f'), base } };
50
51
  },
51
- 'snapshot': () => ({ name: 'eck_snapshot', arguments: {} }),
52
+ 'snapshot': (args) => ({ name: 'eck_snapshot', arguments: { ml: args.includes('--ml') } }),
52
53
  'update': (args) => {
53
54
  const baseIdx = args.indexOf('--base');
54
55
  const base = baseIdx !== -1 && args[baseIdx + 1] ? args[baseIdx + 1] : undefined;
55
- return { name: 'eck_update', arguments: { fail: args.includes('--fail') || args.includes('-f'), base } };
56
+ return { name: 'eck_update', arguments: { fail: args.includes('--fail') || args.includes('-f'), base, ml: args.includes('--ml') } };
56
57
  },
57
58
  'setup-mcp': (args) => ({ name: 'eck_setup_mcp', arguments: { opencode: args.includes('--opencode'), both: args.includes('--both') } }),
58
59
  'detect': () => ({ name: 'eck_detect', arguments: {} }),
59
60
  'doctor': () => ({ name: 'eck_doctor', arguments: {} }),
60
- 'scout': (args) => ({ name: 'eck_scout', arguments: { depth: args[0] !== undefined ? parseInt(args[0], 10) : 0 } }),
61
- 'fetch': (args) => ({ name: 'eck_fetch', arguments: { patterns: args } }),
61
+ 'scout': (args) => ({ name: 'eck_scout', arguments: { depth: args[0] !== undefined ? parseInt(args[0], 10) : 0, ml: args.includes('--ml') } }),
62
+ 'fetch': (args) => ({ name: 'eck_fetch', arguments: { patterns: args.filter(a => a !== '--ml'), ml: args.includes('--ml') } }),
62
63
  'link': (args) => ({ name: 'eck_snapshot', arguments: { isLinkedProject: true, linkDepth: args[0] !== undefined ? parseInt(args[0], 10) : 0 } }),
63
64
  'profile': (args) => args[0] ? ({ name: 'eck_snapshot', arguments: { profile: args.join(',') } }) : ({ name: 'eck_snapshot', arguments: { profile: true } }),
64
65
  'booklm': () => ({ name: 'eck_snapshot', arguments: { notebooklm: 'scout' } }),
@@ -163,6 +164,7 @@ Ranked by frequency of use:
163
164
  queue.feedback.push({ type, message: msg, date: new Date().toISOString() });
164
165
 
165
166
  await fs.mkdir(path.dirname(queuePath), { recursive: true }).catch(() => {});
167
+ await ensureSnapshotsInGitignore(process.cwd()).catch(() => {});
166
168
  await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
167
169
 
168
170
  console.log(chalk.green('Feedback saved locally. It will be sent to developers during the next telemetry sync.'));
@@ -218,6 +220,7 @@ Ranked by frequency of use:
218
220
  } catch(e) { /* no existing queue */ }
219
221
  queue.usage[toolName] = (queue.usage[toolName] || 0) + 1;
220
222
  await fs.mkdir(path.dirname(queuePath), { recursive: true }).catch(() => {});
223
+ await ensureSnapshotsInGitignore(cwd).catch(() => {});
221
224
  await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
222
225
  } catch(e) { /* ignore tracking errors */ }
223
226
  }
@@ -265,6 +268,7 @@ Ranked by frequency of use:
265
268
  } catch(e) { /* no existing queue */ }
266
269
  queue.errors.push({ tool: toolName, error: err.message, date: new Date().toISOString() });
267
270
  await fs.mkdir(path.dirname(queuePath), { recursive: true }).catch(() => {});
271
+ await ensureSnapshotsInGitignore(cwd).catch(() => {});
268
272
  await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
269
273
  } catch(e) { /* ignore tracking errors */ }
270
274
  }
@@ -3,7 +3,6 @@ import path from 'path';
3
3
  import { execa } from 'execa';
4
4
  import pLimit from 'p-limit';
5
5
  import { SingleBar, Presets } from 'cli-progress';
6
- import isBinaryPath from 'is-binary-path';
7
6
  import zlib from 'zlib';
8
7
  import { promisify } from 'util';
9
8
  import ora from 'ora';
@@ -15,7 +14,8 @@ import {
15
14
  scanDirectoryRecursively, loadGitignore, readFileWithSizeCheck,
16
15
  generateDirectoryTree, loadConfig, displayProjectInfo, loadProjectEckManifest,
17
16
  ensureSnapshotsInGitignore, initializeEckManifest, generateTimestamp,
18
- getShortRepoName, SecretScanner, getProjectFiles
17
+ getShortRepoName, SecretScanner, getProjectFiles, readMlModelMetadata,
18
+ isBinaryFile
19
19
  } from '../../utils/fileUtils.js';
20
20
  import { detectProjectType, getProjectSpecificFiltering, getAllDetectedTypes } from '../../utils/projectDetector.js';
21
21
  import { estimateTokensWithPolynomial, generateTrainingCommand } from '../../utils/tokenEstimator.js';
@@ -252,7 +252,7 @@ async function estimateProjectTokens(projectPath, config, projectTypes = null) {
252
252
  continue;
253
253
  }
254
254
 
255
- if (isBinaryPath(file)) {
255
+ if (await isBinaryFile(path.join(projectPath, file))) {
256
256
  continue;
257
257
  }
258
258
 
@@ -394,8 +394,16 @@ async function processProjectFiles(repoPath, options, config, projectTypes = nul
394
394
  return null;
395
395
  }
396
396
 
397
- // Check if binary file
398
- if (isBinaryPath(filePath)) {
397
+ const mlExt = path.extname(filePath).toLowerCase();
398
+ const ML_EXTENSIONS = ['.safetensors', '.onnx', '.pt', '.pth', '.h5', '.pb', '.bin', '.ckpt', '.gguf'];
399
+ // ML peek is opt-in via `arguments.ml: true`. Default: ML files treated as plain binaries.
400
+ // This prevents false-positives where `.bin` raw dumps (mitm captures, sniffer output)
401
+ // get included via readMlModelMetadata when no real model is present.
402
+ const isMlModel = !!options?.ml && ML_EXTENSIONS.includes(mlExt);
403
+
404
+ // Content-aware binary check (catches extensionless ELFs, SQLite DBs, archives).
405
+ // ML models bypass to allow header metadata extraction below.
406
+ if (!isMlModel && await isBinaryFile(path.join(repoPath, filePath))) {
399
407
  stats.binaryFiles++;
400
408
  trackSkippedFile(normalizedPath, 'Binary files');
401
409
  return null;
@@ -421,13 +429,18 @@ async function processProjectFiles(repoPath, options, config, projectTypes = nul
421
429
  stats.totalSize += fileStats.size;
422
430
 
423
431
  const maxFileSize = parseSize(config.maxFileSize);
424
- if (fileStats.size > maxFileSize) {
425
- stats.oversizedFiles++;
426
- trackSkippedFile(normalizedPath, `File too large (${formatSize(fileStats.size)} > ${formatSize(maxFileSize)})`);
427
- return null;
428
- }
432
+ let content;
429
433
 
430
- let content = await readFileWithSizeCheck(fullPath, maxFileSize);
434
+ if (isMlModel) {
435
+ content = await readMlModelMetadata(fullPath);
436
+ } else {
437
+ if (fileStats.size > maxFileSize) {
438
+ stats.oversizedFiles++;
439
+ trackSkippedFile(normalizedPath, `File too large (${formatSize(fileStats.size)} > ${formatSize(maxFileSize)})`);
440
+ return null;
441
+ }
442
+ content = await readFileWithSizeCheck(fullPath, maxFileSize);
443
+ }
431
444
 
432
445
  // Security scan for secrets
433
446
  if (config.security?.scanForSecrets !== false) {