srcpack 0.1.7 → 0.1.8

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
@@ -48,11 +48,14 @@ Or add to `package.json`:
48
48
 
49
49
  ### Options
50
50
 
51
- | Option | Default | Description |
52
- | --------- | ---------- | -------------------------------- |
53
- | `outDir` | `.srcpack` | Output directory for bundles |
54
- | `bundles` | | Named bundles with glob patterns |
55
- | `upload` | — | Upload destination(s) |
51
+ | Option | Default | Description |
52
+ | ------------- | ---------- | -------------------------------------- |
53
+ | `outDir` | `.srcpack` | Output directory for bundles |
54
+ | `emptyOutDir` | `true`\* | Empty output directory before bundling |
55
+ | `bundles` | — | Named bundles with glob patterns |
56
+ | `upload` | — | Upload destination(s) |
57
+
58
+ \*`emptyOutDir` defaults to `true` when `outDir` is inside project root. When `outDir` is outside root, a warning is emitted unless explicitly set.
56
59
 
57
60
  ### Bundle Config
58
61
 
@@ -90,6 +93,7 @@ export default defineConfig({
90
93
  folderId: "1ABC...", // Google Drive folder ID (from URL)
91
94
  clientId: "...",
92
95
  clientSecret: "...",
96
+ exclude: ["local"], // skip specific bundles
93
97
  },
94
98
  });
95
99
  ```
@@ -127,12 +131,14 @@ export function utils() {
127
131
  ## CLI
128
132
 
129
133
  ```bash
130
- npx srcpack # Bundle all, upload if configured
131
- npx srcpack web api # Bundle specific bundles only
132
- npx srcpack --dry-run # Preview without writing files
133
- npx srcpack --no-upload # Bundle only, skip upload
134
- npx srcpack init # Interactive config setup
135
- npx srcpack login # Authenticate with Google Drive
134
+ npx srcpack # Bundle all, upload if configured
135
+ npx srcpack web api # Bundle specific bundles only
136
+ npx srcpack --dry-run # Preview without writing files
137
+ npx srcpack --emptyOutDir # Empty output directory before bundling
138
+ npx srcpack --no-emptyOutDir # Keep existing files in output directory
139
+ npx srcpack --no-upload # Bundle only, skip upload
140
+ npx srcpack init # Interactive config setup
141
+ npx srcpack login # Authenticate with Google Drive
136
142
  ```
137
143
 
138
144
  ## API
package/dist/cli.js CHANGED
@@ -204450,8 +204450,8 @@ var require_src8 = __commonJS((exports, module) => {
204450
204450
  });
204451
204451
 
204452
204452
  // src/cli.ts
204453
- import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
204454
- import { dirname as dirname3, join as join6 } from "node:path";
204453
+ import { mkdir as mkdir3, readdir, rm, writeFile as writeFile4 } from "node:fs/promises";
204454
+ import { dirname as dirname3, isAbsolute, join as join6, relative, resolve } from "node:path";
204455
204455
 
204456
204456
  // node_modules/ora/index.js
204457
204457
  import process8 from "node:process";
@@ -221283,10 +221283,12 @@ var UploadConfigSchema = exports_external.object({
221283
221283
  provider: exports_external.literal("gdrive"),
221284
221284
  folderId: exports_external.string().optional(),
221285
221285
  clientId: exports_external.string().min(1),
221286
- clientSecret: exports_external.string().min(1)
221286
+ clientSecret: exports_external.string().min(1),
221287
+ exclude: exports_external.array(exports_external.string()).optional()
221287
221288
  });
221288
221289
  var ConfigSchema = exports_external.object({
221289
221290
  outDir: exports_external.string().default(".srcpack"),
221291
+ emptyOutDir: exports_external.boolean().optional(),
221290
221292
  upload: exports_external.union([UploadConfigSchema, exports_external.array(UploadConfigSchema).min(1)]).optional(),
221291
221293
  bundles: exports_external.record(exports_external.string(), BundleConfigSchema)
221292
221294
  });
@@ -223393,6 +223395,21 @@ function formatNumber(n) {
223393
223395
  function plural(n, singular, pluralForm) {
223394
223396
  return n === 1 ? singular : pluralForm ?? singular + "s";
223395
223397
  }
223398
+ function isOutDirInsideRoot(outDir, root) {
223399
+ const absoluteOutDir = isAbsolute(outDir) ? outDir : resolve(root, outDir);
223400
+ const rel = relative(root, absoluteOutDir);
223401
+ return !rel.startsWith("..") && !isAbsolute(rel);
223402
+ }
223403
+ async function emptyDirectory(dir, skip = []) {
223404
+ let entries;
223405
+ try {
223406
+ entries = await readdir(dir);
223407
+ } catch {
223408
+ return;
223409
+ }
223410
+ const skipSet = new Set(skip);
223411
+ await Promise.all(entries.filter((entry) => !skipSet.has(entry)).map((entry) => rm(join6(dir, entry), { recursive: true, force: true })));
223412
+ }
223396
223413
  async function main() {
223397
223414
  const args = process.argv.slice(2);
223398
223415
  if (args.includes("--help") || args.includes("-h")) {
@@ -223408,9 +223425,11 @@ Usage:
223408
223425
  npx srcpack login Authenticate with Google Drive
223409
223426
 
223410
223427
  Options:
223411
- --dry-run Preview bundles without writing files
223412
- --no-upload Skip uploading to cloud storage
223413
- -h, --help Show this help message
223428
+ --dry-run Preview bundles without writing files
223429
+ --emptyOutDir Empty output directory before bundling
223430
+ --no-emptyOutDir Keep existing files in output directory
223431
+ --no-upload Skip uploading to cloud storage
223432
+ -h, --help Show this help message
223414
223433
  `);
223415
223434
  return;
223416
223435
  }
@@ -223424,6 +223443,7 @@ Options:
223424
223443
  }
223425
223444
  const dryRun = args.includes("--dry-run");
223426
223445
  const noUpload = args.includes("--no-upload");
223446
+ const emptyOutDirFlag = args.includes("--emptyOutDir") ? true : args.includes("--no-emptyOutDir") ? false : undefined;
223427
223447
  const subcommands = ["init", "login"];
223428
223448
  const requestedBundles = args.filter((arg) => !arg.startsWith("-") && !subcommands.includes(arg));
223429
223449
  const config2 = await loadConfig();
@@ -223443,6 +223463,15 @@ Options:
223443
223463
  return;
223444
223464
  }
223445
223465
  const cwd = process.cwd();
223466
+ const outDirInsideRoot = isOutDirInsideRoot(config2.outDir, cwd);
223467
+ const emptyOutDir = emptyOutDirFlag ?? config2.emptyOutDir ?? outDirInsideRoot;
223468
+ if (!outDirInsideRoot && emptyOutDirFlag === undefined && config2.emptyOutDir === undefined) {
223469
+ console.warn(`Warning: outDir "${config2.outDir}" is outside project root. ` + "Use --emptyOutDir to suppress this warning and empty the directory.");
223470
+ }
223471
+ if (emptyOutDir && !dryRun) {
223472
+ const outDirPath = isAbsolute(config2.outDir) ? config2.outDir : resolve(cwd, config2.outDir);
223473
+ await emptyDirectory(outDirPath, [".git"]);
223474
+ }
223446
223475
  const outputs = [];
223447
223476
  const bundleSpinner = ora({
223448
223477
  text: `Bundling ${bundleNames[0]}...`,
@@ -223555,6 +223584,13 @@ function printUploadConfigHelp() {
223555
223584
  `);
223556
223585
  }
223557
223586
  async function handleGdriveUpload(uploadConfig, outputs, cwd) {
223587
+ const excludeSet = new Set(uploadConfig.exclude ?? []);
223588
+ const toUpload = outputs.filter((o2) => !excludeSet.has(o2.name));
223589
+ if (toUpload.length === 0) {
223590
+ console.log(`
223591
+ No bundles to upload (all excluded).`);
223592
+ return;
223593
+ }
223558
223594
  try {
223559
223595
  await ensureAuthenticated(uploadConfig);
223560
223596
  const uploadSpinner = ora({
@@ -223562,10 +223598,10 @@ async function handleGdriveUpload(uploadConfig, outputs, cwd) {
223562
223598
  color: "cyan"
223563
223599
  }).start();
223564
223600
  const results = [];
223565
- for (let i2 = 0;i2 < outputs.length; i2++) {
223566
- const output = outputs[i2];
223601
+ for (let i2 = 0;i2 < toUpload.length; i2++) {
223602
+ const output = toUpload[i2];
223567
223603
  const filePath = join6(cwd, output.outfile);
223568
- uploadSpinner.text = `Uploading ${output.name}... (${i2 + 1}/${outputs.length})`;
223604
+ uploadSpinner.text = `Uploading ${output.name}... (${i2 + 1}/${toUpload.length})`;
223569
223605
  const result = await uploadFile(filePath, uploadConfig);
223570
223606
  results.push(result);
223571
223607
  }
package/dist/config.d.ts CHANGED
@@ -1,28 +1,52 @@
1
1
  import { z } from "zod";
2
2
  export declare function expandPath(p: string): string;
3
+ /**
4
+ * Bundle configuration. Accepts a string pattern, array of patterns, or object.
5
+ * Patterns prefixed with `!` are exclusions. Patterns prefixed with `+` force
6
+ * inclusion (bypass .gitignore).
7
+ */
3
8
  declare const BundleConfigSchema: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
4
9
  include: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
5
10
  outfile: z.ZodOptional<z.ZodString>;
6
11
  index: z.ZodDefault<z.ZodBoolean>;
7
12
  }, z.core.$strip>]>;
13
+ /**
14
+ * Upload destination configuration.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * upload: {
19
+ * provider: "gdrive",
20
+ * clientId: process.env.GDRIVE_CLIENT_ID,
21
+ * clientSecret: process.env.GDRIVE_CLIENT_SECRET,
22
+ * folderId: "1abc...",
23
+ * exclude: ["local", "debug"],
24
+ * }
25
+ * ```
26
+ */
8
27
  declare const UploadConfigSchema: z.ZodObject<{
9
28
  provider: z.ZodLiteral<"gdrive">;
10
29
  folderId: z.ZodOptional<z.ZodString>;
11
30
  clientId: z.ZodString;
12
31
  clientSecret: z.ZodString;
32
+ exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
13
33
  }, z.core.$strip>;
34
+ /** Root configuration for srcpack. */
14
35
  declare const ConfigSchema: z.ZodObject<{
15
36
  outDir: z.ZodDefault<z.ZodString>;
37
+ emptyOutDir: z.ZodOptional<z.ZodBoolean>;
16
38
  upload: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
17
39
  provider: z.ZodLiteral<"gdrive">;
18
40
  folderId: z.ZodOptional<z.ZodString>;
19
41
  clientId: z.ZodString;
20
42
  clientSecret: z.ZodString;
43
+ exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
21
44
  }, z.core.$strip>, z.ZodArray<z.ZodObject<{
22
45
  provider: z.ZodLiteral<"gdrive">;
23
46
  folderId: z.ZodOptional<z.ZodString>;
24
47
  clientId: z.ZodString;
25
48
  clientSecret: z.ZodString;
49
+ exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
26
50
  }, z.core.$strip>>]>>;
27
51
  bundles: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodObject<{
28
52
  include: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
package/dist/index.js CHANGED
@@ -187020,10 +187020,12 @@ var UploadConfigSchema = exports_external.object({
187020
187020
  provider: exports_external.literal("gdrive"),
187021
187021
  folderId: exports_external.string().optional(),
187022
187022
  clientId: exports_external.string().min(1),
187023
- clientSecret: exports_external.string().min(1)
187023
+ clientSecret: exports_external.string().min(1),
187024
+ exclude: exports_external.array(exports_external.string()).optional()
187024
187025
  });
187025
187026
  var ConfigSchema = exports_external.object({
187026
187027
  outDir: exports_external.string().default(".srcpack"),
187028
+ emptyOutDir: exports_external.boolean().optional(),
187027
187029
  upload: exports_external.union([UploadConfigSchema, exports_external.array(UploadConfigSchema).min(1)]).optional(),
187028
187030
  bundles: exports_external.record(exports_external.string(), BundleConfigSchema)
187029
187031
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "srcpack",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Zero-config CLI for bundling code into LLM-optimized context files",
5
5
  "keywords": [
6
6
  "llm",
package/src/cli.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
- import { mkdir, writeFile } from "node:fs/promises";
5
- import { dirname, join } from "node:path";
4
+ import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
5
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
6
6
  import ora from "ora";
7
7
  import { bundleOne, type BundleResult } from "./bundle.ts";
8
8
  import {
@@ -38,6 +38,31 @@ function plural(n: number, singular: string, pluralForm?: string): string {
38
38
  return n === 1 ? singular : (pluralForm ?? singular + "s");
39
39
  }
40
40
 
41
+ function isOutDirInsideRoot(outDir: string, root: string): boolean {
42
+ const absoluteOutDir = isAbsolute(outDir) ? outDir : resolve(root, outDir);
43
+ const rel = relative(root, absoluteOutDir);
44
+ return !rel.startsWith("..") && !isAbsolute(rel);
45
+ }
46
+
47
+ /**
48
+ * Empty a directory while preserving specified entries (e.g., `.git`).
49
+ * Uses `force: true` to handle read-only or in-use files.
50
+ */
51
+ async function emptyDirectory(dir: string, skip: string[] = []): Promise<void> {
52
+ let entries: string[];
53
+ try {
54
+ entries = await readdir(dir);
55
+ } catch {
56
+ return; // Directory doesn't exist, nothing to empty
57
+ }
58
+ const skipSet = new Set(skip);
59
+ await Promise.all(
60
+ entries
61
+ .filter((entry) => !skipSet.has(entry))
62
+ .map((entry) => rm(join(dir, entry), { recursive: true, force: true })),
63
+ );
64
+ }
65
+
41
66
  async function main() {
42
67
  const args = process.argv.slice(2);
43
68
 
@@ -54,9 +79,11 @@ Usage:
54
79
  npx srcpack login Authenticate with Google Drive
55
80
 
56
81
  Options:
57
- --dry-run Preview bundles without writing files
58
- --no-upload Skip uploading to cloud storage
59
- -h, --help Show this help message
82
+ --dry-run Preview bundles without writing files
83
+ --emptyOutDir Empty output directory before bundling
84
+ --no-emptyOutDir Keep existing files in output directory
85
+ --no-upload Skip uploading to cloud storage
86
+ -h, --help Show this help message
60
87
  `);
61
88
  return;
62
89
  }
@@ -73,6 +100,12 @@ Options:
73
100
 
74
101
  const dryRun = args.includes("--dry-run");
75
102
  const noUpload = args.includes("--no-upload");
103
+ // CLI flags: --emptyOutDir forces true, --no-emptyOutDir forces false
104
+ const emptyOutDirFlag = args.includes("--emptyOutDir")
105
+ ? true
106
+ : args.includes("--no-emptyOutDir")
107
+ ? false
108
+ : undefined;
76
109
  const subcommands = ["init", "login"];
77
110
  const requestedBundles = args.filter(
78
111
  (arg) => !arg.startsWith("-") && !subcommands.includes(arg),
@@ -106,6 +139,31 @@ Options:
106
139
  }
107
140
 
108
141
  const cwd = process.cwd();
142
+
143
+ // Resolve emptyOutDir: CLI flag > config > auto (true if inside root)
144
+ const outDirInsideRoot = isOutDirInsideRoot(config.outDir, cwd);
145
+ const emptyOutDir = emptyOutDirFlag ?? config.emptyOutDir ?? outDirInsideRoot;
146
+
147
+ // Warn if outDir is outside root and emptyOutDir is not explicitly set
148
+ if (
149
+ !outDirInsideRoot &&
150
+ emptyOutDirFlag === undefined &&
151
+ config.emptyOutDir === undefined
152
+ ) {
153
+ console.warn(
154
+ `Warning: outDir "${config.outDir}" is outside project root. ` +
155
+ "Use --emptyOutDir to suppress this warning and empty the directory.",
156
+ );
157
+ }
158
+
159
+ // Empty outDir before bundling (unless dry-run)
160
+ if (emptyOutDir && !dryRun) {
161
+ const outDirPath = isAbsolute(config.outDir)
162
+ ? config.outDir
163
+ : resolve(cwd, config.outDir);
164
+ await emptyDirectory(outDirPath, [".git"]);
165
+ }
166
+
109
167
  const outputs: BundleOutput[] = [];
110
168
 
111
169
  // Process all bundles with progress
@@ -279,6 +337,15 @@ async function handleGdriveUpload(
279
337
  outputs: BundleOutput[],
280
338
  cwd: string,
281
339
  ): Promise<void> {
340
+ // Filter out excluded bundles
341
+ const excludeSet = new Set(uploadConfig.exclude ?? []);
342
+ const toUpload = outputs.filter((o) => !excludeSet.has(o.name));
343
+
344
+ if (toUpload.length === 0) {
345
+ console.log("\nNo bundles to upload (all excluded).");
346
+ return;
347
+ }
348
+
282
349
  try {
283
350
  await ensureAuthenticated(uploadConfig);
284
351
 
@@ -289,10 +356,10 @@ async function handleGdriveUpload(
289
356
 
290
357
  const results: UploadResult[] = [];
291
358
 
292
- for (let i = 0; i < outputs.length; i++) {
293
- const output = outputs[i]!;
359
+ for (let i = 0; i < toUpload.length; i++) {
360
+ const output = toUpload[i]!;
294
361
  const filePath = join(cwd, output.outfile);
295
- uploadSpinner.text = `Uploading ${output.name}... (${i + 1}/${outputs.length})`;
362
+ uploadSpinner.text = `Uploading ${output.name}... (${i + 1}/${toUpload.length})`;
296
363
  const result = await uploadFile(filePath, uploadConfig);
297
364
  results.push(result);
298
365
  }
package/src/config.ts CHANGED
@@ -12,33 +12,68 @@ export function expandPath(p: string): string {
12
12
  return p;
13
13
  }
14
14
 
15
+ /** Glob patterns for file matching. Single pattern or array of patterns. */
15
16
  const PatternsSchema = z.union([
16
17
  z.string().min(1),
17
18
  z.array(z.string().min(1)).min(1),
18
19
  ]);
19
20
 
21
+ /**
22
+ * Bundle configuration. Accepts a string pattern, array of patterns, or object.
23
+ * Patterns prefixed with `!` are exclusions. Patterns prefixed with `+` force
24
+ * inclusion (bypass .gitignore).
25
+ */
20
26
  const BundleConfigSchema = z.union([
21
- z.string().min(1), // "src/**/*"
22
- z.array(z.string().min(1)).min(1), // ["src/**/*", "!src/specs"]
27
+ z.string().min(1),
28
+ z.array(z.string().min(1)).min(1),
23
29
  z.object({
30
+ /** Glob patterns to include in the bundle. */
24
31
  include: PatternsSchema,
32
+ /** Custom output file path. Defaults to `<outDir>/<bundleName>.txt`. */
25
33
  outfile: z.string().optional(),
26
- index: z.boolean().default(true), // Include index header in output
34
+ /** Include file index header in output. Defaults to true. */
35
+ index: z.boolean().default(true),
27
36
  }),
28
37
  ]);
29
38
 
39
+ /**
40
+ * Upload destination configuration.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * upload: {
45
+ * provider: "gdrive",
46
+ * clientId: process.env.GDRIVE_CLIENT_ID,
47
+ * clientSecret: process.env.GDRIVE_CLIENT_SECRET,
48
+ * folderId: "1abc...",
49
+ * exclude: ["local", "debug"],
50
+ * }
51
+ * ```
52
+ */
30
53
  const UploadConfigSchema = z.object({
54
+ /** Upload provider. Currently only "gdrive" is supported. */
31
55
  provider: z.literal("gdrive"),
56
+ /** Google Drive folder ID to upload files to. If omitted, uploads to root. */
32
57
  folderId: z.string().optional(),
58
+ /** OAuth 2.0 client ID from Google Cloud Console. */
33
59
  clientId: z.string().min(1),
60
+ /** OAuth 2.0 client secret from Google Cloud Console. */
34
61
  clientSecret: z.string().min(1),
62
+ /** Bundle names to skip during upload. Supports exact names only. */
63
+ exclude: z.array(z.string()).optional(),
35
64
  });
36
65
 
66
+ /** Root configuration for srcpack. */
37
67
  const ConfigSchema = z.object({
68
+ /** Output directory for bundle files. Defaults to ".srcpack". */
38
69
  outDir: z.string().default(".srcpack"),
70
+ /** Empty outDir before bundling. Auto-enabled when outDir is inside project root. */
71
+ emptyOutDir: z.boolean().optional(),
72
+ /** Upload configuration for cloud storage. Single destination or array. */
39
73
  upload: z
40
74
  .union([UploadConfigSchema, z.array(UploadConfigSchema).min(1)])
41
75
  .optional(),
76
+ /** Named bundles mapping bundle name to glob patterns or config object. */
42
77
  bundles: z.record(z.string(), BundleConfigSchema),
43
78
  });
44
79