@xelth/eck-snapshot 2.2.0 → 3.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -139
  3. package/index.js +251 -56
  4. package/package.json +2 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dmytro Surovtsev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -3,140 +3,124 @@
3
3
  [![npm version](https://badge.fury.io/js/%40xelth%2Feck-snapshot.svg)](https://www.npmjs.com/package/@xelth/eck-snapshot)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/xelth-com/eckSnapshot/blob/main/LICENSE)
5
5
 
6
- A CLI tool to create and restore single-file text snapshots of a Git repository. It generates a single `.txt` file containing the directory structure and the content of all text-based files, which is ideal for providing context to Large Language Models (LLMs).
6
+ A powerful CLI tool to create and restore single-file text snapshots of Git repositories and directories. Generate comprehensive snapshots containing directory structure and file contents, optimized for providing context to Large Language Models (LLMs) like Claude, Gemini, and ChatGPT.
7
7
 
8
- ## Why eck-snapshot?
8
+ ## What's New in v3.0.0
9
9
 
10
- When working with Large Language Models (LLMs), providing the full context of your project is crucial for getting accurate results. Manually copying and pasting dozens of files is tedious and inefficient.
10
+ 🎯 **Universal Directory Support**: Works with any directory, not just Git repositories
11
+ 🤖 **Enhanced AI Instructions**: Improved headers with detailed guidance for AI assistants
12
+ ⚡ **Auto-Detection**: Automatically switches to directory mode when Git isn't available
13
+ 🧹 **Clean Mode**: Option to create snapshots without AI instructions
11
14
 
12
- eck-snapshot automates this by generating a single, comprehensive text file of your entire repository. This is particularly effective with models that support large context windows (like Google's Gemini), as it often allows the entire project snapshot to be analyzed at once—a task that can be challenging with smaller context windows.
15
+ ## Why eck-snapshot?
13
16
 
14
- ## Key Features
17
+ When working with Large Language Models (LLMs), providing complete project context is crucial for accurate results. Manually copying and pasting dozens of files is tedious and error-prone.
15
18
 
16
- * **Git Integration**: Automatically includes all files tracked by Git.
17
- * **Intelligent Ignoring**: Respects `.gitignore` rules and has its own configurable ignore lists for files, extensions, and directories.
18
- * **Advanced Restore**: Powerful `restore` command with filtering, dry-run mode, and parallel processing.
19
- * **Directory Tree**: Generates a clean, readable tree of the repository structure at the top of the snapshot.
20
- * **Multiple Formats**: Supports both plain text and JSON output formats.
21
- * **Configurable**: Customize behavior using an `.ecksnapshot.config.js` file.
22
- * **Progress and Stats**: Provides a progress bar and a detailed summary of what was included and skipped.
23
- * **Compression**: Supports gzipped (`.gz`) snapshots for smaller file sizes.
24
- * **Security**: Built-in path validation to prevent directory traversal attacks during restore.
19
+ eck-snapshot automates this by generating a single, comprehensive text file of your entire project. This is particularly effective with models that support large context windows (like Gemini 2.0 Pro with 1M tokens), allowing the entire project to be analyzed at once.
25
20
 
26
- ## Demo
21
+ ## 🚀 Key Features
27
22
 
28
- Here's an example of `eck-snapshot` in action:
23
+ ### 📁 **Universal Compatibility**
24
+ - **Git Repositories**: Leverages `git ls-files` and respects `.gitignore`
25
+ - **Any Directory**: Recursively scans any folder structure
26
+ - **Auto-Detection**: Automatically switches modes based on Git availability
29
27
 
30
- ```
31
- 🚀 Starting snapshot for repository: /path/to/your/project
32
- .gitignore patterns loaded
33
- 📊 Found 152 total files in the repository
34
- 🌳 Generating directory tree...
35
- 📝 Processing files...
36
- Progress |██████████████████████████████| 100% | 152/152 files
37
-
38
- 📊 Snapshot Summary
39
- ==================================================
40
- 🎉 Snapshot created successfully!
41
- 📄 File saved to: /path/to/your/project/snapshots/project_snapshot_...txt
42
- 📈 Included text files: 130 of 152
43
- ⏭️ Skipped files: 22
44
- ...
45
- ==================================================
46
- ```
28
+ ### 🤖 **AI-Optimized**
29
+ - **Structured Headers**: Detailed instructions for AI assistants
30
+ - **Clean Mode**: Option to skip AI headers for general use
31
+ - **LLM-Ready Format**: Optimized for Claude, Gemini, ChatGPT, and other models
47
32
 
48
- The beginning of the generated file will look like this:
33
+ ### **Advanced Features**
34
+ - **Multiple Formats**: Plain text (Markdown) and JSON output
35
+ - **Compression**: Built-in gzip support for smaller files
36
+ - **Smart Filtering**: Configurable ignore patterns and size limits
37
+ - **Restore Capability**: Recreate entire project structures from snapshots
38
+ - **Progress Tracking**: Real-time progress bars and detailed statistics
49
39
 
50
- ```text
51
- Directory Structure:
40
+ ### 🔒 **Security & Performance**
41
+ - **Path Validation**: Prevents directory traversal attacks
42
+ - **Parallel Processing**: Concurrent file handling for speed
43
+ - **Memory Efficient**: Handles large projects without memory issues
52
44
 
53
- ├── .github/
54
- │ └── workflows/
55
- │ └── publish.yml
56
- ├── src/
57
- │ ├── utils/
58
- │ │ └── formatters.js
59
- │ └── index.js
60
- ├── .gitignore
61
- ├── package.json
62
- └── README.md
45
+ ## 📦 Installation
63
46
 
47
+ ```bash
48
+ npm install -g @xelth/eck-snapshot
49
+ ```
64
50
 
65
- --- File: /src/index.js ---
51
+ ## 🎯 Quick Start
66
52
 
67
- #!/usr/bin/env node
68
- import { Command } from 'commander';
69
- // ... rest of the file content
53
+ ### Create Snapshots
70
54
 
71
- --- File: /package.json ---
55
+ ```bash
56
+ # Git repository (default mode)
57
+ eck-snapshot
72
58
 
73
- {
74
- "name": "eck-snapshot",
75
- "version": "2.1.0",
76
- // ... rest of the file content
77
- ```
59
+ # Any directory (auto-detects non-git folders)
60
+ eck-snapshot /path/to/any/folder
78
61
 
79
- ## Installation
62
+ # Force directory mode (ignores git)
63
+ eck-snapshot --dir .
80
64
 
81
- To install the tool globally, run the following command:
65
+ # Clean snapshot without AI instructions
66
+ eck-snapshot --no-ai-header
82
67
 
83
- ```bash
84
- npm install -g @xelth/eck-snapshot
68
+ # Compressed JSON format
69
+ eck-snapshot --format json --compress
85
70
  ```
86
71
 
87
- ## Usage
88
-
89
- Once installed, you can run the tool from any directory in your terminal.
90
-
91
- ### Creating a Snapshot
72
+ ### Restore from Snapshots
92
73
 
93
74
  ```bash
94
- # Create a snapshot of the current directory
95
- eck-snapshot
75
+ # Basic restore
76
+ eck-snapshot restore snapshot.md
96
77
 
97
- # Specify a path to another repository
98
- eck-snapshot /path/to/your/other/project
78
+ # Restore to specific directory
79
+ eck-snapshot restore snapshot.md ./restored-project
99
80
 
100
- # Save the snapshot to a different directory and exclude the tree view
101
- eck-snapshot --output ./backups --no-tree
81
+ # Preview without writing files
82
+ eck-snapshot restore snapshot.md --dry-run
102
83
 
103
- # Create a compressed JSON snapshot
104
- eck-snapshot --format json --compress
105
-
106
- # Include hidden files and set custom size limits
107
- eck-snapshot --include-hidden --max-file-size 5MB --max-total-size 50MB
84
+ # Restore only specific files
85
+ eck-snapshot restore snapshot.md --include "*.js" "*.json"
108
86
  ```
109
87
 
110
- ### Restoring from a Snapshot
88
+ ## 📋 Usage Examples
89
+
90
+ ### For AI Development
111
91
 
112
92
  ```bash
113
- # Basic restore to current directory
114
- eck-snapshot restore ./snapshots/project_snapshot_...txt
93
+ # Create AI-optimized snapshot for Gemini/Claude
94
+ eck-snapshot --format md --compress
95
+ # Result: project_snapshot_2025-01-19_12-00-00.md.gz
115
96
 
116
- # Restore to a specific directory without confirmation
117
- eck-snapshot restore snapshot.txt ./restored-project --force
97
+ # Clean snapshot for general documentation
98
+ eck-snapshot --no-ai-header --output ./docs
99
+ ```
118
100
 
119
- # Preview what would be restored (dry run)
120
- eck-snapshot restore snapshot.txt --dry-run
101
+ ### Project Backup & Migration
121
102
 
122
- # Restore only specific files using patterns
123
- eck-snapshot restore snapshot.txt --include "*.js" "*.json"
103
+ ```bash
104
+ # Full project backup
105
+ eck-snapshot --include-hidden --format json --compress
124
106
 
125
- # Restore everything except certain files
126
- eck-snapshot restore snapshot.txt --exclude "*.log" "node_modules/*"
107
+ # Selective restore
108
+ eck-snapshot restore backup.json.gz --exclude "node_modules/*" --include "src/*"
109
+ ```
127
110
 
128
- # Restore with custom concurrency and verbose output
129
- eck-snapshot restore snapshot.txt --concurrency 20 --verbose
111
+ ### Cross-Platform Development
130
112
 
131
- # Restore compressed snapshots
132
- eck-snapshot restore project_snapshot.txt.gz ./restored
133
- ```
113
+ ```bash
114
+ # Create snapshot on Windows
115
+ eck-snapshot --output ./transfer
134
116
 
135
- ## Configuration
117
+ # Restore on Linux/Mac
118
+ eck-snapshot restore transfer/project_snapshot.md ./project
119
+ ```
136
120
 
137
- You can create a `.ecksnapshot.config.js` file in your project's root directory to customize the tool's behavior.
121
+ ## ⚙️ Configuration
138
122
 
139
- **Example `.ecksnapshot.config.js`:**
123
+ Create `.ecksnapshot.config.js` in your project root:
140
124
 
141
125
  ```javascript
142
126
  export default {
@@ -144,19 +128,29 @@ export default {
144
128
  filesToIgnore: [
145
129
  'package-lock.json',
146
130
  '*.log',
131
+ '*.tmp'
147
132
  ],
133
+
148
134
  // File extensions to ignore
149
135
  extensionsToIgnore: [
150
136
  '.sqlite3',
151
137
  '.env',
138
+ '.DS_Store',
139
+ '.ico',
140
+ '.png',
141
+ '.jpg'
152
142
  ],
153
- // Directories to ignore (must have a trailing slash)
143
+
144
+ // Directories to ignore
154
145
  dirsToIgnore: [
155
146
  'node_modules/',
156
147
  '.git/',
157
148
  'dist/',
149
+ 'build/',
150
+ 'coverage/'
158
151
  ],
159
- // Size and performance settings
152
+
153
+ // Size and performance limits
160
154
  maxFileSize: '10MB',
161
155
  maxTotalSize: '100MB',
162
156
  maxDepth: 10,
@@ -164,63 +158,135 @@ export default {
164
158
  };
165
159
  ```
166
160
 
167
- ## Advanced Features
161
+ ## 📖 Command Reference
162
+
163
+ ### Snapshot Command
164
+
165
+ ```bash
166
+ eck-snapshot [options] [path]
167
+ ```
168
+
169
+ **Core Options:**
170
+ - `-o, --output <dir>` - Output directory (default: ./snapshots)
171
+ - `-d, --dir` - Directory mode: scan any folder recursively
172
+ - `--no-ai-header` - Skip AI instruction header (clean mode)
173
+ - `-v, --verbose` - Show detailed processing information
168
174
 
169
- ### Restore Command Options
175
+ **Format & Compression:**
176
+ - `--format <type>` - Output format: md (default) or json
177
+ - `--compress` - Create gzipped output (.gz extension)
178
+ - `--no-tree` - Exclude directory tree from output
170
179
 
171
- The restore command offers powerful filtering and control options:
180
+ **Filtering:**
181
+ - `--include-hidden` - Include hidden files (starting with .)
182
+ - `--max-file-size <size>` - Maximum individual file size (e.g., 5MB)
183
+ - `--max-total-size <size>` - Maximum total snapshot size (e.g., 50MB)
184
+ - `--config <path>` - Path to custom configuration file
172
185
 
173
- - **`--dry-run`**: Preview what files would be restored without actually writing them
174
- - **`--include <patterns>`**: Only restore files matching the specified patterns (supports wildcards)
175
- - **`--exclude <patterns>`**: Skip files matching the specified patterns (supports wildcards)
176
- - **`--concurrency <number>`**: Control how many files are processed simultaneously (default: 10)
177
- - **`--force`**: Skip confirmation prompts and overwrite existing files
178
- - **`--verbose`**: Show detailed information about each file being processed
186
+ ### Restore Command
179
187
 
180
- ### Supported Formats
188
+ ```bash
189
+ eck-snapshot restore [options] <snapshot_file> [target_directory]
190
+ ```
181
191
 
182
- - **Plain Text** (`.txt`): Human-readable format, ideal for LLM context
183
- - **JSON** (`.json`): Structured format with metadata and statistics
184
- - **Compressed** (`.gz`): Any format can be gzipped for smaller file sizes
192
+ **Control Options:**
193
+ - `-f, --force` - Skip confirmation prompts
194
+ - `--dry-run` - Preview without writing files
195
+ - `-v, --verbose` - Show detailed processing information
185
196
 
186
- ### Security Features
197
+ **Filtering:**
198
+ - `--include <patterns>` - Include only matching files (wildcards supported)
199
+ - `--exclude <patterns>` - Exclude matching files (wildcards supported)
200
+ - `--concurrency <number>` - Number of concurrent operations (default: 10)
187
201
 
188
- - **Path Validation**: Prevents directory traversal attacks during restore operations
189
- - **File Sanitization**: Validates file paths and names for security
190
- - **Confirmation Prompts**: Requires user confirmation before overwriting files (unless `--force` is used)
202
+ ## 🎭 Working with AI Models
191
203
 
192
- ## Command Reference
204
+ ### For Gemini 2.0 Pro (1M context)
205
+ ```bash
206
+ # Create comprehensive snapshot with AI instructions
207
+ eck-snapshot --format md --compress
208
+ ```
209
+ The generated file includes detailed instructions for Gemini to analyze your project and provide structured commands for Claude Code.
193
210
 
194
- ### Snapshot Command
211
+ ### For Claude Code
212
+ ```bash
213
+ # Clean, focused snapshot
214
+ eck-snapshot --no-ai-header --max-total-size 200MB
215
+ ```
216
+
217
+ ### For ChatGPT/Other Models
195
218
  ```bash
196
- eck-snapshot [options] [repoPath]
219
+ # Standard snapshot with moderate size limits
220
+ eck-snapshot --max-total-size 50MB --no-ai-header
197
221
  ```
198
222
 
199
- **Options:**
200
- - `-o, --output <dir>`: Output directory for snapshots
201
- - `--no-tree`: Exclude directory tree from output
202
- - `-v, --verbose`: Show detailed processing information
203
- - `--max-file-size <size>`: Maximum individual file size (e.g., 10MB)
204
- - `--max-total-size <size>`: Maximum total snapshot size (e.g., 100MB)
205
- - `--max-depth <number>`: Maximum directory depth for tree generation
206
- - `--config <path>`: Path to custom configuration file
207
- - `--compress`: Create gzipped output
208
- - `--include-hidden`: Include hidden files (starting with .)
209
- - `--format <type>`: Output format: txt or json
223
+ ## 🔧 Advanced Use Cases
210
224
 
211
- ### Restore Command
225
+ ### Monorepo Support
212
226
  ```bash
213
- eck-snapshot restore [options] <snapshot_file> [target_directory]
227
+ # Snapshot specific package in monorepo
228
+ eck-snapshot ./packages/core --dir --output ./snapshots/core
229
+
230
+ # Multiple packages
231
+ eck-snapshot ./packages/api --dir && eck-snapshot ./packages/web --dir
214
232
  ```
215
233
 
216
- **Options:**
217
- - `-f, --force`: Force overwrite without confirmation
218
- - `-v, --verbose`: Show detailed processing information
219
- - `--dry-run`: Preview without actually writing files
220
- - `--include <patterns>`: Include only matching files
221
- - `--exclude <patterns>`: Exclude matching files
222
- - `--concurrency <number>`: Number of concurrent operations
234
+ ### CI/CD Integration
235
+ ```bash
236
+ # Create release snapshot
237
+ eck-snapshot --format json --compress --output ./artifacts
238
+
239
+ # Documentation generation
240
+ eck-snapshot --no-ai-header --format md --output ./docs/snapshots
241
+ ```
242
+
243
+ ### Migration & Archival
244
+ ```bash
245
+ # Complete project archive
246
+ eck-snapshot --include-hidden --format json --compress --max-total-size 1GB
247
+
248
+ # Selective migration
249
+ eck-snapshot restore archive.json.gz --include "src/*" "docs/*" --exclude "*.test.*"
250
+ ```
251
+
252
+ ## 📊 Output Formats
253
+
254
+ ### Markdown Format (Default)
255
+ - Human-readable structure
256
+ - AI instruction headers (optional)
257
+ - Directory tree visualization
258
+ - File content with clear delimiters
259
+
260
+ ### JSON Format
261
+ - Structured metadata
262
+ - Programmatic processing friendly
263
+ - Includes statistics and file information
264
+ - Perfect for automation workflows
265
+
266
+ ## 🛡️ Security Features
267
+
268
+ - **Path Validation**: Prevents directory traversal during restore
269
+ - **File Sanitization**: Validates all file paths and names
270
+ - **Confirmation Prompts**: Requires approval before overwriting files
271
+ - **Size Limits**: Protects against extremely large operations
272
+
273
+ ## 🚀 Performance
274
+
275
+ - **Parallel Processing**: Concurrent file operations for speed
276
+ - **Progress Tracking**: Real-time progress bars for long operations
277
+ - **Memory Efficient**: Streams large files to avoid memory issues
278
+ - **Smart Caching**: Optimized for repeated operations
279
+
280
+ ## 📝 License
281
+
282
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
283
+
284
+ ## 🤝 Contributing
285
+
286
+ Contributions are welcome! Please feel free to submit a Pull Request.
223
287
 
224
- ## License
288
+ ## 📞 Support
225
289
 
226
- This project is licensed under the MIT License.
290
+ - **Issues**: [GitHub Issues](https://github.com/xelth-com/eckSnapshot/issues)
291
+ - **Documentation**: This README and `--help` commands
292
+ - **Examples**: See the examples directory in the repository
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+
4
+
3
5
  import { Command } from 'commander';
4
6
  import { execa } from 'execa';
5
7
  import fs from 'fs/promises';
@@ -28,8 +30,157 @@ const DEFAULT_CONFIG = {
28
30
  concurrency: 10
29
31
  };
30
32
 
31
- // ... (остальные существующие функции: parseSize, formatSize, matchesPattern, loadConfig, и т.д. остаются без изменений)
32
- // --- НАЧАЛО СУЩЕСТВУЮЩИХ ФУНКЦИЙ ---
33
+
34
+
35
+ /**
36
+ * Generates the detailed, universal Markdown header for the snapshot file.
37
+ * This header contains the full operational instructions for the AI model.
38
+ * @param {object} stats - The statistics object from the snapshot process.
39
+ * @param {string} repoName - The name of the repository.
40
+ * @returns {string} A formatted Markdown string with comprehensive AI instructions.
41
+ */
42
+ function generateSnapshotHeader(stats, repoName, includeAiInstructions = true) {
43
+ const timestamp = new Date().toISOString();
44
+
45
+ if (!includeAiInstructions) {
46
+ return `# Repository Snapshot
47
+
48
+ **Repository:** ${repoName}
49
+ **Generated:** ${timestamp}
50
+ **Tool:** eck-snapshot
51
+ **Files Included:** ${stats.includedFiles} of ${stats.totalFiles}
52
+
53
+ ---
54
+
55
+ `;
56
+ }
57
+
58
+ return `# AI Instructions
59
+
60
+ ## 1. How to Read This Snapshot
61
+
62
+ This document is a self-contained, single-file snapshot of the **${repoName}** software repository, generated by the \`eck-snapshot\` tool on **${timestamp}**. It is designed to provide a Large Language Model (LLM) with the complete context of a project.
63
+
64
+ * **Source of Truth:** Treat this snapshot as the complete and authoritative source code.
65
+ * **Structure:** The file contains a **Directory Structure** tree, followed by the full content of each file, demarcated by \`--- File: /path/to/file ---\` headers.
66
+
67
+ **Snapshot Stats:**
68
+ - **Files Included:** ${stats.includedFiles}
69
+ - **Total Files in Repo:** ${stats.totalFiles}
70
+
71
+ ---
72
+
73
+ ## 2. Your Core Operational Workflow
74
+
75
+ You are the Project Manager and Solution Architect AI. Your primary goal is to translate user requests into technical plans and then generate precise commands for a code-execution AI agent.
76
+
77
+ ### PROJECT OVERVIEW
78
+ - **Project:** ${repoName}
79
+ - **Description:** A CLI tool to create and restore single-file text snapshots of a Git repository, optimized for providing context to Large Language Models (LLMs).
80
+
81
+ ### CORE WORKFLOW: The Interactive Command Cycle
82
+ 1. **Analyze User Request:** Understand the user's goal in their native language.
83
+ 2. **Formulate a Plan:** Create a high-level technical plan to solve the user's request.
84
+ 3. **Propose & Await Confirmation:** Present the plan to the user in their language and ask for approval to generate the command. **CRITICAL: Stop and wait for the user's response. Do NOT generate the command block at this stage.**
85
+ 4. **Generate Command on Demand:** This is the execution step, triggered ONLY by a positive user response.
86
+ - **On Approval:** If the user confirms the plan (e.g., "yes", "proceed") or provides a minor correction, your *next response* must be **only the command block**. Do not include any conversational text.
87
+ - **On Direct Order:** If the user explicitly asks for the command (e.g., "make the command for Claude now") and you have all the necessary information, you may skip step 3 and directly generate the command block.
88
+ 5. **Review & Report:** After the command is executed, analyze the results and report back to the user in their language.
89
+ 6. **Iterate:** Continue the cycle based on user feedback.
90
+
91
+ ### COMMUNICATION PROTOCOL
92
+ - **User Interaction:** ALWAYS communicate with the user in the language they use.
93
+ - **Agent Commands:** ALWAYS formulate the JSON payload and technical instructions for the execution agent in **ENGLISH** to ensure technical accuracy.
94
+
95
+ ### COMMAND BLOCK FORMAT
96
+ To ensure error-free execution, all tasks for the agent must be presented in a special block with a "Copy" button. Use this enhanced format for maximum clarity and execution accuracy:
97
+
98
+ \`\`\`json
99
+ {
100
+ "command_for_agent": "apply_code_changes",
101
+ "task_id": "unique-task-id",
102
+ "payload": {
103
+ "objective": "Brief, clear task description",
104
+ "context": "Why this change is needed",
105
+ "files_to_modify": [
106
+ {
107
+ "path": "exact/file/path.js",
108
+ "action": "specific action (add, modify, replace, delete)",
109
+ "location": "line numbers, function name, or search pattern",
110
+ "details": "precise description of the change"
111
+ }
112
+ ],
113
+ "new_files": [
114
+ {
115
+ "path": "path/to/new/file.js",
116
+ "content_type": "javascript/json/markdown/config",
117
+ "purpose": "why this file is needed"
118
+ }
119
+ ],
120
+ "dependencies": {
121
+ "install": ["package-name@version"],
122
+ "remove": ["old-package-name"]
123
+ },
124
+ "validation_steps": [
125
+ "npm run test",
126
+ "node index.js --help",
127
+ "specific command to verify functionality"
128
+ ],
129
+ "expected_outcome": "what should work after changes"
130
+ }
131
+ }
132
+ \`\`\`
133
+
134
+ ### PROJECT CONTEXT (\`eck-snapshot\`)
135
+ - **Type:** Node.js CLI Application, executed directly.
136
+ - **Module System:** ES Modules (\`"type": "module"\` in package.json).
137
+ - **Main File:** \`index.js\` contains all primary logic (837 lines).
138
+ - **Configuration:** \`.ecksnapshot.config.js\` is used for custom filtering and settings.
139
+ - **Key Dependencies:** \`commander\`, \`execa\`, \`inquirer\`, \`ignore\`, \`p-limit\`, \`cli-progress\`.
140
+
141
+ ### ARCHITECTURE DETAILS FOR CLAUDE CODE
142
+ **Core Functions Location:**
143
+ - \`createRepoSnapshot()\` - Line 333: Main snapshot creation
144
+ - \`restoreSnapshot()\` - Line 579: Snapshot restoration
145
+ - \`processFile()\` - Line 265: Individual file processing
146
+ - \`generateDirectoryTree()\` - Line 224: Tree generation
147
+ - \`generateSnapshotHeader()\` - Line 42: AI instruction header
148
+ - CLI setup - Lines 800-837: Commander.js configuration
149
+
150
+ **Common Modification Patterns:**
151
+ - CLI options: Modify commander setup (lines 808-822, 824-835)
152
+ - Configuration: Update DEFAULT_CONFIG object (lines 23-31)
153
+ - File processing: Enhance processFile() function
154
+ - Output formats: Modify generateSnapshotHeader() or output logic
155
+ - Dependencies: Update package.json and import statements
156
+
157
+ **Testing Status:**
158
+ - No test framework currently configured
159
+ - package.json test script returns error
160
+ - Manual testing via \`node index.js\` commands
161
+ - Consider adding vitest or jest for future testing
162
+
163
+ **Development Workflow:**
164
+ - Direct execution: \`node index.js [command] [options]\`
165
+ - Package creation: \`npm pack\`
166
+ - Local testing: \`node index.js --help\`
167
+ - Configuration testing: modify \`.ecksnapshot.config.js\`
168
+
169
+ **Critical Implementation Notes:**
170
+ - All file paths normalized to forward slashes
171
+ - ES module imports only (no CommonJS)
172
+ - Error handling with detailed user messages
173
+ - Progress tracking for long operations
174
+ - Security: Path validation prevents directory traversal
175
+ - Cross-platform compatibility maintained
176
+
177
+ ---
178
+ `;
179
+ }
180
+
181
+
182
+
183
+
33
184
 
34
185
  function parseSize(sizeStr) {
35
186
  const units = { B: 1, KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3 };
@@ -67,7 +218,6 @@ function matchesPattern(filePath, patterns) {
67
218
 
68
219
  async function loadConfig(configPath) {
69
220
  let config = { ...DEFAULT_CONFIG };
70
-
71
221
  if (configPath) {
72
222
  try {
73
223
  const configModule = await import(path.resolve(configPath));
@@ -82,7 +232,6 @@ async function loadConfig(configPath) {
82
232
  '.ecksnapshot.config.mjs',
83
233
  'ecksnapshot.config.js'
84
234
  ];
85
-
86
235
  for (const configFile of possibleConfigs) {
87
236
  try {
88
237
  await fs.access(configFile);
@@ -110,11 +259,55 @@ async function checkGitAvailability() {
110
259
  async function checkGitRepository(repoPath) {
111
260
  try {
112
261
  await execa('git', ['rev-parse', '--git-dir'], { cwd: repoPath });
262
+ return true;
113
263
  } catch (error) {
114
- throw new Error(`Not a git repository: ${repoPath}`);
264
+ return false;
115
265
  }
116
266
  }
117
267
 
268
+ async function scanDirectoryRecursively(dirPath, config, relativeTo = dirPath) {
269
+ const files = [];
270
+
271
+ try {
272
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
273
+
274
+ for (const entry of entries) {
275
+ const fullPath = path.join(dirPath, entry.name);
276
+ const relativePath = path.relative(relativeTo, fullPath).replace(/\\/g, '/');
277
+
278
+ // Skip if matches ignore patterns
279
+ if (config.dirsToIgnore.some(dir =>
280
+ entry.name === dir.replace('/', '') ||
281
+ relativePath.startsWith(dir)
282
+ )) {
283
+ continue;
284
+ }
285
+
286
+ // Skip hidden files unless explicitly included
287
+ if (!config.includeHidden && entry.name.startsWith('.')) {
288
+ continue;
289
+ }
290
+
291
+ if (entry.isDirectory()) {
292
+ const subFiles = await scanDirectoryRecursively(fullPath, config, relativeTo);
293
+ files.push(...subFiles);
294
+ } else {
295
+ // Skip ignored files and extensions
296
+ if (config.extensionsToIgnore.includes(path.extname(entry.name)) ||
297
+ matchesPattern(relativePath, config.filesToIgnore)) {
298
+ continue;
299
+ }
300
+
301
+ files.push(relativePath);
302
+ }
303
+ }
304
+ } catch (error) {
305
+ console.warn(`⚠️ Warning: Could not read directory: ${dirPath} - ${error.message}`);
306
+ }
307
+
308
+ return files;
309
+ }
310
+
118
311
  async function loadGitignore(repoPath) {
119
312
  try {
120
313
  const gitignoreContent = await fs.readFile(path.join(repoPath, '.gitignore'), 'utf-8');
@@ -142,7 +335,6 @@ async function readFileWithSizeCheck(filePath, maxFileSize) {
142
335
 
143
336
  async function generateDirectoryTree(dir, prefix = '', allFiles, depth = 0, maxDepth = 10, config) {
144
337
  if (depth > maxDepth) return '';
145
-
146
338
  try {
147
339
  const entries = await fs.readdir(dir, { withFileTypes: true });
148
340
  const sortedEntries = entries.sort((a, b) => {
@@ -150,16 +342,13 @@ async function generateDirectoryTree(dir, prefix = '', allFiles, depth = 0, maxD
150
342
  if (!a.isDirectory() && b.isDirectory()) return 1;
151
343
  return a.name.localeCompare(b.name);
152
344
  });
153
-
154
345
  let tree = '';
155
346
  const validEntries = [];
156
347
 
157
348
  for (const entry of sortedEntries) {
158
349
  if (config.dirsToIgnore.some(d => entry.name.includes(d.replace('/', '')))) continue;
159
-
160
350
  const fullPath = path.join(dir, entry.name);
161
351
  const relativePath = path.relative(process.cwd(), fullPath).replace(/\\/g, '/');
162
-
163
352
  if (entry.isDirectory() || allFiles.includes(relativePath)) {
164
353
  validEntries.push({ entry, fullPath, relativePath });
165
354
  }
@@ -171,7 +360,6 @@ async function generateDirectoryTree(dir, prefix = '', allFiles, depth = 0, maxD
171
360
 
172
361
  const connector = isLast ? '└── ' : '├── ';
173
362
  const nextPrefix = prefix + (isLast ? ' ' : '│ ');
174
-
175
363
  if (entry.isDirectory()) {
176
364
  tree += `${prefix}${connector}${entry.name}/\n`;
177
365
  tree += await generateDirectoryTree(fullPath, nextPrefix, allFiles, depth + 1, maxDepth, config);
@@ -227,10 +415,10 @@ async function processFile(filePath, config, gitignore, stats) {
227
415
 
228
416
  stats.includedFiles++;
229
417
  stats.includedFileTypes.set(fileExt, (stats.includedFileTypes.get(fileExt) || 0) + 1);
230
-
231
418
  return { content: fileContent, size: fileContent.length };
232
419
  } catch (error) {
233
- const errorReason = error.message.includes('too large') ? 'file-too-large' : 'read-error';
420
+ const errorReason = error.message.includes('too large') ?
421
+ 'file-too-large' : 'read-error';
234
422
 
235
423
  stats.errors.push({ file: filePath, error: error.message });
236
424
  stats.skippedFiles++;
@@ -255,12 +443,11 @@ async function processFile(filePath, config, gitignore, stats) {
255
443
  // --- ОСНОВНЫЕ ФУНКЦИИ ДЛЯ КОМАНД ---
256
444
 
257
445
  async function createRepoSnapshot(repoPath, options) {
258
- // ... (эта функция остается без изменений)
259
446
  const absoluteRepoPath = path.resolve(repoPath);
260
447
  const absoluteOutputPath = path.resolve(options.output);
261
448
  const originalCwd = process.cwd();
262
449
 
263
- console.log(`🚀 Starting snapshot for repository: ${absoluteRepoPath}`);
450
+ console.log(`🚀 Starting snapshot for ${options.dir ? 'directory' : 'repository'}: ${absoluteRepoPath}`);
264
451
  console.log(`📁 Snapshots will be saved to: ${absoluteOutputPath}`);
265
452
 
266
453
  try {
@@ -269,19 +456,37 @@ async function createRepoSnapshot(repoPath, options) {
269
456
  config.maxTotalSize = options.maxTotalSize || config.maxTotalSize;
270
457
  config.maxDepth = options.maxDepth || config.maxDepth;
271
458
  config.includeHidden = options.includeHidden || false;
272
-
273
- await checkGitAvailability();
274
- await checkGitRepository(absoluteRepoPath);
459
+
460
+ let allFiles = [];
461
+ let gitignore = null;
462
+ let isGitRepo = false;
463
+
464
+ // Check if it's a git repository (unless --dir is explicitly used)
465
+ if (!options.dir) {
466
+ await checkGitAvailability();
467
+ isGitRepo = await checkGitRepository(absoluteRepoPath);
468
+
469
+ if (!isGitRepo) {
470
+ console.log('ℹ️ Not a git repository, switching to directory mode');
471
+ options.dir = true;
472
+ }
473
+ }
275
474
 
276
475
  process.chdir(absoluteRepoPath);
277
476
  console.log('✅ Successfully changed working directory');
278
477
 
279
- const gitignore = await loadGitignore(absoluteRepoPath);
280
-
281
- console.log('📋 Fetching file list from Git...');
282
- const { stdout } = await execa('git', ['ls-files']);
283
- const allFiles = stdout.split('\n').filter(Boolean);
284
- console.log(`📊 Found ${allFiles.length} total files in the repository`);
478
+ if (options.dir) {
479
+ console.log('📋 Scanning directory recursively...');
480
+ allFiles = await scanDirectoryRecursively(absoluteRepoPath, config);
481
+ gitignore = ignore(); // Empty gitignore for directory mode
482
+ console.log(`📊 Found ${allFiles.length} total files in the directory`);
483
+ } else {
484
+ gitignore = await loadGitignore(absoluteRepoPath);
485
+ console.log('📋 Fetching file list from Git...');
486
+ const { stdout } = await execa('git', ['ls-files']);
487
+ allFiles = stdout.split('\n').filter(Boolean);
488
+ console.log(`📊 Found ${allFiles.length} total files in the repository`);
489
+ }
285
490
 
286
491
  const stats = {
287
492
  totalFiles: allFiles.length,
@@ -295,7 +500,6 @@ async function createRepoSnapshot(repoPath, options) {
295
500
  skipReasons: new Map(),
296
501
  skippedFilesDetails: new Map()
297
502
  };
298
-
299
503
  let snapshotContent = '';
300
504
 
301
505
  if (options.tree) {
@@ -308,13 +512,13 @@ async function createRepoSnapshot(repoPath, options) {
308
512
 
309
513
  console.log('📝 Processing files...');
310
514
  const limit = pLimit(config.concurrency);
311
- const progressBar = options.verbose ? null : new SingleBar({
515
+ const progressBar = options.verbose ?
516
+ null : new SingleBar({
312
517
  format: 'Progress |{bar}| {percentage}% | {value}/{total} files | ETA: {eta}s',
313
518
  barCompleteChar: '\u2588',
314
519
  barIncompleteChar: '\u2591',
315
520
  hideCursor: true
316
521
  }, Presets.shades_classic);
317
-
318
522
  if (progressBar) progressBar.start(allFiles.length, 0);
319
523
 
320
524
  const filePromises = allFiles.map((filePath, index) =>
@@ -325,6 +529,7 @@ async function createRepoSnapshot(repoPath, options) {
325
529
  progressBar.update(index + 1);
326
530
  } else if (options.verbose) {
327
531
  if (result.skipped) {
532
+
328
533
  console.log(`⏭️ Skipping: ${filePath} (${result.reason})`);
329
534
  } else {
330
535
  console.log(`✅ Processed: ${filePath}`);
@@ -334,14 +539,12 @@ async function createRepoSnapshot(repoPath, options) {
334
539
  return result;
335
540
  })
336
541
  );
337
-
338
542
  const results = await Promise.allSettled(filePromises);
339
543
  if (progressBar) progressBar.stop();
340
544
 
341
545
  const contentArray = [];
342
546
  let totalSize = 0;
343
547
  const maxTotalSize = parseSize(config.maxTotalSize);
344
-
345
548
  for (const result of results) {
346
549
  if (result.status === 'rejected') {
347
550
  console.warn(`⚠️ Promise rejected: ${result.reason}`);
@@ -357,21 +560,22 @@ async function createRepoSnapshot(repoPath, options) {
357
560
  }
358
561
  }
359
562
 
360
- snapshotContent += contentArray.join('');
563
+ // Add the header to the beginning of the file content
564
+ const repoName = path.basename(absoluteRepoPath);
565
+ const header = generateSnapshotHeader(stats, repoName, options.aiHeader !== false);
566
+ snapshotContent = header + snapshotContent + contentArray.join('');
567
+
361
568
  const totalChars = snapshotContent.length;
362
569
  const estimatedTokens = Math.round(totalChars / 4);
363
570
 
364
571
  const timestamp = new Date().toISOString().slice(0, 19).replace('T', '_').replace(/:/g, '-');
365
- const repoName = path.basename(absoluteRepoPath);
366
- const extension = options.format === 'json' ? 'json' : 'txt';
572
+ const extension = options.format === 'json' ? 'json' : 'md'; // Changed default to md
367
573
  let outputFilename = `${repoName}_snapshot_${timestamp}.${extension}`;
368
-
369
574
  if (options.compress) {
370
575
  outputFilename += '.gz';
371
576
  }
372
577
 
373
578
  const fullOutputFilePath = path.join(absoluteOutputPath, outputFilename);
374
-
375
579
  let finalContent = snapshotContent;
376
580
  if (options.format === 'json') {
377
581
  const jsonData = {
@@ -383,6 +587,7 @@ async function createRepoSnapshot(repoPath, options) {
383
587
  skippedFileTypes: Object.fromEntries(stats.skippedFileTypes),
384
588
  skipReasons: Object.fromEntries(stats.skipReasons),
385
589
  skippedFilesDetails: Object.fromEntries(
590
+
386
591
  Array.from(stats.skippedFilesDetails.entries()).map(([reason, files]) => [
387
592
  reason,
388
593
  files.map(({file, ext}) => ({file, ext}))
@@ -395,7 +600,6 @@ async function createRepoSnapshot(repoPath, options) {
395
600
  }
396
601
 
397
602
  await fs.mkdir(absoluteOutputPath, { recursive: true });
398
-
399
603
  if (options.compress) {
400
604
  const compressed = await gzip(finalContent);
401
605
  await fs.writeFile(fullOutputFilePath, compressed);
@@ -421,8 +625,7 @@ async function createRepoSnapshot(repoPath, options) {
421
625
  console.log('\n📋 Included File Types Distribution:');
422
626
  const sortedIncludedTypes = Array.from(stats.includedFileTypes.entries())
423
627
  .sort(([,a], [,b]) => b - a)
424
- .slice(0, 10);
425
-
628
+ .slice(0, 10);
426
629
  for (const [ext, count] of sortedIncludedTypes) {
427
630
  console.log(` ${ext}: ${count} files`);
428
631
  }
@@ -432,8 +635,7 @@ async function createRepoSnapshot(repoPath, options) {
432
635
  console.log('\n⏭️ Skipped File Types Distribution:');
433
636
  const sortedSkippedTypes = Array.from(stats.skippedFileTypes.entries())
434
637
  .sort(([,a], [,b]) => b - a)
435
- .slice(0, 10);
436
-
638
+ .slice(0, 10);
437
639
  for (const [ext, count] of sortedSkippedTypes) {
438
640
  console.log(` ${ext}: ${count} files`);
439
641
  }
@@ -443,7 +645,6 @@ async function createRepoSnapshot(repoPath, options) {
443
645
  console.log('\n📊 Skip Reasons:');
444
646
  const sortedReasons = Array.from(stats.skipReasons.entries())
445
647
  .sort(([,a], [,b]) => b - a);
446
-
447
648
  const reasonLabels = {
448
649
  'ignored-directory': 'Ignored directories',
449
650
  'ignored-extension': 'Ignored extensions',
@@ -454,7 +655,6 @@ async function createRepoSnapshot(repoPath, options) {
454
655
  'file-too-large': 'Files too large',
455
656
  'read-error': 'Read errors'
456
657
  };
457
-
458
658
  for (const [reason, count] of sortedReasons) {
459
659
  const label = reasonLabels[reason] || reason;
460
660
  console.log(` ${label}: ${count} files`);
@@ -471,7 +671,7 @@ async function createRepoSnapshot(repoPath, options) {
471
671
  console.log(` ... and ${files.length - 10} more files`);
472
672
  }
473
673
  }
474
- console.log();
674
+ console.log();
475
675
  }
476
676
  }
477
677
 
@@ -486,7 +686,6 @@ async function createRepoSnapshot(repoPath, options) {
486
686
  }
487
687
 
488
688
  console.log('='.repeat(50));
489
-
490
689
  } catch (error) {
491
690
  console.error('\n❌ An error occurred:');
492
691
  if (error.code === 'ENOENT' && error.path && error.path.includes('.git')) {
@@ -512,7 +711,6 @@ async function createRepoSnapshot(repoPath, options) {
512
711
  async function restoreSnapshot(snapshotFile, targetDir, options) {
513
712
  const absoluteSnapshotPath = path.resolve(snapshotFile);
514
713
  const absoluteTargetDir = path.resolve(targetDir);
515
-
516
714
  console.log(`🔄 Starting restore from snapshot: ${absoluteSnapshotPath}`);
517
715
  console.log(`📁 Target directory: ${absoluteTargetDir}`);
518
716
 
@@ -563,7 +761,6 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
563
761
  }
564
762
 
565
763
  console.log(`📊 Found ${filesToRestore.length} files to restore`);
566
-
567
764
  if (options.dryRun) {
568
765
  console.log('\n🔍 Dry run mode - files that would be restored:');
569
766
  filesToRestore.forEach(file => {
@@ -580,7 +777,6 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
580
777
  message: `You are about to write ${filesToRestore.length} files to ${absoluteTargetDir}. Existing files will be overwritten. Continue?`,
581
778
  default: false
582
779
  }]);
583
-
584
780
  if (!confirm) {
585
781
  console.log('🚫 Restore operation cancelled by user');
586
782
  return;
@@ -588,21 +784,18 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
588
784
  }
589
785
 
590
786
  await fs.mkdir(absoluteTargetDir, { recursive: true });
591
-
592
787
  const stats = {
593
788
  totalFiles: filesToRestore.length,
594
789
  restoredFiles: 0,
595
790
  failedFiles: 0,
596
791
  errors: []
597
792
  };
598
-
599
793
  const progressBar = options.verbose ? null : new SingleBar({
600
794
  format: 'Restoring |{bar}| {percentage}% | {value}/{total} files',
601
795
  barCompleteChar: '\u2588',
602
796
  barIncompleteChar: '\u2591',
603
797
  hideCursor: true
604
798
  }, Presets.shades_classic);
605
-
606
799
  if (progressBar) progressBar.start(filesToRestore.length, 0);
607
800
 
608
801
  const limit = pLimit(options.concurrency || 10);
@@ -616,6 +809,7 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
616
809
  await fs.writeFile(fullPath, file.content, 'utf-8');
617
810
 
618
811
  stats.restoredFiles++;
812
+
619
813
 
620
814
  if (progressBar) {
621
815
  progressBar.update(index + 1);
@@ -624,6 +818,7 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
624
818
  }
625
819
 
626
820
  return { success: true, file: file.path };
821
+
627
822
  } catch (error) {
628
823
  stats.failedFiles++;
629
824
  stats.errors.push({ file: file.path, error: error.message });
@@ -632,6 +827,7 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
632
827
  console.log(`❌ Failed to restore: ${file.path} - ${error.message}`);
633
828
  }
634
829
 
830
+
635
831
  return { success: false, file: file.path, error: error.message };
636
832
  }
637
833
  })
@@ -658,7 +854,6 @@ async function restoreSnapshot(snapshotFile, targetDir, options) {
658
854
  }
659
855
  console.log(`📁 Target directory: ${absoluteTargetDir}`);
660
856
  console.log('='.repeat(50));
661
-
662
857
  } catch (error) {
663
858
  console.error('\n❌ An error occurred during restore:');
664
859
  console.error(error.message);
@@ -673,7 +868,6 @@ function parseSnapshotContent(content) {
673
868
  const files = [];
674
869
  const fileRegex = /--- File: \/(.+) ---/g;
675
870
  const sections = content.split(fileRegex);
676
-
677
871
  for (let i = 1; i < sections.length; i += 2) {
678
872
  const filePath = sections[i].trim();
679
873
  let fileContent = sections[i + 1] || '';
@@ -695,7 +889,8 @@ function filterFilesToRestore(files, options) {
695
889
  let filtered = files;
696
890
 
697
891
  if (options.include) {
698
- const includePatterns = Array.isArray(options.include) ? options.include : [options.include];
892
+ const includePatterns = Array.isArray(options.include) ?
893
+ options.include : [options.include];
699
894
  filtered = filtered.filter(file =>
700
895
  includePatterns.some(pattern => {
701
896
  const regex = new RegExp(pattern.replace(/\*/g, '.*'));
@@ -719,10 +914,8 @@ function filterFilesToRestore(files, options) {
719
914
 
720
915
  function validateFilePaths(files, targetDir) {
721
916
  const invalidFiles = [];
722
-
723
917
  for (const file of files) {
724
918
  const normalizedPath = path.normalize(file.path);
725
-
726
919
  if (normalizedPath.includes('..') ||
727
920
  normalizedPath.startsWith('/') ||
728
921
  normalizedPath.includes('\0') ||
@@ -741,14 +934,14 @@ const program = new Command();
741
934
  program
742
935
  .name('eck-snapshot')
743
936
  .description('A CLI tool to create and restore single-file text snapshots of a Git repository.')
744
- .version('2.1.0');
937
+ .version('3.0.0');
745
938
 
746
939
  // Snapshot command (existing)
747
940
  program
748
941
  .command('snapshot', { isDefault: true })
749
942
  .description('Create a snapshot of a Git repository (default command).')
750
943
  .argument('[repoPath]', 'Path to the git repository to snapshot.', process.cwd())
751
- .option('-o, --output <dir>', 'Output directory for the snapshot file.', path.join(process.cwd(), 'snapshots'))
944
+ .option('-o, --output <dir>', 'Output directory for the snapshot file.', path.join(__dirname, 'snapshots'))
752
945
  .option('--no-tree', 'Do not include the directory tree in the snapshot.')
753
946
  .option('-v, --verbose', 'Show detailed processing information, including skipped files.')
754
947
  .option('--max-file-size <size>', 'Maximum file size to include (e.g., 10MB)', '10MB')
@@ -757,7 +950,9 @@ program
757
950
  .option('--config <path>', 'Path to configuration file')
758
951
  .option('--compress', 'Compress output file with gzip')
759
952
  .option('--include-hidden', 'Include hidden files (starting with .)')
760
- .option('--format <type>', 'Output format: txt, json', 'txt')
953
+ .option('--format <type>', 'Output format: md, json', 'md')
954
+ .option('--no-ai-header', 'Skip AI instruction header (create clean snapshot)')
955
+ .option('-d, --dir', 'Directory mode: scan directory recursively (auto-enabled if no git repo found)')
761
956
  .action((repoPath, options) => createRepoSnapshot(repoPath, options));
762
957
 
763
958
  program
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xelth/eck-snapshot",
3
- "version": "2.2.0",
4
- "description": "A CLI tool to create and restore single-file text snapshots of a Git repository.",
3
+ "version": "3.0.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.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {