@gitpagedocs/cli 1.1.62 → 1.1.63

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.
Files changed (59) hide show
  1. package/README.md +193 -193
  2. package/ai/application/ai-command.ts +24 -24
  3. package/ai/application/ai-provider-factory.ts +22 -22
  4. package/ai/application/docs-pattern.ts +68 -68
  5. package/ai/application/run-ai-cli-command.ts +167 -167
  6. package/ai/config.ts +50 -50
  7. package/ai/core/models/ai-cli-config.ts +33 -33
  8. package/ai/core/models/parse-ai-cli-config.ts +78 -78
  9. package/ai/core/ports/illm-provider.ts +32 -32
  10. package/ai/infrastructure/ai-config-file.ts +101 -101
  11. package/ai/infrastructure/file-system-adapter.ts +85 -85
  12. package/ai/infrastructure/llm/tools-llm-provider.ts +54 -54
  13. package/ai/infrastructure/version-docs-writer.ts +143 -143
  14. package/ai/presentation/ai-prompts.ts +239 -239
  15. package/application/ports/cli-runtime-ports.ts +91 -91
  16. package/application/report/config-only-reporter.mjs +64 -64
  17. package/application/use-cases/dispatch-mode.ts +18 -18
  18. package/builders/config-orchestrator.mjs +34 -34
  19. package/builders/project-links.mjs +50 -50
  20. package/builders/root-config-builder.mjs +61 -61
  21. package/builders/route-builders.mjs +136 -136
  22. package/builders/theme-template.mjs +76 -76
  23. package/builders/version-config-builder.mjs +186 -186
  24. package/content/docs-en.mjs +549 -549
  25. package/content/docs-es.mjs +462 -462
  26. package/content/docs-pt.mjs +462 -462
  27. package/contracts/doc-versions.mjs +3 -3
  28. package/data/default-site-config.mjs +321 -321
  29. package/data/i18n-langmenu.mjs +347 -347
  30. package/data/layouts.mjs +436 -436
  31. package/data/path-mappings.mjs +26 -26
  32. package/data/route-metas.mjs +93 -93
  33. package/data/theme-colors.mjs +443 -443
  34. package/data/urls.mjs +12 -12
  35. package/domain/models/cli-options.ts +33 -33
  36. package/home/constants.mjs +4 -4
  37. package/index.mjs +37 -37
  38. package/infrastructure/gitpagedocs-config-file.ts +47 -47
  39. package/infrastructure/home/runtime.mjs +37 -37
  40. package/package.json +3 -3
  41. package/presentation/commands/ai-info.ts +57 -57
  42. package/presentation/commands/config-clear.ts +23 -23
  43. package/presentation/commands/config-info.ts +24 -24
  44. package/presentation/commands/diagnostics.ts +164 -164
  45. package/presentation/commands/docs.ts +37 -37
  46. package/presentation/commands/mcp.ts +20 -20
  47. package/presentation/commands/pages.ts +101 -101
  48. package/presentation/commands/password.ts +62 -62
  49. package/presentation/commands/run-command.ts +70 -70
  50. package/presentation/index.ts +124 -124
  51. package/presentation/options/parser.ts +119 -119
  52. package/presentation/options/resolver.ts +46 -46
  53. package/presentation/ui/clack.ts +91 -91
  54. package/presentation/ui/prompts.ts +146 -146
  55. package/runtime/git-ops.mjs +110 -110
  56. package/runtime/output.mjs +159 -159
  57. package/runtime/workflow.mjs +129 -129
  58. package/start.mjs +12 -12
  59. 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
+ }