@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 +14 -4
- package/ai/application/docs-pattern.ts +68 -0
- package/ai/application/run-ai-cli-command.ts +32 -12
- package/ai/config.ts +28 -0
- package/ai/core/models/ai-cli-config.ts +2 -0
- package/ai/infrastructure/version-docs-writer.ts +143 -0
- package/ai/presentation/ai-prompts.ts +2 -2
- package/data/layouts.mjs +20 -0
- package/data/theme-colors.mjs +22 -0
- package/package.json +3 -3
- package/presentation/index.ts +15 -7
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
|
|
138
|
+
- generates documentation in `pt`, `en`, `es` **in the gitpagedocs pattern**
|
|
138
139
|
- optionally persists config in `.gitpagedocsconfig`
|
|
139
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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';
|
|
@@ -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
|
-
'
|
|
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
|
-
'
|
|
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");
|
package/data/theme-colors.mjs
CHANGED
|
@@ -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.
|
|
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.
|
|
33
|
-
"@gitpagedocs/tools": "^1.1.
|
|
32
|
+
"@gitpagedocs/mcp": "^1.1.53",
|
|
33
|
+
"@gitpagedocs/tools": "^1.1.53",
|
|
34
34
|
"tsx": "^4.21.0"
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
package/presentation/index.ts
CHANGED
|
@@ -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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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();
|