@delfini/cli 0.1.1 → 0.2.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/LICENSE +202 -0
- package/README.md +1 -1
- package/dist/__engine-probe__.cjs +2 -2
- package/dist/__engine-probe__.js +1 -1
- package/dist/{chunk-PMVL53TT.js → chunk-K5X5TSUR.js} +293 -215
- package/dist/{chunk-MUW24ZC4.js → chunk-LJKEHO6F.js} +2 -2
- package/dist/cli.cjs +532 -408
- package/dist/cli.js +2 -2
- package/dist/index.cjs +88 -8
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2 -2
- package/package.json +7 -1
|
@@ -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 {
|
|
@@ -264,8 +457,17 @@ var CLAUDE_MD_FILENAME = "CLAUDE.md";
|
|
|
264
457
|
var SUPPORTED_TOOL = "CLAUDE";
|
|
265
458
|
var TEMPLATES_DIR = resolveTemplatesDir();
|
|
266
459
|
function resolveTemplatesDir() {
|
|
267
|
-
|
|
268
|
-
|
|
460
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
461
|
+
for (let i = 0; i < 5; i++) {
|
|
462
|
+
const candidate = resolve(dir, "templates");
|
|
463
|
+
if (existsSync2(join2(candidate, "SKILL.md"))) {
|
|
464
|
+
return candidate;
|
|
465
|
+
}
|
|
466
|
+
const parent = dirname(dir);
|
|
467
|
+
if (parent === dir) break;
|
|
468
|
+
dir = parent;
|
|
469
|
+
}
|
|
470
|
+
throw new Error("templates/ directory not found relative to the CLI module");
|
|
269
471
|
}
|
|
270
472
|
async function runInstall(targetPath, options) {
|
|
271
473
|
const tool = options?.tool ?? SUPPORTED_TOOL;
|
|
@@ -276,9 +478,74 @@ async function runInstall(targetPath, options) {
|
|
|
276
478
|
const resolvedTarget = resolve(process.cwd(), targetPath);
|
|
277
479
|
const repoRoot = await getRepoRoot(resolvedTarget);
|
|
278
480
|
writeSkillTemplate(repoRoot, logger);
|
|
481
|
+
await applyDocScope(repoRoot, logger, options?.provideDocScope);
|
|
279
482
|
await applyAutoInvokeDecision(repoRoot, logger, options?.confirmAutoInvoke);
|
|
280
483
|
appendGitignoreLine(repoRoot, logger);
|
|
281
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
|
+
}
|
|
282
549
|
function parseYesNo(answer) {
|
|
283
550
|
const normalised = answer.trim().toLowerCase();
|
|
284
551
|
return normalised === "y" || normalised === "yes";
|
|
@@ -394,8 +661,8 @@ function log(logger, message) {
|
|
|
394
661
|
|
|
395
662
|
// src/commands/local-finalize.ts
|
|
396
663
|
init_esm_shims();
|
|
397
|
-
import { promises as
|
|
398
|
-
import
|
|
664
|
+
import { promises as fs2 } from "fs";
|
|
665
|
+
import path2 from "path";
|
|
399
666
|
var TRACE_DIR_RELATIVE = ".delfini-trace";
|
|
400
667
|
var ANALYSIS_INPUT_FILENAME = "analysis-input.json";
|
|
401
668
|
var REPORT_FILENAME = "report.md";
|
|
@@ -416,10 +683,10 @@ async function runLocalFinalize(options) {
|
|
|
416
683
|
const stderr = options.stderr ?? process.stderr;
|
|
417
684
|
const stdout = options.stdout ?? process.stdout;
|
|
418
685
|
const repoRoot = options.repoRoot ?? await getRepoRoot();
|
|
419
|
-
const findingsPath =
|
|
686
|
+
const findingsPath = path2.isAbsolute(options.findingsPath) ? options.findingsPath : path2.join(repoRoot, options.findingsPath);
|
|
420
687
|
let findingsContent;
|
|
421
688
|
try {
|
|
422
|
-
findingsContent = await
|
|
689
|
+
findingsContent = await fs2.readFile(findingsPath, "utf8");
|
|
423
690
|
} catch (err) {
|
|
424
691
|
return emitSchemaValidationError(stderr, [
|
|
425
692
|
{
|
|
@@ -439,10 +706,10 @@ async function runLocalFinalize(options) {
|
|
|
439
706
|
}
|
|
440
707
|
]);
|
|
441
708
|
}
|
|
442
|
-
const analysisInputPath =
|
|
709
|
+
const analysisInputPath = path2.join(repoRoot, TRACE_DIR_RELATIVE, ANALYSIS_INPUT_FILENAME);
|
|
443
710
|
let docs;
|
|
444
711
|
try {
|
|
445
|
-
const raw = await
|
|
712
|
+
const raw = await fs2.readFile(analysisInputPath, "utf8");
|
|
446
713
|
const parsed = JSON.parse(raw);
|
|
447
714
|
if (!Array.isArray(parsed.docs)) {
|
|
448
715
|
return emitSchemaValidationError(stderr, [
|
|
@@ -657,201 +924,6 @@ import path3 from "path";
|
|
|
657
924
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
658
925
|
import { promisify } from "util";
|
|
659
926
|
import simpleGit3 from "simple-git";
|
|
660
|
-
|
|
661
|
-
// src/doc-scope.ts
|
|
662
|
-
init_esm_shims();
|
|
663
|
-
import { promises as fs2 } from "fs";
|
|
664
|
-
import path2 from "path";
|
|
665
|
-
import { glob } from "tinyglobby";
|
|
666
|
-
var DOC_SCOPE_RELATIVE_PATH = ".claude/skills/delfini/doc-scope.json";
|
|
667
|
-
var DOC_SCOPE_VERSION = 1;
|
|
668
|
-
var DOC_SCOPE_VERSION_MISMATCH_MESSAGE = "your doc-scope.json is for a newer @delfini/cli; please upgrade.";
|
|
669
|
-
var REPO_ROOT_REL = ".";
|
|
670
|
-
var DocScopeVersionMismatchError = class extends Error {
|
|
671
|
-
code = "DOC_SCOPE_VERSION_MISMATCH";
|
|
672
|
-
constructor(message = DOC_SCOPE_VERSION_MISMATCH_MESSAGE) {
|
|
673
|
-
super(message);
|
|
674
|
-
this.name = "DocScopeVersionMismatchError";
|
|
675
|
-
}
|
|
676
|
-
};
|
|
677
|
-
var DocScopeCorruptError = class extends Error {
|
|
678
|
-
code = "DOC_SCOPE_CORRUPT";
|
|
679
|
-
constructor(message) {
|
|
680
|
-
super(message);
|
|
681
|
-
this.name = "DocScopeCorruptError";
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
var DocScopeValidationError = class extends Error {
|
|
685
|
-
code = "DOC_SCOPE_VALIDATION";
|
|
686
|
-
constructor(message) {
|
|
687
|
-
super(message);
|
|
688
|
-
this.name = "DocScopeValidationError";
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
var docScopeSchemaV1 = external_exports.object({
|
|
692
|
-
version: external_exports.literal(1),
|
|
693
|
-
doc_scope: external_exports.array(external_exports.string().min(1))
|
|
694
|
-
});
|
|
695
|
-
var versionProbeSchema = external_exports.object({
|
|
696
|
-
version: external_exports.number().int().positive()
|
|
697
|
-
});
|
|
698
|
-
async function readDocScope(repoRoot) {
|
|
699
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
700
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
701
|
-
let raw;
|
|
702
|
-
try {
|
|
703
|
-
raw = await fs2.readFile(target, "utf8");
|
|
704
|
-
} catch (err) {
|
|
705
|
-
if (isNoEntError(err)) return null;
|
|
706
|
-
throw err;
|
|
707
|
-
}
|
|
708
|
-
let parsed;
|
|
709
|
-
try {
|
|
710
|
-
parsed = JSON.parse(raw);
|
|
711
|
-
} catch (err) {
|
|
712
|
-
throw new DocScopeCorruptError(
|
|
713
|
-
`${DOC_SCOPE_RELATIVE_PATH} is malformed: ${err.message}`
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
const probe = versionProbeSchema.safeParse(parsed);
|
|
717
|
-
if (probe.success && probe.data.version > DOC_SCOPE_VERSION) {
|
|
718
|
-
throw new DocScopeVersionMismatchError();
|
|
719
|
-
}
|
|
720
|
-
const result = docScopeSchemaV1.safeParse(parsed);
|
|
721
|
-
if (!result.success) {
|
|
722
|
-
throw new DocScopeCorruptError(
|
|
723
|
-
`${DOC_SCOPE_RELATIVE_PATH} is malformed: ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
return result.data;
|
|
727
|
-
}
|
|
728
|
-
async function writeDocScope(paths, options) {
|
|
729
|
-
const root = options?.repoRoot ?? await getRepoRoot();
|
|
730
|
-
if (!Array.isArray(paths) || paths.length === 0) {
|
|
731
|
-
throw new DocScopeValidationError("at least one path is required");
|
|
732
|
-
}
|
|
733
|
-
const errors = [];
|
|
734
|
-
for (const entry of paths) {
|
|
735
|
-
const err = validateDocScopeEntry(entry, REPO_ROOT_REL);
|
|
736
|
-
if (err !== null) errors.push(err);
|
|
737
|
-
}
|
|
738
|
-
if (errors.length > 0) {
|
|
739
|
-
throw new DocScopeValidationError(
|
|
740
|
-
`${DOC_SCOPE_RELATIVE_PATH}: invalid path(s):
|
|
741
|
-
${errors.map((e) => ` - ${e}`).join("\n")}`
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
const normalised = normalizeDocScope(paths);
|
|
745
|
-
if (normalised.length === 0) {
|
|
746
|
-
throw new DocScopeValidationError(
|
|
747
|
-
`${DOC_SCOPE_RELATIVE_PATH}: every entry collapses to an empty scope after normalisation (e.g. '.', './', 'docs/..') \u2014 provide at least one concrete path`
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
751
|
-
await fs2.mkdir(path2.dirname(target), { recursive: true });
|
|
752
|
-
const payload = { version: DOC_SCOPE_VERSION, doc_scope: normalised };
|
|
753
|
-
const json = `${JSON.stringify(payload, null, 2)}
|
|
754
|
-
`;
|
|
755
|
-
await fs2.writeFile(target, json, "utf8");
|
|
756
|
-
}
|
|
757
|
-
async function docScopeExists(repoRoot) {
|
|
758
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
759
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
760
|
-
try {
|
|
761
|
-
const st = await fs2.stat(target);
|
|
762
|
-
return st.isFile();
|
|
763
|
-
} catch {
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
async function deleteDocScope(repoRoot) {
|
|
768
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
769
|
-
const target = path2.join(root, DOC_SCOPE_RELATIVE_PATH);
|
|
770
|
-
try {
|
|
771
|
-
await fs2.unlink(target);
|
|
772
|
-
} catch (err) {
|
|
773
|
-
if (!isNoEntError(err)) throw err;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
async function expandDocScope(paths, repoRoot) {
|
|
777
|
-
const root = repoRoot ?? await getRepoRoot();
|
|
778
|
-
const normalisedRoot = path2.resolve(root);
|
|
779
|
-
const found = /* @__PURE__ */ new Set();
|
|
780
|
-
const missing = [];
|
|
781
|
-
for (const rawEntry of paths) {
|
|
782
|
-
if (typeof rawEntry !== "string") continue;
|
|
783
|
-
const normalised = normalizeDocScope([rawEntry]);
|
|
784
|
-
if (normalised.length === 0) {
|
|
785
|
-
if (rawEntry.trim().length > 0) missing.push(rawEntry);
|
|
786
|
-
continue;
|
|
787
|
-
}
|
|
788
|
-
const entry = normalised[0];
|
|
789
|
-
if (validateDocScopeEntry(entry, REPO_ROOT_REL) !== null) {
|
|
790
|
-
missing.push(rawEntry);
|
|
791
|
-
continue;
|
|
792
|
-
}
|
|
793
|
-
if (classifyEntry(entry) === "glob") {
|
|
794
|
-
const matches = await glob(entry, {
|
|
795
|
-
cwd: root,
|
|
796
|
-
absolute: true,
|
|
797
|
-
onlyFiles: true,
|
|
798
|
-
dot: false,
|
|
799
|
-
// Case-folding parity with the engine predicate (`nocase: true`).
|
|
800
|
-
caseSensitiveMatch: false,
|
|
801
|
-
// Migrating from fast-glob — disable tinyglobby's directory-pattern
|
|
802
|
-
// auto-expansion so a glob like `packages/*/README.md` keeps exact
|
|
803
|
-
// fast-glob semantics.
|
|
804
|
-
expandDirectories: false
|
|
805
|
-
});
|
|
806
|
-
const inRoot = matches.filter((m) => isInsideRoot(m, normalisedRoot));
|
|
807
|
-
if (inRoot.length === 0) {
|
|
808
|
-
missing.push(rawEntry);
|
|
809
|
-
} else {
|
|
810
|
-
for (const m of inRoot) found.add(m);
|
|
811
|
-
}
|
|
812
|
-
continue;
|
|
813
|
-
}
|
|
814
|
-
const absolute = path2.resolve(root, entry);
|
|
815
|
-
let stat;
|
|
816
|
-
try {
|
|
817
|
-
stat = await fs2.stat(absolute);
|
|
818
|
-
} catch (err) {
|
|
819
|
-
if (isNoEntError(err)) {
|
|
820
|
-
missing.push(rawEntry);
|
|
821
|
-
continue;
|
|
822
|
-
}
|
|
823
|
-
throw err;
|
|
824
|
-
}
|
|
825
|
-
if (stat.isDirectory()) {
|
|
826
|
-
const children = await glob("**/*.md", {
|
|
827
|
-
cwd: absolute,
|
|
828
|
-
absolute: true,
|
|
829
|
-
onlyFiles: true,
|
|
830
|
-
caseSensitiveMatch: false,
|
|
831
|
-
dot: false,
|
|
832
|
-
expandDirectories: false
|
|
833
|
-
});
|
|
834
|
-
for (const c of children) {
|
|
835
|
-
if (isInsideRoot(c, normalisedRoot)) found.add(c);
|
|
836
|
-
}
|
|
837
|
-
} else if (stat.isFile()) {
|
|
838
|
-
if (isInsideRoot(absolute, normalisedRoot)) found.add(absolute);
|
|
839
|
-
} else {
|
|
840
|
-
missing.push(rawEntry);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
const files = Array.from(found).sort();
|
|
844
|
-
return { files, missingPaths: missing };
|
|
845
|
-
}
|
|
846
|
-
function isNoEntError(err) {
|
|
847
|
-
return typeof err === "object" && err !== null && err.code === "ENOENT";
|
|
848
|
-
}
|
|
849
|
-
function isInsideRoot(absolute, normalisedRoot) {
|
|
850
|
-
const resolved = path2.resolve(absolute);
|
|
851
|
-
return resolved === normalisedRoot || resolved.startsWith(normalisedRoot + path2.sep);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
// src/commands/local-prepare.ts
|
|
855
927
|
var execFileAsync = promisify(execFile);
|
|
856
928
|
var UNTRACKED_DIFF_MAX_BUFFER = 64 * 1024 * 1024;
|
|
857
929
|
var PROMPT_TOKEN_BUDGET = 15e4;
|
|
@@ -1179,10 +1251,16 @@ async function main(argv) {
|
|
|
1179
1251
|
});
|
|
1180
1252
|
program.command("install <path>").description(
|
|
1181
1253
|
"Scaffold .claude/skills/delfini/SKILL.md + CLAUDE.md auto-invoke + .gitignore append"
|
|
1182
|
-
).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").
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
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
|
+
);
|
|
1186
1264
|
program.command("local-prepare").description(
|
|
1187
1265
|
"Compute diff + doc-scope + prompt + token-budget gate; write .delfini-trace/"
|
|
1188
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(
|
|
@@ -1256,13 +1334,6 @@ export {
|
|
|
1256
1334
|
RepoRootNotFoundError,
|
|
1257
1335
|
getRepoRoot,
|
|
1258
1336
|
runDiffStatus,
|
|
1259
|
-
ensureTraceDir,
|
|
1260
|
-
appendToGitignore,
|
|
1261
|
-
writeTraceFile,
|
|
1262
|
-
writeRetryAttemptFile,
|
|
1263
|
-
InstallToolNotSupportedError,
|
|
1264
|
-
runInstall,
|
|
1265
|
-
runLocalFinalize,
|
|
1266
1337
|
DOC_SCOPE_RELATIVE_PATH,
|
|
1267
1338
|
DOC_SCOPE_VERSION,
|
|
1268
1339
|
DocScopeVersionMismatchError,
|
|
@@ -1273,6 +1344,13 @@ export {
|
|
|
1273
1344
|
docScopeExists,
|
|
1274
1345
|
deleteDocScope,
|
|
1275
1346
|
expandDocScope,
|
|
1347
|
+
ensureTraceDir,
|
|
1348
|
+
appendToGitignore,
|
|
1349
|
+
writeTraceFile,
|
|
1350
|
+
writeRetryAttemptFile,
|
|
1351
|
+
InstallToolNotSupportedError,
|
|
1352
|
+
runInstall,
|
|
1353
|
+
runLocalFinalize,
|
|
1276
1354
|
PROMPT_TOKEN_BUDGET,
|
|
1277
1355
|
runLocalPrepare,
|
|
1278
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
|
});
|