create-thally-docs 0.7.1 → 0.7.3

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/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  migrateDocs,
4
4
  parseGitHubUrl
5
- } from "./chunk-YAG5BZEM.js";
5
+ } from "./chunk-PGTE4X3G.js";
6
6
  import {
7
7
  logo,
8
8
  readDocsJson,
@@ -10,7 +10,7 @@ import {
10
10
  slugify,
11
11
  success,
12
12
  writeDocsJson
13
- } from "./chunk-6HO6ECUE.js";
13
+ } from "./chunk-WMVWYKAB.js";
14
14
 
15
15
  // src/index.ts
16
16
  import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
@@ -20,7 +20,7 @@ import { resolve as resolve2 } from "path";
20
20
  import { input, select } from "@inquirer/prompts";
21
21
  import { basename } from "path";
22
22
  import { resolve } from "path";
23
- async function gatherAnswers(dirArg, useDefaults) {
23
+ async function gatherAnswers(dirArg, useDefaults, installPreference) {
24
24
  let projectDir;
25
25
  if (dirArg) {
26
26
  projectDir = resolve(dirArg);
@@ -71,13 +71,13 @@ async function gatherAnswers(dirArg, useDefaults) {
71
71
  else console.log(" \u26A0 No valid owner/repo entries \u2014 skipping Track (add it later with `thally track add`).");
72
72
  }
73
73
  }
74
- let doInstall = true;
75
- if (!useDefaults) {
74
+ let doInstall = installPreference ?? false;
75
+ if (!useDefaults && installPreference === void 0) {
76
76
  const shouldInstall = await input({
77
- message: " Install dependencies? (Y/n):",
78
- default: "Y"
77
+ message: " Install dependencies now? (y/N):",
78
+ default: "N"
79
79
  });
80
- doInstall = shouldInstall.toLowerCase() !== "n";
80
+ doInstall = shouldInstall.trim().toLowerCase().startsWith("y");
81
81
  }
82
82
  let i18nLocales;
83
83
  if (!useDefaults) {
@@ -240,8 +240,17 @@ function extractLinks(content) {
240
240
  }
241
241
  return links;
242
242
  }
243
- function pageIdToPath(pageId) {
244
- return pageId === "introduction" ? "/" : `/${pageId}`;
243
+ function localizedPage(pageId, secondaryLocales) {
244
+ const [first, ...rest] = pageId.split("/");
245
+ if (secondaryLocales.has(first) && rest.length > 0) {
246
+ return { navPageId: rest.join("/"), locale: first };
247
+ }
248
+ return { navPageId: pageId };
249
+ }
250
+ function pageIdToPath(pageId, secondaryLocales) {
251
+ const { navPageId, locale } = localizedPage(pageId, secondaryLocales);
252
+ const basePath = navPageId === "introduction" ? "/" : `/${navPageId}`;
253
+ return locale ? `/${locale}${basePath === "/" ? "" : basePath}` : basePath;
245
254
  }
246
255
  function validateOpenApi(projectDir, source, issues) {
247
256
  const specPath = join(projectDir, source);
@@ -292,6 +301,13 @@ async function runCheck(projectDir, options) {
292
301
  const contentDir = join(projectDir, "src", "content");
293
302
  const issues = [];
294
303
  const config = readDocsJson(projectDir);
304
+ const secondaryLocales = new Set(
305
+ (config.i18n?.locales ?? []).map((locale) => locale.code).filter((code) => code !== config.i18n?.defaultLocale)
306
+ );
307
+ const generatedApiPaths = /* @__PURE__ */ new Set([
308
+ "/api",
309
+ ...Array.from(secondaryLocales, (locale) => `/${locale}/api`)
310
+ ]);
295
311
  const navPageIds = /* @__PURE__ */ new Set();
296
312
  const duplicates = /* @__PURE__ */ new Set();
297
313
  for (const tab of config.tabs) {
@@ -324,7 +340,8 @@ async function runCheck(projectDir, options) {
324
340
  for (const filePath of allFiles) {
325
341
  const rel = filePath.slice(contentDir.length + 1).replace(/\.mdx$/, "").replace(/\\/g, "/");
326
342
  const pageId = rel.endsWith("/index") ? rel.slice(0, -6) : rel;
327
- if (!navPageIds.has(pageId)) {
343
+ const { navPageId } = localizedPage(pageId, secondaryLocales);
344
+ if (!navPageIds.has(navPageId)) {
328
345
  if (fix) {
329
346
  addOrphanToNav(projectDir, pageId);
330
347
  fixedOrphans.push(pageId);
@@ -350,7 +367,7 @@ async function runCheck(projectDir, options) {
350
367
  if (!data.description) issues.push({ severity: "warning", message: `Missing "description" in frontmatter`, file: rel2 });
351
368
  if (content.trim().length < 50) issues.push({ severity: "warning", message: `Very short body (${content.trim().length} chars) \u2014 page may be empty`, file: rel2 });
352
369
  if (options.drift) checkDrift(projectDir, rel2, data, issues);
353
- const path = pageIdToPath(pageId);
370
+ const path = pageIdToPath(pageId, secondaryLocales);
354
371
  const anchors = extractHeadingAnchors(content);
355
372
  validPaths.add(path);
356
373
  anchorsByPath.set(path, anchors);
@@ -371,7 +388,10 @@ async function runCheck(projectDir, options) {
371
388
  const [beforeHash, anchor] = target.split("#");
372
389
  let path = beforeHash.split("?")[0];
373
390
  if (path.length > 1) path = path.replace(/\/$/, "");
374
- if (path.startsWith("/api") || path.startsWith("/_next") || /\.[a-z0-9]+$/i.test(path)) continue;
391
+ const isGeneratedApiPath = Array.from(generatedApiPaths).some(
392
+ (prefix) => path === prefix || path.startsWith(`${prefix}/`)
393
+ );
394
+ if (isGeneratedApiPath || path.startsWith("/_next") || /\.[a-z0-9]+$/i.test(path)) continue;
375
395
  if (!validPaths.has(path)) {
376
396
  issues.push({ severity: "error", message: `Broken link: "${target}" \u2014 no page at "${path}"`, file, line });
377
397
  } else if (anchor && !anchorsByPath.get(path)?.has(anchor)) {
@@ -662,10 +682,19 @@ async function runTranslateCommand(locale, pages, force, apiKey, model, yes, pro
662
682
  // src/index.ts
663
683
  var args = process.argv.slice(2);
664
684
  var flags = args.filter((a) => a.startsWith("-"));
685
+ var valueFlags = /* @__PURE__ */ new Set([
686
+ "--api-key",
687
+ "--branch",
688
+ "--docs-dir",
689
+ "--into",
690
+ "--locale",
691
+ "--model",
692
+ "--pages"
693
+ ]);
665
694
  var positional = [];
666
695
  for (let i = 0; i < args.length; i++) {
667
696
  if (args[i].startsWith("-")) {
668
- if (i + 1 < args.length && !args[i + 1].startsWith("-")) {
697
+ if (valueFlags.has(args[i]) && i + 1 < args.length && !args[i + 1].startsWith("-")) {
669
698
  i++;
670
699
  }
671
700
  } else {
@@ -734,6 +763,7 @@ async function runMigrateCommand() {
734
763
  }
735
764
  async function runScaffoldCommand() {
736
765
  const useDefaults = flags.includes("--yes") || flags.includes("-y");
766
+ const installPreference = flags.includes("--install") ? true : flags.includes("--no-install") ? false : void 0;
737
767
  const dirArg = positional[0];
738
768
  if (dirArg) {
739
769
  const resolved = resolve2(dirArg);
@@ -743,7 +773,7 @@ async function runScaffoldCommand() {
743
773
  process.exit(1);
744
774
  }
745
775
  }
746
- const answers = await gatherAnswers(dirArg, useDefaults);
776
+ const answers = await gatherAnswers(dirArg, useDefaults, installPreference);
747
777
  const result = await scaffold({
748
778
  projectDir: answers.projectDir,
749
779
  projectName: answers.projectName,
@@ -754,7 +784,7 @@ async function runScaffoldCommand() {
754
784
  i18nLocales: answers.i18nLocales,
755
785
  trackRepos: answers.trackRepos
756
786
  });
757
- success(result.projectDir, answers.projectName);
787
+ success(result.projectDir, answers.projectName, answers.doInstall);
758
788
  }
759
789
  async function runCheckCommand() {
760
790
  const projectDir = resolve2(positional[1] ?? ".");
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  migrateDocs
4
- } from "../chunk-YAG5BZEM.js";
5
- import "../chunk-6HO6ECUE.js";
4
+ } from "../chunk-PGTE4X3G.js";
5
+ import "../chunk-WMVWYKAB.js";
6
6
  export {
7
7
  migrateDocs
8
8
  };
package/dist/scaffold.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  scaffold
4
- } from "./chunk-6HO6ECUE.js";
4
+ } from "./chunk-WMVWYKAB.js";
5
5
  export {
6
6
  scaffold
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-thally-docs",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Scaffold a new Thally documentation project",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,454 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/scaffold.ts
4
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "fs";
5
- import { resolve } from "path";
6
-
7
- // src/download.ts
8
- import { Readable, pipeline } from "stream";
9
- import { promisify } from "util";
10
- import tar from "tar";
11
- var pipelineAsync = promisify(pipeline);
12
- var TARBALL_URL = "https://codeload.github.com/thallylabs/thally/tar.gz/main";
13
- var EXCLUDE_PATHS = [
14
- "/cli/",
15
- "/packages/",
16
- "/node_modules/",
17
- "/.git/",
18
- "/thally-agent.yml",
19
- "/thally-track.yml",
20
- "/CODEOWNERS",
21
- "/CLAUDE.md",
22
- "/notes/"
23
- ];
24
- function shouldInclude(path) {
25
- for (const excluded of EXCLUDE_PATHS) {
26
- if (path.includes(excluded)) {
27
- return false;
28
- }
29
- }
30
- return true;
31
- }
32
- async function downloadTemplate(targetDir, siteName) {
33
- console.log("");
34
- console.log(` \u23F3 Creating ${siteName?.trim() || "your docs site"}...`);
35
- const response = await fetch(TARBALL_URL);
36
- if (!response.ok) {
37
- throw new Error(`Failed to download template: ${response.status} ${response.statusText}`);
38
- }
39
- if (!response.body) {
40
- throw new Error("Response body is empty");
41
- }
42
- const nodeStream = Readable.fromWeb(response.body);
43
- await pipelineAsync(
44
- nodeStream,
45
- tar.extract({ cwd: targetDir, strip: 1, filter: shouldInclude })
46
- );
47
- }
48
-
49
- // src/docs-json.ts
50
- import { readFileSync, writeFileSync } from "fs";
51
- import { join } from "path";
52
- function readDocsJson(projectDir) {
53
- const docsPath = join(projectDir, "docs.json");
54
- const raw = readFileSync(docsPath, "utf8");
55
- return JSON.parse(raw);
56
- }
57
- function writeDocsJson(projectDir, config) {
58
- const docsPath = join(projectDir, "docs.json");
59
- writeFileSync(docsPath, JSON.stringify(config, null, 2) + "\n", "utf8");
60
- }
61
- function resetTrackingConfig(projectDir) {
62
- const config = readDocsJson(projectDir);
63
- if (config.tracking) {
64
- delete config.tracking;
65
- writeDocsJson(projectDir, config);
66
- }
67
- }
68
- function writeTrackingConfig(projectDir, repos) {
69
- if (repos.length === 0) return;
70
- const config = readDocsJson(projectDir);
71
- config.tracking = { repos: repos.map((r) => ({ owner: r.owner, repo: r.repo, branch: "main" })) };
72
- writeDocsJson(projectDir, config);
73
- }
74
-
75
- // src/customize.ts
76
- import { existsSync, mkdirSync, writeFileSync as writeFileSync2, readFileSync as readFileSync2, readdirSync, cpSync, rmSync } from "fs";
77
- import { join as join2 } from "path";
78
- import { execSync } from "child_process";
79
- var STARTER_PAGES = {
80
- "introduction.mdx": `---
81
- title: Introduction
82
- description: Welcome to {NAME} \u2014 learn what it does, how the docs are organized, and where to start.
83
- keywords:
84
- - {NAME}
85
- - documentation
86
- - overview
87
- - getting started
88
- ---
89
-
90
- ## Welcome
91
-
92
- Welcome to the **{NAME}** documentation. This is your home base for guides, API
93
- references, and everything you need to build with {NAME}. The site is powered by
94
- [Thally](https://github.com/thallylabs/thally), an agent-native docs platform \u2014 every page
95
- is served to humans as polished HTML and to AI agents as structured JSON, JSON-LD,
96
- and Markdown from the same URL, so assistants can read your docs accurately.
97
-
98
- ## What you'll find here
99
-
100
- - **Guides** \u2014 step-by-step walkthroughs of common tasks and workflows.
101
- - **API reference** \u2014 generated from your OpenAPI spec, with a live "Try It" console.
102
- - **Quickstart** \u2014 install {NAME} and make your first call in a few minutes.
103
-
104
- ## Next steps
105
-
106
- Start with the [Quickstart](/quickstart) to get {NAME} running, then make this site
107
- your own by editing \`src/content/introduction.mdx\` and updating the navigation in
108
- \`docs.json\`. Every change you save is instantly reflected for both readers and agents.
109
- `,
110
- "quickstart.mdx": `---
111
- title: Quickstart
112
- description: Install {NAME}, configure your API key, and make your first call in under five minutes.
113
- keywords:
114
- - {NAME}
115
- - quickstart
116
- - installation
117
- - getting started
118
- ---
119
-
120
- ## Installation
121
-
122
- Install {NAME} with your package manager of choice. We recommend pinning the
123
- version in your project so builds stay reproducible across machines and CI:
124
-
125
- \`\`\`bash
126
- npm install {SLUG}
127
- \`\`\`
128
-
129
- ## Basic usage
130
-
131
- Import the client and initialize it with your API key. Keep the key in an
132
- environment variable rather than committing it to source control, so it never
133
- leaks into your repository or build logs:
134
-
135
- \`\`\`ts
136
- import { create } from '{SLUG}'
137
-
138
- const client = create({ apiKey: process.env.API_KEY })
139
- \`\`\`
140
-
141
- ## What's next
142
-
143
- That's the basics \u2014 you're ready to build. Explore the guides for common workflows,
144
- open the API reference to try endpoints against a live "Try It" console, or edit this
145
- page at \`src/content/quickstart.mdx\` to document your own onboarding flow.
146
- `,
147
- "changelog.mdx": `---
148
- title: Changelog
149
- description: Notable changes, releases, and improvements to {NAME}.
150
- keywords:
151
- - {NAME}
152
- - changelog
153
- - releases
154
- - updates
155
- ---
156
-
157
- ## v0.1.0
158
-
159
- The first release of your **{NAME}** documentation.
160
-
161
- - Initial docs site scaffolded with [Thally](https://github.com/thallylabs/thally)
162
- - Agent-ready endpoints live: \`/llms.txt\`, \`/ai.txt\`, \`/api/docs-index\`, and \`/api/agent-readiness\`
163
- - Starter guides in the Overview tab and an interactive API reference
164
-
165
- Edit this page at \`src/content/changelog.mdx\` to announce your own releases as you ship.
166
- `
167
- };
168
- function buildStarterDocsJson({
169
- enableAiChat,
170
- repoUrl,
171
- i18nLocales
172
- }) {
173
- const config = {};
174
- config.theme = "sharp";
175
- config.fonts = {
176
- body: { family: "Plus Jakarta Sans", weight: ["400", "500", "600", "700"] },
177
- heading: { family: "Outfit", weight: ["600", "700"] }
178
- };
179
- if (enableAiChat) {
180
- config.ai = { chat: true };
181
- }
182
- if (repoUrl) {
183
- config.navbar = {
184
- links: [{ label: "GitHub", href: repoUrl, type: "github" }],
185
- primary: { label: "Get started", href: "/quickstart" }
186
- };
187
- }
188
- if (i18nLocales && i18nLocales.length > 0) {
189
- config.i18n = {
190
- defaultLocale: "en",
191
- locales: [{ code: "en", label: "English" }, ...i18nLocales]
192
- };
193
- }
194
- config.tabs = [
195
- {
196
- tab: "Overview",
197
- groups: [{ group: "Getting Started", pages: ["introduction", "quickstart"] }]
198
- },
199
- { tab: "API Reference", api: { source: "openapi.yaml" } },
200
- { tab: "Changelog", href: "/changelog" }
201
- ];
202
- return JSON.stringify(config, null, 2) + "\n";
203
- }
204
- function writeStarterContent(targetDir, projectName, slug, enableAiChat = true, repoUrl = "", i18nLocales) {
205
- const contentDir = join2(targetDir, "src", "content");
206
- if (existsSync(contentDir)) {
207
- const entries = readdirSync(contentDir);
208
- for (const entry of entries) {
209
- const fullPath = join2(contentDir, entry);
210
- execSync(`rm -rf "${fullPath}"`);
211
- }
212
- } else {
213
- mkdirSync(contentDir, { recursive: true });
214
- }
215
- for (const [filename, template] of Object.entries(STARTER_PAGES)) {
216
- const content = template.replace(/\{NAME\}/g, projectName).replace(/\{SLUG\}/g, slug);
217
- writeFileSync2(join2(contentDir, filename), content, "utf8");
218
- }
219
- writeFileSync2(
220
- join2(targetDir, "docs.json"),
221
- buildStarterDocsJson({ enableAiChat, repoUrl: repoUrl || void 0, i18nLocales }),
222
- "utf8"
223
- );
224
- }
225
- function updateSiteConfig(targetDir, projectName, description, brandPreset, repoUrl) {
226
- const siteFile = join2(targetDir, "src", "data", "site.ts");
227
- if (!existsSync(siteFile)) {
228
- console.log(" \u26A0\uFE0F Could not find src/data/site.ts \u2014 skipping config update.");
229
- return;
230
- }
231
- let source = readFileSync2(siteFile, "utf8");
232
- source = source.replace(
233
- /name:\s*'[^']*'/,
234
- `name: '${projectName.replace(/'/g, "\\'")}'`
235
- );
236
- source = source.replace(
237
- /description:\s*\n\s*'[^']*'/,
238
- `description:
239
- '${description.replace(/'/g, "\\'")}'`
240
- );
241
- source = source.replace(
242
- /const brandPreset:\s*BrandPresetKey\s*=\s*'[^']*'/,
243
- `const brandPreset: BrandPresetKey = '${brandPreset}'`
244
- );
245
- source = source.replace(/repoUrl:\s*'[^']*'/, `repoUrl: '${repoUrl}'`);
246
- source = source.replace(
247
- /\{\s*label:\s*'GitHub',\s*href:\s*'[^']*'\s*\}/,
248
- `{ label: 'GitHub', href: '${repoUrl}' }`
249
- );
250
- source = source.replace(
251
- /\{\s*label:\s*'Support',\s*href:\s*'[^']*'\s*\}/,
252
- `{ label: 'Support', href: '${repoUrl ? `${repoUrl}/issues/new` : ""}' }`
253
- );
254
- writeFileSync2(siteFile, source, "utf8");
255
- }
256
- function patchApiReferenceGuard(targetDir) {
257
- const filePath = join2(targetDir, "src", "data", "api-reference.ts");
258
- if (!existsSync(filePath)) return;
259
- let source = readFileSync2(filePath, "utf8");
260
- source = source.replace(
261
- /export async function buildApiNavigation\([^)]*\)[^{]*\{\n/,
262
- (match) => `${match} if (apiReferenceConfig.specs.length === 0) return []
263
- `
264
- );
265
- writeFileSync2(filePath, source, "utf8");
266
- }
267
- function patchTopBarNavigation(targetDir) {
268
- const filePath = join2(targetDir, "src", "components", "layout", "top-bar.tsx");
269
- if (!existsSync(filePath)) return;
270
- const source = readFileSync2(filePath, "utf8");
271
- if (!source.includes("target={isExternal ? '_blank' : undefined}")) return;
272
- const patched = source.replace(
273
- /if \(collection\.href\) \{\n const isExternal[^\n]+\n return \(\n <a[\s\S]*?<\/a>\n \)\n \}/,
274
- `if (collection.href) {
275
- const isExternal = /^https?:\\/\\//.test(collection.href)
276
- if (isExternal) {
277
- return (
278
- <a
279
- key={collection.id}
280
- href={collection.href}
281
- target="_blank"
282
- rel="noreferrer"
283
- className={baseClasses}
284
- >
285
- {collection.label}
286
- </a>
287
- )
288
- }
289
- return (
290
- <Link
291
- key={collection.id}
292
- href={collection.href}
293
- className={baseClasses}
294
- >
295
- {collection.label}
296
- </Link>
297
- )
298
- }`
299
- );
300
- writeFileSync2(filePath, patched, "utf8");
301
- }
302
- function patchOpenApiFetch(targetDir) {
303
- const filePath = join2(targetDir, "src", "lib", "openapi", "fetch.ts");
304
- if (!existsSync(filePath)) return;
305
- let source = readFileSync2(filePath, "utf8");
306
- source = source.replace(
307
- /const absolutePath = path\.isAbsolute\(filePath\) \? filePath : path\.resolve\(process\.cwd\(\), filePath\)/,
308
- `const absolutePath = filePath.startsWith('/')
309
- ? path.resolve(process.cwd(), 'public', filePath.slice(1))
310
- : path.resolve(process.cwd(), filePath)`
311
- );
312
- writeFileSync2(filePath, source, "utf8");
313
- }
314
- function updateEnvExample(targetDir) {
315
- const envFile = join2(targetDir, ".env.example");
316
- if (existsSync(envFile)) {
317
- const envLocal = join2(targetDir, ".env.local");
318
- if (!existsSync(envLocal)) {
319
- cpSync(envFile, envLocal);
320
- }
321
- }
322
- }
323
- function patchPackageJson(targetDir, slug) {
324
- const pkgPath = join2(targetDir, "package.json");
325
- if (!existsSync(pkgPath)) return;
326
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
327
- pkg.name = slug;
328
- delete pkg.workspaces;
329
- if (pkg.scripts) {
330
- if (pkg.scripts["prebuild"]) pkg.scripts["prebuild"] = "npm run embeddings:build";
331
- delete pkg.scripts["pretest"];
332
- delete pkg.scripts["packages:build"];
333
- }
334
- writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
335
- `, "utf8");
336
- const lockPath = join2(targetDir, "package-lock.json");
337
- if (existsSync(lockPath)) rmSync(lockPath);
338
- }
339
-
340
- // src/utils.ts
341
- import { execSync as execSync2 } from "child_process";
342
- import { basename } from "path";
343
- function slugify(name) {
344
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
345
- }
346
- function run(cmd, cwd) {
347
- execSync2(cmd, { cwd, stdio: "inherit" });
348
- }
349
- function initGit(targetDir) {
350
- try {
351
- run("git init", targetDir);
352
- run("git add -A", targetDir);
353
- run('git commit -m "Initial commit from create-thally-docs"', targetDir);
354
- } catch {
355
- console.log(" \u26A0\uFE0F Could not initialize git (you can do this manually).");
356
- }
357
- }
358
- function installDeps(targetDir) {
359
- console.log("");
360
- console.log(" \u{1F4E6} Installing dependencies...");
361
- console.log("");
362
- run("npm install", targetDir);
363
- }
364
- function logo() {
365
- console.log("");
366
- console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
367
- console.log(" \u2551 \u2551");
368
- console.log(" \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2551");
369
- console.log(" \u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D \u2551");
370
- console.log(" \u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2551");
371
- console.log(" \u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D \u2551");
372
- console.log(" \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2551");
373
- console.log(" \u2551 \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u2551");
374
- console.log(" \u2551 \u2551");
375
- console.log(" \u2551 Beautiful docs, zero lock-in. \u2551");
376
- console.log(" \u2551 \u2551");
377
- console.log(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
378
- console.log("");
379
- }
380
- function success(projectDir, projectName) {
381
- console.log("");
382
- console.log(" \u2705 Your Thally project is ready!");
383
- console.log("");
384
- console.log(` \u{1F4C2} ${projectDir}`);
385
- console.log("");
386
- console.log(" Next steps:");
387
- console.log("");
388
- console.log(` cd ${basename(projectDir)}`);
389
- console.log(" npm run dev");
390
- console.log("");
391
- console.log(` Then open http://localhost:3040 to see your ${projectName} docs.`);
392
- console.log("");
393
- console.log(" \u{1F4DD} Key files to edit:");
394
- console.log(" \u2022 src/data/site.ts \u2014 name, links, branding");
395
- console.log(" \u2022 docs.json \u2014 navigation structure");
396
- console.log(" \u2022 src/content/*.mdx \u2014 your documentation");
397
- console.log(" \u2022 openapi.yaml \u2014 API spec (optional)");
398
- console.log("");
399
- console.log(" Happy documenting! \u{1F680}");
400
- console.log("");
401
- }
402
-
403
- // src/scaffold.ts
404
- async function scaffold(options) {
405
- const {
406
- projectDir,
407
- projectName,
408
- description,
409
- brandPreset,
410
- repoUrl,
411
- doInstall,
412
- enableAiChat = true,
413
- i18nLocales,
414
- trackRepos
415
- } = options;
416
- const targetDir = resolve(projectDir);
417
- if (existsSync2(targetDir) && readdirSync2(targetDir).length > 0) {
418
- throw new Error(`Directory "${targetDir}" already exists and is not empty.`);
419
- }
420
- mkdirSync2(targetDir, { recursive: true });
421
- const slug = slugify(projectName);
422
- await downloadTemplate(targetDir, projectName);
423
- resetTrackingConfig(targetDir);
424
- if (trackRepos?.length) {
425
- writeTrackingConfig(targetDir, trackRepos);
426
- const list = trackRepos.map((r) => `${r.owner}/${r.repo}`).join(", ");
427
- console.log(` \u2713 Thally Track enabled \u2014 watching ${list} (branch main, all files; refine in docs.json).`);
428
- console.log(" To finish wiring it: `thally track setup` (pick a trigger) + `thally agent init`,");
429
- console.log(" then add your ANTHROPIC_API_KEY. See /guides/thally-track.");
430
- }
431
- writeStarterContent(targetDir, projectName, slug, enableAiChat, repoUrl, i18nLocales);
432
- updateSiteConfig(targetDir, projectName, description, brandPreset, repoUrl);
433
- patchApiReferenceGuard(targetDir);
434
- patchTopBarNavigation(targetDir);
435
- patchOpenApiFetch(targetDir);
436
- patchPackageJson(targetDir, slug);
437
- updateEnvExample(targetDir);
438
- if (doInstall) {
439
- installDeps(targetDir);
440
- }
441
- initGit(targetDir);
442
- return { projectDir: targetDir };
443
- }
444
-
445
- export {
446
- slugify,
447
- initGit,
448
- installDeps,
449
- logo,
450
- success,
451
- readDocsJson,
452
- writeDocsJson,
453
- scaffold
454
- };