@gitpagedocs/cli 1.1.62 → 1.1.64
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 +193 -193
- package/ai/application/ai-command.ts +24 -24
- package/ai/application/ai-provider-factory.ts +22 -22
- package/ai/application/docs-pattern.ts +68 -68
- package/ai/application/run-ai-cli-command.ts +167 -167
- package/ai/config.ts +50 -50
- package/ai/core/models/ai-cli-config.ts +33 -33
- package/ai/core/models/parse-ai-cli-config.ts +78 -78
- package/ai/core/ports/illm-provider.ts +32 -32
- package/ai/infrastructure/ai-config-file.ts +101 -101
- package/ai/infrastructure/file-system-adapter.ts +85 -85
- package/ai/infrastructure/llm/tools-llm-provider.ts +54 -54
- package/ai/infrastructure/version-docs-writer.ts +143 -143
- package/ai/presentation/ai-prompts.ts +239 -239
- package/application/ports/cli-runtime-ports.ts +91 -91
- package/application/report/config-only-reporter.mjs +64 -64
- package/application/use-cases/dispatch-mode.ts +18 -18
- package/builders/config-orchestrator.mjs +34 -34
- package/builders/project-links.mjs +50 -50
- package/builders/root-config-builder.mjs +61 -61
- package/builders/route-builders.mjs +136 -136
- package/builders/theme-template.mjs +76 -76
- package/builders/version-config-builder.mjs +186 -186
- package/content/docs-en.mjs +549 -549
- package/content/docs-es.mjs +462 -462
- package/content/docs-pt.mjs +462 -462
- package/contracts/doc-versions.mjs +3 -3
- package/data/default-site-config.mjs +321 -321
- package/data/i18n-langmenu.mjs +347 -347
- package/data/layouts.mjs +436 -436
- package/data/path-mappings.mjs +26 -26
- package/data/route-metas.mjs +93 -93
- package/data/theme-colors.mjs +443 -443
- package/data/urls.mjs +12 -12
- package/domain/models/cli-options.ts +33 -33
- package/home/constants.mjs +4 -4
- package/index.mjs +37 -37
- package/infrastructure/gitpagedocs-config-file.ts +47 -47
- package/infrastructure/home/runtime.mjs +37 -37
- package/package.json +3 -3
- package/presentation/commands/ai-info.ts +57 -57
- package/presentation/commands/config-clear.ts +23 -23
- package/presentation/commands/config-info.ts +24 -24
- package/presentation/commands/diagnostics.ts +164 -164
- package/presentation/commands/docs.ts +37 -37
- package/presentation/commands/mcp.ts +20 -20
- package/presentation/commands/pages.ts +101 -101
- package/presentation/commands/password.ts +62 -62
- package/presentation/commands/run-command.ts +70 -70
- package/presentation/index.ts +124 -124
- package/presentation/options/parser.ts +119 -119
- package/presentation/options/resolver.ts +46 -46
- package/presentation/ui/clack.ts +91 -91
- package/presentation/ui/prompts.ts +146 -146
- package/runtime/git-ops.mjs +110 -110
- package/runtime/output.mjs +159 -159
- package/runtime/workflow.mjs +129 -129
- package/start.mjs +12 -12
- package/ui/logger.mjs +9 -9
|
@@ -1,239 +1,239 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AI_MODEL_DEFAULTS,
|
|
3
|
-
OLLAMA_DEFAULT_BASE_URL,
|
|
4
|
-
type AiProviderId,
|
|
5
|
-
} from '../config';
|
|
6
|
-
import { PROVIDER_CATALOG, legacyProviderToCatalogId } from '@gitpagedocs/tools/ai';
|
|
7
|
-
import type { AiCliConfig, AiCliRunPlan } from '../core/models/ai-cli-config';
|
|
8
|
-
import {
|
|
9
|
-
askText,
|
|
10
|
-
askConfirm,
|
|
11
|
-
askSelect,
|
|
12
|
-
askMultiSelect,
|
|
13
|
-
intro,
|
|
14
|
-
} from '../../presentation/ui/clack';
|
|
15
|
-
|
|
16
|
-
export interface InteractiveCliAnswers {
|
|
17
|
-
provider: AiProviderId;
|
|
18
|
-
apiKeyOrHost: string;
|
|
19
|
-
model: string;
|
|
20
|
-
targetDirs: string[];
|
|
21
|
-
languages: Array<'pt' | 'en' | 'es'>;
|
|
22
|
-
outputDir: string;
|
|
23
|
-
filePrefix: string;
|
|
24
|
-
saveConfig: boolean;
|
|
25
|
-
runConfigScaffold: boolean;
|
|
26
|
-
contextPrompt: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function parseCommaSeparated(value: string): string[] {
|
|
30
|
-
return value
|
|
31
|
-
.split(',')
|
|
32
|
-
.map((item) => item.trim())
|
|
33
|
-
.filter(Boolean);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function validateCommaSeparatedDirectories(value: string): string | undefined {
|
|
37
|
-
const values = parseCommaSeparated(value);
|
|
38
|
-
if (!values.length) return 'Provide at least one path.';
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const CUSTOM_MODEL = '__custom_model__';
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Ask for the model as a SELECT of the provider's real catalog models (with the
|
|
46
|
-
* default marked), plus a "Custom" escape hatch — so an invalid id like "4.8"
|
|
47
|
-
* can't be typed by mistake and rejected later as a 404 by the provider.
|
|
48
|
-
*/
|
|
49
|
-
async function askModel(provider: AiProviderId): Promise<string> {
|
|
50
|
-
const catalogId = legacyProviderToCatalogId(provider);
|
|
51
|
-
const spec = catalogId ? PROVIDER_CATALOG[catalogId] : undefined;
|
|
52
|
-
const fallback = AI_MODEL_DEFAULTS[provider] || '';
|
|
53
|
-
|
|
54
|
-
if (!spec || spec.models.length === 0) {
|
|
55
|
-
return String(await askText({ message: 'Which model do you want to use?', defaultValue: fallback }));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const picked = await askSelect<string>(
|
|
59
|
-
'Which model do you want to use?',
|
|
60
|
-
[
|
|
61
|
-
...spec.models.map((m) => ({
|
|
62
|
-
value: m.id,
|
|
63
|
-
label: m.id === spec.defaultModel ? `${m.id} (default)` : m.id,
|
|
64
|
-
})),
|
|
65
|
-
{ value: CUSTOM_MODEL, label: 'Custom — enter a model id manually' },
|
|
66
|
-
],
|
|
67
|
-
spec.defaultModel,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
if (picked !== CUSTOM_MODEL) return picked;
|
|
71
|
-
|
|
72
|
-
return String(
|
|
73
|
-
await askText({
|
|
74
|
-
message: 'Enter the model id:',
|
|
75
|
-
defaultValue: spec.defaultModel,
|
|
76
|
-
validate: (value) => (value.trim() ? undefined : 'Provide a model id'),
|
|
77
|
-
}),
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function toPlan(answers: InteractiveCliAnswers): AiCliRunPlan {
|
|
82
|
-
const config: AiCliConfig = {
|
|
83
|
-
version: 1,
|
|
84
|
-
ai: {
|
|
85
|
-
provider: answers.provider,
|
|
86
|
-
model: answers.model,
|
|
87
|
-
apiKey: answers.provider === 'ollama' ? undefined : answers.apiKeyOrHost,
|
|
88
|
-
baseUrl: answers.provider === 'ollama' ? answers.apiKeyOrHost : undefined,
|
|
89
|
-
paths: answers.targetDirs,
|
|
90
|
-
languages: answers.languages,
|
|
91
|
-
outputDir: answers.outputDir,
|
|
92
|
-
filePrefix: answers.filePrefix,
|
|
93
|
-
contextPrompt: answers.contextPrompt,
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
config,
|
|
99
|
-
saveConfig: answers.saveConfig,
|
|
100
|
-
runConfigScaffold: answers.runConfigScaffold,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export async function promptMissingDirectories(
|
|
105
|
-
missingDirectories: string[],
|
|
106
|
-
): Promise<{ replacementPaths: string[]; abort: boolean }> {
|
|
107
|
-
const recoveryMode = await askSelect(
|
|
108
|
-
`I couldn't find ${missingDirectories.length} directory(ies): ${missingDirectories.join(', ')}`,
|
|
109
|
-
[
|
|
110
|
-
{ value: 'fix', label: 'Fix paths now (recommended)' },
|
|
111
|
-
{ value: 'skip', label: 'Skip missing paths and continue' },
|
|
112
|
-
{ value: 'abort', label: 'Abort execution' },
|
|
113
|
-
],
|
|
114
|
-
'fix',
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
if (recoveryMode === 'abort') {
|
|
118
|
-
return { replacementPaths: [], abort: true };
|
|
119
|
-
}
|
|
120
|
-
if (recoveryMode === 'skip') {
|
|
121
|
-
return { replacementPaths: [], abort: false };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const fixedPaths = await askText({
|
|
125
|
-
message: 'Enter new paths (comma-separated):',
|
|
126
|
-
validate: validateCommaSeparatedDirectories,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
replacementPaths: parseCommaSeparated(String(fixedPaths || '')),
|
|
131
|
-
abort: false,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function promptUseExistingConfig(existingConfig: AiCliConfig): Promise<AiCliRunPlan | null> {
|
|
136
|
-
const useExistingConfig = await askConfirm(
|
|
137
|
-
'Found .gitpagedocsconfig. Do you want to use this configuration?',
|
|
138
|
-
true,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
if (!useExistingConfig) return null;
|
|
142
|
-
|
|
143
|
-
const runConfigScaffold = await askConfirm(
|
|
144
|
-
'Generate the base gitpagedocs structure first, so the AI pages are wired into the docs viewer?',
|
|
145
|
-
true,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
config: existingConfig,
|
|
150
|
-
saveConfig: false,
|
|
151
|
-
runConfigScaffold,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export async function runAiInteractivePrompt(existingConfig?: AiCliConfig | null): Promise<AiCliRunPlan> {
|
|
156
|
-
intro('GitPageDocs AI CLI - interactive mode');
|
|
157
|
-
|
|
158
|
-
if (existingConfig) {
|
|
159
|
-
const existingPlan = await promptUseExistingConfig(existingConfig);
|
|
160
|
-
if (existingPlan) return existingPlan;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const provider = await askSelect<AiProviderId>(
|
|
164
|
-
'Which AI provider should be used to generate documentation?',
|
|
165
|
-
[
|
|
166
|
-
{ value: 'openai', label: 'OpenAI (GPT)' },
|
|
167
|
-
{ value: 'claude', label: 'Anthropic (Claude)' },
|
|
168
|
-
{ value: 'gemini', label: 'Google Gemini' },
|
|
169
|
-
{ value: 'ollama', label: 'Ollama (Local Open Source)' },
|
|
170
|
-
],
|
|
171
|
-
'openai',
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
const apiKeyOrHost = await askText({
|
|
175
|
-
message: provider === 'ollama' ? 'Ollama host URL:' : 'Provider API key:',
|
|
176
|
-
defaultValue: provider === 'ollama' ? OLLAMA_DEFAULT_BASE_URL : undefined,
|
|
177
|
-
validate: (input) => (input.length > 0 ? undefined : 'Required field'),
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const model = await askModel(provider);
|
|
181
|
-
|
|
182
|
-
const targetDirsRaw = await askText({
|
|
183
|
-
message:
|
|
184
|
-
'Paths to analyze (comma-separated). You can include multiple repositories (e.g., src,cli,../other-repo/src):',
|
|
185
|
-
defaultValue: 'src,cli',
|
|
186
|
-
validate: validateCommaSeparatedDirectories,
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const languages = await askMultiSelect<'en' | 'pt' | 'es'>(
|
|
190
|
-
'Documentation languages to generate:',
|
|
191
|
-
[
|
|
192
|
-
{ value: 'en', label: 'English (en)' },
|
|
193
|
-
{ value: 'pt', label: 'Portuguese (pt)' },
|
|
194
|
-
{ value: 'es', label: 'Español (es)' },
|
|
195
|
-
],
|
|
196
|
-
['en', 'pt', 'es'],
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
const outputDir = await askText({
|
|
200
|
-
message: 'Output directory for generated markdown files:',
|
|
201
|
-
defaultValue: 'gitpagedocs/docs',
|
|
202
|
-
validate: (value) => (value.trim() ? undefined : 'Provide an output directory'),
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const filePrefix = await askText({
|
|
206
|
-
message: 'Output file prefix:',
|
|
207
|
-
defaultValue: 'ai-generated',
|
|
208
|
-
validate: (value) => (value.trim() ? undefined : 'Provide a prefix'),
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
const contextPrompt = await askText({
|
|
212
|
-
message: 'Extra instructions for AI (optional):',
|
|
213
|
-
defaultValue:
|
|
214
|
-
'You are a senior technical writer. Generate professional, clear documentation with architectural insight following FSD, SOLID, and Clean Code principles.',
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
const saveConfig = await askConfirm(
|
|
218
|
-
'Save this configuration to .gitpagedocsconfig for future manual use?',
|
|
219
|
-
true,
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
const runConfigScaffold = await askConfirm(
|
|
223
|
-
'Generate the base gitpagedocs structure first, so the AI pages are wired into the docs viewer?',
|
|
224
|
-
true,
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
return toPlan({
|
|
228
|
-
provider,
|
|
229
|
-
apiKeyOrHost,
|
|
230
|
-
model,
|
|
231
|
-
targetDirs: parseCommaSeparated(targetDirsRaw),
|
|
232
|
-
languages,
|
|
233
|
-
outputDir,
|
|
234
|
-
filePrefix,
|
|
235
|
-
contextPrompt,
|
|
236
|
-
saveConfig,
|
|
237
|
-
runConfigScaffold,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AI_MODEL_DEFAULTS,
|
|
3
|
+
OLLAMA_DEFAULT_BASE_URL,
|
|
4
|
+
type AiProviderId,
|
|
5
|
+
} from '../config';
|
|
6
|
+
import { PROVIDER_CATALOG, legacyProviderToCatalogId } from '@gitpagedocs/tools/ai';
|
|
7
|
+
import type { AiCliConfig, AiCliRunPlan } from '../core/models/ai-cli-config';
|
|
8
|
+
import {
|
|
9
|
+
askText,
|
|
10
|
+
askConfirm,
|
|
11
|
+
askSelect,
|
|
12
|
+
askMultiSelect,
|
|
13
|
+
intro,
|
|
14
|
+
} from '../../presentation/ui/clack';
|
|
15
|
+
|
|
16
|
+
export interface InteractiveCliAnswers {
|
|
17
|
+
provider: AiProviderId;
|
|
18
|
+
apiKeyOrHost: string;
|
|
19
|
+
model: string;
|
|
20
|
+
targetDirs: string[];
|
|
21
|
+
languages: Array<'pt' | 'en' | 'es'>;
|
|
22
|
+
outputDir: string;
|
|
23
|
+
filePrefix: string;
|
|
24
|
+
saveConfig: boolean;
|
|
25
|
+
runConfigScaffold: boolean;
|
|
26
|
+
contextPrompt: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parseCommaSeparated(value: string): string[] {
|
|
30
|
+
return value
|
|
31
|
+
.split(',')
|
|
32
|
+
.map((item) => item.trim())
|
|
33
|
+
.filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateCommaSeparatedDirectories(value: string): string | undefined {
|
|
37
|
+
const values = parseCommaSeparated(value);
|
|
38
|
+
if (!values.length) return 'Provide at least one path.';
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const CUSTOM_MODEL = '__custom_model__';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Ask for the model as a SELECT of the provider's real catalog models (with the
|
|
46
|
+
* default marked), plus a "Custom" escape hatch — so an invalid id like "4.8"
|
|
47
|
+
* can't be typed by mistake and rejected later as a 404 by the provider.
|
|
48
|
+
*/
|
|
49
|
+
async function askModel(provider: AiProviderId): Promise<string> {
|
|
50
|
+
const catalogId = legacyProviderToCatalogId(provider);
|
|
51
|
+
const spec = catalogId ? PROVIDER_CATALOG[catalogId] : undefined;
|
|
52
|
+
const fallback = AI_MODEL_DEFAULTS[provider] || '';
|
|
53
|
+
|
|
54
|
+
if (!spec || spec.models.length === 0) {
|
|
55
|
+
return String(await askText({ message: 'Which model do you want to use?', defaultValue: fallback }));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const picked = await askSelect<string>(
|
|
59
|
+
'Which model do you want to use?',
|
|
60
|
+
[
|
|
61
|
+
...spec.models.map((m) => ({
|
|
62
|
+
value: m.id,
|
|
63
|
+
label: m.id === spec.defaultModel ? `${m.id} (default)` : m.id,
|
|
64
|
+
})),
|
|
65
|
+
{ value: CUSTOM_MODEL, label: 'Custom — enter a model id manually' },
|
|
66
|
+
],
|
|
67
|
+
spec.defaultModel,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (picked !== CUSTOM_MODEL) return picked;
|
|
71
|
+
|
|
72
|
+
return String(
|
|
73
|
+
await askText({
|
|
74
|
+
message: 'Enter the model id:',
|
|
75
|
+
defaultValue: spec.defaultModel,
|
|
76
|
+
validate: (value) => (value.trim() ? undefined : 'Provide a model id'),
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toPlan(answers: InteractiveCliAnswers): AiCliRunPlan {
|
|
82
|
+
const config: AiCliConfig = {
|
|
83
|
+
version: 1,
|
|
84
|
+
ai: {
|
|
85
|
+
provider: answers.provider,
|
|
86
|
+
model: answers.model,
|
|
87
|
+
apiKey: answers.provider === 'ollama' ? undefined : answers.apiKeyOrHost,
|
|
88
|
+
baseUrl: answers.provider === 'ollama' ? answers.apiKeyOrHost : undefined,
|
|
89
|
+
paths: answers.targetDirs,
|
|
90
|
+
languages: answers.languages,
|
|
91
|
+
outputDir: answers.outputDir,
|
|
92
|
+
filePrefix: answers.filePrefix,
|
|
93
|
+
contextPrompt: answers.contextPrompt,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
config,
|
|
99
|
+
saveConfig: answers.saveConfig,
|
|
100
|
+
runConfigScaffold: answers.runConfigScaffold,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function promptMissingDirectories(
|
|
105
|
+
missingDirectories: string[],
|
|
106
|
+
): Promise<{ replacementPaths: string[]; abort: boolean }> {
|
|
107
|
+
const recoveryMode = await askSelect(
|
|
108
|
+
`I couldn't find ${missingDirectories.length} directory(ies): ${missingDirectories.join(', ')}`,
|
|
109
|
+
[
|
|
110
|
+
{ value: 'fix', label: 'Fix paths now (recommended)' },
|
|
111
|
+
{ value: 'skip', label: 'Skip missing paths and continue' },
|
|
112
|
+
{ value: 'abort', label: 'Abort execution' },
|
|
113
|
+
],
|
|
114
|
+
'fix',
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (recoveryMode === 'abort') {
|
|
118
|
+
return { replacementPaths: [], abort: true };
|
|
119
|
+
}
|
|
120
|
+
if (recoveryMode === 'skip') {
|
|
121
|
+
return { replacementPaths: [], abort: false };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const fixedPaths = await askText({
|
|
125
|
+
message: 'Enter new paths (comma-separated):',
|
|
126
|
+
validate: validateCommaSeparatedDirectories,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
replacementPaths: parseCommaSeparated(String(fixedPaths || '')),
|
|
131
|
+
abort: false,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function promptUseExistingConfig(existingConfig: AiCliConfig): Promise<AiCliRunPlan | null> {
|
|
136
|
+
const useExistingConfig = await askConfirm(
|
|
137
|
+
'Found .gitpagedocsconfig. Do you want to use this configuration?',
|
|
138
|
+
true,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (!useExistingConfig) return null;
|
|
142
|
+
|
|
143
|
+
const runConfigScaffold = await askConfirm(
|
|
144
|
+
'Generate the base gitpagedocs structure first, so the AI pages are wired into the docs viewer?',
|
|
145
|
+
true,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
config: existingConfig,
|
|
150
|
+
saveConfig: false,
|
|
151
|
+
runConfigScaffold,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function runAiInteractivePrompt(existingConfig?: AiCliConfig | null): Promise<AiCliRunPlan> {
|
|
156
|
+
intro('GitPageDocs AI CLI - interactive mode');
|
|
157
|
+
|
|
158
|
+
if (existingConfig) {
|
|
159
|
+
const existingPlan = await promptUseExistingConfig(existingConfig);
|
|
160
|
+
if (existingPlan) return existingPlan;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const provider = await askSelect<AiProviderId>(
|
|
164
|
+
'Which AI provider should be used to generate documentation?',
|
|
165
|
+
[
|
|
166
|
+
{ value: 'openai', label: 'OpenAI (GPT)' },
|
|
167
|
+
{ value: 'claude', label: 'Anthropic (Claude)' },
|
|
168
|
+
{ value: 'gemini', label: 'Google Gemini' },
|
|
169
|
+
{ value: 'ollama', label: 'Ollama (Local Open Source)' },
|
|
170
|
+
],
|
|
171
|
+
'openai',
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const apiKeyOrHost = await askText({
|
|
175
|
+
message: provider === 'ollama' ? 'Ollama host URL:' : 'Provider API key:',
|
|
176
|
+
defaultValue: provider === 'ollama' ? OLLAMA_DEFAULT_BASE_URL : undefined,
|
|
177
|
+
validate: (input) => (input.length > 0 ? undefined : 'Required field'),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const model = await askModel(provider);
|
|
181
|
+
|
|
182
|
+
const targetDirsRaw = await askText({
|
|
183
|
+
message:
|
|
184
|
+
'Paths to analyze (comma-separated). You can include multiple repositories (e.g., src,cli,../other-repo/src):',
|
|
185
|
+
defaultValue: 'src,cli',
|
|
186
|
+
validate: validateCommaSeparatedDirectories,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const languages = await askMultiSelect<'en' | 'pt' | 'es'>(
|
|
190
|
+
'Documentation languages to generate:',
|
|
191
|
+
[
|
|
192
|
+
{ value: 'en', label: 'English (en)' },
|
|
193
|
+
{ value: 'pt', label: 'Portuguese (pt)' },
|
|
194
|
+
{ value: 'es', label: 'Español (es)' },
|
|
195
|
+
],
|
|
196
|
+
['en', 'pt', 'es'],
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const outputDir = await askText({
|
|
200
|
+
message: 'Output directory for generated markdown files:',
|
|
201
|
+
defaultValue: 'gitpagedocs/docs',
|
|
202
|
+
validate: (value) => (value.trim() ? undefined : 'Provide an output directory'),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const filePrefix = await askText({
|
|
206
|
+
message: 'Output file prefix:',
|
|
207
|
+
defaultValue: 'ai-generated',
|
|
208
|
+
validate: (value) => (value.trim() ? undefined : 'Provide a prefix'),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const contextPrompt = await askText({
|
|
212
|
+
message: 'Extra instructions for AI (optional):',
|
|
213
|
+
defaultValue:
|
|
214
|
+
'You are a senior technical writer. Generate professional, clear documentation with architectural insight following FSD, SOLID, and Clean Code principles.',
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const saveConfig = await askConfirm(
|
|
218
|
+
'Save this configuration to .gitpagedocsconfig for future manual use?',
|
|
219
|
+
true,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const runConfigScaffold = await askConfirm(
|
|
223
|
+
'Generate the base gitpagedocs structure first, so the AI pages are wired into the docs viewer?',
|
|
224
|
+
true,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
return toPlan({
|
|
228
|
+
provider,
|
|
229
|
+
apiKeyOrHost,
|
|
230
|
+
model,
|
|
231
|
+
targetDirs: parseCommaSeparated(targetDirsRaw),
|
|
232
|
+
languages,
|
|
233
|
+
outputDir,
|
|
234
|
+
filePrefix,
|
|
235
|
+
contextPrompt,
|
|
236
|
+
saveConfig,
|
|
237
|
+
runConfigScaffold,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
import type { CliOptions } from "../../domain/models/cli-options";
|
|
2
|
-
|
|
3
|
-
export interface BuildArtifactsInput {
|
|
4
|
-
useLocalLayoutConfig: boolean;
|
|
5
|
-
/** Repo-relative layouts home the generated config should reference. */
|
|
6
|
-
layoutsDir: string;
|
|
7
|
-
githubOwner: string;
|
|
8
|
-
githubRepo: string;
|
|
9
|
-
root: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface LayoutDefinition {
|
|
13
|
-
file: string;
|
|
14
|
-
[key: string]: unknown;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface BuiltConfigArtifacts {
|
|
18
|
-
rootConfig: unknown;
|
|
19
|
-
layoutsConfig: unknown;
|
|
20
|
-
fallbackLayoutsConfig: unknown;
|
|
21
|
-
docs?: Record<string, Record<string, string>>;
|
|
22
|
-
docsHtml?: Record<string, Record<string, string>>;
|
|
23
|
-
versionConfigs: Record<string, Record<string, unknown>>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface CliRuntimeParams {
|
|
27
|
-
root: string;
|
|
28
|
-
pkgRoot: string;
|
|
29
|
-
prebuiltDir: string;
|
|
30
|
-
buildConfigArtifacts: (input: BuildArtifactsInput) => BuiltConfigArtifacts;
|
|
31
|
-
createThemeTemplate: (layout: LayoutDefinition) => unknown;
|
|
32
|
-
layouts: LayoutDefinition[];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CliCommandContext extends CliRuntimeParams {
|
|
36
|
-
options: CliOptions;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface CliCommandRunner {
|
|
40
|
-
runConfigOnly: (params: CliCommandContext) => Promise<void>;
|
|
41
|
-
runHome: (params: CliCommandContext) => Promise<void>;
|
|
42
|
-
runAi: (params: CliCommandContext) => Promise<void>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ConfigOnlyOutputInput {
|
|
46
|
-
root: string;
|
|
47
|
-
pkgRoot: string;
|
|
48
|
-
outputDir: string;
|
|
49
|
-
/** Repo-relative layouts home local layouts are written to. */
|
|
50
|
-
layoutsDir: string;
|
|
51
|
-
artifacts: BuiltConfigArtifacts;
|
|
52
|
-
useLocalLayoutConfig: boolean;
|
|
53
|
-
layouts: LayoutDefinition[];
|
|
54
|
-
createThemeTemplate: (layout: LayoutDefinition) => unknown;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface ConfigOnlyRuntimePort {
|
|
58
|
-
hasPath: (absolutePath: string) => boolean;
|
|
59
|
-
writeConfigOnlyOutput: (input: ConfigOnlyOutputInput) => Promise<void>;
|
|
60
|
-
writeText: (root: string, relativePath: string, data: string) => Promise<void>;
|
|
61
|
-
ensureGitHubPagesWorkflow: (
|
|
62
|
-
getCurrentGitBranch: () => string,
|
|
63
|
-
writeText: (relativePath: string, content: string) => Promise<void>,
|
|
64
|
-
docsPath?: string,
|
|
65
|
-
) => Promise<void>;
|
|
66
|
-
getCurrentGitBranch: (root: string) => string;
|
|
67
|
-
runGitPushForGeneratedArtifacts: (
|
|
68
|
-
options: CliOptions,
|
|
69
|
-
root: string,
|
|
70
|
-
sanitizeSegment: (value: string | undefined | null) => string,
|
|
71
|
-
) => void;
|
|
72
|
-
logInfo: (message: string) => void;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface HomeRuntimePort {
|
|
76
|
-
joinPath: (...parts: string[]) => string;
|
|
77
|
-
ensureDirEmpty: (root: string, relativeDir: string) => void;
|
|
78
|
-
copyRecursive: (src: string, dest: string) => void;
|
|
79
|
-
readDirNames: (absolutePath: string) => string[];
|
|
80
|
-
existsPath: (absolutePath: string) => boolean;
|
|
81
|
-
runNextBuild: (root: string, env: NodeJS.ProcessEnv) => void;
|
|
82
|
-
writeConfigOnlyOutput: (input: ConfigOnlyOutputInput) => Promise<void>;
|
|
83
|
-
writeHomeFiles: (
|
|
84
|
-
root: string,
|
|
85
|
-
outputDir: string,
|
|
86
|
-
options: { repositorySearch?: boolean; basePath?: string },
|
|
87
|
-
) => Promise<void>;
|
|
88
|
-
writeText: (root: string, relativePath: string, data: string) => Promise<void>;
|
|
89
|
-
logSuccess: (message: string) => void;
|
|
90
|
-
logInfo: (message: string) => void;
|
|
91
|
-
}
|
|
1
|
+
import type { CliOptions } from "../../domain/models/cli-options";
|
|
2
|
+
|
|
3
|
+
export interface BuildArtifactsInput {
|
|
4
|
+
useLocalLayoutConfig: boolean;
|
|
5
|
+
/** Repo-relative layouts home the generated config should reference. */
|
|
6
|
+
layoutsDir: string;
|
|
7
|
+
githubOwner: string;
|
|
8
|
+
githubRepo: string;
|
|
9
|
+
root: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LayoutDefinition {
|
|
13
|
+
file: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface BuiltConfigArtifacts {
|
|
18
|
+
rootConfig: unknown;
|
|
19
|
+
layoutsConfig: unknown;
|
|
20
|
+
fallbackLayoutsConfig: unknown;
|
|
21
|
+
docs?: Record<string, Record<string, string>>;
|
|
22
|
+
docsHtml?: Record<string, Record<string, string>>;
|
|
23
|
+
versionConfigs: Record<string, Record<string, unknown>>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CliRuntimeParams {
|
|
27
|
+
root: string;
|
|
28
|
+
pkgRoot: string;
|
|
29
|
+
prebuiltDir: string;
|
|
30
|
+
buildConfigArtifacts: (input: BuildArtifactsInput) => BuiltConfigArtifacts;
|
|
31
|
+
createThemeTemplate: (layout: LayoutDefinition) => unknown;
|
|
32
|
+
layouts: LayoutDefinition[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CliCommandContext extends CliRuntimeParams {
|
|
36
|
+
options: CliOptions;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CliCommandRunner {
|
|
40
|
+
runConfigOnly: (params: CliCommandContext) => Promise<void>;
|
|
41
|
+
runHome: (params: CliCommandContext) => Promise<void>;
|
|
42
|
+
runAi: (params: CliCommandContext) => Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ConfigOnlyOutputInput {
|
|
46
|
+
root: string;
|
|
47
|
+
pkgRoot: string;
|
|
48
|
+
outputDir: string;
|
|
49
|
+
/** Repo-relative layouts home local layouts are written to. */
|
|
50
|
+
layoutsDir: string;
|
|
51
|
+
artifacts: BuiltConfigArtifacts;
|
|
52
|
+
useLocalLayoutConfig: boolean;
|
|
53
|
+
layouts: LayoutDefinition[];
|
|
54
|
+
createThemeTemplate: (layout: LayoutDefinition) => unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ConfigOnlyRuntimePort {
|
|
58
|
+
hasPath: (absolutePath: string) => boolean;
|
|
59
|
+
writeConfigOnlyOutput: (input: ConfigOnlyOutputInput) => Promise<void>;
|
|
60
|
+
writeText: (root: string, relativePath: string, data: string) => Promise<void>;
|
|
61
|
+
ensureGitHubPagesWorkflow: (
|
|
62
|
+
getCurrentGitBranch: () => string,
|
|
63
|
+
writeText: (relativePath: string, content: string) => Promise<void>,
|
|
64
|
+
docsPath?: string,
|
|
65
|
+
) => Promise<void>;
|
|
66
|
+
getCurrentGitBranch: (root: string) => string;
|
|
67
|
+
runGitPushForGeneratedArtifacts: (
|
|
68
|
+
options: CliOptions,
|
|
69
|
+
root: string,
|
|
70
|
+
sanitizeSegment: (value: string | undefined | null) => string,
|
|
71
|
+
) => void;
|
|
72
|
+
logInfo: (message: string) => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface HomeRuntimePort {
|
|
76
|
+
joinPath: (...parts: string[]) => string;
|
|
77
|
+
ensureDirEmpty: (root: string, relativeDir: string) => void;
|
|
78
|
+
copyRecursive: (src: string, dest: string) => void;
|
|
79
|
+
readDirNames: (absolutePath: string) => string[];
|
|
80
|
+
existsPath: (absolutePath: string) => boolean;
|
|
81
|
+
runNextBuild: (root: string, env: NodeJS.ProcessEnv) => void;
|
|
82
|
+
writeConfigOnlyOutput: (input: ConfigOnlyOutputInput) => Promise<void>;
|
|
83
|
+
writeHomeFiles: (
|
|
84
|
+
root: string,
|
|
85
|
+
outputDir: string,
|
|
86
|
+
options: { repositorySearch?: boolean; basePath?: string },
|
|
87
|
+
) => Promise<void>;
|
|
88
|
+
writeText: (root: string, relativePath: string, data: string) => Promise<void>;
|
|
89
|
+
logSuccess: (message: string) => void;
|
|
90
|
+
logInfo: (message: string) => void;
|
|
91
|
+
}
|