create-thally-docs 0.10.80 → 0.10.82

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
@@ -10,10 +10,14 @@ agent discovery files.
10
10
  ```bash
11
11
  npx create-thally-docs my-docs
12
12
  cd my-docs
13
- npm install
14
13
  npm run dev
15
14
  ```
16
15
 
16
+ Sites created from a starter that includes the `thally` CLI
17
+ (`@thallylabs/cli`) as a dev dependency can run `npx thally dev` instead, which
18
+ also prints the local `llms.txt` and MCP endpoints once the server is ready.
19
+ The completion message prints the right command for the project you created.
20
+
17
21
  The dev server starts at [http://localhost:3040](http://localhost:3040) and
18
22
  automatically uses the next available port when needed. A freshly scaffolded
19
23
  site is agent-ready out of the box and scores 100/A on the built-in
@@ -25,9 +29,14 @@ Run non-interactively with smart defaults:
25
29
  npx create-thally-docs my-docs --yes
26
30
  ```
27
31
 
28
- Dependency installation is deliberately opt-in so the scaffold finishes in
29
- seconds. Pass `--install` to run it immediately, or `--no-install` to skip the
30
- interactive question explicitly.
32
+ Dependencies are installed by default, so the preview command works right
33
+ away. Pass `--no-install` to skip installation (for example in CI or when you
34
+ use another package manager). The interactive setup asks only for the project
35
+ directory, name, an optional GitHub repository, Thally Track, installation,
36
+ and extra languages; the description and brand palette are one-line edits in
37
+ `src/data/site.ts`. The generated `.env.local` never pins a `localhost` site
38
+ URL, so a build from this checkout cannot bake local links into `llms.txt` or
39
+ the sitemap.
31
40
 
32
41
  ## Terminal output
33
42
 
@@ -43,7 +52,7 @@ Scaffolding and migration keep subprocess logs quiet in interactive terminals.
43
52
  Add `--verbose` to see installation and build logs as they happen:
44
53
 
45
54
  ```bash
46
- npx create-thally-docs my-docs --yes --install --verbose
55
+ npx create-thally-docs my-docs --yes --verbose
47
56
  npx create-thally-docs migrate https://github.com/your-org/your-docs --platform auto --verbose
48
57
  ```
49
58
 
@@ -57,7 +66,7 @@ runs retain subprocess output for scripts and CI. These options also work with
57
66
 
58
67
  - **MDX content** in `src/content/`, navigation in `docs.json`
59
68
  - **Agent endpoints** — `/llms.txt`, `/ai.txt`, `/api/docs-index`, `/api/agent-readiness`
60
- - **Hybrid search**, **retrieval-grounded AI chat**, and an **admin analytics** dashboard
69
+ - **Hybrid search**, an **admin analytics** dashboard, and **retrieval-grounded AI chat** when the site is linked to Thally Cloud
61
70
  - **Starter content** with keywords and structured pages, ready to edit
62
71
 
63
72
  ## Other commands
@@ -66,15 +75,16 @@ runs retain subprocess output for scripts and CI. These options also work with
66
75
  | --- | --- |
67
76
  | `create-thally-docs <dir>` | Scaffold a new project |
68
77
  | `create-thally-docs migrate <github-or-docs-url> [dir]` | Import a docs repository or public docs site through the shared migration engine |
69
- | `create-thally-docs check [dir] [--fix]` | Lint content for orphan pages and missing frontmatter |
78
+ | `create-thally-docs check [dir] [--fix] [--ci] [--drift] [--external]` | Lint content for broken links, orphan pages, and missing frontmatter; `--external` also checks links to public hosts (bounded, warnings only) |
70
79
  | `create-thally-docs translate --locale <code>` | Translate content into another locale |
71
80
 
72
- Interactive migrations ask whether the source is Mintlify, Docusaurus, or
81
+ Interactive migrations ask whether the source is Mintlify, Docusaurus, Fern, or
73
82
  another auto-detected platform. When automatic detection receives a live
74
83
  website URL, the CLI recommends its source GitHub repository and requires
75
84
  confirmation before continuing with the less precise website crawl. For
76
85
  scripts and CI, pass
77
- `--platform mintlify`, `--platform docusaurus`, or `--platform auto`; `--yes`
86
+ `--platform mintlify`, `--platform docusaurus`, `--platform fern`, or
87
+ `--platform auto`; `--yes`
78
88
  keeps backward-compatible auto-detection when no platform flag is supplied.
79
89
  Explicit `--platform auto` and `--yes` runs print the live-site limitation
80
90
  without introducing an interactive prompt. Running `migrate` without
@@ -245,6 +245,131 @@ function manualApiProblems(file, data, hasOpenApiReference, mdx, docsApi) {
245
245
  import { projectNavigationContract } from "@thallylabs/core/navigation";
246
246
  import { slugify } from "@thallylabs/core/slugify";
247
247
  import { validateIntegrations } from "@thallylabs/migrate";
248
+
249
+ // src/external-links.ts
250
+ import { lookup } from "dns/promises";
251
+ import { request as httpRequest } from "http";
252
+ import { request as httpsRequest } from "https";
253
+ import { isIP } from "net";
254
+ import pLimit from "p-limit";
255
+ var DEFAULT_CONCURRENCY = 8;
256
+ var DEFAULT_TIMEOUT_MS = 8e3;
257
+ var DEFAULT_MAX_URLS = 300;
258
+ var USER_AGENT = "Thally-Check/1.0 (+https://thally.io)";
259
+ function isPublicAddress(address, family) {
260
+ if (family === 4) {
261
+ const parts = address.split(".").map(Number);
262
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return false;
263
+ const [a, b] = parts;
264
+ return !(a === 0 || a === 10 || a === 127 || a >= 224 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && (b === 0 || b === 168) || a === 198 && (b === 18 || b === 19) || a === 203 && b === 0 || a === 198 && b === 51);
265
+ }
266
+ return family === 6 && /^[23][0-9a-f]{0,3}:/i.test(address) && !/^2001:db8:/i.test(address);
267
+ }
268
+ function isLocalHostname(hostname) {
269
+ const host = hostname.toLowerCase().replace(/\.$/, "");
270
+ return !host.includes(".") || host === "localhost" || /\.(?:localhost|local|internal|lan|home|corp|intranet|test|invalid|example)$/.test(host);
271
+ }
272
+ function checkableUrl(value) {
273
+ let url;
274
+ try {
275
+ url = new URL(value);
276
+ } catch {
277
+ return null;
278
+ }
279
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
280
+ if (url.username || url.password) return null;
281
+ const hostname = url.hostname.replace(/^\[|\]$/g, "");
282
+ if (isIP(hostname) || isLocalHostname(hostname)) return null;
283
+ url.hash = "";
284
+ return url;
285
+ }
286
+ async function defaultResolve(hostname) {
287
+ return lookup(hostname, { all: true, verbatim: true });
288
+ }
289
+ var defaultRequest = (url, method, pinned, timeoutMs) => new Promise((resolve, reject) => {
290
+ const send = url.protocol === "https:" ? httpsRequest : httpRequest;
291
+ const req = send(url, {
292
+ method,
293
+ headers: { "User-Agent": USER_AGENT, Accept: "*/*" },
294
+ signal: AbortSignal.timeout(timeoutMs),
295
+ // Node's dual-stack connector asks lookup for `all: true`; answer with
296
+ // the validated address only so the connection cannot be redirected.
297
+ lookup: (_hostname, options, callback) => {
298
+ if (typeof options === "object" && options.all) callback(null, [pinned]);
299
+ else callback(null, pinned.address, pinned.family);
300
+ }
301
+ }, (response) => {
302
+ response.destroy();
303
+ resolve(response.statusCode ?? 0);
304
+ });
305
+ req.on("error", reject);
306
+ req.end();
307
+ });
308
+ async function checkOne(url, resolveHost, send, timeoutMs) {
309
+ let addresses;
310
+ try {
311
+ addresses = await resolveHost(url.hostname);
312
+ } catch {
313
+ return { kind: "broken", message: `host "${url.hostname}" did not resolve` };
314
+ }
315
+ if (addresses.length === 0) return { kind: "broken", message: `host "${url.hostname}" did not resolve` };
316
+ if (addresses.some((candidate) => !isPublicAddress(candidate.address, candidate.family))) {
317
+ return { kind: "skipped" };
318
+ }
319
+ const pinned = addresses[0];
320
+ try {
321
+ let status = await send(url, "HEAD", pinned, timeoutMs);
322
+ if (status === 405 || status === 501) status = await send(url, "GET", pinned, timeoutMs);
323
+ if (status >= 200 && status < 400) return { kind: "ok" };
324
+ if (status === 401 || status === 403 || status === 429 || status === 999) return { kind: "skipped" };
325
+ return { kind: "broken", message: `returned HTTP ${status}` };
326
+ } catch (error) {
327
+ const reason = error instanceof Error && error.name === "TimeoutError" ? `timed out after ${timeoutMs}ms` : "could not be reached";
328
+ return { kind: "broken", message: reason };
329
+ }
330
+ }
331
+ async function checkExternalLinks(references, options = {}) {
332
+ const concurrency = Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY);
333
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
334
+ const maxUrls = Math.max(0, options.maxUrls ?? DEFAULT_MAX_URLS);
335
+ const resolveHost = options.resolve ?? defaultResolve;
336
+ const send = options.request ?? defaultRequest;
337
+ const occurrences = /* @__PURE__ */ new Map();
338
+ for (const reference of references) {
339
+ const url = checkableUrl(reference.url);
340
+ if (!url) continue;
341
+ const key = url.toString();
342
+ const entry = occurrences.get(key) ?? { url, references: [] };
343
+ entry.references.push(reference);
344
+ occurrences.set(key, entry);
345
+ }
346
+ const entries = [...occurrences.values()];
347
+ const checked = entries.slice(0, maxUrls);
348
+ const limit = pLimit(concurrency);
349
+ const outcomes = await Promise.all(checked.map((entry) => limit(() => checkOne(entry.url, resolveHost, send, timeoutMs))));
350
+ const issues = [];
351
+ checked.forEach((entry, index) => {
352
+ const outcome = outcomes[index];
353
+ if (outcome.kind !== "broken") return;
354
+ for (const reference of entry.references) {
355
+ issues.push({
356
+ severity: "warning",
357
+ message: `External link "${reference.url}" ${outcome.message}`,
358
+ file: reference.file,
359
+ line: reference.line
360
+ });
361
+ }
362
+ });
363
+ if (entries.length > checked.length) {
364
+ issues.push({
365
+ severity: "warning",
366
+ message: `Checked the first ${checked.length} of ${entries.length} unique external links; the rest were not checked.`
367
+ });
368
+ }
369
+ return issues;
370
+ }
371
+
372
+ // src/check.ts
248
373
  function gitLocal(projectDir, args) {
249
374
  try {
250
375
  const out = execFileSync("git", args, { cwd: projectDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
@@ -699,10 +824,14 @@ async function runCheck(projectDir, options) {
699
824
  anchorsByPath.set(localized, anchors);
700
825
  }
701
826
  }
827
+ const externalLinks = [];
702
828
  for (const { file, anchors, links, offset } of linksByFile) {
703
829
  for (const { target, line: contentLine } of links) {
704
830
  const line = contentLine + offset;
705
- if (/^(https?:|mailto:|tel:)/i.test(target)) continue;
831
+ if (/^(https?:|mailto:|tel:)/i.test(target)) {
832
+ if (/^https?:/i.test(target)) externalLinks.push({ url: target, file, line });
833
+ continue;
834
+ }
706
835
  if (target.startsWith("#")) {
707
836
  const anchor2 = target.slice(1);
708
837
  if (anchor2 && !anchors.has(decodedFragment(anchor2))) {
@@ -747,6 +876,9 @@ async function runCheck(projectDir, options) {
747
876
  }
748
877
  checkPublicSpecs(projectDir, config.tabs, issues);
749
878
  checkUnpublishedOperationPages(projectDir, config.tabs, operationPages, issues);
879
+ if (options.external && externalLinks.length > 0) {
880
+ issues.push(...await checkExternalLinks(externalLinks, options.externalLinkOptions));
881
+ }
750
882
  const errors = issues.filter((i) => i.severity === "error");
751
883
  options.onIssues?.(issues);
752
884
  const warnings = issues.filter((i) => i.severity === "warning");
@@ -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-03.c42253fd.46afd8f2",
6
+ id: "2026-10-06.bc9ad197.e8e90bb6",
7
7
  source: {
8
8
  repository: "thallylabs/starter",
9
- commitSha: "c42253fd7fa988e8031caa508a500f66113b6d45",
10
- treeSha: "ae6491d255d3fc7e4fca83b383a9786d550d56b9",
11
- archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/c42253fd7fa988e8031caa508a500f66113b6d45",
9
+ commitSha: "bc9ad1979c58c4f4e75eb7a37d042ba4639a6025",
10
+ treeSha: "82f93eeb50bad3d14c2a31d658bf88b977cd41e8",
11
+ archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/bc9ad1979c58c4f4e75eb7a37d042ba4639a6025",
12
12
  manifestPath: "starter-release.json",
13
- manifestSha256: "4d84582a32cc6c58b48f3c29e4ecb1231347d7ee8599afe294d8880dc97c4f32"
13
+ manifestSha256: "b47a511f01e572e19c04353bb678de3fadaf69020224029156350967e07761b3"
14
14
  },
15
15
  runtime: {
16
16
  repository: "thallylabs/thally",
17
- commitSha: "46afd8f23a2768b4cf745d58eb64079207bcd2a4",
18
- treeSha: "4c3ccf148bff52cb523af663743ffaa98c634ab4",
17
+ commitSha: "e8e90bb6f50197bac47c061f553fa2365af7e1e9",
18
+ treeSha: "17ce7a8447626af290868fa9e2c644909493d98b",
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-03.c42253fd.46afd8f2",
30
+ source: {
31
+ repository: "thallylabs/starter",
32
+ commitSha: "c42253fd7fa988e8031caa508a500f66113b6d45",
33
+ treeSha: "ae6491d255d3fc7e4fca83b383a9786d550d56b9",
34
+ archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/c42253fd7fa988e8031caa508a500f66113b6d45",
35
+ manifestPath: "starter-release.json",
36
+ manifestSha256: "4d84582a32cc6c58b48f3c29e4ecb1231347d7ee8599afe294d8880dc97c4f32"
37
+ },
38
+ runtime: {
39
+ repository: "thallylabs/thally",
40
+ commitSha: "46afd8f23a2768b4cf745d58eb64079207bcd2a4",
41
+ treeSha: "4c3ccf148bff52cb523af663743ffaa98c634ab4",
42
+ contentSource: "assets",
43
+ identityContractVersion: 1
44
+ },
45
+ starterVersion: 2
46
+ },
27
47
  {
28
48
  schemaVersion: 1,
29
49
  id: "2026-10-02.644cbada.498675f5",
@@ -2,25 +2,26 @@
2
2
  import {
3
3
  personalizeStarter,
4
4
  updateEnvExample
5
- } from "./chunk-4VKRBJV2.js";
5
+ } from "./chunk-UG3OXVLE.js";
6
6
  import {
7
7
  resetTrackingConfig,
8
8
  writeTrackingConfig
9
9
  } from "./chunk-J7255UQA.js";
10
10
  import {
11
11
  downloadStarter
12
- } from "./chunk-WWIATVAD.js";
12
+ } from "./chunk-MXM4SK6B.js";
13
13
  import {
14
14
  terminal
15
15
  } from "./chunk-L2YCVQBR.js";
16
16
 
17
17
  // src/scaffold.ts
18
- import { existsSync, mkdirSync, readdirSync } from "fs";
18
+ import { existsSync as existsSync2, mkdirSync, readdirSync } from "fs";
19
19
  import { resolve as resolve2 } from "path";
20
20
 
21
21
  // src/utils.ts
22
22
  import { execFileSync, execSync, spawn } from "child_process";
23
- import { resolve } from "path";
23
+ import { existsSync, readFileSync } from "fs";
24
+ import { join, resolve } from "path";
24
25
  function slugify(name) {
25
26
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
26
27
  }
@@ -139,8 +140,21 @@ function quoteDirectory(directory) {
139
140
  const absolute = resolve(directory);
140
141
  return process.platform === "win32" ? `"${absolute.replace(/"/g, '""')}"` : `'${absolute.replace(/'/g, "'\\''")}'`;
141
142
  }
143
+ function declaresThallyCli(directory) {
144
+ try {
145
+ const manifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
146
+ return Boolean(manifest.devDependencies?.["@thallylabs/cli"] ?? manifest.dependencies?.["@thallylabs/cli"]);
147
+ } catch {
148
+ return false;
149
+ }
150
+ }
151
+ function devCommand(directory, needsInstall = false) {
152
+ const binary = join(directory, "node_modules", ".bin", "thally");
153
+ const hasLocalCli = needsInstall ? declaresThallyCli(directory) : existsSync(binary) || existsSync(`${binary}.cmd`);
154
+ return hasLocalCli ? "npx thally dev" : "npm run dev";
155
+ }
142
156
  function previewCommand(directory, needsInstall = false) {
143
- return `cd ${quoteDirectory(directory)} && ${needsInstall ? "npm install && " : ""}npm run dev`;
157
+ return `cd ${quoteDirectory(directory)} && ${needsInstall ? "npm install && " : ""}${devCommand(directory, needsInstall)}`;
144
158
  }
145
159
  function logo(version) {
146
160
  terminal.intro("init", version, "Build and publish docs for people and AI tools.");
@@ -166,13 +180,14 @@ async function scaffold(options) {
166
180
  brandPreset,
167
181
  repoUrl,
168
182
  doInstall,
183
+ continueOnInstallFailure = false,
169
184
  enableAiChat = true,
170
185
  i18nLocales,
171
186
  trackRepos,
172
187
  colors
173
188
  } = options;
174
189
  const targetDir = resolve2(projectDir);
175
- if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
190
+ if (existsSync2(targetDir) && readdirSync(targetDir).length > 0) {
176
191
  throw new Error(`Directory "${targetDir}" already exists and is not empty.`);
177
192
  }
178
193
  mkdirSync(targetDir, { recursive: true });
@@ -203,11 +218,19 @@ async function scaffold(options) {
203
218
  terminal.info("To finish wiring it: `thally track setup` (pick a trigger) + `thally agent init`,");
204
219
  terminal.info("then add your ANTHROPIC_API_KEY. See /guides/thally-track.");
205
220
  }
221
+ let dependenciesInstalled = false;
206
222
  if (doInstall) {
207
- await installDeps(targetDir);
223
+ try {
224
+ await installDeps(targetDir);
225
+ dependenciesInstalled = true;
226
+ } catch (error) {
227
+ const exitCode = error.exitCode;
228
+ if (!continueOnInstallFailure || exitCode === 130 || exitCode === 143) throw error;
229
+ terminal.warn('Dependency installation failed. The project was created; run "npm install" in it to finish.');
230
+ }
208
231
  }
209
232
  await initGit(targetDir);
210
- return { projectDir: targetDir };
233
+ return { projectDir: targetDir, dependenciesInstalled };
211
234
  }
212
235
 
213
236
  export {
@@ -8,10 +8,10 @@ import {
8
8
  installDeps,
9
9
  runProjectCommand,
10
10
  scaffold
11
- } from "./chunk-KGKSB3PU.js";
11
+ } from "./chunk-BFPU5AAU.js";
12
12
  import {
13
13
  pruneMissingSiteLinks
14
- } from "./chunk-4VKRBJV2.js";
14
+ } from "./chunk-UG3OXVLE.js";
15
15
  import {
16
16
  terminal
17
17
  } from "./chunk-L2YCVQBR.js";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readStarterReleaseManifest
4
- } from "./chunk-RI56XMBI.js";
4
+ } from "./chunk-Z4NA6RK6.js";
5
5
  import {
6
6
  STABLE_SCAFFOLD_RELEASE
7
- } from "./chunk-BP2REWTO.js";
7
+ } from "./chunk-46CFQVEL.js";
8
8
  import {
9
9
  terminal
10
10
  } from "./chunk-L2YCVQBR.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/customize.ts
4
- import { cpSync, existsSync, readFileSync, writeFileSync } from "fs";
4
+ import { existsSync, readFileSync, writeFileSync } from "fs";
5
5
  import { join } from "path";
6
6
  function readJsonObject(filePath, label) {
7
7
  let value;
@@ -203,11 +203,16 @@ function updateCloudflareRuntimeName(targetDir, packageName) {
203
203
  );
204
204
  writeFileSync(configPath, updated, "utf8");
205
205
  }
206
+ var SITE_URL_ASSIGNMENT = /^([ \t]*)((?:THALLY|DOX|NEXT_PUBLIC)_SITE_URL[ \t]*=[ \t]*["']?(?:https?:\/\/)?(?:localhost|127\.0\.0\.1|\[::1\]|0\.0\.0\.0)(?:[:/][^\r\n]*)?)$/gm;
207
+ function disableLoopbackSiteUrl(source) {
208
+ return source.replace(SITE_URL_ASSIGNMENT, "$1# $2");
209
+ }
206
210
  function updateEnvExample(targetDir) {
207
211
  const envFile = join(targetDir, ".env.example");
208
212
  if (!existsSync(envFile)) return;
209
213
  const envLocal = join(targetDir, ".env.local");
210
- if (!existsSync(envLocal)) cpSync(envFile, envLocal);
214
+ if (existsSync(envLocal)) return;
215
+ writeFileSync(envLocal, disableLoopbackSiteUrl(readFileSync(envFile, "utf8")), "utf8");
211
216
  }
212
217
  function personalizeStarter(targetDir, options) {
213
218
  updateStarterDocsConfig(
@@ -234,6 +239,7 @@ export {
234
239
  pruneMissingSiteLinks,
235
240
  updatePackageIdentity,
236
241
  updateCloudflareRuntimeName,
242
+ disableLoopbackSiteUrl,
237
243
  updateEnvExample,
238
244
  personalizeStarter
239
245
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  STABLE_SCAFFOLD_RELEASE
4
- } from "./chunk-BP2REWTO.js";
4
+ } from "./chunk-46CFQVEL.js";
5
5
 
6
6
  // src/starter-sync.ts
7
7
  import { createHash } from "crypto";
@@ -46,9 +46,18 @@ declare function pruneMissingSiteLinks(targetDir: string, pageIds: ReadonlySet<s
46
46
  declare function updatePackageIdentity(targetDir: string, packageName: string): void;
47
47
  /** Update the provider-portable Worker name without recreating its config. */
48
48
  declare function updateCloudflareRuntimeName(targetDir: string, packageName: string): void;
49
+ /**
50
+ * Comment out loopback site-URL assignments in a local environment file.
51
+ *
52
+ * The runtime already defaults to the local URL in development. An explicit
53
+ * `localhost` value is harmful instead: Next.js loads `.env.local` during
54
+ * production builds too, so a build or deploy from this checkout would bake
55
+ * dead `localhost` links into llms.txt, sitemap.xml, and JSON-LD.
56
+ */
57
+ declare function disableLoopbackSiteUrl(source: string): string;
49
58
  /** Copy the canonical environment guide to the ignored local filename. */
50
59
  declare function updateEnvExample(targetDir: string): void;
51
60
  /** Apply the complete, intentionally narrow owner-personalization contract. */
52
61
  declare function personalizeStarter(targetDir: string, options: StarterPersonalizationOptions): void;
53
62
 
54
- export { type StarterLocale, type StarterPersonalizationOptions, personalizeStarter, pruneMissingSiteLinks, updateCloudflareRuntimeName, updateEnvExample, updatePackageIdentity, updateSiteConfig, updateStarterDocsConfig };
63
+ export { type StarterLocale, type StarterPersonalizationOptions, disableLoopbackSiteUrl, personalizeStarter, pruneMissingSiteLinks, updateCloudflareRuntimeName, updateEnvExample, updatePackageIdentity, updateSiteConfig, updateStarterDocsConfig };
package/dist/customize.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ disableLoopbackSiteUrl,
3
4
  personalizeStarter,
4
5
  pruneMissingSiteLinks,
5
6
  updateCloudflareRuntimeName,
@@ -7,8 +8,9 @@ import {
7
8
  updatePackageIdentity,
8
9
  updateSiteConfig,
9
10
  updateStarterDocsConfig
10
- } from "./chunk-4VKRBJV2.js";
11
+ } from "./chunk-UG3OXVLE.js";
11
12
  export {
13
+ disableLoopbackSiteUrl,
12
14
  personalizeStarter,
13
15
  pruneMissingSiteLinks,
14
16
  updateCloudflareRuntimeName,
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  migrateDocs
4
- } from "./chunk-VGE5L66Z.js";
4
+ } from "./chunk-KNW5K72A.js";
5
5
  import {
6
6
  parseFrontmatter,
7
7
  runCheck
8
- } from "./chunk-DIX4EUI4.js";
8
+ } from "./chunk-2UHO52SA.js";
9
9
  import "./chunk-KDLP27DS.js";
10
10
  import {
11
11
  logo,
@@ -13,12 +13,12 @@ import {
13
13
  scaffold,
14
14
  slugify,
15
15
  success
16
- } from "./chunk-KGKSB3PU.js";
17
- import "./chunk-4VKRBJV2.js";
16
+ } from "./chunk-BFPU5AAU.js";
17
+ import "./chunk-UG3OXVLE.js";
18
18
  import "./chunk-J7255UQA.js";
19
- import "./chunk-WWIATVAD.js";
20
- import "./chunk-RI56XMBI.js";
21
- import "./chunk-BP2REWTO.js";
19
+ import "./chunk-MXM4SK6B.js";
20
+ import "./chunk-Z4NA6RK6.js";
21
+ import "./chunk-46CFQVEL.js";
22
22
  import {
23
23
  acquireTerminalAccent,
24
24
  formatHelp,
@@ -191,19 +191,8 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
191
191
  message: " Project name:",
192
192
  default: defaultName
193
193
  });
194
- const defaultDesc = `Documentation for ${projectName}.`;
195
- const description = useDefaults ? defaultDesc : await input2({
196
- message: " Description:",
197
- default: defaultDesc
198
- });
199
- const brandPreset = useDefaults ? "primary" : await select3({
200
- message: " Brand preset:",
201
- choices: [
202
- { name: "primary", value: "primary" },
203
- { name: "secondary", value: "secondary" }
204
- ],
205
- default: "primary"
206
- });
194
+ const description = `Documentation for ${projectName}.`;
195
+ const brandPreset = "primary";
207
196
  const repoUrl = useDefaults ? "" : await input2({
208
197
  message: " GitHub repo URL (optional):",
209
198
  default: ""
@@ -224,13 +213,12 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
224
213
  else console.log(" \u26A0 No valid owner/repo entries \u2014 skipping Track (add it later with `thally track add`).");
225
214
  }
226
215
  }
227
- let doInstall = installPreference ?? false;
216
+ let doInstall = installPreference ?? true;
228
217
  if (!useDefaults && installPreference === void 0) {
229
- const shouldInstall = await input2({
230
- message: " Install dependencies now? (y/N):",
231
- default: "N"
218
+ doInstall = await confirm3({
219
+ message: " Install dependencies now?",
220
+ default: true
232
221
  });
233
- doInstall = shouldInstall.trim().toLowerCase().startsWith("y");
234
222
  }
235
223
  let i18nLocales;
236
224
  if (!useDefaults) {
@@ -735,8 +723,8 @@ Commands:
735
723
 
736
724
  Scaffold options:
737
725
  -y, --yes Accept defaults and skip interactive prompts
738
- --install Install project dependencies after scaffolding
739
- --no-install Skip dependency installation without prompting
726
+ --install Install project dependencies (the default)
727
+ --no-install Skip dependency installation
740
728
  --verbose Show detailed installation and Git output
741
729
  -h, --help Show this help
742
730
  -v, -V, --version Show the installed version
@@ -769,7 +757,7 @@ Usage:
769
757
  Options:
770
758
  --fix Add orphan pages to navigation when possible
771
759
  --ci Use CI-oriented validation behavior
772
- --external Check external links
760
+ --external Also check external links (public hosts only; warnings, never errors)
773
761
  --drift Check documentation provenance and freshness
774
762
  -h, --help Show this help
775
763
  `,
@@ -930,10 +918,12 @@ async function runScaffoldCommand() {
930
918
  brandPreset: answers.brandPreset,
931
919
  repoUrl: answers.repoUrl,
932
920
  doInstall: answers.doInstall,
921
+ continueOnInstallFailure: true,
933
922
  i18nLocales: answers.i18nLocales,
934
923
  trackRepos: answers.trackRepos
935
924
  });
936
- success(result.projectDir, answers.projectName, answers.doInstall);
925
+ success(result.projectDir, answers.projectName, result.dependenciesInstalled);
926
+ if (answers.doInstall && !result.dependenciesInstalled) process.exitCode = 1;
937
927
  }
938
928
  async function runCheckCommand() {
939
929
  const projectDir = resolve2(positional[1] ?? ".");
@@ -5,6 +5,7 @@ import { MigrationPlatform, MigrationFetcher, SourceRefMapping, MigrationBundle,
5
5
  * serve, including locale fallback and literal redirect chains. Validation is
6
6
  * read-only unless the caller explicitly enables orphan-navigation fixes.
7
7
  */
8
+
8
9
  interface LintIssue {
9
10
  severity: 'error' | 'warning';
10
11
  message: string;
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  migrateDocs
4
- } from "../chunk-VGE5L66Z.js";
4
+ } from "../chunk-KNW5K72A.js";
5
5
  import "../chunk-KDLP27DS.js";
6
- import "../chunk-KGKSB3PU.js";
7
- import "../chunk-4VKRBJV2.js";
6
+ import "../chunk-BFPU5AAU.js";
7
+ import "../chunk-UG3OXVLE.js";
8
8
  import "../chunk-J7255UQA.js";
9
- import "../chunk-WWIATVAD.js";
10
- import "../chunk-RI56XMBI.js";
11
- import "../chunk-BP2REWTO.js";
9
+ import "../chunk-MXM4SK6B.js";
10
+ import "../chunk-Z4NA6RK6.js";
11
+ import "../chunk-46CFQVEL.js";
12
12
  import "../chunk-L2YCVQBR.js";
13
13
  export {
14
14
  migrateDocs
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCheck
4
- } from "./chunk-DIX4EUI4.js";
4
+ } from "./chunk-2UHO52SA.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-BP2REWTO.js";
6
+ } from "./chunk-46CFQVEL.js";
7
7
  export {
8
8
  STABLE_SCAFFOLD_RELEASE,
9
9
  SUPPORTED_SCAFFOLD_RELEASES,
@@ -39,6 +39,12 @@ interface ScaffoldOptions {
39
39
  brandPreset: string;
40
40
  repoUrl: string;
41
41
  doInstall: boolean;
42
+ /**
43
+ * Report a failed install and still finish the project (Git setup included)
44
+ * instead of throwing. Interactive creation opts in; library callers keep
45
+ * the historical fail-fast contract.
46
+ */
47
+ continueOnInstallFailure?: boolean;
42
48
  enableAiChat?: boolean;
43
49
  i18nLocales?: Array<{
44
50
  code: string;
@@ -58,6 +64,8 @@ interface ScaffoldOptions {
58
64
  }
59
65
  interface ScaffoldResult {
60
66
  projectDir: string;
67
+ /** False when installation was skipped or failed; the project is still usable. */
68
+ dependenciesInstalled: boolean;
61
69
  }
62
70
  /** Download, personalize, and optionally install a new documentation project. */
63
71
  declare function scaffold(options: ScaffoldOptions): Promise<ScaffoldResult>;
package/dist/scaffold.js CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  scaffold
4
- } from "./chunk-KGKSB3PU.js";
5
- import "./chunk-4VKRBJV2.js";
4
+ } from "./chunk-BFPU5AAU.js";
5
+ import "./chunk-UG3OXVLE.js";
6
6
  import "./chunk-J7255UQA.js";
7
7
  import {
8
8
  STARTER_ARCHIVE_ROOT,
9
9
  STARTER_COMMIT_SHA,
10
10
  STARTER_REPOSITORY,
11
11
  validateStarterArchiveEntry
12
- } from "./chunk-WWIATVAD.js";
13
- import "./chunk-RI56XMBI.js";
12
+ } from "./chunk-MXM4SK6B.js";
13
+ import "./chunk-Z4NA6RK6.js";
14
14
  import {
15
15
  STABLE_SCAFFOLD_RELEASE
16
- } from "./chunk-BP2REWTO.js";
16
+ } from "./chunk-46CFQVEL.js";
17
17
  import "./chunk-L2YCVQBR.js";
18
18
  export {
19
19
  STABLE_SCAFFOLD_RELEASE,
@@ -8,8 +8,8 @@ import {
8
8
  planStarterRuntimeSync,
9
9
  readStarterReleaseManifest,
10
10
  starterManifestSha256
11
- } from "./chunk-RI56XMBI.js";
12
- import "./chunk-BP2REWTO.js";
11
+ } from "./chunk-Z4NA6RK6.js";
12
+ import "./chunk-46CFQVEL.js";
13
13
  export {
14
14
  applyStarterRuntimeSyncPlan,
15
15
  classifyStarterPath,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  downloadStarter
4
- } from "./chunk-WWIATVAD.js";
4
+ } from "./chunk-MXM4SK6B.js";
5
5
  import {
6
6
  applyStarterRuntimeSyncPlan,
7
7
  mergeStarterOwnershipContracts,
@@ -9,11 +9,11 @@ import {
9
9
  planStarterRuntimeSync,
10
10
  readStarterReleaseManifest,
11
11
  starterManifestSha256
12
- } from "./chunk-RI56XMBI.js";
12
+ } from "./chunk-Z4NA6RK6.js";
13
13
  import {
14
14
  STABLE_SCAFFOLD_RELEASE,
15
15
  SUPPORTED_SCAFFOLD_RELEASES
16
- } from "./chunk-BP2REWTO.js";
16
+ } from "./chunk-46CFQVEL.js";
17
17
  import "./chunk-L2YCVQBR.js";
18
18
 
19
19
  // src/starter-update.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-thally-docs",
3
- "version": "0.10.80",
3
+ "version": "0.10.82",
4
4
  "description": "Scaffold an open-source Thally docs site for people, search engines, and AI tools.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -49,8 +49,8 @@
49
49
  "@anthropic-ai/sdk": "^0.36.0",
50
50
  "@clack/prompts": "^0.11.0",
51
51
  "@inquirer/prompts": "^7.0.0",
52
- "@thallylabs/core": "0.2.11",
53
- "@thallylabs/migrate": "0.2.25",
52
+ "@thallylabs/core": "0.2.12",
53
+ "@thallylabs/migrate": "0.2.26",
54
54
  "p-limit": "^6.1.0",
55
55
  "remark-mdx": "^3.1.1",
56
56
  "remark-parse": "^11.0.0",