@delfini/cli 0.2.0 → 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 +37 -70
- 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/dist/index.cjs
CHANGED
|
@@ -1802,26 +1802,28 @@ var require_picomatch2 = __commonJS({
|
|
|
1802
1802
|
// src/index.ts
|
|
1803
1803
|
var index_exports = {};
|
|
1804
1804
|
__export(index_exports, {
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1805
|
+
ConfigCorruptError: () => ConfigCorruptError,
|
|
1806
|
+
ConfigValidationError: () => ConfigValidationError,
|
|
1807
|
+
ConfigVersionMismatchError: () => ConfigVersionMismatchError,
|
|
1808
|
+
DELFINI_CONFIG_RELATIVE_PATH: () => DELFINI_CONFIG_RELATIVE_PATH,
|
|
1809
|
+
DELFINI_CONFIG_VERSION: () => DELFINI_CONFIG_VERSION,
|
|
1810
1810
|
InstallToolNotSupportedError: () => InstallToolNotSupportedError,
|
|
1811
|
+
LEGACY_DOC_SCOPE_RELATIVE_PATH: () => LEGACY_DOC_SCOPE_RELATIVE_PATH,
|
|
1811
1812
|
PROMPT_TOKEN_BUDGET: () => PROMPT_TOKEN_BUDGET,
|
|
1812
1813
|
RepoRootNotFoundError: () => RepoRootNotFoundError,
|
|
1813
1814
|
appendToGitignore: () => appendToGitignore,
|
|
1814
|
-
|
|
1815
|
-
|
|
1815
|
+
configExists: () => configExists,
|
|
1816
|
+
deleteConfig: () => deleteConfig,
|
|
1816
1817
|
ensureTraceDir: () => ensureTraceDir,
|
|
1817
1818
|
expandDocScope: () => expandDocScope,
|
|
1818
1819
|
getRepoRoot: () => getRepoRoot,
|
|
1819
1820
|
main: () => main,
|
|
1820
|
-
|
|
1821
|
+
readConfig: () => readConfig,
|
|
1821
1822
|
runDiffStatus: () => runDiffStatus,
|
|
1822
1823
|
runInstall: () => runInstall,
|
|
1823
1824
|
runLocalFinalize: () => runLocalFinalize,
|
|
1824
1825
|
runLocalPrepare: () => runLocalPrepare,
|
|
1826
|
+
writeConfig: () => writeConfig,
|
|
1825
1827
|
writeDocScope: () => writeDocScope,
|
|
1826
1828
|
writeRetryAttemptFile: () => writeRetryAttemptFile,
|
|
1827
1829
|
writeTraceFile: () => writeTraceFile
|
|
@@ -1829,7 +1831,7 @@ __export(index_exports, {
|
|
|
1829
1831
|
module.exports = __toCommonJS(index_exports);
|
|
1830
1832
|
init_cjs_shims();
|
|
1831
1833
|
|
|
1832
|
-
// src/
|
|
1834
|
+
// src/config.ts
|
|
1833
1835
|
init_cjs_shims();
|
|
1834
1836
|
var import_node_fs = require("fs");
|
|
1835
1837
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -6478,6 +6480,18 @@ function classifyEntry(entry) {
|
|
|
6478
6480
|
return "dir";
|
|
6479
6481
|
return lastSegment.includes(".") ? "file" : "dir";
|
|
6480
6482
|
}
|
|
6483
|
+
function isFileInDocScope(filePath, scope) {
|
|
6484
|
+
const file = posixNormalize(toPosix(filePath).replace(/^\/+/, ""));
|
|
6485
|
+
if (file === "" || file === ".")
|
|
6486
|
+
return false;
|
|
6487
|
+
const entries = normalizeDocScope(scope);
|
|
6488
|
+
for (const entry of entries) {
|
|
6489
|
+
const pattern = classifyEntry(entry) === "dir" ? `${entry}/**` : entry;
|
|
6490
|
+
if ((0, import_picomatch.default)(pattern, { dot: false, nocase: true })(file))
|
|
6491
|
+
return true;
|
|
6492
|
+
}
|
|
6493
|
+
return false;
|
|
6494
|
+
}
|
|
6481
6495
|
function toPosix(p) {
|
|
6482
6496
|
return p.split("\\").join("/");
|
|
6483
6497
|
}
|
|
@@ -6511,7 +6525,10 @@ function posixNormalize(input) {
|
|
|
6511
6525
|
|
|
6512
6526
|
// ../drift-engine/dist/diff-filter.js
|
|
6513
6527
|
init_cjs_shims();
|
|
6514
|
-
function filterDiff(diff) {
|
|
6528
|
+
function filterDiff(diff, options = {}) {
|
|
6529
|
+
const builtins = options.builtins ?? true;
|
|
6530
|
+
const ignorePaths = options.ignorePaths ?? [];
|
|
6531
|
+
const hasIgnore = ignorePaths.length > 0;
|
|
6515
6532
|
const droppedPaths = [];
|
|
6516
6533
|
const droppedHunks = [];
|
|
6517
6534
|
const files = parseDiffIntoFiles(diff);
|
|
@@ -6520,6 +6537,14 @@ function filterDiff(diff) {
|
|
|
6520
6537
|
keptParts.push(files.preamble);
|
|
6521
6538
|
}
|
|
6522
6539
|
for (const file of files.files) {
|
|
6540
|
+
if (hasIgnore && isFileInDocScope(file.path, ignorePaths)) {
|
|
6541
|
+
droppedPaths.push({ path: file.path, reason: "ignored" });
|
|
6542
|
+
continue;
|
|
6543
|
+
}
|
|
6544
|
+
if (!builtins) {
|
|
6545
|
+
keptParts.push(file.rawSlice);
|
|
6546
|
+
continue;
|
|
6547
|
+
}
|
|
6523
6548
|
const pathReason = classifyPath(file.path);
|
|
6524
6549
|
if (pathReason !== null) {
|
|
6525
6550
|
droppedPaths.push({ path: file.path, reason: pathReason });
|
|
@@ -7046,42 +7071,48 @@ async function resolveBaseRef(git, explicitBase, stderr) {
|
|
|
7046
7071
|
}
|
|
7047
7072
|
}
|
|
7048
7073
|
|
|
7049
|
-
// src/
|
|
7050
|
-
var
|
|
7051
|
-
var
|
|
7052
|
-
var
|
|
7074
|
+
// src/config.ts
|
|
7075
|
+
var DELFINI_CONFIG_RELATIVE_PATH = ".claude/skills/delfini/delfini-config.json";
|
|
7076
|
+
var LEGACY_DOC_SCOPE_RELATIVE_PATH = ".claude/skills/delfini/doc-scope.json";
|
|
7077
|
+
var DELFINI_CONFIG_VERSION = 1;
|
|
7078
|
+
var CONFIG_VERSION_MISMATCH_MESSAGE = "your delfini-config.json is for a newer @delfini/cli; please upgrade.";
|
|
7053
7079
|
var REPO_ROOT_REL = ".";
|
|
7054
|
-
var
|
|
7055
|
-
code = "
|
|
7056
|
-
constructor(message =
|
|
7080
|
+
var ConfigVersionMismatchError = class extends Error {
|
|
7081
|
+
code = "CONFIG_VERSION_MISMATCH";
|
|
7082
|
+
constructor(message = CONFIG_VERSION_MISMATCH_MESSAGE) {
|
|
7057
7083
|
super(message);
|
|
7058
|
-
this.name = "
|
|
7084
|
+
this.name = "ConfigVersionMismatchError";
|
|
7059
7085
|
}
|
|
7060
7086
|
};
|
|
7061
|
-
var
|
|
7062
|
-
code = "
|
|
7087
|
+
var ConfigCorruptError = class extends Error {
|
|
7088
|
+
code = "CONFIG_CORRUPT";
|
|
7063
7089
|
constructor(message) {
|
|
7064
7090
|
super(message);
|
|
7065
|
-
this.name = "
|
|
7091
|
+
this.name = "ConfigCorruptError";
|
|
7066
7092
|
}
|
|
7067
7093
|
};
|
|
7068
|
-
var
|
|
7069
|
-
code = "
|
|
7094
|
+
var ConfigValidationError = class extends Error {
|
|
7095
|
+
code = "CONFIG_VALIDATION";
|
|
7070
7096
|
constructor(message) {
|
|
7071
7097
|
super(message);
|
|
7072
|
-
this.name = "
|
|
7098
|
+
this.name = "ConfigValidationError";
|
|
7073
7099
|
}
|
|
7074
7100
|
};
|
|
7075
|
-
var
|
|
7101
|
+
var configSchemaV1 = external_exports.object({
|
|
7076
7102
|
version: external_exports.literal(1),
|
|
7077
|
-
doc_scope: external_exports.array(external_exports.string().min(1))
|
|
7103
|
+
doc_scope: external_exports.array(external_exports.string().min(1)),
|
|
7104
|
+
ignore_code_scope: external_exports.array(external_exports.string().min(1)).optional()
|
|
7078
7105
|
});
|
|
7079
7106
|
var versionProbeSchema = external_exports.object({
|
|
7080
7107
|
version: external_exports.number().int().positive()
|
|
7081
7108
|
});
|
|
7082
|
-
async function
|
|
7109
|
+
async function readConfig(repoRoot) {
|
|
7083
7110
|
const root = repoRoot ?? await getRepoRoot();
|
|
7084
|
-
const
|
|
7111
|
+
const primary = await readConfigFile(import_node_path.default.join(root, DELFINI_CONFIG_RELATIVE_PATH));
|
|
7112
|
+
if (primary !== null) return primary;
|
|
7113
|
+
return readConfigFile(import_node_path.default.join(root, LEGACY_DOC_SCOPE_RELATIVE_PATH));
|
|
7114
|
+
}
|
|
7115
|
+
async function readConfigFile(target) {
|
|
7085
7116
|
let raw;
|
|
7086
7117
|
try {
|
|
7087
7118
|
raw = await import_node_fs.promises.readFile(target, "utf8");
|
|
@@ -7093,54 +7124,96 @@ async function readDocScope(repoRoot) {
|
|
|
7093
7124
|
try {
|
|
7094
7125
|
parsed = JSON.parse(raw);
|
|
7095
7126
|
} catch (err) {
|
|
7096
|
-
throw new
|
|
7097
|
-
`${
|
|
7127
|
+
throw new ConfigCorruptError(
|
|
7128
|
+
`${import_node_path.default.basename(target)} is malformed: ${err.message}`
|
|
7098
7129
|
);
|
|
7099
7130
|
}
|
|
7100
7131
|
const probe = versionProbeSchema.safeParse(parsed);
|
|
7101
|
-
if (probe.success && probe.data.version >
|
|
7102
|
-
throw new
|
|
7132
|
+
if (probe.success && probe.data.version > DELFINI_CONFIG_VERSION) {
|
|
7133
|
+
throw new ConfigVersionMismatchError();
|
|
7103
7134
|
}
|
|
7104
|
-
const result =
|
|
7135
|
+
const result = configSchemaV1.safeParse(parsed);
|
|
7105
7136
|
if (!result.success) {
|
|
7106
|
-
throw new
|
|
7107
|
-
`${
|
|
7137
|
+
throw new ConfigCorruptError(
|
|
7138
|
+
`${import_node_path.default.basename(target)} is malformed: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
7108
7139
|
);
|
|
7109
7140
|
}
|
|
7110
|
-
return
|
|
7141
|
+
return {
|
|
7142
|
+
version: DELFINI_CONFIG_VERSION,
|
|
7143
|
+
doc_scope: result.data.doc_scope,
|
|
7144
|
+
ignore_code_scope: result.data.ignore_code_scope ?? []
|
|
7145
|
+
};
|
|
7111
7146
|
}
|
|
7112
|
-
async function
|
|
7147
|
+
async function writeConfig(update, options) {
|
|
7113
7148
|
const root = options?.repoRoot ?? await getRepoRoot();
|
|
7149
|
+
const existing = await readConfig(root);
|
|
7150
|
+
let docScope = existing?.doc_scope ?? [];
|
|
7151
|
+
let ignoreCodeScope = existing?.ignore_code_scope ?? [];
|
|
7152
|
+
if (update.doc_scope !== void 0) {
|
|
7153
|
+
docScope = validateAndNormalize(update.doc_scope, "doc_scope", { requireNonEmpty: true });
|
|
7154
|
+
}
|
|
7155
|
+
if (update.ignore_code_scope !== void 0) {
|
|
7156
|
+
ignoreCodeScope = validateAndNormalize(update.ignore_code_scope, "ignore_code_scope", {
|
|
7157
|
+
requireNonEmpty: false
|
|
7158
|
+
});
|
|
7159
|
+
}
|
|
7160
|
+
if (docScope.length === 0) {
|
|
7161
|
+
throw new ConfigValidationError(
|
|
7162
|
+
`${DELFINI_CONFIG_RELATIVE_PATH}: doc_scope requires at least one path`
|
|
7163
|
+
);
|
|
7164
|
+
}
|
|
7165
|
+
const payload = {
|
|
7166
|
+
version: DELFINI_CONFIG_VERSION,
|
|
7167
|
+
doc_scope: docScope
|
|
7168
|
+
};
|
|
7169
|
+
if (ignoreCodeScope.length > 0) {
|
|
7170
|
+
payload.ignore_code_scope = ignoreCodeScope;
|
|
7171
|
+
}
|
|
7172
|
+
const target = import_node_path.default.join(root, DELFINI_CONFIG_RELATIVE_PATH);
|
|
7173
|
+
await import_node_fs.promises.mkdir(import_node_path.default.dirname(target), { recursive: true });
|
|
7174
|
+
await import_node_fs.promises.writeFile(target, `${JSON.stringify(payload, null, 2)}
|
|
7175
|
+
`, "utf8");
|
|
7176
|
+
await removeLegacyDocScope(root);
|
|
7177
|
+
}
|
|
7178
|
+
async function writeDocScope(paths, options) {
|
|
7114
7179
|
if (!Array.isArray(paths) || paths.length === 0) {
|
|
7115
|
-
throw new
|
|
7180
|
+
throw new ConfigValidationError("at least one path is required");
|
|
7116
7181
|
}
|
|
7182
|
+
await writeConfig({ doc_scope: paths }, options);
|
|
7183
|
+
}
|
|
7184
|
+
function validateAndNormalize(paths, field, opts) {
|
|
7117
7185
|
const errors = [];
|
|
7118
7186
|
for (const entry of paths) {
|
|
7119
7187
|
const err = validateDocScopeEntry(entry, REPO_ROOT_REL);
|
|
7120
7188
|
if (err !== null) errors.push(err);
|
|
7121
7189
|
}
|
|
7122
7190
|
if (errors.length > 0) {
|
|
7123
|
-
throw new
|
|
7124
|
-
`${
|
|
7191
|
+
throw new ConfigValidationError(
|
|
7192
|
+
`${DELFINI_CONFIG_RELATIVE_PATH}: invalid ${field} path(s):
|
|
7125
7193
|
${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
7126
7194
|
);
|
|
7127
7195
|
}
|
|
7128
7196
|
const normalised = normalizeDocScope(paths);
|
|
7129
|
-
if (normalised.length === 0) {
|
|
7130
|
-
throw new
|
|
7131
|
-
`${
|
|
7197
|
+
if (opts.requireNonEmpty && normalised.length === 0) {
|
|
7198
|
+
throw new ConfigValidationError(
|
|
7199
|
+
`${DELFINI_CONFIG_RELATIVE_PATH}: every ${field} entry collapses to an empty scope after normalisation (e.g. '.', './', 'docs/..') \u2014 provide at least one concrete path`
|
|
7132
7200
|
);
|
|
7133
7201
|
}
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
const
|
|
7138
|
-
|
|
7139
|
-
|
|
7202
|
+
return normalised;
|
|
7203
|
+
}
|
|
7204
|
+
async function removeLegacyDocScope(root) {
|
|
7205
|
+
const legacy = import_node_path.default.join(root, LEGACY_DOC_SCOPE_RELATIVE_PATH);
|
|
7206
|
+
try {
|
|
7207
|
+
await import_node_fs.promises.unlink(legacy);
|
|
7208
|
+
} catch (err) {
|
|
7209
|
+
if (!isNoEntError(err)) throw err;
|
|
7210
|
+
}
|
|
7140
7211
|
}
|
|
7141
|
-
async function
|
|
7212
|
+
async function configExists(repoRoot) {
|
|
7142
7213
|
const root = repoRoot ?? await getRepoRoot();
|
|
7143
|
-
|
|
7214
|
+
return await isFile(import_node_path.default.join(root, DELFINI_CONFIG_RELATIVE_PATH)) || await isFile(import_node_path.default.join(root, LEGACY_DOC_SCOPE_RELATIVE_PATH));
|
|
7215
|
+
}
|
|
7216
|
+
async function isFile(target) {
|
|
7144
7217
|
try {
|
|
7145
7218
|
const st = await import_node_fs.promises.stat(target);
|
|
7146
7219
|
return st.isFile();
|
|
@@ -7148,13 +7221,14 @@ async function docScopeExists(repoRoot) {
|
|
|
7148
7221
|
return false;
|
|
7149
7222
|
}
|
|
7150
7223
|
}
|
|
7151
|
-
async function
|
|
7224
|
+
async function deleteConfig(repoRoot) {
|
|
7152
7225
|
const root = repoRoot ?? await getRepoRoot();
|
|
7153
|
-
const
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7226
|
+
for (const rel of [DELFINI_CONFIG_RELATIVE_PATH, LEGACY_DOC_SCOPE_RELATIVE_PATH]) {
|
|
7227
|
+
try {
|
|
7228
|
+
await import_node_fs.promises.unlink(import_node_path.default.join(root, rel));
|
|
7229
|
+
} catch (err) {
|
|
7230
|
+
if (!isNoEntError(err)) throw err;
|
|
7231
|
+
}
|
|
7158
7232
|
}
|
|
7159
7233
|
}
|
|
7160
7234
|
async function expandDocScope(paths, repoRoot) {
|
|
@@ -7413,24 +7487,24 @@ function sanitiseScope(paths) {
|
|
|
7413
7487
|
return paths.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
7414
7488
|
}
|
|
7415
7489
|
async function applyDocScope(repoRoot, logger, provideDocScope) {
|
|
7416
|
-
const target = (0, import_node_path3.join)(repoRoot,
|
|
7490
|
+
const target = (0, import_node_path3.join)(repoRoot, DELFINI_CONFIG_RELATIVE_PATH);
|
|
7417
7491
|
if (provideDocScope) {
|
|
7418
7492
|
const paths2 = sanitiseScope(await provideDocScope());
|
|
7419
7493
|
if (paths2.length === 0) {
|
|
7420
|
-
log(logger, `
|
|
7494
|
+
log(logger, `delfini-config.json \u2192 ${target} (no paths provided, no change)`);
|
|
7421
7495
|
return;
|
|
7422
7496
|
}
|
|
7423
7497
|
await persistDocScope(repoRoot, logger, target, paths2);
|
|
7424
7498
|
return;
|
|
7425
7499
|
}
|
|
7426
|
-
if (await
|
|
7427
|
-
log(logger, `
|
|
7500
|
+
if (await configExists(repoRoot)) {
|
|
7501
|
+
log(logger, `delfini-config.json \u2192 ${target} (already configured, no change)`);
|
|
7428
7502
|
return;
|
|
7429
7503
|
}
|
|
7430
7504
|
if (!process.stdin.isTTY) {
|
|
7431
7505
|
log(
|
|
7432
7506
|
logger,
|
|
7433
|
-
`
|
|
7507
|
+
`delfini-config.json \u2192 ${target} (non-interactive shell: scope prompt skipped, no change)`
|
|
7434
7508
|
);
|
|
7435
7509
|
return;
|
|
7436
7510
|
}
|
|
@@ -7438,7 +7512,7 @@ async function applyDocScope(repoRoot, logger, provideDocScope) {
|
|
|
7438
7512
|
if (paths.length === 0) {
|
|
7439
7513
|
log(
|
|
7440
7514
|
logger,
|
|
7441
|
-
`
|
|
7515
|
+
`delfini-config.json \u2192 ${target} (no paths provided, no change \u2014 first /delfini run will prompt)`
|
|
7442
7516
|
);
|
|
7443
7517
|
return;
|
|
7444
7518
|
}
|
|
@@ -7458,12 +7532,12 @@ async function promptDocScope() {
|
|
|
7458
7532
|
async function persistDocScope(repoRoot, logger, target, paths) {
|
|
7459
7533
|
try {
|
|
7460
7534
|
await writeDocScope(paths, { repoRoot });
|
|
7461
|
-
log(logger, `
|
|
7535
|
+
log(logger, `delfini-config.json \u2192 ${target} (wrote ${paths.length} path(s))`);
|
|
7462
7536
|
} catch (err) {
|
|
7463
|
-
if (err instanceof
|
|
7537
|
+
if (err instanceof ConfigValidationError) {
|
|
7464
7538
|
log(
|
|
7465
7539
|
logger,
|
|
7466
|
-
`
|
|
7540
|
+
`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.`
|
|
7467
7541
|
);
|
|
7468
7542
|
return;
|
|
7469
7543
|
}
|
|
@@ -7889,13 +7963,15 @@ async function runLocalPrepare(options = {}) {
|
|
|
7889
7963
|
);
|
|
7890
7964
|
}
|
|
7891
7965
|
const repoRoot = options.repoRoot ?? await getRepoRoot();
|
|
7892
|
-
const
|
|
7966
|
+
const config = await readConfig(repoRoot);
|
|
7967
|
+
const scopePaths = resolveScopePaths(options.scope, config);
|
|
7893
7968
|
if (scopePaths === null) {
|
|
7894
7969
|
stderr.write(
|
|
7895
|
-
"No doc-scope configured. Pass `--scope <paths>` or run the skill\nfirst-run setup to create `.claude/skills/delfini/
|
|
7970
|
+
"No doc-scope configured. Pass `--scope <paths>` or run the skill\nfirst-run setup to create `.claude/skills/delfini/delfini-config.json`.\n"
|
|
7896
7971
|
);
|
|
7897
7972
|
return 2;
|
|
7898
7973
|
}
|
|
7974
|
+
const ignoreCodeScope = resolveIgnoreCodeScope(options.ignoreCodeScope, config);
|
|
7899
7975
|
const expansion = await expandDocScope(scopePaths, repoRoot);
|
|
7900
7976
|
for (const missing of expansion.missingPaths) {
|
|
7901
7977
|
stderr.write(formatMissingPathWarning(missing));
|
|
@@ -7905,8 +7981,12 @@ async function runLocalPrepare(options = {}) {
|
|
|
7905
7981
|
const rawDiff = await computeDiff(git, repoRoot, baseRef, diffSource);
|
|
7906
7982
|
let diff = rawDiff;
|
|
7907
7983
|
let filterResult = null;
|
|
7908
|
-
|
|
7909
|
-
|
|
7984
|
+
const enableBuiltins = options.enableDiffPreFilter === true;
|
|
7985
|
+
if (enableBuiltins || ignoreCodeScope.length > 0) {
|
|
7986
|
+
filterResult = filterDiff(rawDiff, {
|
|
7987
|
+
builtins: enableBuiltins,
|
|
7988
|
+
ignorePaths: ignoreCodeScope
|
|
7989
|
+
});
|
|
7910
7990
|
diff = filterResult.keptDiff;
|
|
7911
7991
|
}
|
|
7912
7992
|
const docs = await readDocs(expansion.files, repoRoot, stderr);
|
|
@@ -7955,7 +8035,7 @@ async function runLocalPrepare(options = {}) {
|
|
|
7955
8035
|
`);
|
|
7956
8036
|
return 0;
|
|
7957
8037
|
}
|
|
7958
|
-
|
|
8038
|
+
function resolveScopePaths(scopeOption, config) {
|
|
7959
8039
|
if (scopeOption !== void 0) {
|
|
7960
8040
|
const normalised = normaliseScopeOption(scopeOption);
|
|
7961
8041
|
if (normalised.length === 0) {
|
|
@@ -7963,11 +8043,16 @@ async function resolveScopePaths(scopeOption, repoRoot) {
|
|
|
7963
8043
|
}
|
|
7964
8044
|
return normalised;
|
|
7965
8045
|
}
|
|
7966
|
-
|
|
7967
|
-
if (persisted === null) {
|
|
8046
|
+
if (config === null) {
|
|
7968
8047
|
return null;
|
|
7969
8048
|
}
|
|
7970
|
-
return
|
|
8049
|
+
return config.doc_scope;
|
|
8050
|
+
}
|
|
8051
|
+
function resolveIgnoreCodeScope(ignoreOption, config) {
|
|
8052
|
+
if (ignoreOption !== void 0) {
|
|
8053
|
+
return normaliseScopeOption(ignoreOption);
|
|
8054
|
+
}
|
|
8055
|
+
return config?.ignore_code_scope ?? [];
|
|
7971
8056
|
}
|
|
7972
8057
|
function normaliseScopeOption(scope) {
|
|
7973
8058
|
const raw = Array.isArray(scope) ? scope : scope.split(",");
|
|
@@ -8167,7 +8252,7 @@ function readPackageJson() {
|
|
|
8167
8252
|
}
|
|
8168
8253
|
async function main(argv) {
|
|
8169
8254
|
const program = new import_commander.Command();
|
|
8170
|
-
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();
|
|
8255
|
+
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();
|
|
8171
8256
|
program.action(async (opts) => {
|
|
8172
8257
|
if (opts.resetScope) {
|
|
8173
8258
|
await handleResetScope();
|
|
@@ -8177,7 +8262,7 @@ async function main(argv) {
|
|
|
8177
8262
|
"Scaffold .claude/skills/delfini/SKILL.md + CLAUDE.md auto-invoke + .gitignore append"
|
|
8178
8263
|
).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(
|
|
8179
8264
|
"--scope <paths>",
|
|
8180
|
-
"Seed
|
|
8265
|
+
"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."
|
|
8181
8266
|
).action(
|
|
8182
8267
|
async (targetPath, opts) => {
|
|
8183
8268
|
const confirmAutoInvoke = opts.autoInvoke === void 0 ? void 0 : () => Promise.resolve(opts.autoInvoke);
|
|
@@ -8187,7 +8272,10 @@ async function main(argv) {
|
|
|
8187
8272
|
);
|
|
8188
8273
|
program.command("local-prepare").description(
|
|
8189
8274
|
"Compute diff + doc-scope + prompt + token-budget gate; write .delfini-trace/"
|
|
8190
|
-
).option("--scope <paths>", "Comma-separated doc-scope paths (overrides
|
|
8275
|
+
).option("--scope <paths>", "Comma-separated doc-scope paths (overrides delfini-config.json doc_scope)").option(
|
|
8276
|
+
"--ignore-code-scope <paths>",
|
|
8277
|
+
"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."
|
|
8278
|
+
).option("--base <ref>", "Diff base ref (default: git merge-base HEAD origin/main)").option(
|
|
8191
8279
|
"--diff-source <source>",
|
|
8192
8280
|
"Which diff to analyse: 'local' (default), 'committed', or 'both'",
|
|
8193
8281
|
"local"
|
|
@@ -8216,6 +8304,7 @@ async function main(argv) {
|
|
|
8216
8304
|
async (opts) => {
|
|
8217
8305
|
const exitCode = await runLocalPrepare({
|
|
8218
8306
|
scope: opts.scope,
|
|
8307
|
+
ignoreCodeScope: opts.ignoreCodeScope,
|
|
8219
8308
|
base: opts.base,
|
|
8220
8309
|
diffSource: opts.diffSource,
|
|
8221
8310
|
relevanceThreshold: opts.relevanceThreshold,
|
|
@@ -8245,7 +8334,7 @@ async function main(argv) {
|
|
|
8245
8334
|
}
|
|
8246
8335
|
async function handleResetScope() {
|
|
8247
8336
|
try {
|
|
8248
|
-
await
|
|
8337
|
+
await deleteConfig();
|
|
8249
8338
|
} catch (err) {
|
|
8250
8339
|
if (err instanceof RepoRootNotFoundError) {
|
|
8251
8340
|
return;
|
|
@@ -8255,26 +8344,28 @@ async function handleResetScope() {
|
|
|
8255
8344
|
}
|
|
8256
8345
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8257
8346
|
0 && (module.exports = {
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8347
|
+
ConfigCorruptError,
|
|
8348
|
+
ConfigValidationError,
|
|
8349
|
+
ConfigVersionMismatchError,
|
|
8350
|
+
DELFINI_CONFIG_RELATIVE_PATH,
|
|
8351
|
+
DELFINI_CONFIG_VERSION,
|
|
8263
8352
|
InstallToolNotSupportedError,
|
|
8353
|
+
LEGACY_DOC_SCOPE_RELATIVE_PATH,
|
|
8264
8354
|
PROMPT_TOKEN_BUDGET,
|
|
8265
8355
|
RepoRootNotFoundError,
|
|
8266
8356
|
appendToGitignore,
|
|
8267
|
-
|
|
8268
|
-
|
|
8357
|
+
configExists,
|
|
8358
|
+
deleteConfig,
|
|
8269
8359
|
ensureTraceDir,
|
|
8270
8360
|
expandDocScope,
|
|
8271
8361
|
getRepoRoot,
|
|
8272
8362
|
main,
|
|
8273
|
-
|
|
8363
|
+
readConfig,
|
|
8274
8364
|
runDiffStatus,
|
|
8275
8365
|
runInstall,
|
|
8276
8366
|
runLocalFinalize,
|
|
8277
8367
|
runLocalPrepare,
|
|
8368
|
+
writeConfig,
|
|
8278
8369
|
writeDocScope,
|
|
8279
8370
|
writeRetryAttemptFile,
|
|
8280
8371
|
writeTraceFile
|
package/dist/index.d.cts
CHANGED
|
@@ -1,36 +1,78 @@
|
|
|
1
1
|
export { main } from './cli.cjs';
|
|
2
2
|
|
|
3
|
-
declare const
|
|
4
|
-
declare const
|
|
5
|
-
|
|
3
|
+
declare const DELFINI_CONFIG_RELATIVE_PATH = ".claude/skills/delfini/delfini-config.json";
|
|
4
|
+
declare const LEGACY_DOC_SCOPE_RELATIVE_PATH = ".claude/skills/delfini/doc-scope.json";
|
|
5
|
+
declare const DELFINI_CONFIG_VERSION: 1;
|
|
6
|
+
interface DelfiniConfig {
|
|
6
7
|
version: 1;
|
|
7
8
|
doc_scope: string[];
|
|
9
|
+
/** Always present after `readConfig` (defaulted to `[]` when absent on disk). */
|
|
10
|
+
ignore_code_scope: string[];
|
|
8
11
|
}
|
|
9
|
-
interface
|
|
12
|
+
interface ConfigWriteOptions {
|
|
10
13
|
repoRoot?: string;
|
|
11
14
|
}
|
|
15
|
+
/** Partial update applied on top of any existing config by `writeConfig`. */
|
|
16
|
+
interface ConfigUpdate {
|
|
17
|
+
/** Replace `doc_scope`. Must be non-empty after normalisation. */
|
|
18
|
+
doc_scope?: string[];
|
|
19
|
+
/** Replace `ignore_code_scope`. May be empty (means "ignore nothing"). */
|
|
20
|
+
ignore_code_scope?: string[];
|
|
21
|
+
}
|
|
12
22
|
interface DocScopeExpansionResult {
|
|
13
23
|
/** Absolute paths to files matched by the scope entries. Sorted, deduped. */
|
|
14
24
|
files: string[];
|
|
15
25
|
/** Original entries from `paths` that resolved to nothing on disk. */
|
|
16
26
|
missingPaths: string[];
|
|
17
27
|
}
|
|
18
|
-
declare class
|
|
19
|
-
readonly code: "
|
|
28
|
+
declare class ConfigVersionMismatchError extends Error {
|
|
29
|
+
readonly code: "CONFIG_VERSION_MISMATCH";
|
|
20
30
|
constructor(message?: string);
|
|
21
31
|
}
|
|
22
|
-
declare class
|
|
23
|
-
readonly code: "
|
|
32
|
+
declare class ConfigCorruptError extends Error {
|
|
33
|
+
readonly code: "CONFIG_CORRUPT";
|
|
24
34
|
constructor(message: string);
|
|
25
35
|
}
|
|
26
|
-
declare class
|
|
27
|
-
readonly code: "
|
|
36
|
+
declare class ConfigValidationError extends Error {
|
|
37
|
+
readonly code: "CONFIG_VALIDATION";
|
|
28
38
|
constructor(message: string);
|
|
29
39
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Read the effective Delfini config, or null if none is configured.
|
|
42
|
+
*
|
|
43
|
+
* Resolution order: `delfini-config.json` first, then a legacy
|
|
44
|
+
* `doc-scope.json` fallback (read-only — migration to the new filename happens
|
|
45
|
+
* on the next `writeConfig`). `ignore_code_scope` is defaulted to `[]` when
|
|
46
|
+
* absent so callers never branch on undefined.
|
|
47
|
+
*/
|
|
48
|
+
declare function readConfig(repoRoot?: string): Promise<DelfiniConfig | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Persist a partial config update, merging over any existing config and
|
|
51
|
+
* preserving the scope not being edited. Writes `delfini-config.json` and
|
|
52
|
+
* removes a legacy `doc-scope.json` if present (one-time rename migration).
|
|
53
|
+
*
|
|
54
|
+
* `doc_scope` is required to be non-empty in the final config (a Delfini run
|
|
55
|
+
* with no docs is meaningless); `ignore_code_scope` may be empty.
|
|
56
|
+
*/
|
|
57
|
+
declare function writeConfig(update: ConfigUpdate, options?: ConfigWriteOptions): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Persist the doc scope, preserving any existing `ignore_code_scope`. Thin
|
|
60
|
+
* wrapper over `writeConfig` kept for the install / first-run callers whose
|
|
61
|
+
* only job is to seed the docs Delfini tracks.
|
|
62
|
+
*/
|
|
63
|
+
declare function writeDocScope(paths: string[], options?: ConfigWriteOptions): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* True iff a Delfini config exists — the new `delfini-config.json` OR a legacy
|
|
66
|
+
* `doc-scope.json`. Used by `delfini install` to avoid clobbering an existing
|
|
67
|
+
* committed, team-shared config.
|
|
68
|
+
*/
|
|
69
|
+
declare function configExists(repoRoot?: string): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Delete the Delfini config — both the new `delfini-config.json` and any legacy
|
|
72
|
+
* `doc-scope.json` (`delfini --reset-scope`). Idempotent: absent files are a
|
|
73
|
+
* silent no-op.
|
|
74
|
+
*/
|
|
75
|
+
declare function deleteConfig(repoRoot?: string): Promise<void>;
|
|
34
76
|
declare function expandDocScope(paths: string[], repoRoot?: string): Promise<DocScopeExpansionResult>;
|
|
35
77
|
|
|
36
78
|
declare class RepoRootNotFoundError extends Error {
|
|
@@ -66,11 +108,11 @@ interface RunInstallOptions {
|
|
|
66
108
|
/**
|
|
67
109
|
* Resolves the doc-scope path list. When provided, `runInstall` uses it
|
|
68
110
|
* directly (the `--scope` CLI flag and the test seam) and never prompts —
|
|
69
|
-
* a non-empty list is persisted to `
|
|
111
|
+
* a non-empty list is persisted to `delfini-config.json` doc_scope (overwriting any
|
|
70
112
|
* existing file, since an explicit `--scope` is intent-to-overwrite); an
|
|
71
113
|
* empty list is a no-op. When omitted, `runInstall` prompts interactively
|
|
72
|
-
* on a TTY only if no
|
|
73
|
-
* when a scope is already configured, it leaves
|
|
114
|
+
* on a TTY only if no config exists yet; on a non-TTY stdin, or
|
|
115
|
+
* when a scope is already configured, it leaves the config untouched.
|
|
74
116
|
* Invalid paths (rejected by `writeDocScope`) warn-and-skip — the scaffold
|
|
75
117
|
* always completes; the SKILL.md first-run prompt re-seeds the scope later.
|
|
76
118
|
*/
|
|
@@ -88,10 +130,18 @@ declare const PROMPT_TOKEN_BUDGET = 150000;
|
|
|
88
130
|
interface RunLocalPrepareOptions {
|
|
89
131
|
/**
|
|
90
132
|
* The `--scope <paths>` value. Accepts a comma-separated string OR a string[].
|
|
91
|
-
* When provided, overrides the persisted
|
|
133
|
+
* When provided, overrides the persisted `delfini-config.json` doc_scope
|
|
92
134
|
* WITHOUT modifying that file (FR144 per-run override invariant).
|
|
93
135
|
*/
|
|
94
136
|
scope?: string | string[];
|
|
137
|
+
/**
|
|
138
|
+
* The `--ignore-code-scope <paths>` value. Accepts a comma-separated string
|
|
139
|
+
* OR a string[]. When provided, overrides the persisted `delfini-config.json`
|
|
140
|
+
* `ignore_code_scope` WITHOUT modifying that file (per-run override). Changed
|
|
141
|
+
* files matching any entry are dropped from the analysed diff. Empty/omitted
|
|
142
|
+
* → use the persisted `ignore_code_scope` (or none).
|
|
143
|
+
*/
|
|
144
|
+
ignoreCodeScope?: string | string[];
|
|
95
145
|
/**
|
|
96
146
|
* The `--base <ref>` value. When provided, used as the diff base directly.
|
|
97
147
|
* When omitted, defaults to `git merge-base HEAD origin/main`.
|
|
@@ -210,4 +260,4 @@ interface RunLocalFinalizeOptions {
|
|
|
210
260
|
*/
|
|
211
261
|
declare function runLocalFinalize(options: RunLocalFinalizeOptions): Promise<number>;
|
|
212
262
|
|
|
213
|
-
export {
|
|
263
|
+
export { ConfigCorruptError, type ConfigUpdate, ConfigValidationError, ConfigVersionMismatchError, type ConfigWriteOptions, DELFINI_CONFIG_RELATIVE_PATH, DELFINI_CONFIG_VERSION, type DelfiniConfig, type DiffSource, type DiffStatus, type DocScopeExpansionResult, type InstallLogger, InstallToolNotSupportedError, LEGACY_DOC_SCOPE_RELATIVE_PATH, PROMPT_TOKEN_BUDGET, RepoRootNotFoundError, type RunDiffStatusOptions, type RunInstallOptions, type RunLocalFinalizeOptions, type RunLocalPrepareOptions, appendToGitignore, configExists, deleteConfig, ensureTraceDir, expandDocScope, getRepoRoot, readConfig, runDiffStatus, runInstall, runLocalFinalize, runLocalPrepare, writeConfig, writeDocScope, writeRetryAttemptFile, writeTraceFile };
|