create-thally-docs 0.10.127 → 0.10.129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -160,6 +160,19 @@ passes. Review them before publishing. Use `--skip-validation` for an explicit
160
160
  import-only run; its report is marked unverified. An existing `--into` project
161
161
  without a build script is also reported as unverified.
162
162
 
163
+ The report's `provenance` block records what produced the import so a later run
164
+ can be compared with one input changed. `argv` is the command line as typed, with
165
+ `--api-key` values and URL credentials, queries and fragments removed and a local
166
+ source shown as `local-directory`; other paths are kept as typed, and library
167
+ callers that pass no command line get no `argv`. `sources` lists the primary
168
+ repository and each `--source-ref` repository with the `ref` requested and the
169
+ `commit` that was cloned, or the commit of a local source checkout (`dirty: true`
170
+ when it has uncommitted changes or its status cannot be read; ignored files are
171
+ not seen). `commit` is `null` for a live site and for a folder that is not a git
172
+ checkout. `engine` is the version of `create-thally-docs` and, when it runs from a
173
+ Thally checkout, that checkout's commit and `dirty` state; an installed package
174
+ reports its version only.
175
+
163
176
  Migration is a local build workflow for projects you own or trust. Dependency
164
177
  installation and builds execute project code, including imported MDX and
165
178
  JSX/TSX; they are not sandboxed. No additional confirmation or execution flag
@@ -3,19 +3,19 @@
3
3
  // src/stable-scaffold-release.json
4
4
  var stable_scaffold_release_default = {
5
5
  schemaVersion: 1,
6
- id: "2026-10-27.ce5444a9.859c8f71",
6
+ id: "2026-10-30.b232b7ed.96f29a21",
7
7
  source: {
8
8
  repository: "thallylabs/starter",
9
- commitSha: "ce5444a9f980eb0e81df4d15fa95e8031932c5db",
10
- treeSha: "1df0e0e682c89d377d59fabacf201eaab7938942",
11
- archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/ce5444a9f980eb0e81df4d15fa95e8031932c5db",
9
+ commitSha: "b232b7ed8e99ae74335642a3bce18a329f30fee5",
10
+ treeSha: "cb7041aa3fd6810dae79890442bf7053713a2040",
11
+ archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/b232b7ed8e99ae74335642a3bce18a329f30fee5",
12
12
  manifestPath: "starter-release.json",
13
- manifestSha256: "049ed1669f08ba54e11226f56456decfaff723fddb6bf4652711b29a7e3598a2"
13
+ manifestSha256: "6fcfcc392e6183df7d2d114ad780afc777520edabc797c3e184b3972090b4a5d"
14
14
  },
15
15
  runtime: {
16
16
  repository: "thallylabs/thally",
17
- commitSha: "859c8f712939da7c68e6010ff9d3eb9195ccfa91",
18
- treeSha: "66022670e12f83ca639b6c2bdf07a5041c839d21",
17
+ commitSha: "96f29a21f73145b02c883e9a51ee15b1610d270b",
18
+ treeSha: "83c3ea5b90daec74e4ccf7f30fbf1ec0fd8027d2",
19
19
  contentSource: "assets",
20
20
  identityContractVersion: 1
21
21
  },
@@ -24,6 +24,26 @@ var stable_scaffold_release_default = {
24
24
 
25
25
  // src/previous-scaffold-releases.json
26
26
  var previous_scaffold_releases_default = [
27
+ {
28
+ schemaVersion: 1,
29
+ id: "2026-10-27.ce5444a9.859c8f71",
30
+ source: {
31
+ repository: "thallylabs/starter",
32
+ commitSha: "ce5444a9f980eb0e81df4d15fa95e8031932c5db",
33
+ treeSha: "1df0e0e682c89d377d59fabacf201eaab7938942",
34
+ archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/ce5444a9f980eb0e81df4d15fa95e8031932c5db",
35
+ manifestPath: "starter-release.json",
36
+ manifestSha256: "049ed1669f08ba54e11226f56456decfaff723fddb6bf4652711b29a7e3598a2"
37
+ },
38
+ runtime: {
39
+ repository: "thallylabs/thally",
40
+ commitSha: "859c8f712939da7c68e6010ff9d3eb9195ccfa91",
41
+ treeSha: "66022670e12f83ca639b6c2bdf07a5041c839d21",
42
+ contentSource: "assets",
43
+ identityContractVersion: 1
44
+ },
45
+ starterVersion: 3
46
+ },
27
47
  {
28
48
  schemaVersion: 1,
29
49
  id: "2026-10-27.553758d7.d441a873",
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-J7255UQA.js";
10
10
  import {
11
11
  downloadStarter
12
- } from "./chunk-MLHJQSNN.js";
12
+ } from "./chunk-E75L3VIV.js";
13
13
  import {
14
14
  terminal
15
15
  } from "./chunk-L2YCVQBR.js";
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-L2YCVQBR.js";
5
5
  import {
6
6
  readStarterReleaseManifest
7
- } from "./chunk-WWTAKMTJ.js";
7
+ } from "./chunk-YUJUWZ7V.js";
8
8
  import {
9
9
  STABLE_SCAFFOLD_RELEASE
10
- } from "./chunk-QHYMFPRB.js";
10
+ } from "./chunk-4LV2ICTJ.js";
11
11
 
12
12
  // src/download.ts
13
13
  import { Readable, Transform, pipeline } from "stream";
@@ -77,7 +77,7 @@ function pinnedSpec(specRef, specs) {
77
77
  return specs.find((entry) => canonicalRef(entry.source) === wanted) ?? specs.find((entry) => canonicalRef(entry.source).toLowerCase() === wanted.toLowerCase()) ?? (wantedBase ? specs.find((entry) => baseName(entry.source) === wantedBase) : void 0) ?? (wantedBase ? specs.find((entry) => baseName(entry.source).toLowerCase() === wantedBase.toLowerCase()) : void 0);
78
78
  }
79
79
  function pageState(ref, specs) {
80
- const judge = (spec) => spec.document === void 0 ? "unknown" : ref.webhook ? webhookState(spec.document, ref.path, spec.overrides) : operationState(spec.document, ref.method, ref.path, spec.overrides);
80
+ const judge = (spec) => referenceState(ref, spec);
81
81
  if (ref.specRef) {
82
82
  const spec = pinnedSpec(ref.specRef, specs);
83
83
  return spec ? judge(spec) : "unknown";
@@ -87,6 +87,9 @@ function pageState(ref, specs) {
87
87
  if (specs.some((spec) => spec.document === void 0)) return "unknown";
88
88
  return states.find((state) => state === "hidden" || state === "excluded") ?? "unknown";
89
89
  }
90
+ function referenceState(ref, spec) {
91
+ return spec.document === void 0 ? "unknown" : ref.webhook ? webhookState(spec.document, ref.path, spec.overrides) : operationState(spec.document, ref.method, ref.path, spec.overrides);
92
+ }
90
93
  var firstSet = (...values) => values.find((value) => value !== void 0 && value !== null);
91
94
  function listedOperations(ref, spec) {
92
95
  const document = spec?.document;
@@ -120,6 +123,18 @@ function operationDescription(ref, specs) {
120
123
  }
121
124
  return void 0;
122
125
  }
126
+ function hiddenOperationDescription(ref, specs) {
127
+ const pinned = ref.specRef ? pinnedSpec(ref.specRef, specs) : void 0;
128
+ const hiding = (ref.specRef ? [pinned] : specs).find((spec) => spec !== void 0 && referenceState(ref, spec) === "hidden");
129
+ if (!hiding) return void 0;
130
+ const key = (method) => `${ref.webhook ? "WEBHOOK " : ""}${method.toUpperCase()} ${ref.path}`;
131
+ const overrides = isObj(hiding.overrides) ? hiding.overrides : {};
132
+ const revealed = Object.fromEntries((ref.webhook ? METHODS : [ref.method]).map((method) => {
133
+ const operationKey = key(method);
134
+ return [operationKey, { ...isObj(overrides[operationKey]) ? overrides[operationKey] : {}, hidden: false }];
135
+ }));
136
+ return operationDescription(ref, [{ ...hiding, overrides: { ...overrides, ...revealed } }]);
137
+ }
123
138
  function resolveLocalRef(document, ref) {
124
139
  if (ref !== "#" && !ref.startsWith("#/")) return void 0;
125
140
  let node = document;
@@ -626,7 +641,16 @@ function headingText(node, components, footnoteNumber) {
626
641
  if (node.type === "mdxJsxTextElement" && !components) return "";
627
642
  return (node.children ?? []).map((child) => headingText(child, components, footnoteNumber)).join("");
628
643
  }
644
+ var GENERATED_FIELD_ID = /^(?:parameter|body|response)-/;
629
645
  var STEP_HEADING_SIZES = ["h2", "h3", "h4"];
646
+ function numberTabIds(bases, used) {
647
+ return [...bases].map((base) => {
648
+ let id = base;
649
+ for (let occurrence = 2; used.has(id); occurrence++) id = `${base}-${occurrence}`;
650
+ used.add(id);
651
+ return id;
652
+ });
653
+ }
630
654
  function readPageIds(content, parsed) {
631
655
  const tree = parsed.tree ?? parseMdxTree(content);
632
656
  const footnotes = /* @__PURE__ */ new Map();
@@ -634,7 +658,7 @@ function readPageIds(content, parsed) {
634
658
  if (!footnotes.has(identifier)) footnotes.set(identifier, footnotes.size + 1);
635
659
  return footnotes.get(identifier);
636
660
  };
637
- const page = { headings: [], fieldBases: [], reserved: /* @__PURE__ */ new Set() };
661
+ const page = { headings: [], fieldBases: [], tabBases: [], reserved: /* @__PURE__ */ new Set() };
638
662
  const visit = (node, stepSize, inNote) => {
639
663
  let childSize = stepSize;
640
664
  if (node.type === "footnoteReference" && !inNote) footnoteNumber(node.identifier ?? "");
@@ -665,6 +689,11 @@ function readPageIds(content, parsed) {
665
689
  const base = name === void 0 ? void 0 : fieldAnchorBase(name);
666
690
  if (base && constantAttribute(node, "id") === void 0) page.fieldBases.push(base);
667
691
  }
692
+ if (node.name === "Tab") {
693
+ const title = constantAttribute(node, "title");
694
+ const base = title ? slugify(plainInlineText(title)) : "";
695
+ if (base && constantAttribute(node, "id") === void 0) page.tabBases.push(base);
696
+ }
668
697
  }
669
698
  for (const child of node.children ?? []) visit(child, childSize, inNote || node.type === "footnoteDefinition");
670
699
  };
@@ -687,6 +716,7 @@ function extractHeadingAnchors(content, parsed = parseMdxPage(content)) {
687
716
  const { ids, used } = numberHeadingIds(page.headings);
688
717
  for (const id of ids) if (id) anchors.add(id);
689
718
  for (const id of numberFieldIds(page.fieldBases, used, page.reserved)) anchors.add(id);
719
+ for (const id of numberTabIds(page.tabBases, used)) anchors.add(id);
690
720
  for (const id of page.reserved) anchors.add(id);
691
721
  return anchors;
692
722
  }
@@ -888,14 +918,14 @@ function checkOperationPages(projectDir, tabs, pages, issues) {
888
918
  const described = pages.some((page) => !page.hasDescription) ? [...specs, ...load(true)] : specs;
889
919
  for (const { file, reference, hasDescription } of pages) {
890
920
  const state = specs.length > 0 ? pageState(reference, specs) : "unknown";
891
- if (state === "hidden" || state === "excluded") {
921
+ if (state === "excluded") {
892
922
  const operation = `${reference.specRef ? `${reference.specRef} ` : ""}${reference.method} ${reference.path}`;
893
923
  issues.push({
894
924
  severity: "warning",
895
925
  file,
896
926
  message: `page ${file} points at ${state} operation ${operation} and is not published: it returns 404 and is left out of navigation, search, sitemap and llms.txt. Publish the operation or point the page at another one`
897
927
  });
898
- } else if (!hasDescription && operationDescription(reference, described) === void 0) {
928
+ } else if (!hasDescription && operationDescription(reference, described) === void 0 && hiddenOperationDescription(reference, described) === void 0) {
899
929
  issues.push({ severity: "warning", message: MISSING_DESCRIPTION, file });
900
930
  }
901
931
  }
@@ -996,6 +1026,7 @@ async function runCheck(projectDir, options) {
996
1026
  const operationPages = [];
997
1027
  const validPaths = /* @__PURE__ */ new Set(["/"]);
998
1028
  const anchorsByPath = /* @__PURE__ */ new Map();
1029
+ const operationPaths = /* @__PURE__ */ new Set();
999
1030
  const linksByFile = [];
1000
1031
  const linkStubs = [];
1001
1032
  for (const filePath of allFiles) {
@@ -1034,7 +1065,11 @@ async function runCheck(projectDir, options) {
1034
1065
  notices.push({ severity: "warning", message: `Very short body (${content.trim().length} chars) \u2014 page may be empty`, file: rel2 });
1035
1066
  }
1036
1067
  if (isLinkStub) linkStubs.push({ file: rel2, path, issues: notices });
1037
- if (reference) operationPages.push({ file: rel2, reference, hasDescription: Boolean(data.description) });
1068
+ if (reference) {
1069
+ operationPages.push({ file: rel2, reference, hasDescription: Boolean(data.description) });
1070
+ operationPaths.add(path);
1071
+ operationPaths.add(`/${pageId}`);
1072
+ }
1038
1073
  for (const message of manualApiProblems(rel2, data, Boolean(reference), content, config.api)) {
1039
1074
  issues.push({ severity: "warning", file: rel2, message });
1040
1075
  }
@@ -1078,7 +1113,8 @@ async function runCheck(projectDir, options) {
1078
1113
  });
1079
1114
  }
1080
1115
  const externalLinks = [];
1081
- for (const { file, anchors, links, offset } of linksByFile) {
1116
+ const isGeneratedFieldId = (path, fragment) => operationPaths.has(path) && GENERATED_FIELD_ID.test(fragment);
1117
+ for (const { file, path: pagePath, anchors, links, offset } of linksByFile) {
1082
1118
  for (const { target, line: contentLine } of links) {
1083
1119
  const line = contentLine + offset;
1084
1120
  if (/^(https?:|mailto:|tel:)/i.test(target)) {
@@ -1087,7 +1123,7 @@ async function runCheck(projectDir, options) {
1087
1123
  }
1088
1124
  if (target.startsWith("#")) {
1089
1125
  const anchor2 = target.slice(1);
1090
- if (anchor2 && !anchors.has(decodedFragment(anchor2))) {
1126
+ if (anchor2 && !anchors.has(decodedFragment(anchor2)) && !isGeneratedFieldId(pagePath, decodedFragment(anchor2))) {
1091
1127
  issues.push({ severity: "warning", message: `Broken anchor: "${target}" not found on this page`, file, line });
1092
1128
  }
1093
1129
  continue;
@@ -1098,7 +1134,7 @@ async function runCheck(projectDir, options) {
1098
1134
  if (!cycle && isGeneratedPath(path)) continue;
1099
1135
  if (cycle || !validPaths.has(path)) {
1100
1136
  issues.push({ severity: "error", message: `Broken link: "${target}" \u2014 no page at "${path}"`, file, line });
1101
- } else if (anchor && !anchorsByPath.get(path)?.has(decodedFragment(anchor))) {
1137
+ } else if (anchor && !anchorsByPath.get(path)?.has(decodedFragment(anchor)) && !isGeneratedFieldId(path, decodedFragment(anchor))) {
1102
1138
  issues.push({ severity: "warning", message: `Broken anchor: "${target}" \u2014 no heading "#${anchor}" on that page`, file, line });
1103
1139
  }
1104
1140
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  downloadStarter
4
- } from "./chunk-MLHJQSNN.js";
4
+ } from "./chunk-E75L3VIV.js";
5
5
  import {
6
6
  applyStarterRuntimeSyncPlan,
7
7
  areStarterFileContentsEquivalent,
@@ -11,11 +11,11 @@ import {
11
11
  planStarterRuntimeSync,
12
12
  readStarterReleaseManifest,
13
13
  starterManifestSha256
14
- } from "./chunk-WWTAKMTJ.js";
14
+ } from "./chunk-YUJUWZ7V.js";
15
15
  import {
16
16
  STABLE_SCAFFOLD_RELEASE,
17
17
  SUPPORTED_SCAFFOLD_RELEASES
18
- } from "./chunk-QHYMFPRB.js";
18
+ } from "./chunk-4LV2ICTJ.js";
19
19
 
20
20
  // src/starter-update.ts
21
21
  import {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  STABLE_SCAFFOLD_RELEASE
4
- } from "./chunk-QHYMFPRB.js";
4
+ } from "./chunk-4LV2ICTJ.js";
5
5
 
6
6
  // src/starter-sync.ts
7
7
  import { createHash } from "crypto";
@@ -8,28 +8,28 @@ import {
8
8
  installDeps,
9
9
  runProjectCommand,
10
10
  scaffold
11
- } from "./chunk-RYFINUAB.js";
11
+ } from "./chunk-AOOMRNYY.js";
12
12
  import {
13
13
  pruneMissingSiteLinks
14
14
  } from "./chunk-UG3OXVLE.js";
15
15
  import {
16
16
  resolveStarterUpdateBase
17
- } from "./chunk-TXS2KCLI.js";
17
+ } from "./chunk-TJRGX4ZM.js";
18
18
  import {
19
19
  downloadStarter
20
- } from "./chunk-MLHJQSNN.js";
20
+ } from "./chunk-E75L3VIV.js";
21
21
  import {
22
22
  terminal
23
23
  } from "./chunk-L2YCVQBR.js";
24
24
  import {
25
25
  areStarterFileContentsEquivalent
26
- } from "./chunk-WWTAKMTJ.js";
26
+ } from "./chunk-YUJUWZ7V.js";
27
27
 
28
28
  // src/migrate/index.ts
29
29
  import { createHash } from "crypto";
30
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync3, readFileSync as readFileSync4, realpathSync as realpathSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
30
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync3, readFileSync as readFileSync5, realpathSync as realpathSync2, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "fs";
31
31
  import { tmpdir as tmpdir2 } from "os";
32
- import { basename, join as join5, resolve as resolve2 } from "path";
32
+ import { basename, join as join6, resolve as resolve2 } from "path";
33
33
  import {
34
34
  cloneGitHubRepository,
35
35
  importSourceRef,
@@ -405,13 +405,77 @@ function reportUnresolvedMigrationLinks(bundle) {
405
405
  }
406
406
  }
407
407
 
408
+ // src/migrate/provenance.ts
409
+ import { execFileSync } from "child_process";
410
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
411
+ import { dirname as dirname3, join as join5 } from "path";
412
+ import { fileURLToPath } from "url";
413
+ var ENGINE_PACKAGE = "create-thally-docs";
414
+ var REPOSITORY_ENVIRONMENT = ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE", "GIT_OBJECT_DIRECTORY", "GIT_ALTERNATE_OBJECT_DIRECTORIES", "GIT_COMMON_DIR", "GIT_PREFIX"];
415
+ function git(directory, args) {
416
+ const env = Object.fromEntries(Object.entries(process.env).filter(([name]) => !REPOSITORY_ENVIRONMENT.includes(name)));
417
+ try {
418
+ return execFileSync("git", ["--no-optional-locks", "-c", "core.fsmonitor=false", "-C", directory, ...args], {
419
+ encoding: "utf8",
420
+ env,
421
+ stdio: ["ignore", "pipe", "ignore"],
422
+ timeout: 1e4
423
+ }).trim();
424
+ } catch {
425
+ return null;
426
+ }
427
+ }
428
+ function gitCommit(directory, { atRoot = false } = {}) {
429
+ if (atRoot && git(directory, ["rev-parse", "--show-prefix"]) !== "") return null;
430
+ const commit = git(directory, ["rev-parse", "--verify", "HEAD"]);
431
+ return commit && /^[0-9a-f]{40,64}$/.test(commit) ? commit : null;
432
+ }
433
+ function gitHead(directory) {
434
+ const commit = gitCommit(directory);
435
+ return commit ? { commit, dirty: git(directory, ["status", "--porcelain"]) !== "" } : null;
436
+ }
437
+ function enginePackage() {
438
+ let directory = dirname3(fileURLToPath(import.meta.url));
439
+ for (; ; ) {
440
+ const manifestPath = join5(directory, "package.json");
441
+ if (existsSync5(manifestPath)) {
442
+ try {
443
+ const manifest = JSON.parse(readFileSync4(manifestPath, "utf8"));
444
+ if (manifest.name === ENGINE_PACKAGE && typeof manifest.version === "string") return { directory, version: manifest.version };
445
+ } catch {
446
+ }
447
+ }
448
+ const parent = dirname3(directory);
449
+ if (parent === directory) return null;
450
+ directory = parent;
451
+ }
452
+ }
453
+ function engineProvenance() {
454
+ const engine = enginePackage();
455
+ if (!engine) return { name: ENGINE_PACKAGE, version: "unknown", commit: null };
456
+ const head = git(engine.directory, ["rev-parse", "--show-prefix"]) === `packages/${ENGINE_PACKAGE}/` ? gitHead(engine.directory) : null;
457
+ return head ? { name: ENGINE_PACKAGE, version: engine.version, commit: head.commit, dirty: head.dirty } : { name: ENGINE_PACKAGE, version: engine.version, commit: null };
458
+ }
459
+ function redactArgv(argv) {
460
+ return argv.map((arg, index) => {
461
+ if (argv[index - 1] === "--api-key") return "<redacted>";
462
+ if (arg.startsWith("--api-key=")) return "--api-key=<redacted>";
463
+ try {
464
+ const url = new URL(arg);
465
+ if ((url.protocol === "http:" || url.protocol === "https:") && (url.username || url.password || url.search || url.hash)) return `${url.origin}${url.pathname}`;
466
+ } catch {
467
+ }
468
+ return arg;
469
+ });
470
+ }
471
+
408
472
  // src/migrate/index.ts
409
473
  function projectPath(projectDir, candidate) {
410
474
  return assertMigrationDestination(projectDir, candidate);
411
475
  }
412
476
  function quarantineIgnoreFile(projectDir) {
413
477
  const ignorePath = projectPath(projectDir, ".gitignore");
414
- const existing = existsSync5(ignorePath) ? readFileSync4(ignorePath, "utf8") : "";
478
+ const existing = existsSync6(ignorePath) ? readFileSync5(ignorePath, "utf8") : "";
415
479
  const rules = existing.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^!?\/?migration-quarantine(?:\/(?:\*\*?)?)?$/.test(line));
416
480
  if (rules.length > 0 && !rules[rules.length - 1].startsWith("!")) return;
417
481
  const separator = existing === "" || existing.endsWith("\n") ? "" : "\n";
@@ -422,8 +486,8 @@ function quarantineIgnoreFile(projectDir) {
422
486
  }
423
487
  function readExistingConfig(projectDir) {
424
488
  const configPath = projectPath(projectDir, "docs.json");
425
- if (!existsSync5(configPath)) return void 0;
426
- return JSON.parse(readFileSync4(configPath, "utf8"));
489
+ if (!existsSync6(configPath)) return void 0;
490
+ return JSON.parse(readFileSync5(configPath, "utf8"));
427
491
  }
428
492
  function resetFreshMigrationContent(projectDir) {
429
493
  const contentDirectory = projectPath(projectDir, "src/content");
@@ -433,17 +497,24 @@ function resetFreshMigrationContent(projectDir) {
433
497
  rmSync3(projectPath(projectDir, sampleSpec), { force: true });
434
498
  }
435
499
  }
436
- async function fetchSourceRefs(mappings, warnings) {
500
+ function publicSourceUrl(options) {
501
+ if (options.sourceDirectory) return "local-directory";
502
+ const url = new URL(options.sourceUrl);
503
+ return `${url.origin}${url.pathname}`;
504
+ }
505
+ async function fetchSourceRefs(mappings, warnings, sources) {
437
506
  const imports = [];
438
507
  for (const mapping of mappings) {
439
- const root = mkdtempSync3(join5(tmpdir2(), "thally-source-ref-"));
508
+ const root = mkdtempSync3(join6(tmpdir2(), "thally-source-ref-"));
440
509
  try {
441
510
  const source = parseGitHubRepositoryUrl(`https://github.com/${mapping.repo}`);
442
511
  const tooLarge = await sourceRefOversizeReason(mapping.repo);
443
512
  if (tooLarge) throw new Error(tooLarge);
444
- const cloneDir = join5(root, "repository");
513
+ const cloneDir = join6(root, "repository");
445
514
  await cloneGitHubRepository(source, cloneDir, warnings, { skipSubmodules: true });
515
+ const commit = gitCommit(cloneDir, { atRoot: true });
446
516
  imports.push(importSourceRef(mapping, cloneDir));
517
+ sources.push({ role: "sourceRef", url: `https://github.com/${source.owner}/${source.repo}`, ref: source.branch, mountPath: mapping.mountPath, commit });
447
518
  } catch (error) {
448
519
  warnings.push({ code: "fetch-failed", message: `sourceRef ${mapping.repo} was not imported: ${error instanceof Error ? error.message : String(error)}` });
449
520
  } finally {
@@ -453,11 +524,14 @@ async function fetchSourceRefs(mappings, warnings) {
453
524
  return imports;
454
525
  }
455
526
  async function discoverMigration(options) {
527
+ const sources = [];
456
528
  if (options.sourceDirectory) {
457
529
  assertSeparateMigrationDirectories(options.sourceDirectory, options.projectDir);
458
- const temporaryRoot2 = mkdtempSync3(join5(tmpdir2(), "thally-local-migrate-"));
530
+ const temporaryRoot2 = mkdtempSync3(join6(tmpdir2(), "thally-local-migrate-"));
459
531
  try {
460
- const snapshot = join5(temporaryRoot2, "source");
532
+ const snapshot = join6(temporaryRoot2, "source");
533
+ const head = gitHead(options.sourceDirectory);
534
+ sources.push({ role: "primary", url: publicSourceUrl(options), commit: head?.commit ?? null, ...head ? { dirty: head.dirty } : {} });
461
535
  snapshotLocalMigration(options.sourceDirectory, snapshot, options.docsDir);
462
536
  const bundle = await terminal.step("Converting local documentation", () => convertRepository({
463
537
  repositoryDir: snapshot,
@@ -467,7 +541,7 @@ async function discoverMigration(options) {
467
541
  docsDir: options.docsDir,
468
542
  platform: options.platform
469
543
  }), "Local documentation converted");
470
- return hydrateRemoteApiSpecs(bundle, options.fetcher);
544
+ return { bundle: await hydrateRemoteApiSpecs(bundle, options.fetcher), sources };
471
545
  } finally {
472
546
  rmSync3(temporaryRoot2, { recursive: true, force: true });
473
547
  }
@@ -475,7 +549,8 @@ async function discoverMigration(options) {
475
549
  const url = new URL(options.sourceUrl);
476
550
  if (url.hostname.toLowerCase() !== "github.com") {
477
551
  if (options.sourceRefs?.length) terminal.warn("--source-ref applies only to GitHub repository sources and was ignored.");
478
- return terminal.step("Discovering public documentation", () => {
552
+ sources.push({ role: "primary", url: publicSourceUrl(options), commit: null });
553
+ const bundle = await terminal.step("Discovering public documentation", () => {
479
554
  const discovery = {
480
555
  sourceUrl: options.sourceUrl,
481
556
  platform: options.platform,
@@ -485,15 +560,17 @@ async function discoverMigration(options) {
485
560
  if (options.urlMigration) return runUrlMigration({ ...discovery, fetcher: options.fetcher, ...options.urlMigration }).then((run) => options.openapiUrl ? hydrateUrlOpenApi(run.bundle, options.openapiUrl, options.fetcher) : run.bundle);
486
561
  return options.fetcher ? migrateUrl({ ...discovery, fetcher: options.fetcher }) : discoverUrl(discovery);
487
562
  }, "Public documentation discovered");
563
+ return { bundle, sources };
488
564
  }
489
565
  const source = parseGitHubRepositoryUrl(options.sourceUrl);
490
566
  if (options.branch) source.branch = options.branch;
491
- const temporaryRoot = mkdtempSync3(join5(tmpdir2(), "thally-migrate-"));
492
- const cloneDir = join5(temporaryRoot, "repository");
567
+ const temporaryRoot = mkdtempSync3(join6(tmpdir2(), "thally-migrate-"));
568
+ const cloneDir = join6(temporaryRoot, "repository");
493
569
  try {
494
570
  const cloneWarnings = [];
495
571
  await terminal.step(`Cloning ${source.owner}/${source.repo}`, () => cloneGitHubRepository(source, cloneDir, cloneWarnings, { docsDir: options.docsDir }), "Source repository cloned");
496
- const sourceRefs = options.sourceRefs?.length ? await terminal.step("Importing sourceRef repositories", () => fetchSourceRefs(options.sourceRefs ?? [], cloneWarnings), "sourceRef repositories imported") : [];
572
+ sources.push({ role: "primary", url: `https://github.com/${source.owner}/${source.repo}`, ref: source.branch, commit: gitCommit(cloneDir, { atRoot: true }) });
573
+ const sourceRefs = options.sourceRefs?.length ? await terminal.step("Importing sourceRef repositories", () => fetchSourceRefs(options.sourceRefs ?? [], cloneWarnings, sources), "sourceRef repositories imported") : [];
497
574
  const bundle = await terminal.step("Converting repository documentation", () => convertRepository({
498
575
  sourceRefs,
499
576
  repositoryDir: cloneDir,
@@ -502,7 +579,7 @@ async function discoverMigration(options) {
502
579
  platform: options.platform
503
580
  }), "Repository documentation converted");
504
581
  const hydrated = await terminal.step("Loading remote API specifications", () => hydrateRemoteApiSpecs(bundle, options.fetcher), "Remote API specifications loaded");
505
- return cloneWarnings.length > 0 ? { ...hydrated, warnings: [...cloneWarnings, ...hydrated.warnings] } : hydrated;
582
+ return { bundle: cloneWarnings.length > 0 ? { ...hydrated, warnings: [...cloneWarnings, ...hydrated.warnings] } : hydrated, sources };
506
583
  } finally {
507
584
  rmSync3(temporaryRoot, { recursive: true, force: true });
508
585
  }
@@ -523,7 +600,7 @@ function publicSpecWarning(projectDir, bundle) {
523
600
  async function migrateDocs(options) {
524
601
  const projectDir = resolve2(options.projectDir);
525
602
  assertMigrationDestination(projectDir);
526
- const bundle = await discoverMigration(options);
603
+ const { bundle, sources } = await discoverMigration(options);
527
604
  reportUnresolvedMigrationLinks(bundle);
528
605
  if (!options.into) {
529
606
  terminal.detail("Project", projectDir);
@@ -540,7 +617,7 @@ async function migrateDocs(options) {
540
617
  });
541
618
  resetFreshMigrationContent(projectDir);
542
619
  pruneMissingSiteLinks(projectDir, new Set(bundle.pages.map((page) => page.id)));
543
- } else if (!existsSync5(projectDir)) {
620
+ } else if (!existsSync6(projectDir)) {
544
621
  throw new Error(`Project directory "${projectDir}" does not exist. Use without --into to scaffold a new one.`);
545
622
  }
546
623
  if (!options.into) {
@@ -557,7 +634,7 @@ async function migrateDocs(options) {
557
634
  }
558
635
  const existingPlan = options.into ? await migrationExistingConfig(projectDir, readExistingConfig(projectDir), bundle.warnings) : void 0;
559
636
  const registryPath = projectPath(projectDir, "src/mdx/custom-components.tsx");
560
- const registryBytes = existsSync5(registryPath) ? readFileSync4(registryPath) : void 0;
637
+ const registryBytes = existsSync6(registryPath) ? readFileSync5(registryPath) : void 0;
561
638
  existingPlan?.preconditions.set("src/mdx/custom-components.tsx", registryBytes);
562
639
  const rendered = await terminal.step("Rendering documentation", async () => renderFiles(bundle, {
563
640
  existingConfig: existingPlan?.config,
@@ -567,7 +644,7 @@ async function migrateDocs(options) {
567
644
  const writePreconditions = new Map(existingPlan?.preconditions);
568
645
  const writable = rendered.filter((file) => {
569
646
  const path = projectPath(projectDir, file.path);
570
- const current = existsSync5(path) ? readFileSync4(path) : void 0;
647
+ const current = existsSync6(path) ? readFileSync5(path) : void 0;
571
648
  if (!writePreconditions.has(file.path)) writePreconditions.set(file.path, current);
572
649
  if (!options.into || file.path === "docs.json" || file.path === "src/mdx/custom-components.tsx" || !current) return true;
573
650
  if (current.equals(Buffer.from(file.content)) || existingPlan?.seedFiles.get(file.path)?.equals(current)) return true;
@@ -584,7 +661,7 @@ async function migrateDocs(options) {
584
661
  if (quarantineIgnore) {
585
662
  writable.push(quarantineIgnore);
586
663
  const ignorePath = projectPath(projectDir, ".gitignore");
587
- writePreconditions.set(".gitignore", existsSync5(ignorePath) ? readFileSync4(ignorePath) : void 0);
664
+ writePreconditions.set(".gitignore", existsSync6(ignorePath) ? readFileSync5(ignorePath) : void 0);
588
665
  }
589
666
  await terminal.step("Writing documentation", async () => {
590
667
  projectPath(projectDir, "migration-report.json");
@@ -628,7 +705,12 @@ async function migrateDocs(options) {
628
705
  const reportPath = projectPath(projectDir, "migration-report.json");
629
706
  writeFileSync2(reportPath, `${JSON.stringify({
630
707
  version: 1,
631
- sourceUrl: options.sourceDirectory ? "local-directory" : `${new URL(options.sourceUrl).origin}${new URL(options.sourceUrl).pathname}`,
708
+ sourceUrl: publicSourceUrl(options),
709
+ provenance: {
710
+ ...options.argv ? { argv: redactArgv(options.argv) } : {},
711
+ engine: engineProvenance(),
712
+ sources
713
+ },
632
714
  platform: bundle.platform,
633
715
  pages: bundle.pages.length,
634
716
  assets: bundle.assets.length,
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  migrateDocs
4
- } from "./chunk-XERSFGRA.js";
4
+ } from "./chunk-Z5L3F6V5.js";
5
5
  import {
6
6
  parseFrontmatter,
7
7
  runCheck
8
- } from "./chunk-WKJN3RIC.js";
8
+ } from "./chunk-M6CNQ62X.js";
9
9
  import "./chunk-KDLP27DS.js";
10
10
  import {
11
11
  logo,
@@ -13,18 +13,18 @@ import {
13
13
  scaffold,
14
14
  slugify,
15
15
  success
16
- } from "./chunk-RYFINUAB.js";
16
+ } from "./chunk-AOOMRNYY.js";
17
17
  import "./chunk-UG3OXVLE.js";
18
18
  import "./chunk-J7255UQA.js";
19
- import "./chunk-TXS2KCLI.js";
20
- import "./chunk-MLHJQSNN.js";
19
+ import "./chunk-TJRGX4ZM.js";
20
+ import "./chunk-E75L3VIV.js";
21
21
  import {
22
22
  acquireTerminalAccent,
23
23
  formatHelp,
24
24
  terminal
25
25
  } from "./chunk-L2YCVQBR.js";
26
- import "./chunk-WWTAKMTJ.js";
27
- import "./chunk-QHYMFPRB.js";
26
+ import "./chunk-YUJUWZ7V.js";
27
+ import "./chunk-4LV2ICTJ.js";
28
28
 
29
29
  // src/index.ts
30
30
  import { existsSync as existsSync2, readdirSync } from "fs";
@@ -782,6 +782,7 @@ Options:
782
782
  `
783
783
  };
784
784
  var positional = [];
785
+ var positionalIndexes = [];
785
786
  for (let i = 0; i < args.length; i++) {
786
787
  if (args[i].startsWith("-")) {
787
788
  if (valueFlags.has(args[i]) && i + 1 < args.length && !args[i + 1].startsWith("-")) {
@@ -789,6 +790,7 @@ for (let i = 0; i < args.length; i++) {
789
790
  }
790
791
  } else {
791
792
  positional.push(args[i]);
793
+ positionalIndexes.push(i);
792
794
  }
793
795
  }
794
796
  function getFlagValue(flag) {
@@ -897,7 +899,11 @@ async function runMigrateCommand() {
897
899
  platform,
898
900
  yes,
899
901
  skipValidation: flags.includes("--skip-validation"),
900
- sourceRefs
902
+ sourceRefs,
903
+ // The report never names a private checkout path (see its `sourceUrl`), so
904
+ // a local source is masked in the recorded command line too. Every other
905
+ // argument, including output paths, is recorded as typed.
906
+ argv: sourceDirectory ? args.map((arg, i) => i === positionalIndexes[1] ? "local-directory" : arg) : args
901
907
  });
902
908
  terminal.section("Migration summary", [
903
909
  `${result.pagesWritten} pages \xB7 ${result.assetsWritten} assets \xB7 ${result.platform}`,
@@ -51,6 +51,8 @@ interface MigrateOptions {
51
51
  skipValidation?: boolean;
52
52
  /** Mintlify `sourceRef` repositories to import, from `--source-ref owner/repo=<path>`. */
53
53
  sourceRefs?: Array<SourceRefMapping>;
54
+ /** Command line of this run, recorded in the report with credentials removed. Absent for library callers. */
55
+ argv?: ReadonlyArray<string>;
54
56
  }
55
57
  interface MigrateResult {
56
58
  pagesWritten: number;
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  migrateDocs
4
- } from "../chunk-XERSFGRA.js";
4
+ } from "../chunk-Z5L3F6V5.js";
5
5
  import "../chunk-KDLP27DS.js";
6
- import "../chunk-RYFINUAB.js";
6
+ import "../chunk-AOOMRNYY.js";
7
7
  import "../chunk-UG3OXVLE.js";
8
8
  import "../chunk-J7255UQA.js";
9
- import "../chunk-TXS2KCLI.js";
10
- import "../chunk-MLHJQSNN.js";
9
+ import "../chunk-TJRGX4ZM.js";
10
+ import "../chunk-E75L3VIV.js";
11
11
  import "../chunk-L2YCVQBR.js";
12
- import "../chunk-WWTAKMTJ.js";
13
- import "../chunk-QHYMFPRB.js";
12
+ import "../chunk-YUJUWZ7V.js";
13
+ import "../chunk-4LV2ICTJ.js";
14
14
  export {
15
15
  migrateDocs
16
16
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCheck
4
- } from "./chunk-WKJN3RIC.js";
4
+ } from "./chunk-M6CNQ62X.js";
5
5
  import "./chunk-KDLP27DS.js";
6
6
  import "./chunk-J7255UQA.js";
7
7
 
package/dist/release.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  STABLE_SCAFFOLD_RELEASE,
4
4
  SUPPORTED_SCAFFOLD_RELEASES,
5
5
  isStableScaffoldRelease
6
- } from "./chunk-QHYMFPRB.js";
6
+ } from "./chunk-4LV2ICTJ.js";
7
7
  export {
8
8
  STABLE_SCAFFOLD_RELEASE,
9
9
  SUPPORTED_SCAFFOLD_RELEASES,
package/dist/scaffold.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  scaffold
4
- } from "./chunk-RYFINUAB.js";
4
+ } from "./chunk-AOOMRNYY.js";
5
5
  import "./chunk-UG3OXVLE.js";
6
6
  import "./chunk-J7255UQA.js";
7
7
  import {
@@ -9,12 +9,12 @@ import {
9
9
  STARTER_COMMIT_SHA,
10
10
  STARTER_REPOSITORY,
11
11
  validateStarterArchiveEntry
12
- } from "./chunk-MLHJQSNN.js";
12
+ } from "./chunk-E75L3VIV.js";
13
13
  import "./chunk-L2YCVQBR.js";
14
- import "./chunk-WWTAKMTJ.js";
14
+ import "./chunk-YUJUWZ7V.js";
15
15
  import {
16
16
  STABLE_SCAFFOLD_RELEASE
17
- } from "./chunk-QHYMFPRB.js";
17
+ } from "./chunk-4LV2ICTJ.js";
18
18
  export {
19
19
  STABLE_SCAFFOLD_RELEASE,
20
20
  STARTER_ARCHIVE_ROOT,
@@ -9,8 +9,8 @@ import {
9
9
  planStarterRuntimeSync,
10
10
  readStarterReleaseManifest,
11
11
  starterManifestSha256
12
- } from "./chunk-WWTAKMTJ.js";
13
- import "./chunk-QHYMFPRB.js";
12
+ } from "./chunk-YUJUWZ7V.js";
13
+ import "./chunk-4LV2ICTJ.js";
14
14
  export {
15
15
  applyStarterRuntimeSyncPlan,
16
16
  captureTargetPrecondition,
@@ -6,11 +6,11 @@ import {
6
6
  resolveStarterUpdateBase,
7
7
  starterReleaseProviderError,
8
8
  updateStarterProject
9
- } from "./chunk-TXS2KCLI.js";
10
- import "./chunk-MLHJQSNN.js";
9
+ } from "./chunk-TJRGX4ZM.js";
10
+ import "./chunk-E75L3VIV.js";
11
11
  import "./chunk-L2YCVQBR.js";
12
- import "./chunk-WWTAKMTJ.js";
13
- import "./chunk-QHYMFPRB.js";
12
+ import "./chunk-YUJUWZ7V.js";
13
+ import "./chunk-4LV2ICTJ.js";
14
14
  export {
15
15
  fetchStarterReleaseHint,
16
16
  resolveManualReviewMutations,
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  updateStarterProject
4
- } from "./chunk-TXS2KCLI.js";
4
+ } from "./chunk-TJRGX4ZM.js";
5
5
  import {
6
6
  downloadStarter
7
- } from "./chunk-MLHJQSNN.js";
7
+ } from "./chunk-E75L3VIV.js";
8
8
  import "./chunk-L2YCVQBR.js";
9
9
  import {
10
10
  classifyStarterPath,
11
11
  mergeStarterOwnershipContracts,
12
12
  readStarterReleaseManifest
13
- } from "./chunk-WWTAKMTJ.js";
13
+ } from "./chunk-YUJUWZ7V.js";
14
14
  import {
15
15
  STABLE_SCAFFOLD_RELEASE,
16
16
  SUPPORTED_SCAFFOLD_RELEASES
17
- } from "./chunk-QHYMFPRB.js";
17
+ } from "./chunk-4LV2ICTJ.js";
18
18
 
19
19
  // src/starter-upgrade-canary.ts
20
20
  import { createHash } from "crypto";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-thally-docs",
3
- "version": "0.10.127",
3
+ "version": "0.10.129",
4
4
  "description": "Scaffold an open-source Thally docs site for people, search engines, and AI tools.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -54,7 +54,7 @@
54
54
  "@clack/prompts": "^0.11.0",
55
55
  "@inquirer/prompts": "^7.0.0",
56
56
  "@thallylabs/core": "0.2.18",
57
- "@thallylabs/migrate": "0.2.47",
57
+ "@thallylabs/migrate": "0.2.48",
58
58
  "p-limit": "^6.1.0",
59
59
  "path-to-regexp": "6.3.0",
60
60
  "remark-gfm": "^4.0.1",