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 +19 -9
- package/dist/{chunk-DIX4EUI4.js → chunk-2UHO52SA.js} +133 -1
- package/dist/{chunk-BP2REWTO.js → chunk-46CFQVEL.js} +27 -7
- package/dist/{chunk-KGKSB3PU.js → chunk-BFPU5AAU.js} +31 -8
- package/dist/{chunk-VGE5L66Z.js → chunk-KNW5K72A.js} +2 -2
- package/dist/{chunk-WWIATVAD.js → chunk-MXM4SK6B.js} +2 -2
- package/dist/{chunk-4VKRBJV2.js → chunk-UG3OXVLE.js} +8 -2
- package/dist/{chunk-RI56XMBI.js → chunk-Z4NA6RK6.js} +1 -1
- package/dist/customize.d.ts +10 -1
- package/dist/customize.js +3 -1
- package/dist/index.js +19 -29
- package/dist/migrate/index.d.ts +1 -0
- package/dist/migrate/index.js +6 -6
- package/dist/migration-worker.js +1 -1
- package/dist/release.js +1 -1
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.js +5 -5
- package/dist/starter-sync.js +2 -2
- package/dist/starter-update.js +3 -3
- package/package.json +3 -3
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
|
-
|
|
29
|
-
|
|
30
|
-
interactive
|
|
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 --
|
|
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
|
|
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`,
|
|
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))
|
|
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-
|
|
6
|
+
id: "2026-10-06.bc9ad197.e8e90bb6",
|
|
7
7
|
source: {
|
|
8
8
|
repository: "thallylabs/starter",
|
|
9
|
-
commitSha: "
|
|
10
|
-
treeSha: "
|
|
11
|
-
archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/
|
|
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: "
|
|
13
|
+
manifestSha256: "b47a511f01e572e19c04353bb678de3fadaf69020224029156350967e07761b3"
|
|
14
14
|
},
|
|
15
15
|
runtime: {
|
|
16
16
|
repository: "thallylabs/thally",
|
|
17
|
-
commitSha: "
|
|
18
|
-
treeSha: "
|
|
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-
|
|
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-
|
|
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 {
|
|
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 && " : ""}
|
|
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 (
|
|
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
|
-
|
|
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-
|
|
11
|
+
} from "./chunk-BFPU5AAU.js";
|
|
12
12
|
import {
|
|
13
13
|
pruneMissingSiteLinks
|
|
14
|
-
} from "./chunk-
|
|
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-
|
|
4
|
+
} from "./chunk-Z4NA6RK6.js";
|
|
5
5
|
import {
|
|
6
6
|
STABLE_SCAFFOLD_RELEASE
|
|
7
|
-
} from "./chunk-
|
|
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 {
|
|
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 (
|
|
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
|
};
|
package/dist/customize.d.ts
CHANGED
|
@@ -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-
|
|
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-
|
|
4
|
+
} from "./chunk-KNW5K72A.js";
|
|
5
5
|
import {
|
|
6
6
|
parseFrontmatter,
|
|
7
7
|
runCheck
|
|
8
|
-
} from "./chunk-
|
|
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-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-BFPU5AAU.js";
|
|
17
|
+
import "./chunk-UG3OXVLE.js";
|
|
18
18
|
import "./chunk-J7255UQA.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
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
|
|
195
|
-
const
|
|
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 ??
|
|
216
|
+
let doInstall = installPreference ?? true;
|
|
228
217
|
if (!useDefaults && installPreference === void 0) {
|
|
229
|
-
|
|
230
|
-
message: " Install dependencies now?
|
|
231
|
-
default:
|
|
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
|
|
739
|
-
--no-install Skip dependency installation
|
|
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
|
|
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,
|
|
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] ?? ".");
|
package/dist/migrate/index.d.ts
CHANGED
|
@@ -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;
|
package/dist/migrate/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
migrateDocs
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-KNW5K72A.js";
|
|
5
5
|
import "../chunk-KDLP27DS.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-BFPU5AAU.js";
|
|
7
|
+
import "../chunk-UG3OXVLE.js";
|
|
8
8
|
import "../chunk-J7255UQA.js";
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
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
|
package/dist/migration-worker.js
CHANGED
package/dist/release.js
CHANGED
package/dist/scaffold.d.ts
CHANGED
|
@@ -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-
|
|
5
|
-
import "./chunk-
|
|
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-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-MXM4SK6B.js";
|
|
13
|
+
import "./chunk-Z4NA6RK6.js";
|
|
14
14
|
import {
|
|
15
15
|
STABLE_SCAFFOLD_RELEASE
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-46CFQVEL.js";
|
|
17
17
|
import "./chunk-L2YCVQBR.js";
|
|
18
18
|
export {
|
|
19
19
|
STABLE_SCAFFOLD_RELEASE,
|
package/dist/starter-sync.js
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
planStarterRuntimeSync,
|
|
9
9
|
readStarterReleaseManifest,
|
|
10
10
|
starterManifestSha256
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-Z4NA6RK6.js";
|
|
12
|
+
import "./chunk-46CFQVEL.js";
|
|
13
13
|
export {
|
|
14
14
|
applyStarterRuntimeSyncPlan,
|
|
15
15
|
classifyStarterPath,
|
package/dist/starter-update.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
downloadStarter
|
|
4
|
-
} from "./chunk-
|
|
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-
|
|
12
|
+
} from "./chunk-Z4NA6RK6.js";
|
|
13
13
|
import {
|
|
14
14
|
STABLE_SCAFFOLD_RELEASE,
|
|
15
15
|
SUPPORTED_SCAFFOLD_RELEASES
|
|
16
|
-
} from "./chunk-
|
|
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.
|
|
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.
|
|
53
|
-
"@thallylabs/migrate": "0.2.
|
|
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",
|