@blaaiz/docs-core 0.1.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/chunk-3ZX4WIE3.js +2984 -0
  4. package/dist/chunk-3ZX4WIE3.js.map +1 -0
  5. package/dist/chunk-JCYR6RPE.js +31 -0
  6. package/dist/chunk-JCYR6RPE.js.map +1 -0
  7. package/dist/chunk-ZKOOKLZ3.js +124 -0
  8. package/dist/chunk-ZKOOKLZ3.js.map +1 -0
  9. package/dist/cli.js +534 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/generator.cjs +508 -0
  12. package/dist/generator.cjs.map +1 -0
  13. package/dist/generator.d.cts +122 -0
  14. package/dist/generator.d.ts +122 -0
  15. package/dist/generator.js +177 -0
  16. package/dist/generator.js.map +1 -0
  17. package/dist/index.cjs +3024 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +1182 -0
  20. package/dist/index.d.ts +1182 -0
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/navigation-CGqFIPlP.d.cts +498 -0
  24. package/dist/navigation-CGqFIPlP.d.ts +498 -0
  25. package/dist/openapi-types-CJ6p5Cux.d.cts +78 -0
  26. package/dist/openapi-types-CJ6p5Cux.d.ts +78 -0
  27. package/dist/ui/api-try-it.cjs +654 -0
  28. package/dist/ui/api-try-it.cjs.map +1 -0
  29. package/dist/ui/api-try-it.d.cts +78 -0
  30. package/dist/ui/api-try-it.d.ts +78 -0
  31. package/dist/ui/api-try-it.js +509 -0
  32. package/dist/ui/api-try-it.js.map +1 -0
  33. package/dist/ui/ask-ai.cjs +810 -0
  34. package/dist/ui/ask-ai.cjs.map +1 -0
  35. package/dist/ui/ask-ai.d.cts +57 -0
  36. package/dist/ui/ask-ai.d.ts +57 -0
  37. package/dist/ui/ask-ai.js +808 -0
  38. package/dist/ui/ask-ai.js.map +1 -0
  39. package/dist/ui/copy-page.cjs +312 -0
  40. package/dist/ui/copy-page.cjs.map +1 -0
  41. package/dist/ui/copy-page.d.cts +33 -0
  42. package/dist/ui/copy-page.d.ts +33 -0
  43. package/dist/ui/copy-page.js +183 -0
  44. package/dist/ui/copy-page.js.map +1 -0
  45. package/dist/ui/mermaid.cjs +363 -0
  46. package/dist/ui/mermaid.cjs.map +1 -0
  47. package/dist/ui/mermaid.d.cts +13 -0
  48. package/dist/ui/mermaid.d.ts +13 -0
  49. package/dist/ui/mermaid.js +361 -0
  50. package/dist/ui/mermaid.js.map +1 -0
  51. package/dist/ui.cjs +661 -0
  52. package/dist/ui.cjs.map +1 -0
  53. package/dist/ui.d.cts +428 -0
  54. package/dist/ui.d.ts +428 -0
  55. package/dist/ui.js +537 -0
  56. package/dist/ui.js.map +1 -0
  57. package/package.json +145 -0
  58. package/patches/fumadocs-openapi.patch +173 -0
  59. package/skills/AGENTS-section.md +36 -0
  60. package/skills/SKILL.md +363 -0
  61. package/styles/api-reference.css +1417 -0
  62. package/styles/ask-ai.css +563 -0
  63. package/styles/auth.css +462 -0
  64. package/styles/docs.css +247 -0
  65. package/styles/home.css +376 -0
  66. package/templates/init/content/docs/index.mdx.tmpl +52 -0
  67. package/templates/init/content/docs/meta.json.tmpl +3 -0
  68. package/templates/init/content/docs.json.tmpl +12 -0
  69. package/templates/init/content/nav.json.tmpl +7 -0
  70. package/templates/init/docs.config.ts.tmpl +36 -0
  71. package/templates/init/env.example.tmpl +15 -0
package/dist/cli.js ADDED
@@ -0,0 +1,534 @@
1
+ #!/usr/bin/env node
2
+ import path3 from 'path';
3
+ import { readFile, mkdir, writeFile } from 'fs/promises';
4
+ import { existsSync } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ // src/cli/errors.ts
8
+ var CliError = class extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "CliError";
12
+ }
13
+ };
14
+ function formatCliError(error) {
15
+ if (error instanceof CliError) {
16
+ return error.message;
17
+ }
18
+ if (error instanceof Error && error.message !== "") {
19
+ return `${error.name}: ${error.message}`;
20
+ }
21
+ return `Unexpected failure: ${String(error)}`;
22
+ }
23
+
24
+ // src/cli/fs-utils.ts
25
+ function errorCode(error) {
26
+ if (typeof error === "object" && error !== null && "code" in error) {
27
+ const code = error.code;
28
+ return typeof code === "string" ? code : void 0;
29
+ }
30
+ return void 0;
31
+ }
32
+ function describeFailure(action, file, error) {
33
+ const code = errorCode(error);
34
+ if (code === "EISDIR") {
35
+ return new CliError(`Cannot ${action} ${file}: it is a directory, not a file.`);
36
+ }
37
+ if (code === "EACCES" || code === "EPERM") {
38
+ return new CliError(`Cannot ${action} ${file}: permission denied.`);
39
+ }
40
+ return new CliError(
41
+ `Cannot ${action} ${file}: ${error instanceof Error ? error.message : String(error)}`
42
+ );
43
+ }
44
+ async function readTextFile(file) {
45
+ try {
46
+ return await readFile(file, "utf8");
47
+ } catch (error) {
48
+ if (errorCode(error) === "ENOENT") {
49
+ return null;
50
+ }
51
+ throw describeFailure("read", file, error);
52
+ }
53
+ }
54
+ async function readPackagedFile(file) {
55
+ const content = await readTextFile(file);
56
+ if (content === null) {
57
+ throw new CliError(
58
+ `The @blaaiz/docs-core installation is missing ${file}. Reinstall the package and run the command again.`
59
+ );
60
+ }
61
+ return content;
62
+ }
63
+ async function snapshotFiles(dir, relativePaths) {
64
+ const entries = await Promise.all(
65
+ relativePaths.map(async (relativePath) => {
66
+ const content = await readTextFile(path3.join(dir, ...relativePath.split("/")));
67
+ return [relativePath, content];
68
+ })
69
+ );
70
+ return new Map(entries);
71
+ }
72
+ async function executePlan(dir, plan) {
73
+ for (const entry of plan) {
74
+ if (entry.action === "skip") {
75
+ continue;
76
+ }
77
+ const target = path3.join(dir, ...entry.path.split("/"));
78
+ const parent = path3.dirname(target);
79
+ try {
80
+ await mkdir(parent, { recursive: true });
81
+ } catch (error) {
82
+ throw describeFailure("create the directory", parent, error);
83
+ }
84
+ try {
85
+ await writeFile(target, entry.content, "utf8");
86
+ } catch (error) {
87
+ throw describeFailure("write", target, error);
88
+ }
89
+ }
90
+ }
91
+
92
+ // src/cli/plan.ts
93
+ var SECTION_BEGIN = "<!-- docs-core:begin -->";
94
+ var SECTION_END = "<!-- docs-core:end -->";
95
+ var ACTION_LABELS = Object.freeze({
96
+ create: "created",
97
+ overwrite: "overwritten",
98
+ skip: "skipped",
99
+ "update-section": "updated"
100
+ });
101
+ var LABEL_WIDTH = 12;
102
+ var PLACEHOLDER_PATTERN = /__[A-Z][A-Z0-9_]*__/g;
103
+ function planFileWrite(path5, content, existing, force) {
104
+ if (existing === null) {
105
+ return { path: path5, action: "create", content };
106
+ }
107
+ if (existing === content) {
108
+ return { path: path5, action: "skip", content, note: "already up to date" };
109
+ }
110
+ if (force) {
111
+ return { path: path5, action: "overwrite", content };
112
+ }
113
+ return { path: path5, action: "skip", content, note: "exists \u2014 pass --force to overwrite" };
114
+ }
115
+ function countOccurrences(haystack, needle) {
116
+ let count = 0;
117
+ let index = haystack.indexOf(needle);
118
+ while (index !== -1) {
119
+ count += 1;
120
+ index = haystack.indexOf(needle, index + needle.length);
121
+ }
122
+ return count;
123
+ }
124
+ function buildMarkedSection(body) {
125
+ return `${SECTION_BEGIN}
126
+ ${body.trim()}
127
+ ${SECTION_END}`;
128
+ }
129
+ function applyMarkedSection(document, block) {
130
+ const begins = countOccurrences(document, SECTION_BEGIN);
131
+ const ends = countOccurrences(document, SECTION_END);
132
+ if (begins > 1 || ends > 1) {
133
+ throw new CliError(
134
+ `AGENTS.md holds more than one ${SECTION_BEGIN} / ${SECTION_END} pair. Keep exactly one, then run the command again.`
135
+ );
136
+ }
137
+ if (begins !== ends) {
138
+ throw new CliError(
139
+ `AGENTS.md has an unbalanced docs-core marker: ${String(begins)} '${SECTION_BEGIN}' and ${String(ends)} '${SECTION_END}'. Repair or delete the markers, then run the command again.`
140
+ );
141
+ }
142
+ if (begins === 0) {
143
+ const head = document.replace(/\s+$/, "");
144
+ return head === "" ? `${block}
145
+ ` : `${head}
146
+
147
+ ${block}
148
+ `;
149
+ }
150
+ const start = document.indexOf(SECTION_BEGIN);
151
+ const end = document.indexOf(SECTION_END);
152
+ if (end < start) {
153
+ throw new CliError(
154
+ `AGENTS.md has ${SECTION_END} before ${SECTION_BEGIN}. Put the markers in order, then run the command again.`
155
+ );
156
+ }
157
+ return `${document.slice(0, start)}${block}${document.slice(end + SECTION_END.length)}`;
158
+ }
159
+ function planMarkedSection(path5, block, existing, onCreate) {
160
+ if (existing === null) {
161
+ return { path: path5, action: "create", content: onCreate(block) };
162
+ }
163
+ const content = applyMarkedSection(existing, block);
164
+ if (content === existing) {
165
+ return { path: path5, action: "skip", content, note: "section already up to date" };
166
+ }
167
+ return { path: path5, action: "update-section", content };
168
+ }
169
+ function interpolate(template, values) {
170
+ return template.replace(PLACEHOLDER_PATTERN, (token) => {
171
+ const value = values[token.slice(2, -2)];
172
+ if (value === void 0) {
173
+ throw new CliError(
174
+ `The packaged template holds an unknown placeholder ${token}. Reinstall @blaaiz/docs-core, and report the problem if it persists.`
175
+ );
176
+ }
177
+ return value;
178
+ });
179
+ }
180
+ function countActions(plan) {
181
+ const counts = {
182
+ create: 0,
183
+ overwrite: 0,
184
+ skip: 0,
185
+ "update-section": 0
186
+ };
187
+ for (const entry of plan) {
188
+ counts[entry.action] += 1;
189
+ }
190
+ return counts;
191
+ }
192
+ function summarizePlan(plan) {
193
+ const lines = plan.map((entry) => {
194
+ const label = ACTION_LABELS[entry.action].padEnd(LABEL_WIDTH, " ");
195
+ const note = entry.note === void 0 ? "" : ` (${entry.note})`;
196
+ return ` ${label}${entry.path}${note}`;
197
+ });
198
+ const counts = countActions(plan);
199
+ const parts = [];
200
+ for (const action of ["create", "overwrite", "update-section", "skip"]) {
201
+ const count = counts[action];
202
+ if (count > 0) {
203
+ parts.push(`${String(count)} ${ACTION_LABELS[action]}`);
204
+ }
205
+ }
206
+ const total = plan.length;
207
+ const tally = parts.length === 0 ? "nothing to do" : parts.join(", ");
208
+ return [...lines, "", `${String(total)} ${total === 1 ? "file" : "files"}: ${tally}`];
209
+ }
210
+
211
+ // src/cli/agents.ts
212
+ var CLAUDE_SKILL_PATH = ".claude/skills/docs-core/SKILL.md";
213
+ var CODEX_SKILL_PATH = ".agents/skills/docs-core/SKILL.md";
214
+ var AGENTS_FILE = "AGENTS.md";
215
+ var SKILL_TEMPLATE = "SKILL.md";
216
+ var SECTION_TEMPLATE = "AGENTS-section.md";
217
+ var NEW_AGENTS_HEADER = `# Agent instructions
218
+
219
+ Always-on instructions for coding agents working in this repository. Add your own
220
+ rules outside the block below: \`docs-core agents\` rewrites only the text between
221
+ its two markers, and leaves the rest of this file untouched.`;
222
+ function buildNewAgentsFile(block) {
223
+ return `${NEW_AGENTS_HEADER}
224
+
225
+ ${block}
226
+ `;
227
+ }
228
+ function planAgents(input) {
229
+ const block = buildMarkedSection(input.section);
230
+ return [
231
+ planFileWrite(
232
+ CLAUDE_SKILL_PATH,
233
+ input.skill,
234
+ input.existing.get(CLAUDE_SKILL_PATH) ?? null,
235
+ input.force
236
+ ),
237
+ planFileWrite(
238
+ CODEX_SKILL_PATH,
239
+ input.skill,
240
+ input.existing.get(CODEX_SKILL_PATH) ?? null,
241
+ input.force
242
+ ),
243
+ planMarkedSection(
244
+ AGENTS_FILE,
245
+ block,
246
+ input.existing.get(AGENTS_FILE) ?? null,
247
+ buildNewAgentsFile
248
+ )
249
+ ];
250
+ }
251
+ async function runAgents(options) {
252
+ const [skill, section] = await Promise.all([
253
+ readPackagedFile(path3.join(options.paths.skillsDir, SKILL_TEMPLATE)),
254
+ readPackagedFile(path3.join(options.paths.skillsDir, SECTION_TEMPLATE))
255
+ ]);
256
+ const existing = await snapshotFiles(options.cwd, [
257
+ CLAUDE_SKILL_PATH,
258
+ CODEX_SKILL_PATH,
259
+ AGENTS_FILE
260
+ ]);
261
+ const plan = planAgents({ skill, section, existing, force: options.force });
262
+ await executePlan(options.cwd, plan);
263
+ options.print(`docs-core agents \xB7 ${options.cwd}`);
264
+ options.print("");
265
+ for (const line of summarizePlan(plan)) {
266
+ options.print(line);
267
+ }
268
+ options.print("");
269
+ options.print("Claude Code reads .claude/skills, Codex reads .agents/skills, and both read");
270
+ options.print("AGENTS.md. Re-run `npx docs-core agents --force` after a docs-core upgrade to");
271
+ options.print("refresh the skill; the AGENTS.md block refreshes without --force.");
272
+ return plan;
273
+ }
274
+
275
+ // src/cli/args.ts
276
+ var CLI_COMMAND_NAMES = Object.freeze([
277
+ "init",
278
+ "agents"
279
+ ]);
280
+ var USAGE = `docs-core \u2014 scaffold and maintain a @blaaiz/docs-core documentation site
281
+
282
+ Usage
283
+ docs-core <command> [options]
284
+
285
+ Commands
286
+ init Write docs.config.ts, the content tree, and .env.example into this directory
287
+ agents Install the docs-core skill for Claude Code and Codex, and update AGENTS.md
288
+
289
+ Options
290
+ -f, --force Overwrite files that already exist
291
+ -h, --help Show this help
292
+ -v, --version Show the version
293
+
294
+ Both commands write into the current directory, never overwrite a file without
295
+ --force, and print every path they touched.`;
296
+ function isCommandName(value) {
297
+ return CLI_COMMAND_NAMES.includes(value);
298
+ }
299
+ function parseCliArgs(argv) {
300
+ if (argv.some((arg) => arg === "-h" || arg === "--help")) {
301
+ return { kind: "help" };
302
+ }
303
+ if (argv.some((arg) => arg === "-v" || arg === "--version")) {
304
+ return { kind: "version" };
305
+ }
306
+ let command;
307
+ let force = false;
308
+ for (const arg of argv) {
309
+ if (arg === "--force" || arg === "-f") {
310
+ force = true;
311
+ continue;
312
+ }
313
+ if (arg.startsWith("-")) {
314
+ return {
315
+ kind: "error",
316
+ message: `Unknown option '${arg}'. The only option is --force; run 'docs-core --help' for usage.`
317
+ };
318
+ }
319
+ if (command !== void 0) {
320
+ return {
321
+ kind: "error",
322
+ message: `Unexpected argument '${arg}'. Run one command at a time: 'docs-core ${command}'.`
323
+ };
324
+ }
325
+ if (!isCommandName(arg)) {
326
+ return {
327
+ kind: "error",
328
+ message: `Unknown command '${arg}'. Expected one of ${CLI_COMMAND_NAMES.join(", ")}.`
329
+ };
330
+ }
331
+ command = arg;
332
+ }
333
+ if (command === void 0) {
334
+ return argv.length === 0 ? { kind: "help" } : {
335
+ kind: "error",
336
+ message: `Missing command. Expected one of ${CLI_COMMAND_NAMES.join(", ")} before the options.`
337
+ };
338
+ }
339
+ return { kind: "command", command, force };
340
+ }
341
+ var MAX_PARENT_LEVELS = 8;
342
+ function resolvePackageRootFrom(startDir, exists) {
343
+ let current = path3.resolve(startDir);
344
+ for (let level = 0; level <= MAX_PARENT_LEVELS; level += 1) {
345
+ if (exists(path3.join(current, "package.json"))) {
346
+ return current;
347
+ }
348
+ const parent = path3.dirname(current);
349
+ if (parent === current) {
350
+ break;
351
+ }
352
+ current = parent;
353
+ }
354
+ throw new CliError(
355
+ `Cannot find the @blaaiz/docs-core package root above ${startDir}. Reinstall the package and run the command again.`
356
+ );
357
+ }
358
+ function resolveCliPaths(moduleUrl, exists = existsSync) {
359
+ const moduleDir = path3.dirname(fileURLToPath(moduleUrl));
360
+ const packageRoot = resolvePackageRootFrom(moduleDir, exists);
361
+ return {
362
+ packageRoot,
363
+ templatesDir: path3.join(packageRoot, "templates"),
364
+ skillsDir: path3.join(packageRoot, "skills")
365
+ };
366
+ }
367
+ async function readPackageVersion(packageRoot) {
368
+ const raw = await readTextFile(path3.join(packageRoot, "package.json"));
369
+ if (raw === null) {
370
+ return "unknown";
371
+ }
372
+ try {
373
+ const parsed = JSON.parse(raw);
374
+ if (typeof parsed === "object" && parsed !== null && "version" in parsed) {
375
+ const version = parsed.version;
376
+ if (typeof version === "string" && version !== "") {
377
+ return version;
378
+ }
379
+ }
380
+ } catch {
381
+ return "unknown";
382
+ }
383
+ return "unknown";
384
+ }
385
+ var DEFAULT_SITE_NAME = "My Docs";
386
+ var MAX_SITE_NAME_CHARS = 60;
387
+ var UNSAFE_NAME_CHARS = /[^A-Za-z0-9 ._@/+-]/g;
388
+ var QUICKSTART_URL = "https://docs-core.blaaiz.dev/docs/quickstart";
389
+ var INIT_TEMPLATES = Object.freeze([
390
+ { template: "docs.config.ts.tmpl", target: "docs.config.ts" },
391
+ { template: "content/docs.json.tmpl", target: "content/docs.json" },
392
+ { template: "content/docs/index.mdx.tmpl", target: "content/docs/index.mdx" },
393
+ { template: "content/docs/meta.json.tmpl", target: "content/docs/meta.json" },
394
+ { template: "content/nav.json.tmpl", target: "content/nav.json" },
395
+ { template: "env.example.tmpl", target: ".env.example" }
396
+ ]);
397
+ var NEXT_STEPS = Object.freeze([
398
+ "Next steps",
399
+ "",
400
+ " 1. Install the site dependencies:",
401
+ " pnpm add next react react-dom fumadocs-core fumadocs-ui fumadocs-mdx remark-gfm",
402
+ " pnpm add -D typescript @types/node @types/react @types/react-dom @types/mdx \\",
403
+ " tailwindcss @tailwindcss/postcss",
404
+ "",
405
+ " pnpm 10 does not run install scripts unless you allow them, and Next.js",
406
+ " needs esbuild built. Add this to package.json:",
407
+ ' "pnpm": { "onlyBuiltDependencies": ["esbuild"] }',
408
+ "",
409
+ " 2. Install the framework:",
410
+ " pnpm add @blaaiz/docs-core",
411
+ "",
412
+ " 3. Add the app files \u2014 source.config.ts, lib/source.ts, app/layout.tsx,",
413
+ " app/docs/layout.tsx, app/docs/[[...slug]]/page.tsx, app/globals.css, and",
414
+ " app/api/search/route.ts. Each one is copy-and-paste ready here:",
415
+ ` ${QUICKSTART_URL}`,
416
+ "",
417
+ " 4. Teach your coding agents this project:",
418
+ " npx docs-core agents",
419
+ "",
420
+ " 5. Run the site:",
421
+ " pnpm dev"
422
+ ]);
423
+ function resolveSiteName(packageJsonText) {
424
+ if (packageJsonText === null) {
425
+ return DEFAULT_SITE_NAME;
426
+ }
427
+ let parsed;
428
+ try {
429
+ parsed = JSON.parse(packageJsonText);
430
+ } catch {
431
+ return DEFAULT_SITE_NAME;
432
+ }
433
+ if (typeof parsed !== "object" || parsed === null || !("name" in parsed)) {
434
+ return DEFAULT_SITE_NAME;
435
+ }
436
+ const name = parsed.name;
437
+ if (typeof name !== "string") {
438
+ return DEFAULT_SITE_NAME;
439
+ }
440
+ const cleaned = name.replace(UNSAFE_NAME_CHARS, " ").replace(/\s+/g, " ").trim().slice(0, MAX_SITE_NAME_CHARS).trim();
441
+ return cleaned === "" ? DEFAULT_SITE_NAME : cleaned;
442
+ }
443
+ function planInit(input) {
444
+ return INIT_TEMPLATES.map((file) => {
445
+ const raw = input.templates.get(file.template) ?? "";
446
+ const content = interpolate(raw, { NAME: input.siteName });
447
+ return planFileWrite(
448
+ file.target,
449
+ content,
450
+ input.existing.get(file.target) ?? null,
451
+ input.force
452
+ );
453
+ });
454
+ }
455
+ async function runInit(options) {
456
+ const templateEntries = await Promise.all(
457
+ INIT_TEMPLATES.map(async (file) => {
458
+ const source = path3.join(options.paths.templatesDir, "init", ...file.template.split("/"));
459
+ return [file.template, await readPackagedFile(source)];
460
+ })
461
+ );
462
+ const [existing, packageJson] = await Promise.all([
463
+ snapshotFiles(
464
+ options.cwd,
465
+ INIT_TEMPLATES.map((file) => file.target)
466
+ ),
467
+ snapshotFiles(options.cwd, ["package.json"])
468
+ ]);
469
+ const plan = planInit({
470
+ templates: new Map(templateEntries),
471
+ existing,
472
+ siteName: resolveSiteName(packageJson.get("package.json") ?? null),
473
+ force: options.force
474
+ });
475
+ await executePlan(options.cwd, plan);
476
+ options.print(`docs-core init \xB7 ${options.cwd}`);
477
+ options.print("");
478
+ for (const line of summarizePlan(plan)) {
479
+ options.print(line);
480
+ }
481
+ options.print("");
482
+ for (const line of NEXT_STEPS) {
483
+ options.print(line);
484
+ }
485
+ return plan;
486
+ }
487
+
488
+ // src/cli/index.ts
489
+ function printLine(line) {
490
+ process.stdout.write(`${line}
491
+ `);
492
+ }
493
+ async function main(argv) {
494
+ const request = parseCliArgs(argv);
495
+ if (request.kind === "help") {
496
+ printLine(USAGE);
497
+ return 0;
498
+ }
499
+ if (request.kind === "error") {
500
+ process.stderr.write(`${request.message}
501
+
502
+ ${USAGE}
503
+ `);
504
+ return 1;
505
+ }
506
+ const paths = resolveCliPaths(import.meta.url);
507
+ if (request.kind === "version") {
508
+ printLine(await readPackageVersion(paths.packageRoot));
509
+ return 0;
510
+ }
511
+ const options = {
512
+ cwd: process.cwd(),
513
+ paths,
514
+ force: request.force,
515
+ print: printLine
516
+ };
517
+ if (request.command === "init") {
518
+ await runInit(options);
519
+ } else {
520
+ await runAgents(options);
521
+ }
522
+ return 0;
523
+ }
524
+ try {
525
+ process.exitCode = await main(process.argv.slice(2));
526
+ } catch (error) {
527
+ process.stderr.write(`${formatCliError(error)}
528
+ `);
529
+ process.exitCode = 1;
530
+ }
531
+
532
+ export { main };
533
+ //# sourceMappingURL=cli.js.map
534
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/errors.ts","../src/cli/fs-utils.ts","../src/cli/plan.ts","../src/cli/agents.ts","../src/cli/args.ts","../src/cli/context.ts","../src/cli/init.ts","../src/cli/index.ts"],"names":["path"],"mappings":"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","file":"cli.js","sourcesContent":["/**\n * The CLI's one error type.\n *\n * A command line tool has a single output channel and no stack-trace reader, so\n * every failure it can anticipate is reported as one plain line that names the\n * problem and the fix. {@link CliError} marks such a failure; anything else that\n * escapes is a bug, and {@link formatCliError} still reduces it to one line.\n *\n * @packageDocumentation\n */\n\n/**\n * A failure the CLI anticipated. The message is the whole report: one line,\n * naming what went wrong and what to do about it.\n */\nexport class CliError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CliError';\n }\n}\n\n/**\n * Reduce any thrown value to the single line the CLI prints.\n *\n * @param error - the value a command threw\n * @returns one line, never empty, never a stack trace\n */\nexport function formatCliError(error: unknown): string {\n if (error instanceof CliError) {\n return error.message;\n }\n if (error instanceof Error && error.message !== '') {\n return `${error.name}: ${error.message}`;\n }\n return `Unexpected failure: ${String(error)}`;\n}\n","/**\n * The thin filesystem layer.\n *\n * Everything above this module decides; this module reads and writes. Reads are\n * forgiving in exactly one way — a missing file is `null`, not a throw, because\n * \"does not exist yet\" is the normal case for a scaffolder — and unforgiving\n * about everything else, so a permission problem or a directory where a file\n * belongs is reported as one actionable line instead of a stack trace.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { CliError } from './errors.js';\nimport type { WritePlan } from './plan.js';\n\nfunction errorCode(error: unknown): string | undefined {\n if (typeof error === 'object' && error !== null && 'code' in error) {\n const code: unknown = error.code;\n return typeof code === 'string' ? code : undefined;\n }\n return undefined;\n}\n\nfunction describeFailure(action: string, file: string, error: unknown): CliError {\n const code = errorCode(error);\n if (code === 'EISDIR') {\n return new CliError(`Cannot ${action} ${file}: it is a directory, not a file.`);\n }\n if (code === 'EACCES' || code === 'EPERM') {\n return new CliError(`Cannot ${action} ${file}: permission denied.`);\n }\n return new CliError(\n `Cannot ${action} ${file}: ${error instanceof Error ? error.message : String(error)}`,\n );\n}\n\n/**\n * Read a UTF-8 text file, treating \"not found\" as a value.\n *\n * @param file - absolute path to the file\n * @returns the file's content, or `null` when the file does not exist\n * @throws {@link CliError} when the path exists but cannot be read\n */\nexport async function readTextFile(file: string): Promise<string | null> {\n try {\n return await readFile(file, 'utf8');\n } catch (error) {\n if (errorCode(error) === 'ENOENT') {\n return null;\n }\n throw describeFailure('read', file, error);\n }\n}\n\n/**\n * Read a file that ships inside the package. A missing one is a broken install,\n * never a user mistake, so it says so.\n *\n * @param file - absolute path to the packaged file\n * @returns the file's content\n * @throws {@link CliError} when the file is missing or unreadable\n */\nexport async function readPackagedFile(file: string): Promise<string> {\n const content = await readTextFile(file);\n if (content === null) {\n throw new CliError(\n `The @blaaiz/docs-core installation is missing ${file}. Reinstall the package and run the command again.`,\n );\n }\n return content;\n}\n\n/**\n * Snapshot the current state of every target path, as the planners want it:\n * content when the file exists, `null` when it does not.\n *\n * @param dir - the target directory\n * @param relativePaths - target paths, relative to `dir`, with `/` separators\n * @returns one entry per requested path, in the requested order\n * @throws {@link CliError} when a path exists but cannot be read\n */\nexport async function snapshotFiles(\n dir: string,\n relativePaths: readonly string[],\n): Promise<ReadonlyMap<string, string | null>> {\n const entries = await Promise.all(\n relativePaths.map(async (relativePath) => {\n const content = await readTextFile(path.join(dir, ...relativePath.split('/')));\n return [relativePath, content] as const;\n }),\n );\n return new Map(entries);\n}\n\n/**\n * Write every entry of a plan that is not a skip, creating parent directories as\n * needed.\n *\n * @param dir - the target directory\n * @param plan - the plan to execute\n * @throws {@link CliError} when a directory or a file cannot be written\n */\nexport async function executePlan(dir: string, plan: WritePlan): Promise<void> {\n for (const entry of plan) {\n if (entry.action === 'skip') {\n continue;\n }\n const target = path.join(dir, ...entry.path.split('/'));\n const parent = path.dirname(target);\n try {\n await mkdir(parent, { recursive: true });\n } catch (error) {\n throw describeFailure('create the directory', parent, error);\n }\n try {\n await writeFile(target, entry.content, 'utf8');\n } catch (error) {\n throw describeFailure('write', target, error);\n }\n }\n}\n","/**\n * The write plan.\n *\n * Every command decides first and writes second. A command reads the target\n * directory into a snapshot, hands the snapshot to a pure planner, and gets back\n * a list of {@link PlannedWrite} entries. The planner is where the rules live —\n * never clobber a file without `--force`, report an identical file as unchanged,\n * rewrite only what is between the markers — and because it is pure, those rules\n * are unit-testable without a filesystem.\n *\n * @packageDocumentation\n */\n\nimport { CliError } from './errors.js';\n\n/**\n * What a command will do to one file.\n *\n * - `create` — the file does not exist yet.\n * - `overwrite` — the file exists, differs, and `--force` was given.\n * - `skip` — the file exists; either it is already identical, or `--force` was\n * not given. Nothing is written.\n * - `update-section` — the file exists and only the marked block is rewritten.\n */\nexport type WriteAction = 'create' | 'overwrite' | 'skip' | 'update-section';\n\n/** One file in a {@link WritePlan}. */\nexport interface PlannedWrite {\n /** Path relative to the target directory, with `/` separators. */\n readonly path: string;\n readonly action: WriteAction;\n /** The full content to write. Carried for a `skip` too, so a plan is uniform. */\n readonly content: string;\n /** Why the entry was skipped, for the summary line. */\n readonly note?: string;\n}\n\n/** An ordered list of file decisions. Order drives the summary output. */\nexport type WritePlan = readonly PlannedWrite[];\n\n/** Opening marker of the block `docs-core agents` owns inside `AGENTS.md`. */\nexport const SECTION_BEGIN = '<!-- docs-core:begin -->';\n\n/** Closing marker of the block `docs-core agents` owns inside `AGENTS.md`. */\nexport const SECTION_END = '<!-- docs-core:end -->';\n\nconst ACTION_LABELS: Readonly<Record<WriteAction, string>> = Object.freeze({\n create: 'created',\n overwrite: 'overwritten',\n skip: 'skipped',\n 'update-section': 'updated',\n});\n\nconst LABEL_WIDTH = 12;\n\nconst PLACEHOLDER_PATTERN = /__[A-Z][A-Z0-9_]*__/g;\n\n/**\n * Decide what to do with one whole-file target.\n *\n * @param path - the target path, relative to the target directory\n * @param content - the content the command would write\n * @param existing - the file's current content, or `null` when it does not exist\n * @param force - whether `--force` was given\n * @returns the decision for this file\n */\nexport function planFileWrite(\n path: string,\n content: string,\n existing: string | null,\n force: boolean,\n): PlannedWrite {\n if (existing === null) {\n return { path, action: 'create', content };\n }\n if (existing === content) {\n return { path, action: 'skip', content, note: 'already up to date' };\n }\n if (force) {\n return { path, action: 'overwrite', content };\n }\n return { path, action: 'skip', content, note: 'exists — pass --force to overwrite' };\n}\n\nfunction countOccurrences(haystack: string, needle: string): number {\n let count = 0;\n let index = haystack.indexOf(needle);\n while (index !== -1) {\n count += 1;\n index = haystack.indexOf(needle, index + needle.length);\n }\n return count;\n}\n\n/**\n * Build the marked block: the markers, and the section body between them.\n *\n * @param body - the section content, without markers\n * @returns the block, with no trailing newline\n */\nexport function buildMarkedSection(body: string): string {\n return `${SECTION_BEGIN}\\n${body.trim()}\\n${SECTION_END}`;\n}\n\n/**\n * Insert or replace the marked block in a document, leaving everything else\n * byte-for-byte as it was.\n *\n * A document with no markers gets the block appended. A document with exactly\n * one marker pair gets the text between the markers replaced in place, which is\n * what makes `docs-core agents` safe to re-run. Anything else — a stray marker,\n * a second pair, an `end` before its `begin` — is a document the CLI refuses to\n * guess at.\n *\n * @param document - the current file content\n * @param block - the block from {@link buildMarkedSection}\n * @returns the new file content\n * @throws {@link CliError} when the document's markers are unbalanced or duplicated\n */\nexport function applyMarkedSection(document: string, block: string): string {\n const begins = countOccurrences(document, SECTION_BEGIN);\n const ends = countOccurrences(document, SECTION_END);\n\n if (begins > 1 || ends > 1) {\n throw new CliError(\n `AGENTS.md holds more than one ${SECTION_BEGIN} / ${SECTION_END} pair. Keep exactly one, then run the command again.`,\n );\n }\n if (begins !== ends) {\n throw new CliError(\n `AGENTS.md has an unbalanced docs-core marker: ${String(begins)} '${SECTION_BEGIN}' and ${String(ends)} '${SECTION_END}'. Repair or delete the markers, then run the command again.`,\n );\n }\n if (begins === 0) {\n const head = document.replace(/\\s+$/, '');\n return head === '' ? `${block}\\n` : `${head}\\n\\n${block}\\n`;\n }\n\n const start = document.indexOf(SECTION_BEGIN);\n const end = document.indexOf(SECTION_END);\n if (end < start) {\n throw new CliError(\n `AGENTS.md has ${SECTION_END} before ${SECTION_BEGIN}. Put the markers in order, then run the command again.`,\n );\n }\n return `${document.slice(0, start)}${block}${document.slice(end + SECTION_END.length)}`;\n}\n\n/**\n * Decide what to do with a marked-section target.\n *\n * The markers exist so the block can be rewritten safely, so a section update is\n * never treated as an overwrite and never needs `--force`.\n *\n * @param path - the target path, relative to the target directory\n * @param block - the block from {@link buildMarkedSection}\n * @param existing - the file's current content, or `null` when it does not exist\n * @param onCreate - builds the whole file when it does not exist yet\n * @returns the decision for this file\n * @throws {@link CliError} when the existing document's markers are unusable\n */\nexport function planMarkedSection(\n path: string,\n block: string,\n existing: string | null,\n onCreate: (block: string) => string,\n): PlannedWrite {\n if (existing === null) {\n return { path, action: 'create', content: onCreate(block) };\n }\n const content = applyMarkedSection(existing, block);\n if (content === existing) {\n return { path, action: 'skip', content, note: 'section already up to date' };\n }\n return { path, action: 'update-section', content };\n}\n\n/**\n * Replace every `__PLACEHOLDER__` token in a packaged template.\n *\n * A token with no value is a packaging bug, not a user mistake, so it fails\n * loudly rather than shipping `__NAME__` into a scaffolded site.\n *\n * @param template - the raw template text\n * @param values - the value for each placeholder name, without the underscores\n * @returns the interpolated text\n * @throws {@link CliError} when the template holds a placeholder with no value\n */\nexport function interpolate(template: string, values: Readonly<Record<string, string>>): string {\n return template.replace(PLACEHOLDER_PATTERN, (token) => {\n const value = values[token.slice(2, -2)];\n if (value === undefined) {\n throw new CliError(\n `The packaged template holds an unknown placeholder ${token}. Reinstall @blaaiz/docs-core, and report the problem if it persists.`,\n );\n }\n return value;\n });\n}\n\n/**\n * Count the plan's entries by action, for the tally line.\n *\n * @param plan - the plan\n * @returns one count per action, including zeros\n */\nexport function countActions(plan: WritePlan): Readonly<Record<WriteAction, number>> {\n const counts: Record<WriteAction, number> = {\n create: 0,\n overwrite: 0,\n skip: 0,\n 'update-section': 0,\n };\n for (const entry of plan) {\n counts[entry.action] += 1;\n }\n return counts;\n}\n\n/**\n * Render the plan as the lines the command prints: one aligned line per file,\n * then a tally.\n *\n * @param plan - the plan, already executed\n * @returns the summary lines, without a trailing blank\n */\nexport function summarizePlan(plan: WritePlan): readonly string[] {\n const lines = plan.map((entry) => {\n const label = ACTION_LABELS[entry.action].padEnd(LABEL_WIDTH, ' ');\n const note = entry.note === undefined ? '' : ` (${entry.note})`;\n return ` ${label}${entry.path}${note}`;\n });\n\n const counts = countActions(plan);\n const parts: string[] = [];\n for (const action of ['create', 'overwrite', 'update-section', 'skip'] as const) {\n const count = counts[action];\n if (count > 0) {\n parts.push(`${String(count)} ${ACTION_LABELS[action]}`);\n }\n }\n\n const total = plan.length;\n const tally = parts.length === 0 ? 'nothing to do' : parts.join(', ');\n return [...lines, '', `${String(total)} ${total === 1 ? 'file' : 'files'}: ${tally}`];\n}\n","/**\n * `docs-core agents` — agent support for the project.\n *\n * Coding agents read two kinds of file: a skill, loaded when a task looks like\n * the skill's subject, and an always-on instruction file at the repository root.\n * This command installs both. Claude Code reads `.claude/skills/**\\/SKILL.md`,\n * Codex reads `.agents/skills/**\\/SKILL.md`, and both read `AGENTS.md`, so the\n * one skill is written to both locations verbatim and the root file gets a\n * marked block.\n *\n * `AGENTS.md` is the file a team writes in, so this command never rewrites it\n * wholesale. It owns the text between `<!-- docs-core:begin -->` and\n * `<!-- docs-core:end -->` and nothing else. That is what makes the command safe\n * to re-run after every upgrade, with no `--force` and no lost edits.\n *\n * @packageDocumentation\n */\n\nimport path from 'node:path';\n\nimport type { RunCommandOptions } from './context.js';\nimport { executePlan, readPackagedFile, snapshotFiles } from './fs-utils.js';\nimport {\n buildMarkedSection,\n planFileWrite,\n planMarkedSection,\n summarizePlan,\n type WritePlan,\n} from './plan.js';\n\n/** Where Claude Code looks for a project skill. */\nexport const CLAUDE_SKILL_PATH = '.claude/skills/docs-core/SKILL.md';\n\n/** Where Codex looks for a project skill. */\nexport const CODEX_SKILL_PATH = '.agents/skills/docs-core/SKILL.md';\n\n/** The always-on instruction file both agents read. */\nexport const AGENTS_FILE = 'AGENTS.md';\n\n/** The packaged skill body, copied to both skill paths without interpolation. */\nconst SKILL_TEMPLATE = 'SKILL.md';\n\n/** The packaged body of the `AGENTS.md` block. */\nconst SECTION_TEMPLATE = 'AGENTS-section.md';\n\nconst NEW_AGENTS_HEADER = `# Agent instructions\n\nAlways-on instructions for coding agents working in this repository. Add your own\nrules outside the block below: \\`docs-core agents\\` rewrites only the text between\nits two markers, and leaves the rest of this file untouched.`;\n\n/**\n * Build a fresh `AGENTS.md` around the docs-core block.\n *\n * @param block - the marked block\n * @returns the whole file content\n */\nexport function buildNewAgentsFile(block: string): string {\n return `${NEW_AGENTS_HEADER}\\n\\n${block}\\n`;\n}\n\n/** Input for {@link planAgents}: the packaged content and the project's state. */\nexport interface PlanAgentsInput {\n /** The packaged `SKILL.md` content, written verbatim. */\n readonly skill: string;\n /** The packaged `AGENTS.md` section body, without markers. */\n readonly section: string;\n /** Current content of each target, keyed by target path. */\n readonly existing: ReadonlyMap<string, string | null>;\n /** Whether `--force` was given. Applies to the two skill files only. */\n readonly force: boolean;\n}\n\n/**\n * Decide the whole installation. Pure: skill files follow the create-or-skip\n * rule every scaffolded file follows, and `AGENTS.md` follows the marker rule.\n *\n * @param input - packaged content, current state, and the force flag\n * @returns the plan, in the order the summary lists it\n * @throws {@link CliError} when the existing `AGENTS.md` markers are unusable\n */\nexport function planAgents(input: PlanAgentsInput): WritePlan {\n const block = buildMarkedSection(input.section);\n return [\n planFileWrite(\n CLAUDE_SKILL_PATH,\n input.skill,\n input.existing.get(CLAUDE_SKILL_PATH) ?? null,\n input.force,\n ),\n planFileWrite(\n CODEX_SKILL_PATH,\n input.skill,\n input.existing.get(CODEX_SKILL_PATH) ?? null,\n input.force,\n ),\n planMarkedSection(\n AGENTS_FILE,\n block,\n input.existing.get(AGENTS_FILE) ?? null,\n buildNewAgentsFile,\n ),\n ];\n}\n\n/**\n * Run `docs-core agents`: read, plan, write, report.\n *\n * @param options - target directory, packaged paths, the force flag, and the printer\n * @returns the executed plan, so a caller can assert on it\n * @throws {@link CliError} when a packaged file is missing, the markers are unusable, or a write fails\n */\nexport async function runAgents(options: RunCommandOptions): Promise<WritePlan> {\n const [skill, section] = await Promise.all([\n readPackagedFile(path.join(options.paths.skillsDir, SKILL_TEMPLATE)),\n readPackagedFile(path.join(options.paths.skillsDir, SECTION_TEMPLATE)),\n ]);\n\n const existing = await snapshotFiles(options.cwd, [\n CLAUDE_SKILL_PATH,\n CODEX_SKILL_PATH,\n AGENTS_FILE,\n ]);\n\n const plan = planAgents({ skill, section, existing, force: options.force });\n\n await executePlan(options.cwd, plan);\n\n options.print(`docs-core agents · ${options.cwd}`);\n options.print('');\n for (const line of summarizePlan(plan)) {\n options.print(line);\n }\n options.print('');\n options.print('Claude Code reads .claude/skills, Codex reads .agents/skills, and both read');\n options.print('AGENTS.md. Re-run `npx docs-core agents --force` after a docs-core upgrade to');\n options.print('refresh the skill; the AGENTS.md block refreshes without --force.');\n\n return plan;\n}\n","/**\n * The command line surface.\n *\n * Parsing is separated from doing, and it is pure: {@link parseCliArgs} turns an\n * argument list into one of four outcomes and touches nothing. Anything it does\n * not recognize becomes a usage error naming the exact offending token, because\n * a tool that silently ignores a mistyped flag is worse than one that refuses to\n * run.\n *\n * @packageDocumentation\n */\n\n/** The commands the CLI accepts. */\nexport type CliCommandName = 'init' | 'agents';\n\n/** Every command name, in the order the usage text lists them. */\nexport const CLI_COMMAND_NAMES: readonly CliCommandName[] = Object.freeze([\n 'init',\n 'agents',\n] as const);\n\n/** Run one command. */\nexport interface CliCommandRequest {\n readonly kind: 'command';\n readonly command: CliCommandName;\n /** `--force`: overwrite files that already exist. */\n readonly force: boolean;\n}\n\n/** Print the usage text and exit successfully. */\nexport interface CliHelpRequest {\n readonly kind: 'help';\n}\n\n/** Print the package version and exit successfully. */\nexport interface CliVersionRequest {\n readonly kind: 'version';\n}\n\n/** The arguments do not name a command the CLI can run. */\nexport interface CliUsageError {\n readonly kind: 'error';\n /** One plain line: what is wrong, and what to do instead. */\n readonly message: string;\n}\n\n/** What {@link parseCliArgs} resolved the arguments to. */\nexport type CliRequest = CliCommandRequest | CliHelpRequest | CliVersionRequest | CliUsageError;\n\n/** The help text. Printed for `--help`, for a bare invocation, and on any usage error. */\nexport const USAGE = `docs-core — scaffold and maintain a @blaaiz/docs-core documentation site\n\nUsage\n docs-core <command> [options]\n\nCommands\n init Write docs.config.ts, the content tree, and .env.example into this directory\n agents Install the docs-core skill for Claude Code and Codex, and update AGENTS.md\n\nOptions\n -f, --force Overwrite files that already exist\n -h, --help Show this help\n -v, --version Show the version\n\nBoth commands write into the current directory, never overwrite a file without\n--force, and print every path they touched.`;\n\nfunction isCommandName(value: string): value is CliCommandName {\n return (CLI_COMMAND_NAMES as readonly string[]).includes(value);\n}\n\n/**\n * Parse the arguments after the executable name.\n *\n * `--help` wins over everything, then `--version`; both are accepted anywhere in\n * the list so a reader who has already typed a command can still ask for help. A\n * bare invocation is treated as a request for help, not as an error.\n *\n * @param argv - the arguments, normally `process.argv.slice(2)`\n * @returns the resolved request, including the usage-error case\n */\nexport function parseCliArgs(argv: readonly string[]): CliRequest {\n if (argv.some((arg) => arg === '-h' || arg === '--help')) {\n return { kind: 'help' };\n }\n if (argv.some((arg) => arg === '-v' || arg === '--version')) {\n return { kind: 'version' };\n }\n\n let command: CliCommandName | undefined;\n let force = false;\n\n for (const arg of argv) {\n if (arg === '--force' || arg === '-f') {\n force = true;\n continue;\n }\n if (arg.startsWith('-')) {\n return {\n kind: 'error',\n message: `Unknown option '${arg}'. The only option is --force; run 'docs-core --help' for usage.`,\n };\n }\n if (command !== undefined) {\n return {\n kind: 'error',\n message: `Unexpected argument '${arg}'. Run one command at a time: 'docs-core ${command}'.`,\n };\n }\n if (!isCommandName(arg)) {\n return {\n kind: 'error',\n message: `Unknown command '${arg}'. Expected one of ${CLI_COMMAND_NAMES.join(', ')}.`,\n };\n }\n command = arg;\n }\n\n if (command === undefined) {\n // A bare invocation asks for help. Options without a command do not: they\n // were typed with an intention the CLI must not guess at.\n return argv.length === 0\n ? { kind: 'help' }\n : {\n kind: 'error',\n message: `Missing command. Expected one of ${CLI_COMMAND_NAMES.join(', ')} before the options.`,\n };\n }\n\n return { kind: 'command', command, force };\n}\n","/**\n * Where the packaged assets are.\n *\n * The templates and the agent skills ship inside the published package, so the\n * CLI must find them relative to its own module, never relative to the user's\n * working directory. The bundled entry lives at `<packageRoot>/dist/cli.js` and\n * the sources at `<packageRoot>/src/cli/*`, so both resolve by walking up to the\n * nearest `package.json` — one rule that holds in a checkout, in `node_modules`,\n * and under a test runner.\n *\n * @packageDocumentation\n */\n\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { CliError } from './errors.js';\nimport { readTextFile } from './fs-utils.js';\n\n/** How far up the tree the search for `package.json` goes before giving up. */\nconst MAX_PARENT_LEVELS = 8;\n\n/** The packaged directories a command reads from. */\nexport interface CliPaths {\n /** The installed `@blaaiz/docs-core` package root. */\n readonly packageRoot: string;\n /** `<packageRoot>/templates`, holding the `init` scaffold. */\n readonly templatesDir: string;\n /** `<packageRoot>/skills`, holding the agent skill and the `AGENTS.md` section. */\n readonly skillsDir: string;\n}\n\n/** What a command needs to run: where to write, what to read, and how to report. */\nexport interface RunCommandOptions {\n /** The target directory. Every path a command writes is relative to it. */\n readonly cwd: string;\n /** The packaged directories, from {@link resolveCliPaths}. */\n readonly paths: CliPaths;\n /** Whether `--force` was given. */\n readonly force: boolean;\n /** Receives one output line at a time, so tests can read the report. */\n readonly print: (line: string) => void;\n}\n\n/** Tests an absolute path for existence. Injected so the walk is testable. */\nexport type PathExists = (candidate: string) => boolean;\n\n/**\n * Walk up from a directory to the nearest one holding a `package.json`.\n *\n * @param startDir - the directory to start from\n * @param exists - existence test, `node:fs.existsSync` in production\n * @returns the package root\n * @throws {@link CliError} when no `package.json` is found within the walk limit\n */\nexport function resolvePackageRootFrom(startDir: string, exists: PathExists): string {\n let current = path.resolve(startDir);\n for (let level = 0; level <= MAX_PARENT_LEVELS; level += 1) {\n if (exists(path.join(current, 'package.json'))) {\n return current;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n break;\n }\n current = parent;\n }\n throw new CliError(\n `Cannot find the @blaaiz/docs-core package root above ${startDir}. Reinstall the package and run the command again.`,\n );\n}\n\n/**\n * Resolve the packaged directories from the calling module's URL.\n *\n * @param moduleUrl - the caller's `import.meta.url`\n * @param exists - existence test, `node:fs.existsSync` by default\n * @returns the package root and the directories the commands read\n * @throws {@link CliError} when the package root cannot be located\n */\nexport function resolveCliPaths(moduleUrl: string, exists: PathExists = existsSync): CliPaths {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl));\n const packageRoot = resolvePackageRootFrom(moduleDir, exists);\n return {\n packageRoot,\n templatesDir: path.join(packageRoot, 'templates'),\n skillsDir: path.join(packageRoot, 'skills'),\n };\n}\n\n/**\n * Read the installed package's version, for `--version`.\n *\n * @param packageRoot - the package root from {@link resolveCliPaths}\n * @returns the version string, or `'unknown'` when it cannot be read\n */\nexport async function readPackageVersion(packageRoot: string): Promise<string> {\n const raw = await readTextFile(path.join(packageRoot, 'package.json'));\n if (raw === null) {\n return 'unknown';\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed === 'object' && parsed !== null && 'version' in parsed) {\n const version: unknown = parsed.version;\n if (typeof version === 'string' && version !== '') {\n return version;\n }\n }\n } catch {\n return 'unknown';\n }\n return 'unknown';\n}\n","/**\n * `docs-core init` — the scaffold.\n *\n * It writes the foundation of a docs site: the one config file, the navigation,\n * a starter page, the generated-file companions, and an environment template. It\n * deliberately does not scaffold Next.js. A docs site is a Next.js app plus this\n * content, and the app files belong to the site's own stack, which the quickstart\n * walks through. What this command owns is the part that is the same everywhere.\n *\n * The files are real templates under `templates/init`, copied and interpolated,\n * so the scaffold a site receives is a file a maintainer can read and edit — not\n * a string buried in a code path.\n *\n * @packageDocumentation\n */\n\nimport path from 'node:path';\n\nimport type { RunCommandOptions } from './context.js';\nimport { executePlan, readPackagedFile, snapshotFiles } from './fs-utils.js';\nimport { interpolate, planFileWrite, summarizePlan, type WritePlan } from './plan.js';\n\n/** The site name used when the directory has no readable `package.json` name. */\nexport const DEFAULT_SITE_NAME = 'My Docs';\n\n/** Longest site name carried into the templates. Longer names are cut. */\nconst MAX_SITE_NAME_CHARS = 60;\n\n/** Characters a site name may keep. Everything else becomes a space. */\nconst UNSAFE_NAME_CHARS = /[^A-Za-z0-9 ._@/+-]/g;\n\n/** The quickstart the next-steps block points at. */\nconst QUICKSTART_URL = 'https://docs-core.blaaiz.dev/docs/quickstart';\n\n/** One packaged template and where it lands in the site. */\nexport interface InitTemplate {\n /** Path under `templates/init`, with `/` separators. */\n readonly template: string;\n /** Path in the target directory, with `/` separators. */\n readonly target: string;\n}\n\n/**\n * Every file `init` writes, in the order the summary lists them.\n *\n * The mapping is explicit rather than a directory walk: the scaffold is a fixed\n * set, the summary order is stable, and `.env.example` can be packaged under a\n * name that no packing tool treats specially.\n */\nexport const INIT_TEMPLATES: readonly InitTemplate[] = Object.freeze([\n { template: 'docs.config.ts.tmpl', target: 'docs.config.ts' },\n { template: 'content/docs.json.tmpl', target: 'content/docs.json' },\n { template: 'content/docs/index.mdx.tmpl', target: 'content/docs/index.mdx' },\n { template: 'content/docs/meta.json.tmpl', target: 'content/docs/meta.json' },\n { template: 'content/nav.json.tmpl', target: 'content/nav.json' },\n { template: 'env.example.tmpl', target: '.env.example' },\n] as const);\n\n/**\n * The numbered block printed after the summary. `init` writes config and\n * content; these are the steps that turn them into a running site.\n */\nexport const NEXT_STEPS: readonly string[] = Object.freeze([\n 'Next steps',\n '',\n ' 1. Install the site dependencies:',\n ' pnpm add next react react-dom fumadocs-core fumadocs-ui fumadocs-mdx remark-gfm',\n ' pnpm add -D typescript @types/node @types/react @types/react-dom @types/mdx \\\\',\n ' tailwindcss @tailwindcss/postcss',\n '',\n ' pnpm 10 does not run install scripts unless you allow them, and Next.js',\n ' needs esbuild built. Add this to package.json:',\n ' \"pnpm\": { \"onlyBuiltDependencies\": [\"esbuild\"] }',\n '',\n ' 2. Install the framework:',\n ' pnpm add @blaaiz/docs-core',\n '',\n ' 3. Add the app files — source.config.ts, lib/source.ts, app/layout.tsx,',\n ' app/docs/layout.tsx, app/docs/[[...slug]]/page.tsx, app/globals.css, and',\n ' app/api/search/route.ts. Each one is copy-and-paste ready here:',\n ` ${QUICKSTART_URL}`,\n '',\n ' 4. Teach your coding agents this project:',\n ' npx docs-core agents',\n '',\n ' 5. Run the site:',\n ' pnpm dev',\n] as const);\n\n/**\n * Derive the site's display name from the directory's `package.json`.\n *\n * The name reaches a TypeScript string literal and two JSON files, so it is\n * reduced to characters that are safe in all three. A name is untrusted input\n * like any other, even when it comes from the user's own file.\n *\n * @param packageJsonText - the raw `package.json`, or `null` when there is none\n * @returns the display name, or {@link DEFAULT_SITE_NAME}\n */\nexport function resolveSiteName(packageJsonText: string | null): string {\n if (packageJsonText === null) {\n return DEFAULT_SITE_NAME;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(packageJsonText);\n } catch {\n return DEFAULT_SITE_NAME;\n }\n if (typeof parsed !== 'object' || parsed === null || !('name' in parsed)) {\n return DEFAULT_SITE_NAME;\n }\n const name: unknown = parsed.name;\n if (typeof name !== 'string') {\n return DEFAULT_SITE_NAME;\n }\n const cleaned = name\n .replace(UNSAFE_NAME_CHARS, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_SITE_NAME_CHARS)\n .trim();\n return cleaned === '' ? DEFAULT_SITE_NAME : cleaned;\n}\n\n/** Input for {@link planInit}: the packaged templates and the directory's state. */\nexport interface PlanInitInput {\n /** Raw template text, keyed by {@link InitTemplate.template}. */\n readonly templates: ReadonlyMap<string, string>;\n /** Current content of each target, keyed by {@link InitTemplate.target}. */\n readonly existing: ReadonlyMap<string, string | null>;\n /** The resolved display name, from {@link resolveSiteName}. */\n readonly siteName: string;\n /** Whether `--force` was given. */\n readonly force: boolean;\n}\n\n/**\n * Decide the whole scaffold. Pure: the caller supplies the template text and the\n * directory's current state, and gets back one decision per file.\n *\n * @param input - templates, current state, site name, and the force flag\n * @returns the plan, in {@link INIT_TEMPLATES} order\n */\nexport function planInit(input: PlanInitInput): WritePlan {\n return INIT_TEMPLATES.map((file) => {\n const raw = input.templates.get(file.template) ?? '';\n const content = interpolate(raw, { NAME: input.siteName });\n return planFileWrite(\n file.target,\n content,\n input.existing.get(file.target) ?? null,\n input.force,\n );\n });\n}\n\n/**\n * Run `docs-core init`: read, plan, write, report.\n *\n * @param options - target directory, packaged paths, the force flag, and the printer\n * @returns the executed plan, so a caller can assert on it\n * @throws {@link CliError} when a packaged template is missing or a write fails\n */\nexport async function runInit(options: RunCommandOptions): Promise<WritePlan> {\n const templateEntries = await Promise.all(\n INIT_TEMPLATES.map(async (file) => {\n const source = path.join(options.paths.templatesDir, 'init', ...file.template.split('/'));\n return [file.template, await readPackagedFile(source)] as const;\n }),\n );\n\n const [existing, packageJson] = await Promise.all([\n snapshotFiles(\n options.cwd,\n INIT_TEMPLATES.map((file) => file.target),\n ),\n snapshotFiles(options.cwd, ['package.json']),\n ]);\n\n const plan = planInit({\n templates: new Map(templateEntries),\n existing,\n siteName: resolveSiteName(packageJson.get('package.json') ?? null),\n force: options.force,\n });\n\n await executePlan(options.cwd, plan);\n\n options.print(`docs-core init · ${options.cwd}`);\n options.print('');\n for (const line of summarizePlan(plan)) {\n options.print(line);\n }\n options.print('');\n for (const line of NEXT_STEPS) {\n options.print(line);\n }\n\n return plan;\n}\n","/**\n * `docs-core` — the command line entry point.\n *\n * The binary is thin on purpose: it parses, dispatches, prints, and sets an exit\n * code. Every decision belongs to a pure module it calls, so the behaviour of the\n * CLI is tested without spawning a process.\n *\n * Exit codes are the contract a script depends on: `0` for a completed command\n * and for `--help` or `--version`, `1` for a usage mistake or a failure. A\n * failure prints one line to stderr — never a stack trace.\n *\n * @packageDocumentation\n */\n\nimport { runAgents } from './agents.js';\nimport { parseCliArgs, USAGE } from './args.js';\nimport { readPackageVersion, resolveCliPaths, type RunCommandOptions } from './context.js';\nimport { formatCliError } from './errors.js';\nimport { runInit } from './init.js';\n\nfunction printLine(line: string): void {\n process.stdout.write(`${line}\\n`);\n}\n\n/**\n * Run the CLI.\n *\n * @param argv - the arguments after the executable name\n * @returns the process exit code\n */\nexport async function main(argv: readonly string[]): Promise<number> {\n const request = parseCliArgs(argv);\n\n if (request.kind === 'help') {\n printLine(USAGE);\n return 0;\n }\n if (request.kind === 'error') {\n process.stderr.write(`${request.message}\\n\\n${USAGE}\\n`);\n return 1;\n }\n\n const paths = resolveCliPaths(import.meta.url);\n\n if (request.kind === 'version') {\n printLine(await readPackageVersion(paths.packageRoot));\n return 0;\n }\n\n const options: RunCommandOptions = {\n cwd: process.cwd(),\n paths,\n force: request.force,\n print: printLine,\n };\n\n if (request.command === 'init') {\n await runInit(options);\n } else {\n await runAgents(options);\n }\n return 0;\n}\n\ntry {\n process.exitCode = await main(process.argv.slice(2));\n} catch (error) {\n process.stderr.write(`${formatCliError(error)}\\n`);\n process.exitCode = 1;\n}\n"]}