srcpack 0.1.3 → 0.1.4
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 +21 -2
- package/dist/bundle.d.ts +3 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +32010 -19234
- package/dist/index.js +1512 -1511
- package/package.json +11 -4
- package/src/bundle.ts +52 -25
- package/src/cli.ts +3 -3
- package/src/gdrive.ts +6 -1
- package/src/init.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srcpack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Zero-config CLI for bundling code into LLM-optimized context files",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"homepage": "https://kriasoft.com/srcpack/",
|
|
33
33
|
"repository": "github:kriasoft/srcpack",
|
|
34
|
+
"bugs": "https://github.com/kriasoft/srcpack/issues",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
34
38
|
"type": "module",
|
|
35
39
|
"types": "./dist/index.d.ts",
|
|
36
40
|
"exports": {
|
|
@@ -48,8 +52,7 @@
|
|
|
48
52
|
"schema.json"
|
|
49
53
|
],
|
|
50
54
|
"scripts": {
|
|
51
|
-
"build": "bun build ./src/cli.ts ./src/index.ts --outdir ./dist --target
|
|
52
|
-
"typecheck": "tsc -p tsconfig.check.json",
|
|
55
|
+
"build": "bun build ./src/cli.ts ./src/index.ts --outdir ./dist --target node && tsc",
|
|
53
56
|
"check": "tsc -p tsconfig.check.json",
|
|
54
57
|
"test": "bun test tests/unit/ tests/e2e/",
|
|
55
58
|
"test:unit": "bun test tests/unit/",
|
|
@@ -67,17 +70,21 @@
|
|
|
67
70
|
"@clack/prompts": "^0.11.0",
|
|
68
71
|
"@googleapis/drive": "^20.0.0",
|
|
69
72
|
"cosmiconfig": "^9.0.0",
|
|
73
|
+
"fast-glob": "^3.3.3",
|
|
70
74
|
"google-auth-library": "^10.5.0",
|
|
71
75
|
"ignore": "^7.0.5",
|
|
72
76
|
"oauth-callback": "^1.2.5",
|
|
73
77
|
"ora": "^9.0.0",
|
|
78
|
+
"picomatch": "^4.0.2",
|
|
74
79
|
"zod": "^4.3.5"
|
|
75
80
|
},
|
|
76
81
|
"devDependencies": {
|
|
77
82
|
"@types/bun": "^1.3.6",
|
|
83
|
+
"@types/picomatch": "^4.0.2",
|
|
78
84
|
"gh-pages": "^6.3.0",
|
|
79
85
|
"prettier": "^3.8.0",
|
|
80
86
|
"typescript": "^5.9.3",
|
|
81
|
-
"vitepress": "^2.0.0-alpha.15"
|
|
87
|
+
"vitepress": "^2.0.0-alpha.15",
|
|
88
|
+
"vitepress-plugin-llms": "^1.10.0"
|
|
82
89
|
}
|
|
83
90
|
}
|
package/src/bundle.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
|
|
3
|
+
import { open, readFile, stat } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
+
import { glob } from "fast-glob";
|
|
6
|
+
import picomatch from "picomatch";
|
|
5
7
|
import ignore, { type Ignore } from "ignore";
|
|
6
8
|
import type { BundleConfigInput } from "./config.ts";
|
|
7
9
|
|
|
@@ -9,12 +11,17 @@ import type { BundleConfigInput } from "./config.ts";
|
|
|
9
11
|
const BINARY_CHECK_SIZE = 8192;
|
|
10
12
|
|
|
11
13
|
async function isBinary(filePath: string): Promise<boolean> {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
if (size === 0) return false;
|
|
14
|
+
const stats = await stat(filePath);
|
|
15
|
+
if (stats.size === 0) return false;
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
-
|
|
17
|
+
const fd = await open(filePath, "r");
|
|
18
|
+
try {
|
|
19
|
+
const buffer = Buffer.alloc(Math.min(stats.size, BINARY_CHECK_SIZE));
|
|
20
|
+
await fd.read(buffer, 0, buffer.length, 0);
|
|
21
|
+
return buffer.includes(0);
|
|
22
|
+
} finally {
|
|
23
|
+
await fd.close();
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
export interface FileEntry {
|
|
@@ -30,11 +37,15 @@ export interface BundleResult {
|
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
/**
|
|
33
|
-
* Normalize BundleConfig to arrays of include/exclude patterns
|
|
40
|
+
* Normalize BundleConfig to arrays of include/exclude/force patterns.
|
|
41
|
+
* - Regular patterns: included, filtered by .gitignore
|
|
42
|
+
* - `!pattern`: excluded from results
|
|
43
|
+
* - `+pattern`: force-included, bypasses .gitignore
|
|
34
44
|
*/
|
|
35
45
|
function normalizePatterns(config: BundleConfigInput): {
|
|
36
46
|
include: string[];
|
|
37
47
|
exclude: string[];
|
|
48
|
+
force: string[];
|
|
38
49
|
} {
|
|
39
50
|
let patterns: string[];
|
|
40
51
|
|
|
@@ -50,29 +61,28 @@ function normalizePatterns(config: BundleConfigInput): {
|
|
|
50
61
|
|
|
51
62
|
const include: string[] = [];
|
|
52
63
|
const exclude: string[] = [];
|
|
64
|
+
const force: string[] = [];
|
|
53
65
|
|
|
54
66
|
for (const p of patterns) {
|
|
55
67
|
if (p.startsWith("!")) {
|
|
56
68
|
exclude.push(p.slice(1));
|
|
69
|
+
} else if (p.startsWith("+")) {
|
|
70
|
+
force.push(p.slice(1));
|
|
57
71
|
} else {
|
|
58
72
|
include.push(p);
|
|
59
73
|
}
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
return { include, exclude };
|
|
76
|
+
return { include, exclude, force };
|
|
63
77
|
}
|
|
64
78
|
|
|
79
|
+
type Matcher = (path: string) => boolean;
|
|
80
|
+
|
|
65
81
|
/**
|
|
66
|
-
* Check if a path matches any of the exclusion
|
|
82
|
+
* Check if a path matches any of the exclusion matchers
|
|
67
83
|
*/
|
|
68
|
-
function isExcluded(filePath: string,
|
|
69
|
-
|
|
70
|
-
const glob = new Glob(pattern);
|
|
71
|
-
if (glob.match(filePath)) {
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
84
|
+
function isExcluded(filePath: string, matchers: Matcher[]): boolean {
|
|
85
|
+
return matchers.some((match) => match(filePath));
|
|
76
86
|
}
|
|
77
87
|
|
|
78
88
|
/**
|
|
@@ -83,7 +93,7 @@ async function loadGitignore(cwd: string): Promise<Ignore> {
|
|
|
83
93
|
const gitignorePath = join(cwd, ".gitignore");
|
|
84
94
|
|
|
85
95
|
try {
|
|
86
|
-
const content = await
|
|
96
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
87
97
|
ig.add(content);
|
|
88
98
|
} catch {
|
|
89
99
|
// No .gitignore file, return empty ignore instance
|
|
@@ -94,20 +104,37 @@ async function loadGitignore(cwd: string): Promise<Ignore> {
|
|
|
94
104
|
|
|
95
105
|
/**
|
|
96
106
|
* Resolve bundle config to a list of file paths.
|
|
97
|
-
*
|
|
107
|
+
* - Regular patterns respect .gitignore
|
|
108
|
+
* - Force patterns (+prefix) bypass .gitignore
|
|
109
|
+
* - Exclude patterns (!prefix) filter both
|
|
98
110
|
*/
|
|
99
111
|
export async function resolvePatterns(
|
|
100
112
|
config: BundleConfigInput,
|
|
101
113
|
cwd: string,
|
|
102
114
|
): Promise<string[]> {
|
|
103
|
-
const { include, exclude } = normalizePatterns(config);
|
|
115
|
+
const { include, exclude, force } = normalizePatterns(config);
|
|
116
|
+
const excludeMatchers = exclude.map((p) => picomatch(p));
|
|
104
117
|
const gitignore = await loadGitignore(cwd);
|
|
105
118
|
const files = new Set<string>();
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
// Regular includes: respect .gitignore
|
|
121
|
+
if (include.length > 0) {
|
|
122
|
+
const matches = await glob(include, { cwd, onlyFiles: true, dot: true });
|
|
123
|
+
for (const match of matches) {
|
|
124
|
+
if (!isExcluded(match, excludeMatchers) && !gitignore.ignores(match)) {
|
|
125
|
+
const fullPath = join(cwd, match);
|
|
126
|
+
if (!(await isBinary(fullPath))) {
|
|
127
|
+
files.add(match);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Force includes: bypass .gitignore
|
|
134
|
+
if (force.length > 0) {
|
|
135
|
+
const matches = await glob(force, { cwd, onlyFiles: true, dot: true });
|
|
136
|
+
for (const match of matches) {
|
|
137
|
+
if (!isExcluded(match, excludeMatchers)) {
|
|
111
138
|
const fullPath = join(cwd, match);
|
|
112
139
|
if (!(await isBinary(fullPath))) {
|
|
113
140
|
files.add(match);
|
|
@@ -184,7 +211,7 @@ export async function createBundle(
|
|
|
184
211
|
for (let i = 0; i < files.length; i++) {
|
|
185
212
|
const filePath = files[i]!;
|
|
186
213
|
const fullPath = join(cwd, filePath);
|
|
187
|
-
const content = await
|
|
214
|
+
const content = await readFile(fullPath, "utf-8");
|
|
188
215
|
const lines = countLines(content);
|
|
189
216
|
|
|
190
217
|
// Separator takes 1 line, then content starts on next line
|
package/src/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
|
|
4
|
-
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import ora from "ora";
|
|
7
7
|
import { bundleOne, type BundleResult } from "./bundle.ts";
|
|
@@ -154,7 +154,7 @@ Options:
|
|
|
154
154
|
}
|
|
155
155
|
} else {
|
|
156
156
|
await mkdir(dirname(outPath), { recursive: true });
|
|
157
|
-
await
|
|
157
|
+
await writeFile(outPath, result.content);
|
|
158
158
|
console.log(
|
|
159
159
|
` ${nameCol} ${filesCol} ${plural(fileCount, "file")} ${linesCol} ${plural(lineCount, "line")} → ${outfile}`,
|
|
160
160
|
);
|
package/src/gdrive.ts
CHANGED
|
@@ -13,7 +13,12 @@ const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
|
13
13
|
const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
14
14
|
const SCOPES = ["https://www.googleapis.com/auth/drive.file"];
|
|
15
15
|
const REDIRECT_URI = "http://localhost:3000/callback";
|
|
16
|
-
const CREDENTIALS_PATH = join(
|
|
16
|
+
const CREDENTIALS_PATH = join(
|
|
17
|
+
homedir(),
|
|
18
|
+
".config",
|
|
19
|
+
"srcpack",
|
|
20
|
+
"credentials.json",
|
|
21
|
+
);
|
|
17
22
|
|
|
18
23
|
export interface Tokens {
|
|
19
24
|
access_token: string;
|
package/src/init.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
|
-
import { appendFile, readFile } from "node:fs/promises";
|
|
4
|
+
import { appendFile, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
|
|
@@ -70,7 +70,7 @@ export async function runInit(): Promise<void> {
|
|
|
70
70
|
|
|
71
71
|
// Generate and write config
|
|
72
72
|
const config = generateConfig(bundles, outDirValue);
|
|
73
|
-
await
|
|
73
|
+
await writeFile(configPath, config);
|
|
74
74
|
|
|
75
75
|
// Add output directory to .gitignore
|
|
76
76
|
await addToGitignore(cwd, outDirValue);
|