@gitpagedocs/cli 1.1.51 → 1.1.53

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
@@ -21,9 +21,10 @@ npx @gitpagedocs/cli
21
21
  | `gitpagedocs --push --owner <o> --repo <r> [--path <sub>]` | Generate, configure Pages, create workflow, commit + push |
22
22
  | `gitpagedocs --home` | Standalone distribution (`gitpagedocshome/`: static site + .env + Dockerfile) |
23
23
  | `gitpagedocs -i` / `--interactive` | Interactive prompts |
24
- | `gitpagedocs ai` | Interactive AI documentation generator (see below) |
24
+ | `gitpagedocs ai` | Interactive AI documentation generator — writes pages in the gitpagedocs pattern (see below) |
25
25
  | `gitpagedocs provider [id]` · `models [provider]` | List AI providers / catalog models |
26
- | `gitpagedocs document[:repo\|:file\|:folder]` | Generate documentation with AI |
26
+ | `gitpagedocs document[:repo\|:file\|:folder]` | Generate documentation with AI in the gitpagedocs pattern |
27
+ | `gitpagedocs password` | Set a documentation access password (writes the public key to `config.json`, prints the private key) |
27
28
  | `gitpagedocs deploy` / `pages [actions\|deploy]` | Configure GitHub Pages via Actions + push |
28
29
  | `gitpagedocs docs` | Refresh managed regions of README/CONTRIBUTING/SECURITY |
29
30
  | `gitpagedocs doctor` · `version` · `update` | Diagnostics / version / update hint |
@@ -134,9 +135,18 @@ npx gitpagedocs ai
134
135
  - asks provider (`openai`, `claude`, `gemini`, `ollama`)
135
136
  - asks API key or Ollama URL
136
137
  - asks paths to scan (supports one or many paths, including other repositories)
137
- - generates markdown documentation in `pt`, `en`, `es`
138
+ - generates documentation in `pt`, `en`, `es` **in the gitpagedocs pattern**
138
139
  - optionally persists config in `.gitpagedocsconfig`
139
- - optionally runs standard `gitpagedocs` scaffolding after AI generation
140
+
141
+ ### gitpagedocs pattern output
142
+
143
+ The model is given a gitpagedocs-aware system prompt and returns documentation split into
144
+ pages (delimited by `=== PAGE: <slug> | <Title> ===`). The CLI scaffolds the base
145
+ `gitpagedocs/` structure first, then writes each page to
146
+ `gitpagedocs/docs/versions/<latest>/<lang>/<slug>.md` for every language and **wires it into
147
+ that version's `config.json`** (`routes-md` + `menus-header-md`) so the pages render in the
148
+ viewer menu. Entries are tagged `aiGenerated` and idempotent (re-running replaces them). A
149
+ later plain `gitpagedocs` rebuild drops the wiring — re-run `gitpagedocs ai` to restore.
140
150
 
141
151
  ### `.gitpagedocsconfig` (manual mode)
142
152
 
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Parse an AI documentation response into gitpagedocs pages.
3
+ *
4
+ * The model is instructed (see GITPAGEDOCS_DOC_SYSTEM_PROMPT) to delimit each
5
+ * page with a line `=== PAGE: <slug> | <Title> ===`. This splits that response
6
+ * into individual pages. If the model ignored the format, the whole response is
7
+ * returned as a single "ai-overview" page so content is never lost.
8
+ */
9
+
10
+ export interface AiDocPage {
11
+ /** lowercase-kebab, English, identical across languages (used as the filename) */
12
+ slug: string;
13
+ title: string;
14
+ body: string;
15
+ }
16
+
17
+ const PAGE_DELIMITER = /^===\s*PAGE:\s*(.+?)\s*\|\s*(.*?)\s*===\s*$/;
18
+
19
+ /** Turn an arbitrary heading into a safe lowercase-kebab slug. */
20
+ export function slugify(raw: string): string {
21
+ const slug = (raw ?? "")
22
+ .toLowerCase()
23
+ .normalize("NFKD")
24
+ .replace(/[^a-z0-9]+/g, "-")
25
+ .replace(/^-+|-+$/g, "")
26
+ .slice(0, 60);
27
+ return slug || "page";
28
+ }
29
+
30
+ export function parseAiPages(markdown: string): AiDocPage[] {
31
+ const text = (markdown ?? "").replace(/\r\n/g, "\n").trim();
32
+ if (!text) return [];
33
+
34
+ const pages: AiDocPage[] = [];
35
+ let current: { slug: string; title: string; body: string[] } | null = null;
36
+
37
+ const flush = () => {
38
+ if (current) {
39
+ pages.push({ slug: current.slug, title: current.title, body: current.body.join("\n").trim() });
40
+ }
41
+ };
42
+
43
+ for (const line of text.split("\n")) {
44
+ const match = line.match(PAGE_DELIMITER);
45
+ if (match) {
46
+ flush();
47
+ const rawSlug = match[1].trim();
48
+ const title = match[2].trim() || rawSlug;
49
+ current = { slug: slugify(rawSlug), title, body: [] };
50
+ } else if (current) {
51
+ current.body.push(line);
52
+ }
53
+ }
54
+ flush();
55
+
56
+ const withBody = pages.filter((page) => page.body.length > 0);
57
+ if (withBody.length === 0) {
58
+ return [{ slug: "ai-overview", title: "Overview", body: text }];
59
+ }
60
+
61
+ // De-duplicate slugs within a single response (suffix later collisions).
62
+ const seen = new Map<string, number>();
63
+ return withBody.map((page) => {
64
+ const count = seen.get(page.slug) ?? 0;
65
+ seen.set(page.slug, count + 1);
66
+ return count === 0 ? page : { ...page, slug: `${page.slug}-${count + 1}` };
67
+ });
68
+ }
@@ -1,4 +1,3 @@
1
- import path from "node:path";
2
1
  import { AiCommandService } from "./ai-command";
3
2
  import { createAiProvider } from "./ai-provider-factory";
4
3
  import { FileSystemAdapter, type FilePayload } from "../infrastructure/file-system-adapter";
@@ -7,6 +6,11 @@ import {
7
6
  } from "../infrastructure/ai-config-file";
8
7
  import type { AiCliRunPlan, AiCliRunSummary } from "../core/models/ai-cli-config";
9
8
  import { promptMissingDirectories, runAiInteractivePrompt } from "../presentation/ai-prompts";
9
+ import { GITPAGEDOCS_DOC_SYSTEM_PROMPT } from "../config";
10
+ import { parseAiPages, type AiDocPage } from "./docs-pattern";
11
+ import { writeVersionDocs } from "../infrastructure/version-docs-writer";
12
+ // @ts-expect-error .mjs runtime module is type-less in this package.
13
+ import { DOC_VERSIONS } from "../../contracts/doc-versions.mjs";
10
14
 
11
15
  const LANGUAGE_TO_LABEL: Record<string, string> = {
12
16
  pt: "Portuguese",
@@ -22,7 +26,13 @@ function aggregateFilesPayload(files: FilePayload[]): string {
22
26
 
23
27
  function buildLanguagePrompt(basePrompt: string, language: "pt" | "en" | "es"): string {
24
28
  const languageLabel = LANGUAGE_TO_LABEL[language] ?? language;
25
- return `${basePrompt}\n\nMandatory output rules:\n- Respond 100% in ${languageLabel}.\n- Generate professional markdown for technical documentation.\n- Include architectural overview, layer responsibilities, and next steps.\n- Avoid content outside the scope of the analyzed files.`;
29
+ const authorGuidance = basePrompt?.trim() ? `Additional author guidance:\n${basePrompt.trim()}\n\n` : "";
30
+ return (
31
+ `${GITPAGEDOCS_DOC_SYSTEM_PROMPT}\n\n` +
32
+ authorGuidance +
33
+ `Output language: write every page Title and body 100% in ${languageLabel}. ` +
34
+ `Keep each page slug lowercase-kebab and in English so the languages line up.`
35
+ );
26
36
  }
27
37
 
28
38
  async function collectFilesWithInteractiveFallback(
@@ -66,7 +76,7 @@ async function collectFilesWithInteractiveFallback(
66
76
  };
67
77
  }
68
78
 
69
- async function runPlan(plan: AiCliRunPlan): Promise<AiCliRunSummary> {
79
+ async function runPlan(plan: AiCliRunPlan, cwd: string): Promise<AiCliRunSummary> {
70
80
  const provider = createAiProvider({
71
81
  provider: plan.config.ai.provider,
72
82
  model: plan.config.ai.model,
@@ -92,30 +102,33 @@ async function runPlan(plan: AiCliRunPlan): Promise<AiCliRunSummary> {
92
102
  }
93
103
 
94
104
  const payload = aggregateFilesPayload(files);
95
- const outputs: string[] = [];
105
+ const pagesByLang: Partial<Record<"pt" | "en" | "es", AiDocPage[]>> = {};
96
106
 
97
107
  for (const language of plan.config.ai.languages) {
98
108
  const prompt = buildLanguagePrompt(plan.config.ai.contextPrompt, language);
99
109
  const markdown = await aiService.runGeneration(payload, prompt);
100
- const outputFile = path.posix.join(
101
- plan.config.ai.outputDir.replace(/\\/g, "/"),
102
- `${plan.config.ai.filePrefix}-${plan.config.ai.provider}-${language}.md`,
103
- );
104
- await fileSystem.writeDocumentationOutput(markdown, outputFile);
105
- outputs.push(outputFile);
110
+ pagesByLang[language] = parseAiPages(markdown);
106
111
  }
107
112
 
113
+ // Wire the AI pages into the latest documentation version (gitpagedocs pattern).
114
+ const versionId = DOC_VERSIONS[DOC_VERSIONS.length - 1] as string;
115
+ const result = await writeVersionDocs({ cwd, versionId, pagesByLang });
116
+
108
117
  return {
109
118
  scannedDirectories: scanned,
110
119
  skippedDirectories: skipped,
111
120
  scannedFilesCount: files.length,
112
- outputs,
121
+ outputs: result.files,
122
+ pages: result.slugs,
113
123
  };
114
124
  }
115
125
 
116
126
  export async function runAiCliCommand(options: {
117
127
  cwd: string;
118
128
  onInfo?: (message: string) => void;
129
+ /** Provided by the composition root to scaffold the base gitpagedocs/ tree
130
+ * (so the version config.json exists before AI pages are wired into it). */
131
+ onScaffold?: () => Promise<void>;
119
132
  }): Promise<{ summary: AiCliRunSummary; runConfigScaffold: boolean }> {
120
133
  const logInfo = options.onInfo ?? (() => undefined);
121
134
  const configRepo = new AiConfigFileRepository(options.cwd);
@@ -128,7 +141,14 @@ export async function runAiCliCommand(options: {
128
141
  logInfo(`[gitpagedocs:ai] Configuration saved to ${configRepo.getConfigPath()}`);
129
142
  }
130
143
 
131
- const summary = await runPlan(plan);
144
+ // Build the base gitpagedocs structure BEFORE generation so the version
145
+ // config.json exists and the AI pages can be wired into it.
146
+ if (plan.runConfigScaffold && options.onScaffold) {
147
+ logInfo("[gitpagedocs] Generating base gitpagedocs structure...");
148
+ await options.onScaffold();
149
+ }
150
+
151
+ const summary = await runPlan(plan, options.cwd);
132
152
  return {
133
153
  summary,
134
154
  runConfigScaffold: plan.runConfigScaffold,
package/ai/config.ts CHANGED
@@ -19,4 +19,32 @@ export const AI_MODEL_DEFAULTS: Readonly<Record<AiProviderId, string>> = {
19
19
  export const DEFAULT_AI_DOC_PROMPT =
20
20
  'You are a senior tech writer. Describe the provided code.';
21
21
 
22
+ /**
23
+ * System prompt that teaches the model what gitpagedocs is and forces it to
24
+ * return documentation in the gitpagedocs pattern: a sequence of pages, each
25
+ * introduced by a machine-parseable delimiter line so the CLI can split the
26
+ * response into individual versioned markdown pages and wire them into the
27
+ * viewer's config.json (routes-md + menus-header-md).
28
+ */
29
+ export const GITPAGEDOCS_DOC_SYSTEM_PROMPT = [
30
+ 'You are the documentation engine for "gitpagedocs", a generator of multilingual,',
31
+ 'versioned static documentation sites. gitpagedocs renders markdown pages from',
32
+ 'gitpagedocs/docs/versions/<version>/<lang>/<page>.md and wires them into a viewer',
33
+ 'through a config.json contract (routes + header menus). Your job is to analyze the',
34
+ 'provided source code and produce clean, professional documentation that fits this',
35
+ 'pattern so it can be dropped straight into a gitpagedocs site.',
36
+ '',
37
+ 'STRICT OUTPUT FORMAT — return ONLY a sequence of pages. Begin every page with a',
38
+ 'delimiter line on its own, exactly:',
39
+ '=== PAGE: <slug> | <Title> ===',
40
+ 'followed by the page body in GitHub-flavored markdown. Rules:',
41
+ '- Produce 4 to 8 cohesive pages. Suggested slugs: overview, getting-started,',
42
+ ' architecture, configuration, usage, deployment (adapt to the project).',
43
+ '- <slug> MUST be lowercase-kebab-case and in ENGLISH, and identical across every',
44
+ ' language run, so the languages line up (only the Title and body are translated).',
45
+ '- Do NOT wrap the whole answer in a code fence. Do NOT add any preamble or epilogue',
46
+ ' outside the page blocks. The first line of your reply MUST be a "=== PAGE:" line.',
47
+ '- Base everything strictly on the analyzed files; do not invent features.',
48
+ ].join('\n');
49
+
22
50
  export const OLLAMA_DEFAULT_BASE_URL = 'http://localhost:11434';
@@ -28,4 +28,6 @@ export interface AiCliRunSummary {
28
28
  skippedDirectories: string[];
29
29
  scannedFilesCount: number;
30
30
  outputs: string[];
31
+ /** Slugs of the gitpagedocs pages wired into the version config (if any). */
32
+ pages?: string[];
31
33
  }
@@ -0,0 +1,143 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ // @ts-expect-error .mjs runtime module is type-less in this package.
4
+ import { buildMdRoute } from "../../builders/route-builders.mjs";
5
+ import type { AiDocPage } from "../application/docs-pattern";
6
+
7
+ const CANONICAL_LANGS = ["pt", "en", "es"] as const;
8
+ type Lang = (typeof CANONICAL_LANGS)[number];
9
+
10
+ /** AI routes/menus use a high, dedicated id range so they never clash with the
11
+ * deterministic base routes (ids 1..N) and can be identified for idempotency. */
12
+ const AI_ROUTE_ID_BASE = 1000;
13
+
14
+ export interface VersionDocsWriteInput {
15
+ cwd: string;
16
+ versionId: string;
17
+ /** Pages generated per language (only the languages the user selected). */
18
+ pagesByLang: Partial<Record<Lang, AiDocPage[]>>;
19
+ }
20
+
21
+ export interface VersionDocsWriteResult {
22
+ configPath: string;
23
+ slugs: string[];
24
+ files: string[];
25
+ }
26
+
27
+ interface LangContent {
28
+ title: string;
29
+ body: string;
30
+ }
31
+
32
+ function indexBySlug(pages: AiDocPage[] | undefined): Map<string, AiDocPage> {
33
+ const map = new Map<string, AiDocPage>();
34
+ for (const page of pages ?? []) {
35
+ if (!map.has(page.slug)) map.set(page.slug, page);
36
+ }
37
+ return map;
38
+ }
39
+
40
+ /**
41
+ * Write AI-generated pages into the gitpagedocs versioned structure and wire
42
+ * them into the version config.json (routes-md + menus-header-md). Pages are
43
+ * written for ALL three canonical languages (missing languages reuse an
44
+ * available translation) so every route path stays valid. Idempotent: any prior
45
+ * `aiGenerated` routes/menus are replaced, never duplicated. The base config and
46
+ * its deterministic routes are preserved untouched.
47
+ */
48
+ export async function writeVersionDocs(input: VersionDocsWriteInput): Promise<VersionDocsWriteResult> {
49
+ const { cwd, versionId, pagesByLang } = input;
50
+ const base = `gitpagedocs/docs/versions/${versionId}`;
51
+
52
+ const perLang: Record<Lang, Map<string, AiDocPage>> = {
53
+ pt: indexBySlug(pagesByLang.pt),
54
+ en: indexBySlug(pagesByLang.en),
55
+ es: indexBySlug(pagesByLang.es),
56
+ };
57
+
58
+ // Ordered unique slug list (prefer en order, then pt, then es).
59
+ const order: string[] = [];
60
+ const seen = new Set<string>();
61
+ for (const lang of ["en", "pt", "es"] as Lang[]) {
62
+ for (const page of pagesByLang[lang] ?? []) {
63
+ if (!seen.has(page.slug)) {
64
+ seen.add(page.slug);
65
+ order.push(page.slug);
66
+ }
67
+ }
68
+ }
69
+ if (order.length === 0) {
70
+ throw new Error("No AI pages to write.");
71
+ }
72
+
73
+ const resolve = (slug: string): Record<Lang, LangContent> => {
74
+ const fallback = CANONICAL_LANGS.map((lang) => perLang[lang].get(slug)).find(Boolean);
75
+ const out = {} as Record<Lang, LangContent>;
76
+ for (const lang of CANONICAL_LANGS) {
77
+ const page = perLang[lang].get(slug) ?? fallback!;
78
+ out[lang] = { title: page.title, body: page.body };
79
+ }
80
+ return out;
81
+ };
82
+
83
+ // 1) Write the markdown files (all 3 canonical languages).
84
+ const writtenFiles: string[] = [];
85
+ for (const slug of order) {
86
+ const content = resolve(slug);
87
+ for (const lang of CANONICAL_LANGS) {
88
+ const rel = `${base}/${lang}/${slug}.md`;
89
+ const abs = path.resolve(cwd, rel);
90
+ await fs.mkdir(path.dirname(abs), { recursive: true });
91
+ await fs.writeFile(abs, `${content[lang].body}\n`, "utf-8");
92
+ writtenFiles.push(rel);
93
+ }
94
+ }
95
+
96
+ // 2) Patch the version config.json (raw JSON round-trip, preserve all keys).
97
+ const configRel = `${base}/config.json`;
98
+ const configAbs = path.resolve(cwd, configRel);
99
+ let raw: string;
100
+ try {
101
+ raw = await fs.readFile(configAbs, "utf-8");
102
+ } catch {
103
+ throw new Error(
104
+ `Version config not found at ${configRel}. Run the base scaffold (\`gitpagedocs\`) first.`,
105
+ );
106
+ }
107
+ const config = JSON.parse(raw) as Record<string, unknown>;
108
+
109
+ const routesMd = Array.isArray(config["routes-md"]) ? (config["routes-md"] as Array<Record<string, unknown>>) : [];
110
+ const menusMd = Array.isArray(config["menus-header-md"]) ? (config["menus-header-md"] as Array<Record<string, unknown>>) : [];
111
+ const keptRoutes = routesMd.filter((route) => route?.aiGenerated !== true);
112
+ const keptMenus = menusMd.filter((menu) => menu?.aiGenerated !== true);
113
+
114
+ const newRoutes: Array<Record<string, unknown>> = [];
115
+ const newMenus: Array<Record<string, unknown>> = [];
116
+ order.forEach((slug, index) => {
117
+ const content = resolve(slug);
118
+ const id = AI_ROUTE_ID_BASE + index + 1;
119
+ const pathByLang = {
120
+ pt: `${base}/pt/${slug}.md`,
121
+ en: `${base}/en/${slug}.md`,
122
+ es: `${base}/es/${slug}.md`,
123
+ };
124
+ const titles = { pt: content.pt.title, en: content.en.title, es: content.es.title };
125
+ const descriptions = { pt: "", en: "", es: "" };
126
+ const route = buildMdRoute(versionId, id, pathByLang, titles, descriptions, {}) as Record<string, unknown>;
127
+ route.aiGenerated = true;
128
+ newRoutes.push(route);
129
+ newMenus.push({
130
+ id,
131
+ aiGenerated: true,
132
+ pt: { title: content.pt.title, "path-click": pathByLang.pt },
133
+ en: { title: content.en.title, "path-click": pathByLang.en },
134
+ es: { title: content.es.title, "path-click": pathByLang.es },
135
+ });
136
+ });
137
+
138
+ config["routes-md"] = [...keptRoutes, ...newRoutes];
139
+ config["menus-header-md"] = [...keptMenus, ...newMenus];
140
+ await fs.writeFile(configAbs, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
141
+
142
+ return { configPath: configRel, slugs: order, files: writtenFiles };
143
+ }
@@ -141,7 +141,7 @@ export async function promptUseExistingConfig(existingConfig: AiCliConfig): Prom
141
141
  if (!useExistingConfig) return null;
142
142
 
143
143
  const runConfigScaffold = await askConfirm(
144
- 'After generating docs with AI, run the default gitpagedocs scaffolding?',
144
+ 'Generate the base gitpagedocs structure first, so the AI pages are wired into the docs viewer?',
145
145
  true,
146
146
  );
147
147
 
@@ -220,7 +220,7 @@ export async function runAiInteractivePrompt(existingConfig?: AiCliConfig | null
220
220
  );
221
221
 
222
222
  const runConfigScaffold = await askConfirm(
223
- 'After generating docs with AI, run the default gitpagedocs scaffolding?',
223
+ 'Generate the base gitpagedocs structure first, so the AI pages are wired into the docs viewer?',
224
224
  true,
225
225
  );
226
226
 
package/data/layouts.mjs CHANGED
@@ -348,6 +348,26 @@ export const LAYOUTS = [
348
348
  supportsLightAndDarkModesReference: "slate-1",
349
349
  mode: "light",
350
350
  },
351
+ {
352
+ id: "vscode-dark",
353
+ name: "VSCode Dark",
354
+ author: "Kauan Vidigal",
355
+ file: "templates/vscode-dark.json",
356
+ preview: "Visual Studio Code Dark+ theme with editor blue accents",
357
+ supportsLightAndDarkModes: true,
358
+ supportsLightAndDarkModesReference: "vscode-1",
359
+ mode: "dark",
360
+ },
361
+ {
362
+ id: "vscode-light",
363
+ name: "VSCode Light",
364
+ author: "Kauan Vidigal",
365
+ file: "templates/vscode-light.json",
366
+ preview: "Visual Studio Code Light+ theme with clean editor tones",
367
+ supportsLightAndDarkModes: true,
368
+ supportsLightAndDarkModesReference: "vscode-1",
369
+ mode: "light",
370
+ },
351
371
  ];
352
372
 
353
373
  export const FALLBACK_LAYOUTS = LAYOUTS.filter((layout) => layout.id === "aurora-dark" || layout.id === "aurora-light");
@@ -384,4 +384,26 @@ export const THEME_COLORS = {
384
384
  error: "#DC2626",
385
385
  success: "#16A34A",
386
386
  },
387
+ "vscode-dark": {
388
+ background: "#1E1E1E",
389
+ primary: "#007ACC",
390
+ secondary: "#4EC9B0",
391
+ text: "#D4D4D4",
392
+ textSecondary: "#858585",
393
+ cardBackground: "#252526",
394
+ cardBorder: "#3C3C3C",
395
+ error: "#F14C4C",
396
+ success: "#4EC9B0",
397
+ },
398
+ "vscode-light": {
399
+ background: "#FFFFFF",
400
+ primary: "#005FB8",
401
+ secondary: "#267F99",
402
+ text: "#1E1E1E",
403
+ textSecondary: "#6E6E6E",
404
+ cardBackground: "#F3F3F3",
405
+ cardBorder: "#E5E5E5",
406
+ error: "#CD3131",
407
+ success: "#098658",
408
+ },
387
409
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitpagedocs/cli",
3
- "version": "1.1.51",
3
+ "version": "1.1.53",
4
4
  "description": "CLI that scaffolds and maintains gitpagedocs documentation, generates docs with AI, configures GitHub Pages, and runs the MCP server.",
5
5
  "bin": {
6
6
  "gitpagedocs": "index.mjs"
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@clack/prompts": "^1.5.1",
32
- "@gitpagedocs/mcp": "^1.1.51",
33
- "@gitpagedocs/tools": "^1.1.51",
32
+ "@gitpagedocs/mcp": "^1.1.53",
33
+ "@gitpagedocs/tools": "^1.1.53",
34
34
  "tsx": "^4.21.0"
35
35
  },
36
36
  "repository": {
@@ -78,11 +78,19 @@ async function main(): Promise<void> {
78
78
  },
79
79
  runAi: async (context) => {
80
80
  console.log("[gitpagedocs] Iniciando Módulo de IA na CLI...");
81
+ const safePrebuiltDir =
82
+ typeof context.prebuiltDir === "string"
83
+ ? context.prebuiltDir
84
+ : path.join(String(context.pkgRoot ?? process.cwd()), "prebuilt");
81
85
  // @ts-ignore
82
86
  const { runAiCliCommand } = await import("../ai/application/run-ai-cli-command.ts");
83
87
  const result = await runAiCliCommand({
84
88
  cwd: process.cwd(),
85
89
  onInfo: (message: string) => console.log(message),
90
+ // Scaffold the base gitpagedocs/ structure before AI pages are wired in.
91
+ onScaffold: async () => {
92
+ await executeConfigOnly({ ...context, prebuiltDir: safePrebuiltDir }, configOnlyRuntime);
93
+ },
86
94
  });
87
95
 
88
96
  if (result.summary.scannedFilesCount === 0) {
@@ -90,13 +98,13 @@ async function main(): Promise<void> {
90
98
  return;
91
99
  }
92
100
 
93
- if (result.runConfigScaffold) {
94
- console.log("\n[gitpagedocs] Scaffolding GitPageDocs ecosystem automatically...");
95
- const safePrebuiltDir = typeof context.prebuiltDir === "string" ? context.prebuiltDir : path.join(String(context.pkgRoot ?? process.cwd()), "prebuilt");
96
- await executeConfigOnly({ ...context, prebuiltDir: safePrebuiltDir }, configOnlyRuntime);
97
- }
98
-
99
- console.log(`\n🎉 Processo completo! Arquivos gerados: ${result.summary.outputs.join(", ")}`);
101
+ const pages = result.summary.pages ?? [];
102
+ console.log(
103
+ `\n🎉 Processo completo! Páginas no padrão gitpagedocs: ${pages.join(", ") || "(nenhuma)"}`,
104
+ );
105
+ console.log(
106
+ `[gitpagedocs:ai] ${result.summary.outputs.length} arquivos markdown gerados e conectados ao config.json da versão mais recente.`,
107
+ );
100
108
  },
101
109
  });
102
110
  printCredits();