combicode 1.0.2 → 1.3.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/CHANGELOG.md +35 -0
- package/README.md +6 -4
- package/config/ignore.json +53 -0
- package/index.js +92 -65
- package/package.json +1 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.3.0](https://github.com/aaurelions/combicode/compare/combicode-js-v1.2.1...combicode-js-v1.3.0) (2025-06-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Add package-lock.json for reproducible builds ([06a417a](https://github.com/aaurelions/combicode/commit/06a417a155e9b72e26d0e091d181cfb8c53f0d28))
|
|
9
|
+
* **ci:** implement independent package versioning for monorepo ([d02cf23](https://github.com/aaurelions/combicode/commit/d02cf233239c7af8db19061f34b769178334b388))
|
|
10
|
+
* improve CLI output and version reporting ([7963a10](https://github.com/aaurelions/combicode/commit/7963a10782c2626608750de53023d37d327d51b2))
|
|
11
|
+
* improve CLI output and version reporting ([e74f6d8](https://github.com/aaurelions/combicode/commit/e74f6d8fbed4f9cdf8ad82f3dae87069f66f7bb6))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **ci:** permission error ([156b76d](https://github.com/aaurelions/combicode/commit/156b76d3ab1550123df2ded6b1da5d6e2e2cc008))
|
|
17
|
+
* **npm:** Set public access for publishing and bump version to 1.0.1 ([6c91eb7](https://github.com/aaurelions/combicode/commit/6c91eb714c81ec0201bb0fcfad8ad9fb4124cd7e))
|
|
18
|
+
* Use scoped npm package name and bump python version ([8a1b347](https://github.com/aaurelions/combicode/commit/8a1b347f6c54c9762acf354ef289c293d3ef21a3))
|
|
19
|
+
|
|
20
|
+
## [1.2.0](https://github.com/aaurelions/combicode/compare/combicode-js-v1.1.0...combicode-js-v1.2.0) (2025-06-25)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* Add package-lock.json for reproducible builds ([06a417a](https://github.com/aaurelions/combicode/commit/06a417a155e9b72e26d0e091d181cfb8c53f0d28))
|
|
26
|
+
* **ci:** implement independent package versioning for monorepo ([d02cf23](https://github.com/aaurelions/combicode/commit/d02cf233239c7af8db19061f34b769178334b388))
|
|
27
|
+
* improve CLI output and version reporting ([7963a10](https://github.com/aaurelions/combicode/commit/7963a10782c2626608750de53023d37d327d51b2))
|
|
28
|
+
* improve CLI output and version reporting ([e74f6d8](https://github.com/aaurelions/combicode/commit/e74f6d8fbed4f9cdf8ad82f3dae87069f66f7bb6))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
* **ci:** permission error ([156b76d](https://github.com/aaurelions/combicode/commit/156b76d3ab1550123df2ded6b1da5d6e2e2cc008))
|
|
34
|
+
* **npm:** Set public access for publishing and bump version to 1.0.1 ([6c91eb7](https://github.com/aaurelions/combicode/commit/6c91eb714c81ec0201bb0fcfad8ad9fb4124cd7e))
|
|
35
|
+
* Use scoped npm package name and bump python version ([8a1b347](https://github.com/aaurelions/combicode/commit/8a1b347f6c54c9762acf354ef289c293d3ef21a3))
|
package/README.md
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
[](https://pypi.org/project/combicode/)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
<img align="center" src="https://github.com/aaurelions/combicode/raw/main/screenshot.png" width="
|
|
7
|
+
<img align="center" src="https://github.com/aaurelions/combicode/raw/main/screenshot.png" width="800"/>
|
|
8
8
|
|
|
9
9
|
**Combicode** is a zero-dependency CLI tool that intelligently combines your project's source code into a single, LLM-friendly text file.
|
|
10
10
|
|
|
11
|
-
Paste the contents of `combicode.txt` into ChatGPT, Claude, or any other LLM to
|
|
11
|
+
The generated file starts with a system prompt and a file tree overview, priming the LLM to understand your project's complete context instantly. Paste the contents of `combicode.txt` into ChatGPT, Claude, or any other LLM to get started.
|
|
12
12
|
|
|
13
13
|
## Why use Combicode?
|
|
14
14
|
|
|
15
|
-
- **Maximum Context:**
|
|
15
|
+
- **Maximum Context:** Gives your LLM a complete picture of your project structure and code.
|
|
16
|
+
- **Intelligent Priming:** Starts the output with a system prompt and a file tree, directing the LLM to analyze the entire codebase before responding.
|
|
16
17
|
- **Intelligent Ignoring:** Automatically skips `node_modules`, `.venv`, `dist`, `.git`, binary files, and other common junk.
|
|
17
18
|
- **`.gitignore` Aware:** Respects your project's existing `.gitignore` rules out of the box.
|
|
18
19
|
- **Zero-Install Usage:** Run it directly with `npx` or `pipx` without polluting your environment.
|
|
@@ -34,7 +35,7 @@ npx combicode
|
|
|
34
35
|
pipx run combicode
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
This will create a `combicode.txt` file in your project directory.
|
|
38
|
+
This will create a `combicode.txt` file in your project directory, complete with the context-setting header.
|
|
38
39
|
|
|
39
40
|
## Usage and Options
|
|
40
41
|
|
|
@@ -88,6 +89,7 @@ npx combicode -e "**/*_test.py,docs/**"
|
|
|
88
89
|
| `--include-ext` | `-i` | Comma-separated list of extensions to exclusively include. | (include all) |
|
|
89
90
|
| `--exclude` | `-e` | Comma-separated list of additional glob patterns to exclude. | (none) |
|
|
90
91
|
| `--no-gitignore` | | Do not use patterns from the project's `.gitignore` file. | `false` |
|
|
92
|
+
| `--no-header` | | Omit the introductory prompt and file tree from the output. | `false` |
|
|
91
93
|
| `--version` | `-v` | Show the version number. | |
|
|
92
94
|
| `--help` | `-h` | Show the help message. | |
|
|
93
95
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[
|
|
2
|
+
"**/node_modules/**",
|
|
3
|
+
"**/.git/**",
|
|
4
|
+
"**/.vscode/**",
|
|
5
|
+
"**/.idea/**",
|
|
6
|
+
"**/*.log",
|
|
7
|
+
"**/.env",
|
|
8
|
+
"**/*.lock",
|
|
9
|
+
"**/.venv/**",
|
|
10
|
+
"**/venv/**",
|
|
11
|
+
"**/env/**",
|
|
12
|
+
"**/__pycache__/**",
|
|
13
|
+
"**/*.pyc",
|
|
14
|
+
"**/*.egg-info/**",
|
|
15
|
+
"**/build/**",
|
|
16
|
+
"**/dist/**",
|
|
17
|
+
"**/.pytest_cache/**",
|
|
18
|
+
"**/.npm/**",
|
|
19
|
+
"**/pnpm-lock.yaml",
|
|
20
|
+
"**/package-lock.json",
|
|
21
|
+
"**/.next/**",
|
|
22
|
+
"**/.DS_Store",
|
|
23
|
+
"**/Thumbs.db",
|
|
24
|
+
"**/*.png",
|
|
25
|
+
"**/*.jpg",
|
|
26
|
+
"**/*.jpeg",
|
|
27
|
+
"**/*.gif",
|
|
28
|
+
"**/*.ico",
|
|
29
|
+
"**/*.svg",
|
|
30
|
+
"**/*.webp",
|
|
31
|
+
"**/*.mp3",
|
|
32
|
+
"**/*.wav",
|
|
33
|
+
"**/*.flac",
|
|
34
|
+
"**/*.mp4",
|
|
35
|
+
"**/*.mov",
|
|
36
|
+
"**/*.avi",
|
|
37
|
+
"**/*.zip",
|
|
38
|
+
"**/*.tar.gz",
|
|
39
|
+
"**/*.rar",
|
|
40
|
+
"**/*.pdf",
|
|
41
|
+
"**/*.doc",
|
|
42
|
+
"**/*.docx",
|
|
43
|
+
"**/*.xls",
|
|
44
|
+
"**/*.xlsx",
|
|
45
|
+
"**/*.dll",
|
|
46
|
+
"**/*.exe",
|
|
47
|
+
"**/*.so",
|
|
48
|
+
"**/*.a",
|
|
49
|
+
"**/*.lib",
|
|
50
|
+
"**/*.o",
|
|
51
|
+
"**/*.bin",
|
|
52
|
+
"**/*.iso"
|
|
53
|
+
]
|
package/index.js
CHANGED
|
@@ -8,60 +8,29 @@ const glob = require("fast-glob");
|
|
|
8
8
|
|
|
9
9
|
const { version } = require("./package.json");
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Common binary file extensions
|
|
35
|
-
"**/*.png",
|
|
36
|
-
"**/*.jpg",
|
|
37
|
-
"**/*.jpeg",
|
|
38
|
-
"**/*.gif",
|
|
39
|
-
"**/*.ico",
|
|
40
|
-
"**/*.svg",
|
|
41
|
-
"**/*.webp",
|
|
42
|
-
"**/*.mp3",
|
|
43
|
-
"**/*.wav",
|
|
44
|
-
"**/*.flac",
|
|
45
|
-
"**/*.mp4",
|
|
46
|
-
"**/*.mov",
|
|
47
|
-
"**/*.avi",
|
|
48
|
-
"**/*.zip",
|
|
49
|
-
"**/*.tar.gz",
|
|
50
|
-
"**/*.rar",
|
|
51
|
-
"**/*.pdf",
|
|
52
|
-
"**/*.doc",
|
|
53
|
-
"**/*.docx",
|
|
54
|
-
"**/*.xls",
|
|
55
|
-
"**/*.xlsx",
|
|
56
|
-
"**/*.dll",
|
|
57
|
-
"**/*.exe",
|
|
58
|
-
"**/*.so",
|
|
59
|
-
"**/*.a",
|
|
60
|
-
"**/*.lib",
|
|
61
|
-
"**/*.o",
|
|
62
|
-
"**/*.bin",
|
|
63
|
-
"**/*.iso",
|
|
64
|
-
];
|
|
11
|
+
const SYSTEM_PROMPT = `You are an expert software architect. The user is providing you with the complete source code for a project, contained in a single file. Your task is to meticulously analyze the provided codebase to gain a comprehensive understanding of its structure, functionality, dependencies, and overall architecture.
|
|
12
|
+
|
|
13
|
+
A file tree is provided below to give you a high-level overview. The subsequent sections contain the full content of each file, clearly marked with "// FILE: <path>".
|
|
14
|
+
|
|
15
|
+
Your instructions are:
|
|
16
|
+
1. **Analyze Thoroughly:** Read through every file to understand its purpose and how it interacts with other files.
|
|
17
|
+
2. **Identify Key Components:** Pay close attention to configuration files (like package.json, pyproject.toml), entry points (like index.js, main.py), and core logic.
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
function loadDefaultIgnorePatterns() {
|
|
21
|
+
const configPath = path.resolve(__dirname, "config", "ignore.json");
|
|
22
|
+
try {
|
|
23
|
+
const rawConfig = fs.readFileSync(configPath, "utf8");
|
|
24
|
+
return JSON.parse(rawConfig);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.error(
|
|
27
|
+
`❌ Critical: Could not read or parse bundled ignore config at ${configPath}`
|
|
28
|
+
);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const DEFAULT_IGNORE_PATTERNS = loadDefaultIgnorePatterns();
|
|
65
34
|
|
|
66
35
|
function isLikelyBinary(file) {
|
|
67
36
|
const buffer = Buffer.alloc(512);
|
|
@@ -69,18 +38,52 @@ function isLikelyBinary(file) {
|
|
|
69
38
|
try {
|
|
70
39
|
fd = fs.openSync(file, "r");
|
|
71
40
|
const bytesRead = fs.readSync(fd, buffer, 0, 512, 0);
|
|
72
|
-
// Check for null bytes, a strong indicator of a binary file
|
|
73
41
|
return buffer.slice(0, bytesRead).includes(0);
|
|
74
42
|
} catch (e) {
|
|
75
|
-
// If we can't read it, treat it as something to skip
|
|
76
43
|
return true;
|
|
77
44
|
} finally {
|
|
78
45
|
if (fd) fs.closeSync(fd);
|
|
79
46
|
}
|
|
80
47
|
}
|
|
81
48
|
|
|
49
|
+
function generateFileTree(files, root) {
|
|
50
|
+
let tree = `${path.basename(root)}/\n`;
|
|
51
|
+
const structure = {};
|
|
52
|
+
|
|
53
|
+
files.forEach((file) => {
|
|
54
|
+
const parts = file.split(path.sep);
|
|
55
|
+
let currentLevel = structure;
|
|
56
|
+
parts.forEach((part) => {
|
|
57
|
+
if (!currentLevel[part]) {
|
|
58
|
+
currentLevel[part] = {};
|
|
59
|
+
}
|
|
60
|
+
currentLevel = currentLevel[part];
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const buildTree = (level, prefix) => {
|
|
65
|
+
const entries = Object.keys(level);
|
|
66
|
+
entries.forEach((entry, index) => {
|
|
67
|
+
const isLast = index === entries.length - 1;
|
|
68
|
+
tree += `${prefix}${isLast ? "└── " : "├── "}${entry}\n`;
|
|
69
|
+
if (Object.keys(level[entry]).length > 0) {
|
|
70
|
+
buildTree(level[entry], `${prefix}${isLast ? " " : "│ "}`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
buildTree(structure, "");
|
|
76
|
+
return tree;
|
|
77
|
+
}
|
|
78
|
+
|
|
82
79
|
async function main() {
|
|
83
|
-
const
|
|
80
|
+
const rawArgv = hideBin(process.argv);
|
|
81
|
+
if (rawArgv.includes("--version") || rawArgv.includes("-v")) {
|
|
82
|
+
console.log(`Combicode (JavaScript), version ${version}`);
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const argv = yargs(rawArgv)
|
|
84
87
|
.scriptName("combicode")
|
|
85
88
|
.usage("$0 [options]")
|
|
86
89
|
.option("o", {
|
|
@@ -110,7 +113,12 @@ async function main() {
|
|
|
110
113
|
type: "boolean",
|
|
111
114
|
default: false,
|
|
112
115
|
})
|
|
113
|
-
.
|
|
116
|
+
.option("no-header", {
|
|
117
|
+
describe: "Omit the introductory prompt and file tree from the output",
|
|
118
|
+
type: "boolean",
|
|
119
|
+
default: false,
|
|
120
|
+
})
|
|
121
|
+
.version(version)
|
|
114
122
|
.alias("v", "version")
|
|
115
123
|
.help()
|
|
116
124
|
.alias("h", "help").argv;
|
|
@@ -155,7 +163,8 @@ async function main() {
|
|
|
155
163
|
|
|
156
164
|
const includedFiles = allFiles
|
|
157
165
|
.filter((file) => {
|
|
158
|
-
|
|
166
|
+
const stats = fs.statSync(file, { throwIfNoEntry: false });
|
|
167
|
+
if (!stats || stats.isDirectory()) return false;
|
|
159
168
|
if (isLikelyBinary(file)) return false;
|
|
160
169
|
if (allowedExtensions && !allowedExtensions.has(path.extname(file)))
|
|
161
170
|
return false;
|
|
@@ -168,22 +177,40 @@ async function main() {
|
|
|
168
177
|
process.exit(1);
|
|
169
178
|
}
|
|
170
179
|
|
|
180
|
+
const relativeFiles = includedFiles.map((file) =>
|
|
181
|
+
path.relative(projectRoot, file)
|
|
182
|
+
);
|
|
183
|
+
|
|
171
184
|
if (argv.dryRun) {
|
|
172
|
-
console.log("\n📋 Files to be included (Dry Run)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
);
|
|
185
|
+
console.log("\n📋 Files to be included (Dry Run):\n");
|
|
186
|
+
const tree = generateFileTree(relativeFiles, projectRoot);
|
|
187
|
+
console.log(tree);
|
|
176
188
|
console.log(`\nTotal: ${includedFiles.length} files.`);
|
|
177
189
|
return;
|
|
178
190
|
}
|
|
179
191
|
|
|
180
192
|
const outputStream = fs.createWriteStream(argv.output);
|
|
193
|
+
|
|
194
|
+
if (!argv.noHeader) {
|
|
195
|
+
outputStream.write(SYSTEM_PROMPT + "\n");
|
|
196
|
+
outputStream.write("## Project File Tree\n\n");
|
|
197
|
+
outputStream.write("```\n");
|
|
198
|
+
const tree = generateFileTree(relativeFiles, projectRoot);
|
|
199
|
+
outputStream.write(tree);
|
|
200
|
+
outputStream.write("```\n\n");
|
|
201
|
+
outputStream.write("---\n\n");
|
|
202
|
+
}
|
|
203
|
+
|
|
181
204
|
for (const file of includedFiles) {
|
|
182
205
|
const relativePath = path.relative(projectRoot, file).replace(/\\/g, "/");
|
|
183
206
|
outputStream.write(`// FILE: ${relativePath}` + "\n");
|
|
184
207
|
outputStream.write("```\n");
|
|
185
|
-
|
|
186
|
-
|
|
208
|
+
try {
|
|
209
|
+
const content = fs.readFileSync(file, "utf8");
|
|
210
|
+
outputStream.write(content);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
outputStream.write(`... (error reading file: ${e.message}) ...`);
|
|
213
|
+
}
|
|
187
214
|
outputStream.write("\n```\n\n");
|
|
188
215
|
}
|
|
189
216
|
outputStream.end();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "combicode",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A CLI tool to combine a project's codebase into a single file for LLM context.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"fast-glob": "^3.3.1",
|
|
33
|
-
"ignore": "^5.2.4",
|
|
34
33
|
"yargs": "^17.7.2"
|
|
35
34
|
}
|
|
36
35
|
}
|