@delfini/cli 0.2.1 → 0.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/README.md CHANGED
@@ -37,11 +37,12 @@ Scaffold the Skill into a repo (idempotent — safe to re-run).
37
37
  - `<path>` — repo root or any subdirectory; resolves up to the git root.
38
38
  - Writes `.claude/skills/delfini/SKILL.md`, appends a `/delfini` block to `CLAUDE.md` (creating it if absent, never duplicating), and adds `.delfini-trace/` to `.gitignore`.
39
39
 
40
- ### `delfini local-prepare [--scope <paths>] [--base <ref>] [--relevance-threshold <N>]`
40
+ ### `delfini local-prepare [--scope <paths>] [--ignore-code-scope <paths>] [--base <ref>] [--relevance-threshold <N>]`
41
41
 
42
42
  Assemble the analysis input for the coding agent to dispatch.
43
43
 
44
- - `--scope <paths>` — comma-separated paths overriding the persisted doc-scope, for this run only.
44
+ - `--scope <paths>` — comma-separated paths overriding the persisted `doc_scope`, for this run only.
45
+ - `--ignore-code-scope <paths>` — comma-separated code paths whose changes are ignored for analysis (directories, files, or globs), overriding the persisted `ignore_code_scope` for this run only.
45
46
  - `--base <ref>` — diff base ref. Defaults to `git merge-base HEAD origin/main`.
46
47
  - `--relevance-threshold <N>` — render only the doc sections scoring at/above `N` against the diff, most-relevant-first up to the prompt budget. **Default `5`** (a measured ~40% prompt-token reduction on doc-heavy runs); pass `0` to embed every in-scope doc whole.
47
48
  - Writes `analysis-input.json`, `analysis-prompt.md`, and `schema.json` to `.delfini-trace/`.
@@ -59,7 +60,7 @@ Report the branch's change state as JSON (used by the Skill protocol to choose w
59
60
 
60
61
  ### `delfini --reset-scope`
61
62
 
62
- Delete the persisted `.claude/skills/delfini/doc-scope.json`.
63
+ Delete the persisted `.claude/skills/delfini/delfini-config.json` (and any legacy `doc-scope.json`).
63
64
 
64
65
  ### `delfini --version`
65
66
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  buildPrompt,
3
3
  init_esm_shims
4
- } from "./chunk-LJKEHO6F.js";
4
+ } from "./chunk-IUXS75FH.js";
5
5
 
6
6
  // src/__engine-probe__.ts
7
7
  init_esm_shims();
@@ -10,7 +10,7 @@ import {
10
10
  normalizeDocScope,
11
11
  validateAndReconcile,
12
12
  validateDocScopeEntry
13
- } from "./chunk-LJKEHO6F.js";
13
+ } from "./chunk-IUXS75FH.js";
14
14
 
15
15
  // src/cli.ts
16
16
  init_esm_shims();
@@ -170,46 +170,52 @@ import { dirname, join as join2, resolve } from "path";
170
170
  import { createInterface } from "readline/promises";
171
171
  import { fileURLToPath } from "url";
172
172
 
173
- // src/doc-scope.ts
173
+ // src/config.ts
174
174
  init_esm_shims();
175
175
  import { promises as fs } from "fs";
176
176
  import path from "path";
177
177
  import { glob } from "tinyglobby";
178
- var DOC_SCOPE_RELATIVE_PATH = ".claude/skills/delfini/doc-scope.json";
179
- var DOC_SCOPE_VERSION = 1;
180
- var DOC_SCOPE_VERSION_MISMATCH_MESSAGE = "your doc-scope.json is for a newer @delfini/cli; please upgrade.";
178
+ var DELFINI_CONFIG_RELATIVE_PATH = ".claude/skills/delfini/delfini-config.json";
179
+ var LEGACY_DOC_SCOPE_RELATIVE_PATH = ".claude/skills/delfini/doc-scope.json";
180
+ var DELFINI_CONFIG_VERSION = 1;
181
+ var CONFIG_VERSION_MISMATCH_MESSAGE = "your delfini-config.json is for a newer @delfini/cli; please upgrade.";
181
182
  var REPO_ROOT_REL = ".";
182
- var DocScopeVersionMismatchError = class extends Error {
183
- code = "DOC_SCOPE_VERSION_MISMATCH";
184
- constructor(message = DOC_SCOPE_VERSION_MISMATCH_MESSAGE) {
183
+ var ConfigVersionMismatchError = class extends Error {
184
+ code = "CONFIG_VERSION_MISMATCH";
185
+ constructor(message = CONFIG_VERSION_MISMATCH_MESSAGE) {
185
186
  super(message);
186
- this.name = "DocScopeVersionMismatchError";
187
+ this.name = "ConfigVersionMismatchError";
187
188
  }
188
189
  };
189
- var DocScopeCorruptError = class extends Error {
190
- code = "DOC_SCOPE_CORRUPT";
190
+ var ConfigCorruptError = class extends Error {
191
+ code = "CONFIG_CORRUPT";
191
192
  constructor(message) {
192
193
  super(message);
193
- this.name = "DocScopeCorruptError";
194
+ this.name = "ConfigCorruptError";
194
195
  }
195
196
  };
196
- var DocScopeValidationError = class extends Error {
197
- code = "DOC_SCOPE_VALIDATION";
197
+ var ConfigValidationError = class extends Error {
198
+ code = "CONFIG_VALIDATION";
198
199
  constructor(message) {
199
200
  super(message);
200
- this.name = "DocScopeValidationError";
201
+ this.name = "ConfigValidationError";
201
202
  }
202
203
  };
203
- var docScopeSchemaV1 = external_exports.object({
204
+ var configSchemaV1 = external_exports.object({
204
205
  version: external_exports.literal(1),
205
- doc_scope: external_exports.array(external_exports.string().min(1))
206
+ doc_scope: external_exports.array(external_exports.string().min(1)),
207
+ ignore_code_scope: external_exports.array(external_exports.string().min(1)).optional()
206
208
  });
207
209
  var versionProbeSchema = external_exports.object({
208
210
  version: external_exports.number().int().positive()
209
211
  });
210
- async function readDocScope(repoRoot) {
212
+ async function readConfig(repoRoot) {
211
213
  const root = repoRoot ?? await getRepoRoot();
212
- const target = path.join(root, DOC_SCOPE_RELATIVE_PATH);
214
+ const primary = await readConfigFile(path.join(root, DELFINI_CONFIG_RELATIVE_PATH));
215
+ if (primary !== null) return primary;
216
+ return readConfigFile(path.join(root, LEGACY_DOC_SCOPE_RELATIVE_PATH));
217
+ }
218
+ async function readConfigFile(target) {
213
219
  let raw;
214
220
  try {
215
221
  raw = await fs.readFile(target, "utf8");
@@ -221,54 +227,96 @@ async function readDocScope(repoRoot) {
221
227
  try {
222
228
  parsed = JSON.parse(raw);
223
229
  } catch (err) {
224
- throw new DocScopeCorruptError(
225
- `${DOC_SCOPE_RELATIVE_PATH} is malformed: ${err.message}`
230
+ throw new ConfigCorruptError(
231
+ `${path.basename(target)} is malformed: ${err.message}`
226
232
  );
227
233
  }
228
234
  const probe = versionProbeSchema.safeParse(parsed);
229
- if (probe.success && probe.data.version > DOC_SCOPE_VERSION) {
230
- throw new DocScopeVersionMismatchError();
235
+ if (probe.success && probe.data.version > DELFINI_CONFIG_VERSION) {
236
+ throw new ConfigVersionMismatchError();
231
237
  }
232
- const result = docScopeSchemaV1.safeParse(parsed);
238
+ const result = configSchemaV1.safeParse(parsed);
233
239
  if (!result.success) {
234
- throw new DocScopeCorruptError(
235
- `${DOC_SCOPE_RELATIVE_PATH} is malformed: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
240
+ throw new ConfigCorruptError(
241
+ `${path.basename(target)} is malformed: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
236
242
  );
237
243
  }
238
- return result.data;
244
+ return {
245
+ version: DELFINI_CONFIG_VERSION,
246
+ doc_scope: result.data.doc_scope,
247
+ ignore_code_scope: result.data.ignore_code_scope ?? []
248
+ };
239
249
  }
240
- async function writeDocScope(paths, options) {
250
+ async function writeConfig(update, options) {
241
251
  const root = options?.repoRoot ?? await getRepoRoot();
252
+ const existing = await readConfig(root);
253
+ let docScope = existing?.doc_scope ?? [];
254
+ let ignoreCodeScope = existing?.ignore_code_scope ?? [];
255
+ if (update.doc_scope !== void 0) {
256
+ docScope = validateAndNormalize(update.doc_scope, "doc_scope", { requireNonEmpty: true });
257
+ }
258
+ if (update.ignore_code_scope !== void 0) {
259
+ ignoreCodeScope = validateAndNormalize(update.ignore_code_scope, "ignore_code_scope", {
260
+ requireNonEmpty: false
261
+ });
262
+ }
263
+ if (docScope.length === 0) {
264
+ throw new ConfigValidationError(
265
+ `${DELFINI_CONFIG_RELATIVE_PATH}: doc_scope requires at least one path`
266
+ );
267
+ }
268
+ const payload = {
269
+ version: DELFINI_CONFIG_VERSION,
270
+ doc_scope: docScope
271
+ };
272
+ if (ignoreCodeScope.length > 0) {
273
+ payload.ignore_code_scope = ignoreCodeScope;
274
+ }
275
+ const target = path.join(root, DELFINI_CONFIG_RELATIVE_PATH);
276
+ await fs.mkdir(path.dirname(target), { recursive: true });
277
+ await fs.writeFile(target, `${JSON.stringify(payload, null, 2)}
278
+ `, "utf8");
279
+ await removeLegacyDocScope(root);
280
+ }
281
+ async function writeDocScope(paths, options) {
242
282
  if (!Array.isArray(paths) || paths.length === 0) {
243
- throw new DocScopeValidationError("at least one path is required");
283
+ throw new ConfigValidationError("at least one path is required");
244
284
  }
285
+ await writeConfig({ doc_scope: paths }, options);
286
+ }
287
+ function validateAndNormalize(paths, field, opts) {
245
288
  const errors = [];
246
289
  for (const entry of paths) {
247
290
  const err = validateDocScopeEntry(entry, REPO_ROOT_REL);
248
291
  if (err !== null) errors.push(err);
249
292
  }
250
293
  if (errors.length > 0) {
251
- throw new DocScopeValidationError(
252
- `${DOC_SCOPE_RELATIVE_PATH}: invalid path(s):
294
+ throw new ConfigValidationError(
295
+ `${DELFINI_CONFIG_RELATIVE_PATH}: invalid ${field} path(s):
253
296
  ${errors.map((e) => ` - ${e}`).join("\n")}`
254
297
  );
255
298
  }
256
299
  const normalised = normalizeDocScope(paths);
257
- if (normalised.length === 0) {
258
- throw new DocScopeValidationError(
259
- `${DOC_SCOPE_RELATIVE_PATH}: every entry collapses to an empty scope after normalisation (e.g. '.', './', 'docs/..') \u2014 provide at least one concrete path`
300
+ if (opts.requireNonEmpty && normalised.length === 0) {
301
+ throw new ConfigValidationError(
302
+ `${DELFINI_CONFIG_RELATIVE_PATH}: every ${field} entry collapses to an empty scope after normalisation (e.g. '.', './', 'docs/..') \u2014 provide at least one concrete path`
260
303
  );
261
304
  }
262
- const target = path.join(root, DOC_SCOPE_RELATIVE_PATH);
263
- await fs.mkdir(path.dirname(target), { recursive: true });
264
- const payload = { version: DOC_SCOPE_VERSION, doc_scope: normalised };
265
- const json = `${JSON.stringify(payload, null, 2)}
266
- `;
267
- await fs.writeFile(target, json, "utf8");
305
+ return normalised;
268
306
  }
269
- async function docScopeExists(repoRoot) {
307
+ async function removeLegacyDocScope(root) {
308
+ const legacy = path.join(root, LEGACY_DOC_SCOPE_RELATIVE_PATH);
309
+ try {
310
+ await fs.unlink(legacy);
311
+ } catch (err) {
312
+ if (!isNoEntError(err)) throw err;
313
+ }
314
+ }
315
+ async function configExists(repoRoot) {
270
316
  const root = repoRoot ?? await getRepoRoot();
271
- const target = path.join(root, DOC_SCOPE_RELATIVE_PATH);
317
+ return await isFile(path.join(root, DELFINI_CONFIG_RELATIVE_PATH)) || await isFile(path.join(root, LEGACY_DOC_SCOPE_RELATIVE_PATH));
318
+ }
319
+ async function isFile(target) {
272
320
  try {
273
321
  const st = await fs.stat(target);
274
322
  return st.isFile();
@@ -276,13 +324,14 @@ async function docScopeExists(repoRoot) {
276
324
  return false;
277
325
  }
278
326
  }
279
- async function deleteDocScope(repoRoot) {
327
+ async function deleteConfig(repoRoot) {
280
328
  const root = repoRoot ?? await getRepoRoot();
281
- const target = path.join(root, DOC_SCOPE_RELATIVE_PATH);
282
- try {
283
- await fs.unlink(target);
284
- } catch (err) {
285
- if (!isNoEntError(err)) throw err;
329
+ for (const rel of [DELFINI_CONFIG_RELATIVE_PATH, LEGACY_DOC_SCOPE_RELATIVE_PATH]) {
330
+ try {
331
+ await fs.unlink(path.join(root, rel));
332
+ } catch (err) {
333
+ if (!isNoEntError(err)) throw err;
334
+ }
286
335
  }
287
336
  }
288
337
  async function expandDocScope(paths, repoRoot) {
@@ -489,24 +538,24 @@ function sanitiseScope(paths) {
489
538
  return paths.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
490
539
  }
491
540
  async function applyDocScope(repoRoot, logger, provideDocScope) {
492
- const target = join2(repoRoot, DOC_SCOPE_RELATIVE_PATH);
541
+ const target = join2(repoRoot, DELFINI_CONFIG_RELATIVE_PATH);
493
542
  if (provideDocScope) {
494
543
  const paths2 = sanitiseScope(await provideDocScope());
495
544
  if (paths2.length === 0) {
496
- log(logger, `doc-scope.json \u2192 ${target} (no paths provided, no change)`);
545
+ log(logger, `delfini-config.json \u2192 ${target} (no paths provided, no change)`);
497
546
  return;
498
547
  }
499
548
  await persistDocScope(repoRoot, logger, target, paths2);
500
549
  return;
501
550
  }
502
- if (await docScopeExists(repoRoot)) {
503
- log(logger, `doc-scope.json \u2192 ${target} (already configured, no change)`);
551
+ if (await configExists(repoRoot)) {
552
+ log(logger, `delfini-config.json \u2192 ${target} (already configured, no change)`);
504
553
  return;
505
554
  }
506
555
  if (!process.stdin.isTTY) {
507
556
  log(
508
557
  logger,
509
- `doc-scope.json \u2192 ${target} (non-interactive shell: scope prompt skipped, no change)`
558
+ `delfini-config.json \u2192 ${target} (non-interactive shell: scope prompt skipped, no change)`
510
559
  );
511
560
  return;
512
561
  }
@@ -514,7 +563,7 @@ async function applyDocScope(repoRoot, logger, provideDocScope) {
514
563
  if (paths.length === 0) {
515
564
  log(
516
565
  logger,
517
- `doc-scope.json \u2192 ${target} (no paths provided, no change \u2014 first /delfini run will prompt)`
566
+ `delfini-config.json \u2192 ${target} (no paths provided, no change \u2014 first /delfini run will prompt)`
518
567
  );
519
568
  return;
520
569
  }
@@ -534,12 +583,12 @@ async function promptDocScope() {
534
583
  async function persistDocScope(repoRoot, logger, target, paths) {
535
584
  try {
536
585
  await writeDocScope(paths, { repoRoot });
537
- log(logger, `doc-scope.json \u2192 ${target} (wrote ${paths.length} path(s))`);
586
+ log(logger, `delfini-config.json \u2192 ${target} (wrote ${paths.length} path(s))`);
538
587
  } catch (err) {
539
- if (err instanceof DocScopeValidationError) {
588
+ if (err instanceof ConfigValidationError) {
540
589
  log(
541
590
  logger,
542
- `doc-scope.json \u2192 ${target} (skipped \u2014 ${err.message}). Fix the path(s) and re-run \`delfini install\`, edit the file directly, or set the scope on the first /delfini run.`
591
+ `delfini-config.json \u2192 ${target} (skipped \u2014 ${err.message}). Fix the path(s) and re-run \`delfini install\`, edit the file directly, or set the scope on the first /delfini run.`
543
592
  );
544
593
  return;
545
594
  }
@@ -965,13 +1014,15 @@ async function runLocalPrepare(options = {}) {
965
1014
  );
966
1015
  }
967
1016
  const repoRoot = options.repoRoot ?? await getRepoRoot();
968
- const scopePaths = await resolveScopePaths(options.scope, repoRoot);
1017
+ const config = await readConfig(repoRoot);
1018
+ const scopePaths = resolveScopePaths(options.scope, config);
969
1019
  if (scopePaths === null) {
970
1020
  stderr.write(
971
- "No doc-scope configured. Pass `--scope <paths>` or run the skill\nfirst-run setup to create `.claude/skills/delfini/doc-scope.json`.\n"
1021
+ "No doc-scope configured. Pass `--scope <paths>` or run the skill\nfirst-run setup to create `.claude/skills/delfini/delfini-config.json`.\n"
972
1022
  );
973
1023
  return 2;
974
1024
  }
1025
+ const ignoreCodeScope = resolveIgnoreCodeScope(options.ignoreCodeScope, config);
975
1026
  const expansion = await expandDocScope(scopePaths, repoRoot);
976
1027
  for (const missing of expansion.missingPaths) {
977
1028
  stderr.write(formatMissingPathWarning(missing));
@@ -981,8 +1032,12 @@ async function runLocalPrepare(options = {}) {
981
1032
  const rawDiff = await computeDiff(git, repoRoot, baseRef, diffSource);
982
1033
  let diff = rawDiff;
983
1034
  let filterResult = null;
984
- if (options.enableDiffPreFilter === true) {
985
- filterResult = filterDiff(rawDiff);
1035
+ const enableBuiltins = options.enableDiffPreFilter === true;
1036
+ if (enableBuiltins || ignoreCodeScope.length > 0) {
1037
+ filterResult = filterDiff(rawDiff, {
1038
+ builtins: enableBuiltins,
1039
+ ignorePaths: ignoreCodeScope
1040
+ });
986
1041
  diff = filterResult.keptDiff;
987
1042
  }
988
1043
  const docs = await readDocs(expansion.files, repoRoot, stderr);
@@ -1031,7 +1086,7 @@ async function runLocalPrepare(options = {}) {
1031
1086
  `);
1032
1087
  return 0;
1033
1088
  }
1034
- async function resolveScopePaths(scopeOption, repoRoot) {
1089
+ function resolveScopePaths(scopeOption, config) {
1035
1090
  if (scopeOption !== void 0) {
1036
1091
  const normalised = normaliseScopeOption(scopeOption);
1037
1092
  if (normalised.length === 0) {
@@ -1039,11 +1094,16 @@ async function resolveScopePaths(scopeOption, repoRoot) {
1039
1094
  }
1040
1095
  return normalised;
1041
1096
  }
1042
- const persisted = await readDocScope(repoRoot);
1043
- if (persisted === null) {
1097
+ if (config === null) {
1044
1098
  return null;
1045
1099
  }
1046
- return persisted.doc_scope;
1100
+ return config.doc_scope;
1101
+ }
1102
+ function resolveIgnoreCodeScope(ignoreOption, config) {
1103
+ if (ignoreOption !== void 0) {
1104
+ return normaliseScopeOption(ignoreOption);
1105
+ }
1106
+ return config?.ignore_code_scope ?? [];
1047
1107
  }
1048
1108
  function normaliseScopeOption(scope) {
1049
1109
  const raw = Array.isArray(scope) ? scope : scope.split(",");
@@ -1243,7 +1303,7 @@ function readPackageJson() {
1243
1303
  }
1244
1304
  async function main(argv) {
1245
1305
  const program = new Command();
1246
- program.name("delfini").description("Delfini Skill CLI \u2014 deterministic, never calls an LLM.").version(pkg.version, "-V, --version", "print the @delfini/cli version").option("--reset-scope", "delete the persisted doc-scope.json").exitOverride();
1306
+ program.name("delfini").description("Delfini Skill CLI \u2014 deterministic, never calls an LLM.").version(pkg.version, "-V, --version", "print the @delfini/cli version").option("--reset-scope", "delete the persisted delfini-config.json (and any legacy doc-scope.json)").exitOverride();
1247
1307
  program.action(async (opts) => {
1248
1308
  if (opts.resetScope) {
1249
1309
  await handleResetScope();
@@ -1253,7 +1313,7 @@ async function main(argv) {
1253
1313
  "Scaffold .claude/skills/delfini/SKILL.md + CLAUDE.md auto-invoke + .gitignore append"
1254
1314
  ).option("--tool <agent>", "Coding agent target (only 'CLAUDE' supported in V1)", "CLAUDE").option("--auto-invoke", "append the CLAUDE.md auto-invoke block without prompting").option("--no-auto-invoke", "strip the CLAUDE.md auto-invoke block without prompting").option(
1255
1315
  "--scope <paths>",
1256
- "Seed doc-scope.json with these paths (space- or comma-separated; overwrites any existing scope) without prompting. Omit to be prompted interactively on a TTY."
1316
+ "Seed delfini-config.json doc_scope with these paths (space- or comma-separated; overwrites any existing scope) without prompting. Omit to be prompted interactively on a TTY."
1257
1317
  ).action(
1258
1318
  async (targetPath, opts) => {
1259
1319
  const confirmAutoInvoke = opts.autoInvoke === void 0 ? void 0 : () => Promise.resolve(opts.autoInvoke);
@@ -1263,7 +1323,10 @@ async function main(argv) {
1263
1323
  );
1264
1324
  program.command("local-prepare").description(
1265
1325
  "Compute diff + doc-scope + prompt + token-budget gate; write .delfini-trace/"
1266
- ).option("--scope <paths>", "Comma-separated doc-scope paths (overrides doc-scope.json)").option("--base <ref>", "Diff base ref (default: git merge-base HEAD origin/main)").option(
1326
+ ).option("--scope <paths>", "Comma-separated doc-scope paths (overrides delfini-config.json doc_scope)").option(
1327
+ "--ignore-code-scope <paths>",
1328
+ "Comma-separated code paths whose changes are ignored for analysis (overrides delfini-config.json ignore_code_scope). Each entry is a directory, file, or glob."
1329
+ ).option("--base <ref>", "Diff base ref (default: git merge-base HEAD origin/main)").option(
1267
1330
  "--diff-source <source>",
1268
1331
  "Which diff to analyse: 'local' (default), 'committed', or 'both'",
1269
1332
  "local"
@@ -1292,6 +1355,7 @@ async function main(argv) {
1292
1355
  async (opts) => {
1293
1356
  const exitCode = await runLocalPrepare({
1294
1357
  scope: opts.scope,
1358
+ ignoreCodeScope: opts.ignoreCodeScope,
1295
1359
  base: opts.base,
1296
1360
  diffSource: opts.diffSource,
1297
1361
  relevanceThreshold: opts.relevanceThreshold,
@@ -1321,7 +1385,7 @@ async function main(argv) {
1321
1385
  }
1322
1386
  async function handleResetScope() {
1323
1387
  try {
1324
- await deleteDocScope();
1388
+ await deleteConfig();
1325
1389
  } catch (err) {
1326
1390
  if (err instanceof RepoRootNotFoundError) {
1327
1391
  return;
@@ -1334,15 +1398,17 @@ export {
1334
1398
  RepoRootNotFoundError,
1335
1399
  getRepoRoot,
1336
1400
  runDiffStatus,
1337
- DOC_SCOPE_RELATIVE_PATH,
1338
- DOC_SCOPE_VERSION,
1339
- DocScopeVersionMismatchError,
1340
- DocScopeCorruptError,
1341
- DocScopeValidationError,
1342
- readDocScope,
1401
+ DELFINI_CONFIG_RELATIVE_PATH,
1402
+ LEGACY_DOC_SCOPE_RELATIVE_PATH,
1403
+ DELFINI_CONFIG_VERSION,
1404
+ ConfigVersionMismatchError,
1405
+ ConfigCorruptError,
1406
+ ConfigValidationError,
1407
+ readConfig,
1408
+ writeConfig,
1343
1409
  writeDocScope,
1344
- docScopeExists,
1345
- deleteDocScope,
1410
+ configExists,
1411
+ deleteConfig,
1346
1412
  expandDocScope,
1347
1413
  ensureTraceDir,
1348
1414
  appendToGitignore,
@@ -6444,6 +6444,18 @@ function classifyEntry(entry) {
6444
6444
  return "dir";
6445
6445
  return lastSegment.includes(".") ? "file" : "dir";
6446
6446
  }
6447
+ function isFileInDocScope(filePath, scope) {
6448
+ const file = posixNormalize(toPosix(filePath).replace(/^\/+/, ""));
6449
+ if (file === "" || file === ".")
6450
+ return false;
6451
+ const entries = normalizeDocScope(scope);
6452
+ for (const entry of entries) {
6453
+ const pattern = classifyEntry(entry) === "dir" ? `${entry}/**` : entry;
6454
+ if ((0, import_picomatch.default)(pattern, { dot: false, nocase: true })(file))
6455
+ return true;
6456
+ }
6457
+ return false;
6458
+ }
6447
6459
  function toPosix(p) {
6448
6460
  return p.split("\\").join("/");
6449
6461
  }
@@ -6477,7 +6489,10 @@ function posixNormalize(input) {
6477
6489
 
6478
6490
  // ../drift-engine/dist/diff-filter.js
6479
6491
  init_esm_shims();
6480
- function filterDiff(diff) {
6492
+ function filterDiff(diff, options = {}) {
6493
+ const builtins = options.builtins ?? true;
6494
+ const ignorePaths = options.ignorePaths ?? [];
6495
+ const hasIgnore = ignorePaths.length > 0;
6481
6496
  const droppedPaths = [];
6482
6497
  const droppedHunks = [];
6483
6498
  const files = parseDiffIntoFiles(diff);
@@ -6486,6 +6501,14 @@ function filterDiff(diff) {
6486
6501
  keptParts.push(files.preamble);
6487
6502
  }
6488
6503
  for (const file of files.files) {
6504
+ if (hasIgnore && isFileInDocScope(file.path, ignorePaths)) {
6505
+ droppedPaths.push({ path: file.path, reason: "ignored" });
6506
+ continue;
6507
+ }
6508
+ if (!builtins) {
6509
+ keptParts.push(file.rawSlice);
6510
+ continue;
6511
+ }
6489
6512
  const pathReason = classifyPath(file.path);
6490
6513
  if (pathReason !== null) {
6491
6514
  droppedPaths.push({ path: file.path, reason: pathReason });