@kungfu-tech/buildchain 3.0.4-alpha.4 → 3.0.4-alpha.6

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 (34) hide show
  1. package/AGENTS.md +14 -5
  2. package/README.md +18 -0
  3. package/bin/buildchain.mjs +10 -1
  4. package/dist/site/buildchain-contract.json +5 -5
  5. package/dist/site/buildchain-site.json +1459 -58
  6. package/dist/site/capability-registry.json +8 -5
  7. package/dist/site/cli-registry.json +1869 -0
  8. package/dist/site/kfd-claims.json +79 -7
  9. package/dist/site/kfd-upstream-aggregate.json +1 -1
  10. package/dist/site/manual-registry.json +47 -4
  11. package/dist/site/node-api-registry.json +17761 -6
  12. package/dist/site/page-registry.json +1435 -58
  13. package/dist/site/public-surface-audit.json +2809 -352
  14. package/dist/site/publication-registry.json +4 -4
  15. package/dist/site/site-manifest.json +32 -8
  16. package/docs/MAP.md +20 -5
  17. package/docs/cli-reference.md +1936 -0
  18. package/docs/cli.md +13 -0
  19. package/docs/getting-started.md +167 -0
  20. package/docs/node-api-reference.md +1949 -0
  21. package/docs/site-bundle-contract.md +10 -4
  22. package/docs/versioning.md +5 -4
  23. package/package.json +8 -5
  24. package/packages/core/buildchain-agent-manuals.js +37 -0
  25. package/packages/core/buildchain-kfd-claims.js +3 -36
  26. package/packages/core/paper-agent-entry.js +8 -4
  27. package/packages/core/paper.js +1 -0
  28. package/packages/core/publication-package.js +7 -0
  29. package/scripts/check-inventory.mjs +2 -2
  30. package/scripts/generate-public-reference.mjs +68 -0
  31. package/scripts/generate-site-bundle.mjs +19 -34
  32. package/scripts/public-reference.mjs +557 -0
  33. package/scripts/site-reference-registry.mjs +174 -0
  34. package/scripts/verify-golden-path.mjs +169 -0
@@ -0,0 +1,557 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ BUILDCHAIN_COMMAND_REGISTRY,
6
+ resolveBuildchainCommand,
7
+ } from "../bin/internal/command-registry.mjs";
8
+ import { commandId } from "../packages/core/public-surface-cli.js";
9
+
10
+ function unique(values) {
11
+ return [...new Set(values.filter(Boolean))];
12
+ }
13
+
14
+ function normalizeSpace(value) {
15
+ return String(value || "")
16
+ .replace(/\s+/g, " ")
17
+ .trim();
18
+ }
19
+
20
+ export function usageSyntaxes(usageText) {
21
+ const usage = String(usageText || "").split("\n\nExamples:", 1)[0];
22
+ const syntaxes = [];
23
+ let current = "";
24
+ for (const line of usage.split(/\r?\n/)) {
25
+ if (/^\s*buildchain\s+/.test(line)) {
26
+ if (current) syntaxes.push(normalizeSpace(current));
27
+ current = line.trim();
28
+ continue;
29
+ }
30
+ if (current && /^\s{4,}\S/.test(line)) {
31
+ current += ` ${line.trim()}`;
32
+ }
33
+ }
34
+ if (current) syntaxes.push(normalizeSpace(current));
35
+ return unique(syntaxes);
36
+ }
37
+
38
+ function expandPathToken(paths, token) {
39
+ const alternatives = token.slice(1, -1).split("|");
40
+ return paths.flatMap((entry) =>
41
+ alternatives.map((value) => [...entry, value]),
42
+ );
43
+ }
44
+
45
+ export function syntaxPaths(syntax) {
46
+ const tokens = normalizeSpace(syntax).split(" ");
47
+ if (tokens.shift() !== "buildchain") return [];
48
+ let paths = [[]];
49
+ for (const token of tokens) {
50
+ if (!token || token === "..." || token.startsWith("[")) break;
51
+ if (token.startsWith("--")) {
52
+ if (token === "--dry-run" && paths[0].length === 1) {
53
+ paths = paths.map((entry) => [...entry, token]);
54
+ }
55
+ break;
56
+ }
57
+ if (token.startsWith("<") && token.endsWith(">")) {
58
+ if (token.includes("|")) {
59
+ paths = expandPathToken(paths, token);
60
+ }
61
+ break;
62
+ }
63
+ if (/[\[<{]/.test(token)) break;
64
+ paths = paths.map((entry) => [...entry, token]);
65
+ }
66
+ return paths.filter((entry) => entry.length > 0);
67
+ }
68
+
69
+ export function createCliReference(usageText) {
70
+ const syntaxEntries = usageSyntaxes(usageText).map((syntax) => ({
71
+ syntax,
72
+ paths: syntaxPaths(syntax),
73
+ }));
74
+ const byPath = new Map();
75
+ for (const entry of syntaxEntries) {
76
+ for (const pathParts of entry.paths) {
77
+ const key = pathParts.join(" ");
78
+ const current = byPath.get(key) || { path: pathParts, syntaxes: [] };
79
+ current.syntaxes.push(entry.syntax);
80
+ byPath.set(key, current);
81
+ }
82
+ }
83
+ for (const registration of BUILDCHAIN_COMMAND_REGISTRY) {
84
+ if (!byPath.has(registration.id)) {
85
+ byPath.set(registration.id, {
86
+ path: [registration.id],
87
+ syntaxes: [`buildchain ${registration.id}`],
88
+ });
89
+ }
90
+ }
91
+ return [...byPath.values()]
92
+ .map((entry) => {
93
+ const [head, second = "", third = ""] = entry.path;
94
+ const registration = resolveBuildchainCommand(head);
95
+ const canonicalHead = registration?.id || head;
96
+ const canonicalPath = [canonicalHead, ...entry.path.slice(1)];
97
+ const id = commandId(canonicalHead, second, third);
98
+ return {
99
+ id,
100
+ path: canonicalPath,
101
+ command: `buildchain ${canonicalPath.join(" ")}`,
102
+ syntaxes: unique(entry.syntaxes).sort(),
103
+ options: unique(
104
+ entry.syntaxes.flatMap(
105
+ (syntax) => syntax.match(/--[a-z0-9][a-z0-9-]*/gi) || [],
106
+ ),
107
+ ).sort(),
108
+ aliases: canonicalPath.length === 1 ? registration?.aliases || [] : [],
109
+ helpCommand: `buildchain ${canonicalPath.join(" ")} --help`,
110
+ };
111
+ })
112
+ .sort((left, right) => left.command.localeCompare(right.command));
113
+ }
114
+
115
+ export function cliReferenceById(reference) {
116
+ const grouped = new Map();
117
+ for (const entry of reference) {
118
+ const current = grouped.get(entry.id) || {
119
+ paths: [],
120
+ syntaxes: [],
121
+ options: [],
122
+ aliases: [],
123
+ helpCommands: [],
124
+ };
125
+ current.paths.push(entry.path.join(" "));
126
+ current.syntaxes.push(...entry.syntaxes);
127
+ current.options.push(...entry.options);
128
+ current.aliases.push(...entry.aliases);
129
+ current.helpCommands.push(entry.helpCommand);
130
+ grouped.set(entry.id, current);
131
+ }
132
+ return new Map(
133
+ [...grouped].map(([id, entry]) => [
134
+ id,
135
+ {
136
+ paths: unique(entry.paths).sort(),
137
+ syntaxes: unique(entry.syntaxes).sort(),
138
+ options: unique(entry.options).sort(),
139
+ aliases: unique(entry.aliases).sort(),
140
+ helpCommands: unique(entry.helpCommands).sort(),
141
+ },
142
+ ]),
143
+ );
144
+ }
145
+
146
+ function canonicalHelpPath(pathParts) {
147
+ if (pathParts.length === 0) return [];
148
+ const registration = resolveBuildchainCommand(pathParts[0]);
149
+ return [registration?.id || pathParts[0], ...pathParts.slice(1)];
150
+ }
151
+
152
+ export function formatCliHelp({ usageText, pathParts = [] } = {}) {
153
+ const requested = canonicalHelpPath(
154
+ pathParts.filter((entry) => entry && !["--help", "-h"].includes(entry)),
155
+ );
156
+ if (requested.length === 0) return String(usageText || "");
157
+ const reference = createCliReference(usageText);
158
+ const descendants = reference.filter((entry) =>
159
+ requested.every((part, index) => entry.path[index] === part),
160
+ );
161
+ const exact = reference.find(
162
+ (entry) => entry.path.join(" ") === requested.join(" "),
163
+ );
164
+ const family =
165
+ descendants.length > 0
166
+ ? descendants
167
+ : reference.filter(
168
+ (entry) => entry.path[0] === requested[0] && entry.path.length === 1,
169
+ );
170
+ if (family.length === 0) {
171
+ throw new Error(`unsupported buildchain help path: ${requested.join(" ")}`);
172
+ }
173
+ const syntaxes = unique(family.flatMap((entry) => entry.syntaxes)).sort();
174
+ const subcommands = unique(
175
+ descendants.map((entry) => entry.path[requested.length]).filter(Boolean),
176
+ ).sort();
177
+ const lines = [
178
+ `Buildchain help: ${requested.join(" ")}`,
179
+ "",
180
+ "Usage:",
181
+ ...syntaxes.map((syntax) => ` ${syntax}`),
182
+ ];
183
+ if (subcommands.length > 0) {
184
+ lines.push("", "Subcommands:", ...subcommands.map((entry) => ` ${entry}`));
185
+ }
186
+ if (exact?.aliases.length) {
187
+ lines.push("", `Aliases: ${exact.aliases.join(", ")}`);
188
+ }
189
+ lines.push(
190
+ "",
191
+ "Help is read-only and exits without executing the command.",
192
+ "",
193
+ );
194
+ return lines.join("\n");
195
+ }
196
+
197
+ function lineNumber(source, index) {
198
+ return source.slice(0, index).split("\n").length;
199
+ }
200
+
201
+ function splitTopLevel(value) {
202
+ const entries = [];
203
+ let current = "";
204
+ let depth = 0;
205
+ let quote = "";
206
+ for (let index = 0; index < value.length; index += 1) {
207
+ const character = value[index];
208
+ if (quote) {
209
+ current += character;
210
+ if (character === quote && value[index - 1] !== "\\") quote = "";
211
+ continue;
212
+ }
213
+ if (['"', "'", "`"].includes(character)) {
214
+ quote = character;
215
+ current += character;
216
+ continue;
217
+ }
218
+ if (["(", "[", "{"].includes(character)) depth += 1;
219
+ if ([")", "]", "}"].includes(character)) depth -= 1;
220
+ if (character === "," && depth === 0) {
221
+ entries.push(current.trim());
222
+ current = "";
223
+ continue;
224
+ }
225
+ current += character;
226
+ }
227
+ if (current.trim()) entries.push(current.trim());
228
+ return entries;
229
+ }
230
+
231
+ function matchingParen(source, start) {
232
+ let depth = 0;
233
+ let quote = "";
234
+ for (let index = start; index < source.length; index += 1) {
235
+ const character = source[index];
236
+ if (quote) {
237
+ if (character === quote && source[index - 1] !== "\\") quote = "";
238
+ continue;
239
+ }
240
+ if (['"', "'", "`"].includes(character)) {
241
+ quote = character;
242
+ continue;
243
+ }
244
+ if (character === "(") depth += 1;
245
+ if (character === ")") {
246
+ depth -= 1;
247
+ if (depth === 0) return index;
248
+ }
249
+ }
250
+ return -1;
251
+ }
252
+
253
+ function parameterName(signature, index) {
254
+ const value = signature
255
+ .replace(/^\.\.\./, "")
256
+ .split("=", 1)[0]
257
+ .trim();
258
+ if (/^[A-Za-z_$][\w$]*$/.test(value)) return value;
259
+ return `parameter${index + 1}`;
260
+ }
261
+
262
+ function functionDetails(source, start, name, asyncFunction) {
263
+ const open = source.indexOf("(", start);
264
+ const close = matchingParen(source, open);
265
+ const parametersText = close === -1 ? "" : source.slice(open + 1, close);
266
+ const parameters = splitTopLevel(parametersText).map((signature, index) => ({
267
+ name: parameterName(signature, index),
268
+ signature: normalizeSpace(signature),
269
+ }));
270
+ const signature = `${asyncFunction ? "async " : ""}function ${name}(${parameters.map((entry) => entry.signature).join(", ")})`;
271
+ const nextExport = source.indexOf("\nexport ", Math.max(close, start) + 1);
272
+ const body = source.slice(
273
+ start,
274
+ nextExport === -1 ? source.length : nextExport,
275
+ );
276
+ const effects = [];
277
+ if (
278
+ /\b(?:writeFile|appendFile|mkdir|rename|unlink|rm|copyFile|symlink)(?:Sync)?\s*\(/.test(
279
+ body,
280
+ )
281
+ )
282
+ effects.push("local-filesystem-write");
283
+ if (
284
+ /\b(?:spawn|spawnSync|exec|execFile|execFileSync|execSync)\s*\(/.test(body)
285
+ )
286
+ effects.push("subprocess");
287
+ if (/\bfetch\s*\(|\bhttps?\./.test(body)) effects.push("network");
288
+ if (
289
+ effects.length === 0 &&
290
+ /^(?:write|update|append|run|execute|report|apply|register|mark|record|transition|revoke|qualify|set|abort|rollback|start)/i.test(
291
+ name,
292
+ )
293
+ ) {
294
+ effects.push("may-write-or-invoke-external-actions");
295
+ }
296
+ return {
297
+ signature,
298
+ parameters,
299
+ returns: asyncFunction ? "Promise<unknown>" : "unknown",
300
+ errors: /\bthrow\b/.test(body)
301
+ ? [
302
+ "May throw an Error on rejected input or failed operations; follow the linked source contract.",
303
+ ]
304
+ : [
305
+ "Errors from called operations may propagate; no narrower throw contract is declared in source.",
306
+ ],
307
+ sideEffects:
308
+ effects.length > 0 ? effects : ["none-detected-by-static-source-scan"],
309
+ };
310
+ }
311
+
312
+ function localDeclaration(source, name) {
313
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
314
+ const functionMatch = new RegExp(
315
+ `(?:export\\s+)?(async\\s+)?function\\s+${escaped}\\s*\\(`,
316
+ ).exec(source);
317
+ if (functionMatch) {
318
+ return {
319
+ name,
320
+ kind: "function",
321
+ line: lineNumber(source, functionMatch.index),
322
+ start: functionMatch.index,
323
+ ...functionDetails(
324
+ source,
325
+ functionMatch.index,
326
+ name,
327
+ Boolean(functionMatch[1]),
328
+ ),
329
+ };
330
+ }
331
+ const classMatch = new RegExp(`(?:export\\s+)?class\\s+${escaped}\\b`).exec(
332
+ source,
333
+ );
334
+ if (classMatch) {
335
+ return {
336
+ name,
337
+ kind: "class",
338
+ line: lineNumber(source, classMatch.index),
339
+ signature: `class ${name}`,
340
+ parameters: [],
341
+ returns: name,
342
+ errors: [
343
+ "Construction and method errors follow the linked source implementation.",
344
+ ],
345
+ sideEffects: ["class-dependent"],
346
+ };
347
+ }
348
+ const valueMatch = new RegExp(
349
+ `(?:export\\s+)?(const|let|var)\\s+${escaped}\\b`,
350
+ ).exec(source);
351
+ if (valueMatch) {
352
+ return {
353
+ name,
354
+ kind: "constant",
355
+ line: lineNumber(source, valueMatch.index),
356
+ signature: `${valueMatch[1]} ${name}`,
357
+ parameters: [],
358
+ returns: "value",
359
+ errors: ["Import does not declare a throw contract."],
360
+ sideEffects: ["none-on-import"],
361
+ };
362
+ }
363
+ return {
364
+ name,
365
+ kind: "value",
366
+ line: 1,
367
+ signature: name,
368
+ parameters: [],
369
+ returns: "unknown",
370
+ errors: ["No narrower error contract was mechanically discoverable."],
371
+ sideEffects: ["unknown"],
372
+ };
373
+ }
374
+
375
+ function resolveModulePath(fromPath, specifier) {
376
+ const resolved = path.posix.normalize(
377
+ path.posix.join(path.posix.dirname(fromPath), specifier),
378
+ );
379
+ return path.posix.extname(resolved) ? resolved : `${resolved}.js`;
380
+ }
381
+
382
+ function moduleExports({ root, relPath, cache, stack = [] }) {
383
+ if (cache.has(relPath)) return cache.get(relPath);
384
+ if (stack.includes(relPath))
385
+ throw new Error(
386
+ `cyclic public export chain: ${[...stack, relPath].join(" -> ")}`,
387
+ );
388
+ const source = fs.readFileSync(path.join(root, relPath), "utf8");
389
+ const exports = new Map();
390
+ const directPattern =
391
+ /export\s+(async\s+)?(function|class|const|let|var)\s+([A-Za-z_$][\w$]*)/g;
392
+ for (const match of source.matchAll(directPattern)) {
393
+ const detail = localDeclaration(source, match[3]);
394
+ exports.set(match[3], { ...detail, sourcePath: relPath });
395
+ }
396
+ const namedPattern =
397
+ /export\s*\{([\s\S]*?)\}\s*(?:from\s*["']([^"']+)["'])?\s*;/g;
398
+ for (const match of source.matchAll(namedPattern)) {
399
+ const target = match[2] ? resolveModulePath(relPath, match[2]) : "";
400
+ const targetExports = target
401
+ ? moduleExports({
402
+ root,
403
+ relPath: target,
404
+ cache,
405
+ stack: [...stack, relPath],
406
+ })
407
+ : null;
408
+ for (const item of splitTopLevel(match[1])) {
409
+ const cleaned = item.replace(/\/\*[\s\S]*?\*\//g, "").trim();
410
+ if (!cleaned) continue;
411
+ const [sourceName, exportedName = sourceName] = cleaned.split(/\s+as\s+/);
412
+ const detail =
413
+ targetExports?.get(sourceName) || localDeclaration(source, sourceName);
414
+ exports.set(exportedName, {
415
+ ...detail,
416
+ name: exportedName,
417
+ sourcePath: detail.sourcePath || relPath,
418
+ });
419
+ }
420
+ }
421
+ const starPattern = /export\s+\*\s+from\s+["']([^"']+)["']\s*;/g;
422
+ for (const match of source.matchAll(starPattern)) {
423
+ const target = resolveModulePath(relPath, match[1]);
424
+ for (const [name, detail] of moduleExports({
425
+ root,
426
+ relPath: target,
427
+ cache,
428
+ stack: [...stack, relPath],
429
+ })) {
430
+ if (name !== "default") exports.set(name, detail);
431
+ }
432
+ }
433
+ cache.set(relPath, exports);
434
+ return exports;
435
+ }
436
+
437
+ export function createNodeApiReference({ root, packageJson }) {
438
+ const cache = new Map();
439
+ return Object.entries(packageJson.exports || {})
440
+ .filter(
441
+ ([specifier, target]) =>
442
+ !specifier.startsWith("./site/") &&
443
+ specifier !== "./package.json" &&
444
+ typeof target === "string" &&
445
+ target.endsWith(".js"),
446
+ )
447
+ .map(([exportName, target]) => {
448
+ const relPath = target.replace(/^\.\//, "");
449
+ const specifier =
450
+ exportName === "."
451
+ ? packageJson.name
452
+ : `${packageJson.name}/${exportName.replace(/^\.\//, "")}`;
453
+ const symbols = [...moduleExports({ root, relPath, cache })]
454
+ .map(([name, detail]) => ({
455
+ name,
456
+ kind: detail.kind,
457
+ signature: detail.signature,
458
+ parameters: detail.parameters,
459
+ returns: detail.returns,
460
+ errors: detail.errors,
461
+ sideEffects: detail.sideEffects,
462
+ source: { path: detail.sourcePath, line: detail.line },
463
+ example: `import { ${name} } from ${JSON.stringify(specifier)};`,
464
+ }))
465
+ .sort((left, right) => left.name.localeCompare(right.name));
466
+ return { export: exportName, specifier, target, symbols };
467
+ });
468
+ }
469
+
470
+ const GENERATED_FRONTMATTER = `---
471
+ status: active
472
+ period: ongoing
473
+ theme: buildchain-generated-reference
474
+ doc_type: technical-reference
475
+ source_level: local-files
476
+ confidence: high
477
+ sensitivity: public
478
+ evidence_grade: A
479
+ review_state: generated
480
+ last_reviewed: 2026-08-01
481
+ ai_provenance:
482
+ model_family: GPT-5
483
+ product: Codex
484
+ generated_at: 2026-08-01
485
+ invisible_context: not asserted
486
+ ---`;
487
+
488
+ export function renderCliReference(reference) {
489
+ const lines = [
490
+ GENERATED_FRONTMATTER,
491
+ "",
492
+ "# Buildchain CLI Reference",
493
+ "",
494
+ "> Generated from `BUILDCHAIN_USAGE` and the runtime command registry. Do not edit this file by hand.",
495
+ "",
496
+ "Every listed help command is intercepted before dispatch, exits zero, and performs no command side effects.",
497
+ ];
498
+ let currentHead = "";
499
+ for (const entry of reference) {
500
+ if (entry.path[0] !== currentHead) {
501
+ currentHead = entry.path[0];
502
+ lines.push("", `## \`${currentHead}\``);
503
+ }
504
+ lines.push(
505
+ "",
506
+ `### \`${entry.command}\``,
507
+ "",
508
+ `- Help: \`${entry.helpCommand}\``,
509
+ `- Canonical id: \`${entry.id}\``,
510
+ `- Options: ${entry.options.length ? entry.options.map((option) => `\`${option}\``).join(", ") : "none declared"}`,
511
+ "- Syntax:",
512
+ "",
513
+ "```text",
514
+ ...entry.syntaxes,
515
+ "```",
516
+ );
517
+ }
518
+ return `${lines.join("\n")}\n`;
519
+ }
520
+
521
+ function markdownCell(value) {
522
+ return String(value || "")
523
+ .replaceAll("|", "\\|")
524
+ .replace(/\s+/g, " ");
525
+ }
526
+
527
+ export function renderNodeApiReference(reference) {
528
+ const lines = [
529
+ GENERATED_FRONTMATTER,
530
+ "",
531
+ "# Buildchain Node API Reference",
532
+ "",
533
+ "> Generated from `package.json#exports` and the exported ESM symbols in each target. Do not edit this file by hand.",
534
+ "",
535
+ "Signatures and source locations are mechanical. JavaScript return types remain conservative where the source declares no static type.",
536
+ ];
537
+ for (const surface of reference) {
538
+ lines.push(
539
+ "",
540
+ `## \`${surface.specifier}\``,
541
+ "",
542
+ `Target: \`${surface.target}\`. Public symbols: ${surface.symbols.length}.`,
543
+ "",
544
+ "| Symbol | Kind and signature | Parameters | Return | Errors | Side effects | Example | Source |",
545
+ "| --- | --- | --- | --- | --- | --- | --- | --- |",
546
+ );
547
+ for (const symbol of surface.symbols) {
548
+ const parameters = symbol.parameters.length
549
+ ? symbol.parameters.map((entry) => entry.signature).join(", ")
550
+ : "none";
551
+ lines.push(
552
+ `| \`${symbol.name}\` | ${markdownCell(`${symbol.kind}: ${symbol.signature}`)} | ${markdownCell(parameters)} | ${markdownCell(symbol.returns)} | ${markdownCell(symbol.errors.join(" "))} | ${markdownCell(symbol.sideEffects.join(", "))} | \`${markdownCell(symbol.example)}\` | \`${symbol.source.path}:${symbol.source.line}\` |`,
553
+ );
554
+ }
555
+ }
556
+ return `${lines.join("\n")}\n`;
557
+ }