@delfini/cli 0.1.2 → 0.2.1
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/LICENSE +202 -0
- package/README.md +36 -70
- package/dist/__engine-probe__.cjs +2 -2
- package/dist/__engine-probe__.js +1 -1
- package/dist/{chunk-IDYMVTTU.js → chunk-K5X5TSUR.js} +282 -213
- package/dist/{chunk-MUW24ZC4.js → chunk-LJKEHO6F.js} +2 -2
- package/dist/cli.cjs +528 -413
- package/dist/cli.js +2 -2
- package/dist/index.cjs +77 -6
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2 -2
- package/package.json +8 -2
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
normalizeDocScope,
|
|
11
11
|
validateAndReconcile,
|
|
12
12
|
validateDocScopeEntry
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LJKEHO6F.js";
|
|
14
14
|
|
|
15
15
|
// src/cli.ts
|
|
16
16
|
init_esm_shims();
|
|
@@ -170,6 +170,199 @@ 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
|
|
174
|
+
init_esm_shims();
|
|
175
|
+
import { promises as fs } from "fs";
|
|
176
|
+
import path from "path";
|
|
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.";
|
|
181
|
+
var REPO_ROOT_REL = ".";
|
|
182
|
+
var DocScopeVersionMismatchError = class extends Error {
|
|
183
|
+
code = "DOC_SCOPE_VERSION_MISMATCH";
|
|
184
|
+
constructor(message = DOC_SCOPE_VERSION_MISMATCH_MESSAGE) {
|
|
185
|
+
super(message);
|
|
186
|
+
this.name = "DocScopeVersionMismatchError";
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
var DocScopeCorruptError = class extends Error {
|
|
190
|
+
code = "DOC_SCOPE_CORRUPT";
|
|
191
|
+
constructor(message) {
|
|
192
|
+
super(message);
|
|
193
|
+
this.name = "DocScopeCorruptError";
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
var DocScopeValidationError = class extends Error {
|
|
197
|
+
code = "DOC_SCOPE_VALIDATION";
|
|
198
|
+
constructor(message) {
|
|
199
|
+
super(message);
|
|
200
|
+
this.name = "DocScopeValidationError";
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var docScopeSchemaV1 = external_exports.object({
|
|
204
|
+
version: external_exports.literal(1),
|
|
205
|
+
doc_scope: external_exports.array(external_exports.string().min(1))
|
|
206
|
+
});
|
|
207
|
+
var versionProbeSchema = external_exports.object({
|
|
208
|
+
version: external_exports.number().int().positive()
|
|
209
|
+
});
|
|
210
|
+
async function readDocScope(repoRoot) {
|
|
211
|
+
const root = repoRoot ?? await getRepoRoot();
|
|
212
|
+
const target = path.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
213
|
+
let raw;
|
|
214
|
+
try {
|
|
215
|
+
raw = await fs.readFile(target, "utf8");
|
|
216
|
+
} catch (err) {
|
|
217
|
+
if (isNoEntError(err)) return null;
|
|
218
|
+
throw err;
|
|
219
|
+
}
|
|
220
|
+
let parsed;
|
|
221
|
+
try {
|
|
222
|
+
parsed = JSON.parse(raw);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
throw new DocScopeCorruptError(
|
|
225
|
+
`${DOC_SCOPE_RELATIVE_PATH} is malformed: ${err.message}`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const probe = versionProbeSchema.safeParse(parsed);
|
|
229
|
+
if (probe.success && probe.data.version > DOC_SCOPE_VERSION) {
|
|
230
|
+
throw new DocScopeVersionMismatchError();
|
|
231
|
+
}
|
|
232
|
+
const result = docScopeSchemaV1.safeParse(parsed);
|
|
233
|
+
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("; ")}`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return result.data;
|
|
239
|
+
}
|
|
240
|
+
async function writeDocScope(paths, options) {
|
|
241
|
+
const root = options?.repoRoot ?? await getRepoRoot();
|
|
242
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
243
|
+
throw new DocScopeValidationError("at least one path is required");
|
|
244
|
+
}
|
|
245
|
+
const errors = [];
|
|
246
|
+
for (const entry of paths) {
|
|
247
|
+
const err = validateDocScopeEntry(entry, REPO_ROOT_REL);
|
|
248
|
+
if (err !== null) errors.push(err);
|
|
249
|
+
}
|
|
250
|
+
if (errors.length > 0) {
|
|
251
|
+
throw new DocScopeValidationError(
|
|
252
|
+
`${DOC_SCOPE_RELATIVE_PATH}: invalid path(s):
|
|
253
|
+
${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
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`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
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");
|
|
268
|
+
}
|
|
269
|
+
async function docScopeExists(repoRoot) {
|
|
270
|
+
const root = repoRoot ?? await getRepoRoot();
|
|
271
|
+
const target = path.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
272
|
+
try {
|
|
273
|
+
const st = await fs.stat(target);
|
|
274
|
+
return st.isFile();
|
|
275
|
+
} catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function deleteDocScope(repoRoot) {
|
|
280
|
+
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;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async function expandDocScope(paths, repoRoot) {
|
|
289
|
+
const root = repoRoot ?? await getRepoRoot();
|
|
290
|
+
const normalisedRoot = path.resolve(root);
|
|
291
|
+
const found = /* @__PURE__ */ new Set();
|
|
292
|
+
const missing = [];
|
|
293
|
+
for (const rawEntry of paths) {
|
|
294
|
+
if (typeof rawEntry !== "string") continue;
|
|
295
|
+
const normalised = normalizeDocScope([rawEntry]);
|
|
296
|
+
if (normalised.length === 0) {
|
|
297
|
+
if (rawEntry.trim().length > 0) missing.push(rawEntry);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const entry = normalised[0];
|
|
301
|
+
if (validateDocScopeEntry(entry, REPO_ROOT_REL) !== null) {
|
|
302
|
+
missing.push(rawEntry);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (classifyEntry(entry) === "glob") {
|
|
306
|
+
const matches = await glob(entry, {
|
|
307
|
+
cwd: root,
|
|
308
|
+
absolute: true,
|
|
309
|
+
onlyFiles: true,
|
|
310
|
+
dot: false,
|
|
311
|
+
// Case-folding parity with the engine predicate (`nocase: true`).
|
|
312
|
+
caseSensitiveMatch: false,
|
|
313
|
+
// Migrating from fast-glob — disable tinyglobby's directory-pattern
|
|
314
|
+
// auto-expansion so a glob like `packages/*/README.md` keeps exact
|
|
315
|
+
// fast-glob semantics.
|
|
316
|
+
expandDirectories: false
|
|
317
|
+
});
|
|
318
|
+
const inRoot = matches.filter((m) => isInsideRoot(m, normalisedRoot));
|
|
319
|
+
if (inRoot.length === 0) {
|
|
320
|
+
missing.push(rawEntry);
|
|
321
|
+
} else {
|
|
322
|
+
for (const m of inRoot) found.add(m);
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const absolute = path.resolve(root, entry);
|
|
327
|
+
let stat;
|
|
328
|
+
try {
|
|
329
|
+
stat = await fs.stat(absolute);
|
|
330
|
+
} catch (err) {
|
|
331
|
+
if (isNoEntError(err)) {
|
|
332
|
+
missing.push(rawEntry);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
throw err;
|
|
336
|
+
}
|
|
337
|
+
if (stat.isDirectory()) {
|
|
338
|
+
const children = await glob("**/*.md", {
|
|
339
|
+
cwd: absolute,
|
|
340
|
+
absolute: true,
|
|
341
|
+
onlyFiles: true,
|
|
342
|
+
caseSensitiveMatch: false,
|
|
343
|
+
dot: false,
|
|
344
|
+
expandDirectories: false
|
|
345
|
+
});
|
|
346
|
+
for (const c of children) {
|
|
347
|
+
if (isInsideRoot(c, normalisedRoot)) found.add(c);
|
|
348
|
+
}
|
|
349
|
+
} else if (stat.isFile()) {
|
|
350
|
+
if (isInsideRoot(absolute, normalisedRoot)) found.add(absolute);
|
|
351
|
+
} else {
|
|
352
|
+
missing.push(rawEntry);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const files = Array.from(found).sort();
|
|
356
|
+
return { files, missingPaths: missing };
|
|
357
|
+
}
|
|
358
|
+
function isNoEntError(err) {
|
|
359
|
+
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
360
|
+
}
|
|
361
|
+
function isInsideRoot(absolute, normalisedRoot) {
|
|
362
|
+
const resolved = path.resolve(absolute);
|
|
363
|
+
return resolved === normalisedRoot || resolved.startsWith(normalisedRoot + path.sep);
|
|
364
|
+
}
|
|
365
|
+
|
|
173
366
|
// src/trace.ts
|
|
174
367
|
init_esm_shims();
|
|
175
368
|
import {
|
|
@@ -285,9 +478,74 @@ async function runInstall(targetPath, options) {
|
|
|
285
478
|
const resolvedTarget = resolve(process.cwd(), targetPath);
|
|
286
479
|
const repoRoot = await getRepoRoot(resolvedTarget);
|
|
287
480
|
writeSkillTemplate(repoRoot, logger);
|
|
481
|
+
await applyDocScope(repoRoot, logger, options?.provideDocScope);
|
|
288
482
|
await applyAutoInvokeDecision(repoRoot, logger, options?.confirmAutoInvoke);
|
|
289
483
|
appendGitignoreLine(repoRoot, logger);
|
|
290
484
|
}
|
|
485
|
+
function parseScopeInput(answer) {
|
|
486
|
+
return answer.split(/[\s,]+/u).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
487
|
+
}
|
|
488
|
+
function sanitiseScope(paths) {
|
|
489
|
+
return paths.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
490
|
+
}
|
|
491
|
+
async function applyDocScope(repoRoot, logger, provideDocScope) {
|
|
492
|
+
const target = join2(repoRoot, DOC_SCOPE_RELATIVE_PATH);
|
|
493
|
+
if (provideDocScope) {
|
|
494
|
+
const paths2 = sanitiseScope(await provideDocScope());
|
|
495
|
+
if (paths2.length === 0) {
|
|
496
|
+
log(logger, `doc-scope.json \u2192 ${target} (no paths provided, no change)`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
await persistDocScope(repoRoot, logger, target, paths2);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (await docScopeExists(repoRoot)) {
|
|
503
|
+
log(logger, `doc-scope.json \u2192 ${target} (already configured, no change)`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (!process.stdin.isTTY) {
|
|
507
|
+
log(
|
|
508
|
+
logger,
|
|
509
|
+
`doc-scope.json \u2192 ${target} (non-interactive shell: scope prompt skipped, no change)`
|
|
510
|
+
);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const paths = await promptDocScope();
|
|
514
|
+
if (paths.length === 0) {
|
|
515
|
+
log(
|
|
516
|
+
logger,
|
|
517
|
+
`doc-scope.json \u2192 ${target} (no paths provided, no change \u2014 first /delfini run will prompt)`
|
|
518
|
+
);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
await persistDocScope(repoRoot, logger, target, paths);
|
|
522
|
+
}
|
|
523
|
+
async function promptDocScope() {
|
|
524
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
525
|
+
try {
|
|
526
|
+
const answer = await rl.question(
|
|
527
|
+
"Which docs should Delfini track? Enter one or more paths \u2014 directories (recursive .md scan), files, or globs, space- or comma-separated (e.g. `docs/ specs/architecture.md packages/*/README.md`). Leave blank to skip: "
|
|
528
|
+
);
|
|
529
|
+
return parseScopeInput(answer);
|
|
530
|
+
} finally {
|
|
531
|
+
rl.close();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function persistDocScope(repoRoot, logger, target, paths) {
|
|
535
|
+
try {
|
|
536
|
+
await writeDocScope(paths, { repoRoot });
|
|
537
|
+
log(logger, `doc-scope.json \u2192 ${target} (wrote ${paths.length} path(s))`);
|
|
538
|
+
} catch (err) {
|
|
539
|
+
if (err instanceof DocScopeValidationError) {
|
|
540
|
+
log(
|
|
541
|
+
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.`
|
|
543
|
+
);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
throw err;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
291
549
|
function parseYesNo(answer) {
|
|
292
550
|
const normalised = answer.trim().toLowerCase();
|
|
293
551
|
return normalised === "y" || normalised === "yes";
|
|
@@ -403,8 +661,8 @@ function log(logger, message) {
|
|
|
403
661
|
|
|
404
662
|
// src/commands/local-finalize.ts
|
|
405
663
|
init_esm_shims();
|
|
406
|
-
import { promises as
|
|
407
|
-
import
|
|
664
|
+
import { promises as fs2 } from "fs";
|
|
665
|
+
import path2 from "path";
|
|
408
666
|
var TRACE_DIR_RELATIVE = ".delfini-trace";
|
|
409
667
|
var ANALYSIS_INPUT_FILENAME = "analysis-input.json";
|
|
410
668
|
var REPORT_FILENAME = "report.md";
|
|
@@ -425,10 +683,10 @@ async function runLocalFinalize(options) {
|
|
|
425
683
|
const stderr = options.stderr ?? process.stderr;
|
|
426
684
|
const stdout = options.stdout ?? process.stdout;
|
|
427
685
|
const repoRoot = options.repoRoot ?? await getRepoRoot();
|
|
428
|
-
const findingsPath =
|
|
686
|
+
const findingsPath = path2.isAbsolute(options.findingsPath) ? options.findingsPath : path2.join(repoRoot, options.findingsPath);
|
|
429
687
|
let findingsContent;
|
|
430
688
|
try {
|
|
431
|
-
findingsContent = await
|
|
689
|
+
findingsContent = await fs2.readFile(findingsPath, "utf8");
|
|
432
690
|
} catch (err) {
|
|
433
691
|
return emitSchemaValidationError(stderr, [
|
|
434
692
|
{
|
|
@@ -448,10 +706,10 @@ async function runLocalFinalize(options) {
|
|
|
448
706
|
}
|
|
449
707
|
]);
|
|
450
708
|
}
|
|
451
|
-
const analysisInputPath =
|
|
709
|
+
const analysisInputPath = path2.join(repoRoot, TRACE_DIR_RELATIVE, ANALYSIS_INPUT_FILENAME);
|
|
452
710
|
let docs;
|
|
453
711
|
try {
|
|
454
|
-
const raw = await
|
|
712
|
+
const raw = await fs2.readFile(analysisInputPath, "utf8");
|
|
455
713
|
const parsed = JSON.parse(raw);
|
|
456
714
|
if (!Array.isArray(parsed.docs)) {
|
|
457
715
|
return emitSchemaValidationError(stderr, [
|
|
@@ -666,201 +924,6 @@ import path3 from "path";
|
|
|
666
924
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
667
925
|
import { promisify } from "util";
|
|
668
926
|
import simpleGit3 from "simple-git";
|
|
669
|
-
|
|
670
|
-
// src/doc-scope.ts
|
|
671
|
-
init_esm_shims();
|
|
672
|
-
import { promises as fs2 } from "fs";
|
|
673
|
-
import path2 from "path";
|
|
674
|
-
import { glob } from "tinyglobby";
|
|
675
|
-
var DOC_SCOPE_RELATIVE_PATH = ".claude/skills/delfini/doc-scope.json";
|
|
676
|
-
var DOC_SCOPE_VERSION = 1;
|
|
677
|
-
var DOC_SCOPE_VERSION_MISMATCH_MESSAGE = "your doc-scope.json is for a newer @delfini/cli; please upgrade.";
|
|
678
|
-
var REPO_ROOT_REL = ".";
|
|
679
|
-
var DocScopeVersionMismatchError = class extends Error {
|
|
680
|
-
code = "DOC_SCOPE_VERSION_MISMATCH";
|
|
681
|
-
constructor(message = DOC_SCOPE_VERSION_MISMATCH_MESSAGE) {
|
|
682
|
-
super(message);
|
|
683
|
-
this.name = "DocScopeVersionMismatchError";
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
var DocScopeCorruptError = class extends Error {
|
|
687
|
-
code = "DOC_SCOPE_CORRUPT";
|
|
688
|
-
constructor(message) {
|
|
689
|
-
super(message);
|
|
690
|
-
this.name = "DocScopeCorruptError";
|
|
691
|
-
}
|
|
692
|
-
};
|
|
693
|
-
var DocScopeValidationError = class extends Error {
|
|
694
|
-
code = "DOC_SCOPE_VALIDATION";
|
|
695
|
-
constructor(message) {
|
|
696
|
-
super(message);
|
|
697
|
-
this.name = "DocScopeValidationError";
|
|
698
|
-
}
|
|
699
|
-
};
|
|
700
|
-
var docScopeSchemaV1 = external_exports.object({
|
|
701
|
-
version: external_exports.literal(1),
|
|
702
|
-
doc_scope: external_exports.array(external_exports.string().min(1))
|
|
703
|
-
});
|
|
704
|
-
var versionProbeSchema = external_exports.object({
|
|
705
|
-
version: external_exports.number().int().positive()
|
|
706
|
-
});
|
|
707
|
-
async function readDocScope(repoRoot) {
|
|
708
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
709
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
710
|
-
let raw;
|
|
711
|
-
try {
|
|
712
|
-
raw = await fs2.readFile(target, "utf8");
|
|
713
|
-
} catch (err) {
|
|
714
|
-
if (isNoEntError(err)) return null;
|
|
715
|
-
throw err;
|
|
716
|
-
}
|
|
717
|
-
let parsed;
|
|
718
|
-
try {
|
|
719
|
-
parsed = JSON.parse(raw);
|
|
720
|
-
} catch (err) {
|
|
721
|
-
throw new DocScopeCorruptError(
|
|
722
|
-
`${DOC_SCOPE_RELATIVE_PATH} is malformed: ${err.message}`
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
const probe = versionProbeSchema.safeParse(parsed);
|
|
726
|
-
if (probe.success && probe.data.version > DOC_SCOPE_VERSION) {
|
|
727
|
-
throw new DocScopeVersionMismatchError();
|
|
728
|
-
}
|
|
729
|
-
const result = docScopeSchemaV1.safeParse(parsed);
|
|
730
|
-
if (!result.success) {
|
|
731
|
-
throw new DocScopeCorruptError(
|
|
732
|
-
`${DOC_SCOPE_RELATIVE_PATH} is malformed: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
733
|
-
);
|
|
734
|
-
}
|
|
735
|
-
return result.data;
|
|
736
|
-
}
|
|
737
|
-
async function writeDocScope(paths, options) {
|
|
738
|
-
const root = options?.repoRoot ?? await getRepoRoot();
|
|
739
|
-
if (!Array.isArray(paths) || paths.length === 0) {
|
|
740
|
-
throw new DocScopeValidationError("at least one path is required");
|
|
741
|
-
}
|
|
742
|
-
const errors = [];
|
|
743
|
-
for (const entry of paths) {
|
|
744
|
-
const err = validateDocScopeEntry(entry, REPO_ROOT_REL);
|
|
745
|
-
if (err !== null) errors.push(err);
|
|
746
|
-
}
|
|
747
|
-
if (errors.length > 0) {
|
|
748
|
-
throw new DocScopeValidationError(
|
|
749
|
-
`${DOC_SCOPE_RELATIVE_PATH}: invalid path(s):
|
|
750
|
-
${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
const normalised = normalizeDocScope(paths);
|
|
754
|
-
if (normalised.length === 0) {
|
|
755
|
-
throw new DocScopeValidationError(
|
|
756
|
-
`${DOC_SCOPE_RELATIVE_PATH}: every entry collapses to an empty scope after normalisation (e.g. '.', './', 'docs/..') \u2014 provide at least one concrete path`
|
|
757
|
-
);
|
|
758
|
-
}
|
|
759
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
760
|
-
await fs2.mkdir(path2.dirname(target), { recursive: true });
|
|
761
|
-
const payload = { version: DOC_SCOPE_VERSION, doc_scope: normalised };
|
|
762
|
-
const json = `${JSON.stringify(payload, null, 2)}
|
|
763
|
-
`;
|
|
764
|
-
await fs2.writeFile(target, json, "utf8");
|
|
765
|
-
}
|
|
766
|
-
async function docScopeExists(repoRoot) {
|
|
767
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
768
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
769
|
-
try {
|
|
770
|
-
const st = await fs2.stat(target);
|
|
771
|
-
return st.isFile();
|
|
772
|
-
} catch {
|
|
773
|
-
return false;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
async function deleteDocScope(repoRoot) {
|
|
777
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
778
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
779
|
-
try {
|
|
780
|
-
await fs2.unlink(target);
|
|
781
|
-
} catch (err) {
|
|
782
|
-
if (!isNoEntError(err)) throw err;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
async function expandDocScope(paths, repoRoot) {
|
|
786
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
787
|
-
const normalisedRoot = path2.resolve(root);
|
|
788
|
-
const found = /* @__PURE__ */ new Set();
|
|
789
|
-
const missing = [];
|
|
790
|
-
for (const rawEntry of paths) {
|
|
791
|
-
if (typeof rawEntry !== "string") continue;
|
|
792
|
-
const normalised = normalizeDocScope([rawEntry]);
|
|
793
|
-
if (normalised.length === 0) {
|
|
794
|
-
if (rawEntry.trim().length > 0) missing.push(rawEntry);
|
|
795
|
-
continue;
|
|
796
|
-
}
|
|
797
|
-
const entry = normalised[0];
|
|
798
|
-
if (validateDocScopeEntry(entry, REPO_ROOT_REL) !== null) {
|
|
799
|
-
missing.push(rawEntry);
|
|
800
|
-
continue;
|
|
801
|
-
}
|
|
802
|
-
if (classifyEntry(entry) === "glob") {
|
|
803
|
-
const matches = await glob(entry, {
|
|
804
|
-
cwd: root,
|
|
805
|
-
absolute: true,
|
|
806
|
-
onlyFiles: true,
|
|
807
|
-
dot: false,
|
|
808
|
-
// Case-folding parity with the engine predicate (`nocase: true`).
|
|
809
|
-
caseSensitiveMatch: false,
|
|
810
|
-
// Migrating from fast-glob — disable tinyglobby's directory-pattern
|
|
811
|
-
// auto-expansion so a glob like `packages/*/README.md` keeps exact
|
|
812
|
-
// fast-glob semantics.
|
|
813
|
-
expandDirectories: false
|
|
814
|
-
});
|
|
815
|
-
const inRoot = matches.filter((m) => isInsideRoot(m, normalisedRoot));
|
|
816
|
-
if (inRoot.length === 0) {
|
|
817
|
-
missing.push(rawEntry);
|
|
818
|
-
} else {
|
|
819
|
-
for (const m of inRoot) found.add(m);
|
|
820
|
-
}
|
|
821
|
-
continue;
|
|
822
|
-
}
|
|
823
|
-
const absolute = path2.resolve(root, entry);
|
|
824
|
-
let stat;
|
|
825
|
-
try {
|
|
826
|
-
stat = await fs2.stat(absolute);
|
|
827
|
-
} catch (err) {
|
|
828
|
-
if (isNoEntError(err)) {
|
|
829
|
-
missing.push(rawEntry);
|
|
830
|
-
continue;
|
|
831
|
-
}
|
|
832
|
-
throw err;
|
|
833
|
-
}
|
|
834
|
-
if (stat.isDirectory()) {
|
|
835
|
-
const children = await glob("**/*.md", {
|
|
836
|
-
cwd: absolute,
|
|
837
|
-
absolute: true,
|
|
838
|
-
onlyFiles: true,
|
|
839
|
-
caseSensitiveMatch: false,
|
|
840
|
-
dot: false,
|
|
841
|
-
expandDirectories: false
|
|
842
|
-
});
|
|
843
|
-
for (const c of children) {
|
|
844
|
-
if (isInsideRoot(c, normalisedRoot)) found.add(c);
|
|
845
|
-
}
|
|
846
|
-
} else if (stat.isFile()) {
|
|
847
|
-
if (isInsideRoot(absolute, normalisedRoot)) found.add(absolute);
|
|
848
|
-
} else {
|
|
849
|
-
missing.push(rawEntry);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
const files = Array.from(found).sort();
|
|
853
|
-
return { files, missingPaths: missing };
|
|
854
|
-
}
|
|
855
|
-
function isNoEntError(err) {
|
|
856
|
-
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
857
|
-
}
|
|
858
|
-
function isInsideRoot(absolute, normalisedRoot) {
|
|
859
|
-
const resolved = path2.resolve(absolute);
|
|
860
|
-
return resolved === normalisedRoot || resolved.startsWith(normalisedRoot + path2.sep);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// src/commands/local-prepare.ts
|
|
864
927
|
var execFileAsync = promisify(execFile);
|
|
865
928
|
var UNTRACKED_DIFF_MAX_BUFFER = 64 * 1024 * 1024;
|
|
866
929
|
var PROMPT_TOKEN_BUDGET = 15e4;
|
|
@@ -1188,10 +1251,16 @@ async function main(argv) {
|
|
|
1188
1251
|
});
|
|
1189
1252
|
program.command("install <path>").description(
|
|
1190
1253
|
"Scaffold .claude/skills/delfini/SKILL.md + CLAUDE.md auto-invoke + .gitignore append"
|
|
1191
|
-
).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").
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1254
|
+
).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
|
+
"--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."
|
|
1257
|
+
).action(
|
|
1258
|
+
async (targetPath, opts) => {
|
|
1259
|
+
const confirmAutoInvoke = opts.autoInvoke === void 0 ? void 0 : () => Promise.resolve(opts.autoInvoke);
|
|
1260
|
+
const provideDocScope = opts.scope === void 0 ? void 0 : () => Promise.resolve(parseScopeInput(opts.scope));
|
|
1261
|
+
await runInstall(targetPath, { tool: opts.tool, confirmAutoInvoke, provideDocScope });
|
|
1262
|
+
}
|
|
1263
|
+
);
|
|
1195
1264
|
program.command("local-prepare").description(
|
|
1196
1265
|
"Compute diff + doc-scope + prompt + token-budget gate; write .delfini-trace/"
|
|
1197
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(
|
|
@@ -1265,13 +1334,6 @@ export {
|
|
|
1265
1334
|
RepoRootNotFoundError,
|
|
1266
1335
|
getRepoRoot,
|
|
1267
1336
|
runDiffStatus,
|
|
1268
|
-
ensureTraceDir,
|
|
1269
|
-
appendToGitignore,
|
|
1270
|
-
writeTraceFile,
|
|
1271
|
-
writeRetryAttemptFile,
|
|
1272
|
-
InstallToolNotSupportedError,
|
|
1273
|
-
runInstall,
|
|
1274
|
-
runLocalFinalize,
|
|
1275
1337
|
DOC_SCOPE_RELATIVE_PATH,
|
|
1276
1338
|
DOC_SCOPE_VERSION,
|
|
1277
1339
|
DocScopeVersionMismatchError,
|
|
@@ -1282,6 +1344,13 @@ export {
|
|
|
1282
1344
|
docScopeExists,
|
|
1283
1345
|
deleteDocScope,
|
|
1284
1346
|
expandDocScope,
|
|
1347
|
+
ensureTraceDir,
|
|
1348
|
+
appendToGitignore,
|
|
1349
|
+
writeTraceFile,
|
|
1350
|
+
writeRetryAttemptFile,
|
|
1351
|
+
InstallToolNotSupportedError,
|
|
1352
|
+
runInstall,
|
|
1353
|
+
runLocalFinalize,
|
|
1285
1354
|
PROMPT_TOKEN_BUDGET,
|
|
1286
1355
|
runLocalPrepare,
|
|
1287
1356
|
main
|
|
@@ -31,11 +31,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
-
// ../../node_modules/.pnpm/tsup@8.5.
|
|
34
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.15_tsx@4.22.4_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
|
|
35
35
|
import path from "path";
|
|
36
36
|
import { fileURLToPath } from "url";
|
|
37
37
|
var init_esm_shims = __esm({
|
|
38
|
-
"../../node_modules/.pnpm/tsup@8.5.
|
|
38
|
+
"../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.15_tsx@4.22.4_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js"() {
|
|
39
39
|
"use strict";
|
|
40
40
|
}
|
|
41
41
|
});
|