@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 +4 -3
- package/dist/__engine-probe__.js +1 -1
- package/dist/{chunk-K5X5TSUR.js → chunk-IU4AWQKF.js} +143 -77
- package/dist/{chunk-LJKEHO6F.js → chunk-IUXS75FH.js} +24 -1
- package/dist/cli.cjs +157 -70
- package/dist/cli.js +2 -2
- package/dist/index.cjs +177 -86
- package/dist/index.d.cts +69 -19
- package/dist/index.d.ts +69 -19
- package/dist/index.js +22 -18
- package/package.json +2 -2
- package/templates/SKILL.md +20 -7
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
|
|
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
|
|
package/dist/__engine-probe__.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
normalizeDocScope,
|
|
11
11
|
validateAndReconcile,
|
|
12
12
|
validateDocScopeEntry
|
|
13
|
-
} from "./chunk-
|
|
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/
|
|
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
|
|
179
|
-
var
|
|
180
|
-
var
|
|
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
|
|
183
|
-
code = "
|
|
184
|
-
constructor(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 = "
|
|
187
|
+
this.name = "ConfigVersionMismatchError";
|
|
187
188
|
}
|
|
188
189
|
};
|
|
189
|
-
var
|
|
190
|
-
code = "
|
|
190
|
+
var ConfigCorruptError = class extends Error {
|
|
191
|
+
code = "CONFIG_CORRUPT";
|
|
191
192
|
constructor(message) {
|
|
192
193
|
super(message);
|
|
193
|
-
this.name = "
|
|
194
|
+
this.name = "ConfigCorruptError";
|
|
194
195
|
}
|
|
195
196
|
};
|
|
196
|
-
var
|
|
197
|
-
code = "
|
|
197
|
+
var ConfigValidationError = class extends Error {
|
|
198
|
+
code = "CONFIG_VALIDATION";
|
|
198
199
|
constructor(message) {
|
|
199
200
|
super(message);
|
|
200
|
-
this.name = "
|
|
201
|
+
this.name = "ConfigValidationError";
|
|
201
202
|
}
|
|
202
203
|
};
|
|
203
|
-
var
|
|
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
|
|
212
|
+
async function readConfig(repoRoot) {
|
|
211
213
|
const root = repoRoot ?? await getRepoRoot();
|
|
212
|
-
const
|
|
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
|
|
225
|
-
`${
|
|
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 >
|
|
230
|
-
throw new
|
|
235
|
+
if (probe.success && probe.data.version > DELFINI_CONFIG_VERSION) {
|
|
236
|
+
throw new ConfigVersionMismatchError();
|
|
231
237
|
}
|
|
232
|
-
const result =
|
|
238
|
+
const result = configSchemaV1.safeParse(parsed);
|
|
233
239
|
if (!result.success) {
|
|
234
|
-
throw new
|
|
235
|
-
`${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
252
|
-
`${
|
|
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
|
|
259
|
-
`${
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
327
|
+
async function deleteConfig(repoRoot) {
|
|
280
328
|
const root = repoRoot ?? await getRepoRoot();
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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,
|
|
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, `
|
|
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
|
|
503
|
-
log(logger, `
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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, `
|
|
586
|
+
log(logger, `delfini-config.json \u2192 ${target} (wrote ${paths.length} path(s))`);
|
|
538
587
|
} catch (err) {
|
|
539
|
-
if (err instanceof
|
|
588
|
+
if (err instanceof ConfigValidationError) {
|
|
540
589
|
log(
|
|
541
590
|
logger,
|
|
542
|
-
`
|
|
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
|
|
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/
|
|
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
|
-
|
|
985
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1043
|
-
if (persisted === null) {
|
|
1097
|
+
if (config === null) {
|
|
1044
1098
|
return null;
|
|
1045
1099
|
}
|
|
1046
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
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 });
|