specweave 0.26.14 ā 0.28.0
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/CLAUDE.md +73 -1
- package/README.md +245 -446
- package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/setup-wizard.js +57 -78
- package/dist/plugins/specweave-jira/lib/setup-wizard.js.map +1 -1
- package/dist/src/cli/commands/import-docs.d.ts.map +1 -1
- package/dist/src/cli/commands/import-docs.js +23 -31
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- package/dist/src/cli/commands/import-external.d.ts.map +1 -1
- package/dist/src/cli/commands/import-external.js +6 -10
- package/dist/src/cli/commands/import-external.js.map +1 -1
- package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/init-multiproject.js +58 -73
- package/dist/src/cli/commands/init-multiproject.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts +17 -11
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +221 -1874
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/install.d.ts.map +1 -1
- package/dist/src/cli/commands/install.js +14 -22
- package/dist/src/cli/commands/install.js.map +1 -1
- package/dist/src/cli/commands/migrate-config.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-config.js +6 -10
- package/dist/src/cli/commands/migrate-config.js.map +1 -1
- package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
- package/dist/src/cli/commands/switch-project.js.map +1 -1
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -1
- package/dist/src/cli/helpers/ado-area-path-mapper.js +36 -49
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.d.ts.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
- package/dist/src/cli/helpers/github/profile-manager.d.ts.map +1 -1
- package/dist/src/cli/helpers/github/profile-manager.js +8 -11
- package/dist/src/cli/helpers/github/profile-manager.js.map +1 -1
- package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -1
- package/dist/src/cli/helpers/github-repo-selector.js +26 -50
- package/dist/src/cli/helpers/github-repo-selector.js.map +1 -1
- package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -1
- package/dist/src/cli/helpers/import-strategy-prompter.js +39 -52
- package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -1
- package/dist/src/cli/helpers/init/config-detection.d.ts +40 -0
- package/dist/src/cli/helpers/init/config-detection.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/config-detection.js +125 -0
- package/dist/src/cli/helpers/init/config-detection.js.map +1 -0
- package/dist/src/cli/helpers/init/directory-structure.d.ts +26 -0
- package/dist/src/cli/helpers/init/directory-structure.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/directory-structure.js +190 -0
- package/dist/src/cli/helpers/init/directory-structure.js.map +1 -0
- package/dist/src/cli/helpers/init/external-import.d.ts +15 -0
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/external-import.js +251 -0
- package/dist/src/cli/helpers/init/external-import.js.map +1 -0
- package/dist/src/cli/helpers/init/index.d.ts +15 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/index.js +26 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -0
- package/dist/src/cli/helpers/init/next-steps.d.ts +15 -0
- package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/next-steps.js +72 -0
- package/dist/src/cli/helpers/init/next-steps.js.map +1 -0
- package/dist/src/cli/helpers/init/path-utils.d.ts +41 -0
- package/dist/src/cli/helpers/init/path-utils.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/path-utils.js +146 -0
- package/dist/src/cli/helpers/init/path-utils.js.map +1 -0
- package/dist/src/cli/helpers/init/plugin-installer.d.ts +28 -0
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/plugin-installer.js +238 -0
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts +28 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/repository-setup.js +78 -0
- package/dist/src/cli/helpers/init/repository-setup.js.map +1 -0
- package/dist/src/cli/helpers/init/smart-reinit.d.ts +30 -0
- package/dist/src/cli/helpers/init/smart-reinit.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/smart-reinit.js +140 -0
- package/dist/src/cli/helpers/init/smart-reinit.js.map +1 -0
- package/dist/src/cli/helpers/init/testing-config.d.ts +27 -0
- package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/testing-config.js +131 -0
- package/dist/src/cli/helpers/init/testing-config.js.map +1 -0
- package/dist/src/cli/helpers/init/types.d.ts +86 -0
- package/dist/src/cli/helpers/init/types.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/types.js +5 -0
- package/dist/src/cli/helpers/init/types.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +10 -12
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +43 -60
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +193 -230
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github.js +43 -54
- package/dist/src/cli/helpers/issue-tracker/github.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +27 -40
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.js +54 -70
- package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -1
- package/dist/src/cli/helpers/smart-filter.js +62 -85
- package/dist/src/cli/helpers/smart-filter.js.map +1 -1
- package/dist/src/core/increment/auto-transition-manager.d.ts +12 -0
- package/dist/src/core/increment/auto-transition-manager.d.ts.map +1 -1
- package/dist/src/core/increment/auto-transition-manager.js +45 -0
- package/dist/src/core/increment/auto-transition-manager.js.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +46 -0
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts +12 -0
- package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.js +48 -2
- package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +40 -0
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/repo-structure/repo-bulk-discovery.d.ts +5 -1
- package/dist/src/core/repo-structure/repo-bulk-discovery.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-bulk-discovery.js +68 -86
- package/dist/src/core/repo-structure/repo-bulk-discovery.js.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +345 -425
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.js +21 -29
- package/dist/src/core/sync/bidirectional-engine.js.map +1 -1
- package/dist/src/init/InitFlow.js +15 -19
- package/dist/src/init/InitFlow.js.map +1 -1
- package/dist/src/init/repo/types.d.ts +1 -1
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -1
- package/dist/src/integrations/ado/area-path-mapper.js +19 -23
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -1
- package/dist/src/utils/external-resource-validator.d.ts.map +1 -1
- package/dist/src/utils/external-resource-validator.js +41 -65
- package/dist/src/utils/external-resource-validator.js.map +1 -1
- package/dist/src/utils/project-detection.d.ts.map +1 -1
- package/dist/src/utils/project-detection.js +19 -21
- package/dist/src/utils/project-detection.js.map +1 -1
- package/dist/src/utils/project-validator.d.ts.map +1 -1
- package/dist/src/utils/project-validator.js +5 -7
- package/dist/src/utils/project-validator.js.map +1 -1
- package/package.json +2 -3
- package/plugins/specweave/agents/tech-lead/AGENT.md +9 -0
- package/plugins/specweave/hooks/post-edit-write-consolidated.sh +87 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +12 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +45 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +46 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/skills/increment-planner/SKILL.md +10 -5
- package/plugins/specweave/skills/specweave-framework/SKILL.md +6 -4
- package/plugins/specweave/templates/coding-standards.md.template +36 -0
- package/plugins/specweave-ado/lib/project-selector.js +56 -67
- package/plugins/specweave-ado/lib/project-selector.ts +72 -85
- package/plugins/specweave-github/lib/repo-selector.js +55 -66
- package/plugins/specweave-github/lib/repo-selector.ts +73 -84
- package/plugins/specweave-jira/commands/import-projects.js +3 -5
- package/plugins/specweave-jira/commands/import-projects.ts +3 -5
- package/plugins/specweave-jira/lib/project-selector.js +60 -71
- package/plugins/specweave-jira/lib/project-selector.ts +78 -91
- package/plugins/specweave-jira/lib/setup-wizard.js +51 -72
- package/plugins/specweave-jira/lib/setup-wizard.ts +56 -74
- package/src/templates/CLAUDE.md.template +14 -0
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
18
18
|
import path from 'path';
|
|
19
19
|
import chalk from 'chalk';
|
|
20
|
-
import
|
|
20
|
+
import { select, input, confirm, number } from '@inquirer/prompts';
|
|
21
21
|
import ora from 'ora';
|
|
22
22
|
import { execSync } from 'child_process';
|
|
23
23
|
import { execFileNoThrowSync } from '../../utils/execFileNoThrow.js';
|
|
@@ -54,12 +54,10 @@ export class RepoStructureManager {
|
|
|
54
54
|
console.log(chalk.yellow('\nāøļø Detected interrupted setup!'));
|
|
55
55
|
console.log(chalk.gray(` Last step: ${resumedState.currentStep}`));
|
|
56
56
|
console.log(chalk.gray(` Time: ${new Date(resumedState.timestamp).toLocaleString()}\n`));
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
default: true
|
|
62
|
-
}]);
|
|
57
|
+
const shouldResume = await confirm({
|
|
58
|
+
message: 'Resume previous setup?',
|
|
59
|
+
default: true
|
|
60
|
+
});
|
|
63
61
|
if (shouldResume) {
|
|
64
62
|
return this.resumeSetup(resumedState);
|
|
65
63
|
}
|
|
@@ -78,38 +76,31 @@ export class RepoStructureManager {
|
|
|
78
76
|
else {
|
|
79
77
|
// Ask user for architecture choice
|
|
80
78
|
const promptData = getArchitecturePrompt();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
})),
|
|
90
|
-
default: 'single'
|
|
91
|
-
}]);
|
|
92
|
-
architecture = result.architecture;
|
|
79
|
+
architecture = await select({
|
|
80
|
+
message: promptData.question,
|
|
81
|
+
choices: promptData.options.map(opt => ({
|
|
82
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}\n${chalk.dim(opt.example)}`,
|
|
83
|
+
value: opt.value
|
|
84
|
+
})),
|
|
85
|
+
default: 'single'
|
|
86
|
+
});
|
|
93
87
|
}
|
|
94
88
|
// Step 2: Ask about Git hosting platform
|
|
95
89
|
const registry = getPlatformRegistry();
|
|
96
90
|
const platformOptions = registry.getPlatformOptions(true); // Include unsupported platforms
|
|
97
91
|
const platformPromptData = getPlatformSelectionPrompt();
|
|
98
92
|
console.log(chalk.cyan('\n' + platformPromptData.message));
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
})),
|
|
111
|
-
default: 'github'
|
|
112
|
-
}]);
|
|
93
|
+
const platform = await select({
|
|
94
|
+
message: platformPromptData.question,
|
|
95
|
+
choices: platformOptions.map(opt => ({
|
|
96
|
+
name: opt.disabled
|
|
97
|
+
? `${opt.name}\n${chalk.gray(opt.description)}\n${chalk.yellow('ā ļø ' + opt.disabled)}`
|
|
98
|
+
: `${opt.name}\n${chalk.gray(opt.description)}`,
|
|
99
|
+
value: opt.value,
|
|
100
|
+
disabled: opt.disabled ? opt.disabled : false
|
|
101
|
+
})),
|
|
102
|
+
default: 'github'
|
|
103
|
+
});
|
|
113
104
|
// Get provider instance
|
|
114
105
|
const provider = registry.getProvider(platform);
|
|
115
106
|
if (!provider) {
|
|
@@ -118,17 +109,14 @@ export class RepoStructureManager {
|
|
|
118
109
|
console.log(chalk.green(`\nā Using ${provider.config.name} as Git hosting platform\n`));
|
|
119
110
|
// Step 3: Ask about Git remote URL format (SSH vs HTTPS)
|
|
120
111
|
const urlTypePromptData = getUrlTypePrompt();
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
})),
|
|
130
|
-
default: urlTypePromptData.default
|
|
131
|
-
}]);
|
|
112
|
+
const urlType = await select({
|
|
113
|
+
message: urlTypePromptData.question,
|
|
114
|
+
choices: urlTypePromptData.options.map(opt => ({
|
|
115
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}`,
|
|
116
|
+
value: opt.value
|
|
117
|
+
})),
|
|
118
|
+
default: urlTypePromptData.default
|
|
119
|
+
});
|
|
132
120
|
console.log(chalk.green(`\nā Using ${urlType.toUpperCase()} remote URLs\n`));
|
|
133
121
|
// Map ArchitectureChoice to internal architecture
|
|
134
122
|
const mappedArch = this.mapArchitectureChoice(architecture);
|
|
@@ -222,12 +210,10 @@ export class RepoStructureManager {
|
|
|
222
210
|
const owner = match[1];
|
|
223
211
|
const repo = match[2];
|
|
224
212
|
console.log(chalk.green(`ā Existing repository detected: ${owner}/${repo}`));
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
default: true
|
|
230
|
-
}]);
|
|
213
|
+
const useExisting = await confirm({
|
|
214
|
+
message: 'Use existing repository?',
|
|
215
|
+
default: true
|
|
216
|
+
});
|
|
231
217
|
if (useExisting) {
|
|
232
218
|
// Fetch description and visibility from GitHub API
|
|
233
219
|
let description = `${repo} - SpecWeave project`;
|
|
@@ -274,49 +260,41 @@ export class RepoStructureManager {
|
|
|
274
260
|
}
|
|
275
261
|
}
|
|
276
262
|
// Manual configuration
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
message: 'Create repository on GitHub?',
|
|
301
|
-
default: !hasGit
|
|
302
|
-
}
|
|
303
|
-
]);
|
|
263
|
+
const ownerAnswer = await input({
|
|
264
|
+
message: 'GitHub owner/organization:',
|
|
265
|
+
validate: (val) => !!val.trim() || 'Owner is required'
|
|
266
|
+
});
|
|
267
|
+
const repoAnswer = await input({
|
|
268
|
+
message: 'Repository name:',
|
|
269
|
+
default: path.basename(this.projectPath),
|
|
270
|
+
validate: (val) => !!val.trim() || 'Repository name is required'
|
|
271
|
+
});
|
|
272
|
+
const descriptionAnswer = await input({
|
|
273
|
+
message: 'Repository description:',
|
|
274
|
+
default: 'My SpecWeave project'
|
|
275
|
+
});
|
|
276
|
+
const createOnGitHubAnswer = await confirm({
|
|
277
|
+
message: 'Create repository on GitHub?',
|
|
278
|
+
default: !hasGit
|
|
279
|
+
});
|
|
280
|
+
const answers = {
|
|
281
|
+
owner: ownerAnswer,
|
|
282
|
+
repo: repoAnswer,
|
|
283
|
+
description: descriptionAnswer,
|
|
284
|
+
createOnGitHub: createOnGitHubAnswer
|
|
285
|
+
};
|
|
304
286
|
// Ask about visibility only if creating a new repository
|
|
305
287
|
let visibility = 'private';
|
|
306
288
|
if (answers.createOnGitHub) {
|
|
307
289
|
const visibilityPrompt = getVisibilityPrompt(answers.repo);
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
})),
|
|
317
|
-
default: visibilityPrompt.default
|
|
318
|
-
}]);
|
|
319
|
-
visibility = result.visibility;
|
|
290
|
+
visibility = await select({
|
|
291
|
+
message: visibilityPrompt.question,
|
|
292
|
+
choices: visibilityPrompt.options.map(opt => ({
|
|
293
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}`,
|
|
294
|
+
value: opt.value
|
|
295
|
+
})),
|
|
296
|
+
default: visibilityPrompt.default
|
|
297
|
+
});
|
|
320
298
|
}
|
|
321
299
|
return {
|
|
322
300
|
architecture: 'single',
|
|
@@ -380,72 +358,62 @@ export class RepoStructureManager {
|
|
|
380
358
|
if (!isLocalParent && this.githubToken && useParent) {
|
|
381
359
|
console.log(chalk.cyan('\nš Repository Discovery\n'));
|
|
382
360
|
console.log(chalk.gray('You\'re setting up multiple repositories. We can discover them automatically!\n'));
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
short: 'Manual'
|
|
411
|
-
}
|
|
412
|
-
],
|
|
413
|
-
default: 'bulk-discovery' // Make bulk discovery the default!
|
|
414
|
-
}
|
|
415
|
-
]);
|
|
361
|
+
const configMethod = await select({
|
|
362
|
+
message: 'How do you want to configure repositories?',
|
|
363
|
+
choices: [
|
|
364
|
+
{
|
|
365
|
+
name: [
|
|
366
|
+
chalk.bold.green('šÆ Bulk Discovery (RECOMMENDED)'),
|
|
367
|
+
chalk.gray(' Automatically discover repos from ' + provider.config.name),
|
|
368
|
+
chalk.gray(' ⢠Select parent from discovered list'),
|
|
369
|
+
chalk.gray(' ⢠Auto-configure implementation repos'),
|
|
370
|
+
chalk.gray(' ⢠Supports: all, pattern, regex filtering'),
|
|
371
|
+
''
|
|
372
|
+
].join('\n'),
|
|
373
|
+
value: 'bulk-discovery'
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: [
|
|
377
|
+
chalk.bold('āļø Manual Entry'),
|
|
378
|
+
chalk.gray(' Enter each repository manually'),
|
|
379
|
+
chalk.gray(' ⢠Full control over settings'),
|
|
380
|
+
chalk.gray(' ⢠Best for new repos or custom setup'),
|
|
381
|
+
''
|
|
382
|
+
].join('\n'),
|
|
383
|
+
value: 'manual'
|
|
384
|
+
}
|
|
385
|
+
],
|
|
386
|
+
default: 'bulk-discovery'
|
|
387
|
+
});
|
|
416
388
|
discoveryStrategy = configMethod;
|
|
417
389
|
}
|
|
418
390
|
// Step 2: If bulk discovery, discover repos FIRST, then ask which is parent
|
|
419
391
|
if (discoveryStrategy === 'bulk-discovery') {
|
|
420
392
|
// Get owner FIRST (needed for discovery)
|
|
421
393
|
console.log(chalk.cyan('\nš¤ Repository Owner\n'));
|
|
422
|
-
|
|
423
|
-
{
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const result = await provider.validateOwner(input, this.githubToken);
|
|
433
|
-
if (!result.valid) {
|
|
434
|
-
return result.error || `Invalid ${provider.config.name} owner`;
|
|
435
|
-
}
|
|
394
|
+
owner = await input({
|
|
395
|
+
message: `${provider.config.name} owner/organization:`,
|
|
396
|
+
validate: async (val) => {
|
|
397
|
+
if (!val.trim())
|
|
398
|
+
return 'Owner is required';
|
|
399
|
+
// Validate owner exists on the platform
|
|
400
|
+
if (this.githubToken) {
|
|
401
|
+
const result = await provider.validateOwner(val, this.githubToken);
|
|
402
|
+
if (!result.valid) {
|
|
403
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
436
404
|
}
|
|
437
|
-
return true;
|
|
438
405
|
}
|
|
406
|
+
return true;
|
|
439
407
|
}
|
|
440
|
-
|
|
441
|
-
owner = ownerPrompt.owner;
|
|
408
|
+
});
|
|
442
409
|
// Discover repositories via pattern matching
|
|
443
410
|
const octokit = new Octokit({ auth: this.githubToken });
|
|
444
411
|
const isOrg = await provider.isOrganization(owner, this.githubToken);
|
|
445
412
|
// Retry loop for pattern adjustment
|
|
446
413
|
let discoveryResult = null;
|
|
447
414
|
while (discoveryResult === null) {
|
|
448
|
-
|
|
415
|
+
// Discovery-first flow: skip count validation since user discovers THEN selects
|
|
416
|
+
discoveryResult = await discoverRepositories(octokit, owner, isOrg, 0, { skipValidation: true });
|
|
449
417
|
// If null, user selected "go back and adjust pattern", loop will retry
|
|
450
418
|
// If user selected "manual", discoveryResult will be { repositories: [], strategy: 'manual' }
|
|
451
419
|
}
|
|
@@ -466,15 +434,11 @@ export class RepoStructureManager {
|
|
|
466
434
|
short: 'Enter manually'
|
|
467
435
|
}
|
|
468
436
|
];
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
choices: parentChoices,
|
|
475
|
-
pageSize: 15
|
|
476
|
-
}
|
|
477
|
-
]);
|
|
437
|
+
const parentSelection = await select({
|
|
438
|
+
message: 'Which repository is the parent?',
|
|
439
|
+
choices: parentChoices,
|
|
440
|
+
pageSize: 15
|
|
441
|
+
});
|
|
478
442
|
if (parentSelection === 'manual') {
|
|
479
443
|
// User wants to enter parent manually - fall back to old flow
|
|
480
444
|
discoveryStrategy = 'manual';
|
|
@@ -541,106 +505,92 @@ export class RepoStructureManager {
|
|
|
541
505
|
// Local parent: Skip GitHub questions, just ask for folder name
|
|
542
506
|
console.log(chalk.blue('\nš” Local Parent Folder Setup'));
|
|
543
507
|
console.log(chalk.gray('This folder will contain .specweave/ but will NOT be pushed to GitHub.\n'));
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (!
|
|
562
|
-
return
|
|
563
|
-
// Validate owner exists on the platform
|
|
564
|
-
if (this.githubToken) {
|
|
565
|
-
const result = await provider.validateOwner(input, this.githubToken);
|
|
566
|
-
if (!result.valid) {
|
|
567
|
-
return result.error || `Invalid ${provider.config.name} owner`;
|
|
568
|
-
}
|
|
508
|
+
const parentNameAnswer = await input({
|
|
509
|
+
message: 'Parent folder name:',
|
|
510
|
+
default: `${path.basename(this.projectPath)}`,
|
|
511
|
+
validate: (val) => {
|
|
512
|
+
if (!val.trim())
|
|
513
|
+
return 'Folder name is required';
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
const ownerForLocalAnswer = await input({
|
|
518
|
+
message: `${provider.config.name} owner/organization for IMPLEMENTATION repos:`,
|
|
519
|
+
validate: async (val) => {
|
|
520
|
+
if (!val.trim())
|
|
521
|
+
return 'Owner is required';
|
|
522
|
+
// Validate owner exists on the platform
|
|
523
|
+
if (this.githubToken) {
|
|
524
|
+
const result = await provider.validateOwner(val, this.githubToken);
|
|
525
|
+
if (!result.valid) {
|
|
526
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
569
527
|
}
|
|
570
|
-
return true;
|
|
571
528
|
}
|
|
529
|
+
return true;
|
|
572
530
|
}
|
|
573
|
-
|
|
531
|
+
});
|
|
532
|
+
parentAnswers = {
|
|
533
|
+
parentName: parentNameAnswer,
|
|
534
|
+
owner: ownerForLocalAnswer
|
|
535
|
+
};
|
|
574
536
|
// Set defaults for local parent
|
|
575
537
|
parentAnswers.description = 'Local parent folder (not synced to GitHub)';
|
|
576
538
|
parentAnswers.createOnGitHub = false; // Never create GitHub repo for local parent
|
|
577
539
|
}
|
|
578
540
|
else {
|
|
579
541
|
// GitHub parent: First ask if using existing or creating new
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
short: 'Create new'
|
|
595
|
-
}
|
|
596
|
-
],
|
|
597
|
-
default: 'new'
|
|
598
|
-
}
|
|
599
|
-
]);
|
|
542
|
+
const parentChoice = await select({
|
|
543
|
+
message: 'Parent repository setup:',
|
|
544
|
+
choices: [
|
|
545
|
+
{
|
|
546
|
+
name: `${chalk.green('Use existing parent repository')}\n${chalk.gray('Connect to an existing GitHub repo that already has .specweave/ structure')}`,
|
|
547
|
+
value: 'existing'
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
name: `${chalk.blue('Create new parent repository')}\n${chalk.gray('Create a new GitHub repo for specs, docs, and architecture')}`,
|
|
551
|
+
value: 'new'
|
|
552
|
+
}
|
|
553
|
+
],
|
|
554
|
+
default: 'new'
|
|
555
|
+
});
|
|
600
556
|
if (parentChoice === 'existing') {
|
|
601
557
|
// Using existing parent repository
|
|
602
558
|
console.log(chalk.cyan('\nš Existing Parent Repository\n'));
|
|
603
559
|
// Ask for owner first
|
|
604
|
-
const
|
|
605
|
-
{
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const result = await provider.validateOwner(input, this.githubToken);
|
|
615
|
-
if (!result.valid) {
|
|
616
|
-
return result.error || `Invalid ${provider.config.name} owner`;
|
|
617
|
-
}
|
|
560
|
+
const existingOwner = await input({
|
|
561
|
+
message: `${provider.config.name} owner/organization:`,
|
|
562
|
+
validate: async (val) => {
|
|
563
|
+
if (!val.trim())
|
|
564
|
+
return 'Owner is required';
|
|
565
|
+
// Validate owner exists on the platform
|
|
566
|
+
if (this.githubToken) {
|
|
567
|
+
const result = await provider.validateOwner(val, this.githubToken);
|
|
568
|
+
if (!result.valid) {
|
|
569
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
618
570
|
}
|
|
619
|
-
return true;
|
|
620
571
|
}
|
|
572
|
+
return true;
|
|
621
573
|
}
|
|
622
|
-
|
|
574
|
+
});
|
|
575
|
+
const ownerPrompt = { owner: existingOwner };
|
|
623
576
|
// Ask for existing repo name
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const result = await provider.validateRepository(ownerPrompt.owner, input, this.githubToken);
|
|
636
|
-
if (!result.exists) {
|
|
637
|
-
return `Repository ${ownerPrompt.owner}/${input} not found on ${provider.config.name}. Please check the name or choose 'Create new'.`;
|
|
638
|
-
}
|
|
577
|
+
const existingParentName = await input({
|
|
578
|
+
message: 'Existing parent repository name:',
|
|
579
|
+
default: `${path.basename(this.projectPath)}-parent`,
|
|
580
|
+
validate: async (val) => {
|
|
581
|
+
if (!val.trim())
|
|
582
|
+
return 'Repository name is required';
|
|
583
|
+
// Validate repository EXISTS on the platform
|
|
584
|
+
if (this.githubToken && ownerPrompt.owner) {
|
|
585
|
+
const result = await provider.validateRepository(ownerPrompt.owner, val, this.githubToken);
|
|
586
|
+
if (!result.exists) {
|
|
587
|
+
return `Repository ${ownerPrompt.owner}/${val} not found on ${provider.config.name}. Please check the name or choose 'Create new'.`;
|
|
639
588
|
}
|
|
640
|
-
return true;
|
|
641
589
|
}
|
|
590
|
+
return true;
|
|
642
591
|
}
|
|
643
|
-
|
|
592
|
+
});
|
|
593
|
+
const repoPrompt = { parentName: existingParentName };
|
|
644
594
|
// Fetch description and visibility from GitHub API (or use defaults)
|
|
645
595
|
let description = 'SpecWeave parent repository - specs, docs, and architecture';
|
|
646
596
|
let existingVisibility = 'private';
|
|
@@ -675,60 +625,54 @@ export class RepoStructureManager {
|
|
|
675
625
|
// Creating new parent repository
|
|
676
626
|
console.log(chalk.cyan('\n⨠New Parent Repository\n'));
|
|
677
627
|
// Ask for owner (separate prompt to avoid validator issues)
|
|
678
|
-
const
|
|
679
|
-
{
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const result = await provider.validateOwner(input, this.githubToken);
|
|
689
|
-
if (!result.valid) {
|
|
690
|
-
return result.error || `Invalid ${provider.config.name} owner`;
|
|
691
|
-
}
|
|
628
|
+
const newOwner = await input({
|
|
629
|
+
message: `${provider.config.name} owner/organization for ALL repos:`,
|
|
630
|
+
validate: async (val) => {
|
|
631
|
+
if (!val.trim())
|
|
632
|
+
return 'Owner is required';
|
|
633
|
+
// Validate owner exists on the platform
|
|
634
|
+
if (this.githubToken) {
|
|
635
|
+
const result = await provider.validateOwner(val, this.githubToken);
|
|
636
|
+
if (!result.valid) {
|
|
637
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
692
638
|
}
|
|
693
|
-
return true;
|
|
694
639
|
}
|
|
640
|
+
return true;
|
|
695
641
|
}
|
|
696
|
-
|
|
642
|
+
});
|
|
643
|
+
const ownerPrompt = { owner: newOwner };
|
|
697
644
|
// Now ask remaining questions, using the owner value
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const result = await provider.validateRepository(ownerPrompt.owner, input, this.githubToken);
|
|
710
|
-
if (result.exists) {
|
|
711
|
-
return `Repository ${ownerPrompt.owner}/${input} already exists at ${result.url}. Please choose 'Use existing' or pick a different name.`;
|
|
712
|
-
}
|
|
645
|
+
const newParentName = await input({
|
|
646
|
+
message: 'Parent repository name:',
|
|
647
|
+
default: `${path.basename(this.projectPath)}-parent`,
|
|
648
|
+
validate: async (val) => {
|
|
649
|
+
if (!val.trim())
|
|
650
|
+
return 'Repository name is required';
|
|
651
|
+
// Validate repository DOESN'T exist
|
|
652
|
+
if (this.githubToken && ownerPrompt.owner) {
|
|
653
|
+
const result = await provider.validateRepository(ownerPrompt.owner, val, this.githubToken);
|
|
654
|
+
if (result.exists) {
|
|
655
|
+
return `Repository ${ownerPrompt.owner}/${val} already exists at ${result.url}. Please choose 'Use existing' or pick a different name.`;
|
|
713
656
|
}
|
|
714
|
-
return true;
|
|
715
657
|
}
|
|
716
|
-
|
|
717
|
-
{
|
|
718
|
-
type: 'input',
|
|
719
|
-
name: 'description',
|
|
720
|
-
message: 'Parent repository description:',
|
|
721
|
-
default: 'SpecWeave parent repository - specs, docs, and architecture'
|
|
722
|
-
},
|
|
723
|
-
{
|
|
724
|
-
type: 'confirm',
|
|
725
|
-
name: 'createOnGitHub',
|
|
726
|
-
message: 'Create parent repository on GitHub?',
|
|
727
|
-
default: true
|
|
658
|
+
return true;
|
|
728
659
|
}
|
|
729
|
-
|
|
660
|
+
});
|
|
661
|
+
const newParentDesc = await input({
|
|
662
|
+
message: 'Parent repository description:',
|
|
663
|
+
default: 'SpecWeave parent repository - specs, docs, and architecture'
|
|
664
|
+
});
|
|
665
|
+
const createParentOnGitHub = await confirm({
|
|
666
|
+
message: 'Create parent repository on GitHub?',
|
|
667
|
+
default: true
|
|
668
|
+
});
|
|
730
669
|
// Merge the answers
|
|
731
|
-
parentAnswers = {
|
|
670
|
+
parentAnswers = {
|
|
671
|
+
...ownerPrompt,
|
|
672
|
+
parentName: newParentName,
|
|
673
|
+
description: newParentDesc,
|
|
674
|
+
createOnGitHub: createParentOnGitHub
|
|
675
|
+
};
|
|
732
676
|
}
|
|
733
677
|
}
|
|
734
678
|
// Ask about visibility for parent repo (only if creating NEW repo on GitHub)
|
|
@@ -736,18 +680,14 @@ export class RepoStructureManager {
|
|
|
736
680
|
if (!isLocalParent && parentAnswers.createOnGitHub) {
|
|
737
681
|
// Only prompt for visibility when creating a NEW repository
|
|
738
682
|
const parentVisibilityPrompt = getVisibilityPrompt(parentAnswers.parentName);
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
})),
|
|
748
|
-
default: parentVisibilityPrompt.default
|
|
749
|
-
}]);
|
|
750
|
-
parentVisibility = result.parentVisibility;
|
|
683
|
+
parentVisibility = await select({
|
|
684
|
+
message: parentVisibilityPrompt.question,
|
|
685
|
+
choices: parentVisibilityPrompt.options.map(opt => ({
|
|
686
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}`,
|
|
687
|
+
value: opt.value
|
|
688
|
+
})),
|
|
689
|
+
default: parentVisibilityPrompt.default
|
|
690
|
+
});
|
|
751
691
|
}
|
|
752
692
|
else if (!isLocalParent && parentAnswers.visibility) {
|
|
753
693
|
// Use existing repository's visibility (fetched from GitHub API)
|
|
@@ -803,24 +743,22 @@ export class RepoStructureManager {
|
|
|
803
743
|
console.log(chalk.gray('\nNext question asks for: IMPLEMENTATION repositories ONLY (not counting parent)\n'));
|
|
804
744
|
}
|
|
805
745
|
// Ask how many implementation repositories
|
|
806
|
-
const
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}]);
|
|
823
|
-
repoCount = promptResult.repoCount;
|
|
746
|
+
const repoCountAnswer = await number({
|
|
747
|
+
message: useParent
|
|
748
|
+
? 'š¦ How many IMPLEMENTATION repositories? (not counting parent)'
|
|
749
|
+
: 'How many repositories?',
|
|
750
|
+
default: hints.suggestedCount, // Use auto-detected count
|
|
751
|
+
validate: (val) => {
|
|
752
|
+
if (val === undefined || val < 1)
|
|
753
|
+
return useParent
|
|
754
|
+
? 'Need at least 1 implementation repository'
|
|
755
|
+
: 'Need at least 2 repositories';
|
|
756
|
+
if (val > 10)
|
|
757
|
+
return 'Maximum 10 repositories supported';
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
repoCount = repoCountAnswer ?? hints.suggestedCount;
|
|
824
762
|
// Show summary AFTER for confirmation
|
|
825
763
|
if (useParent && config.parentRepo) {
|
|
826
764
|
if (isLocalParent) {
|
|
@@ -873,12 +811,10 @@ export class RepoStructureManager {
|
|
|
873
811
|
console.log(chalk.green(` ā Discovered: ${chalk.bold(discoveredRepo.name)}`));
|
|
874
812
|
console.log(chalk.gray(` Description: ${discoveredRepo.description || '(none)'}`));
|
|
875
813
|
console.log(chalk.gray(` Visibility: ${discoveredRepo.private ? 'Private' : 'Public'}`));
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
default: true
|
|
881
|
-
}]);
|
|
814
|
+
const confirmDiscovered = await confirm({
|
|
815
|
+
message: 'Use this repository configuration?',
|
|
816
|
+
default: true
|
|
817
|
+
});
|
|
882
818
|
if (!confirmDiscovered) {
|
|
883
819
|
// Allow manual override
|
|
884
820
|
console.log(chalk.yellow(` ā Switching to manual entry for this repository\n`));
|
|
@@ -904,38 +840,39 @@ export class RepoStructureManager {
|
|
|
904
840
|
}
|
|
905
841
|
}
|
|
906
842
|
// Manual entry (original behavior)
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
const result = await provider.validateRepository(config.parentRepo.owner, input, this.githubToken);
|
|
919
|
-
if (result.exists) {
|
|
920
|
-
return `Repository ${config.parentRepo.owner}/${input} already exists at ${result.url}`;
|
|
921
|
-
}
|
|
843
|
+
const repoName = await input({
|
|
844
|
+
message: 'Repository name:',
|
|
845
|
+
default: suggestedName,
|
|
846
|
+
validate: async (val) => {
|
|
847
|
+
if (!val.trim())
|
|
848
|
+
return 'Repository name is required';
|
|
849
|
+
// Validate repository doesn't exist (skip for discovered repos)
|
|
850
|
+
if (!isDiscovered && this.githubToken && config.parentRepo) {
|
|
851
|
+
const result = await provider.validateRepository(config.parentRepo.owner, val, this.githubToken);
|
|
852
|
+
if (result.exists) {
|
|
853
|
+
return `Repository ${config.parentRepo.owner}/${val} already exists at ${result.url}`;
|
|
922
854
|
}
|
|
923
|
-
return true;
|
|
924
855
|
}
|
|
925
|
-
|
|
926
|
-
{
|
|
927
|
-
type: 'input',
|
|
928
|
-
name: 'description',
|
|
929
|
-
message: 'Repository description:',
|
|
930
|
-
default: (answers) => `${path.basename(answers.name)} service`
|
|
931
|
-
},
|
|
932
|
-
{
|
|
933
|
-
type: 'confirm',
|
|
934
|
-
name: 'createOnGitHub',
|
|
935
|
-
message: 'Create this repository on GitHub?',
|
|
936
|
-
default: !isDiscovered // Default to true for new repos, false for discovered
|
|
856
|
+
return true;
|
|
937
857
|
}
|
|
938
|
-
|
|
858
|
+
});
|
|
859
|
+
const repoDescription = await input({
|
|
860
|
+
message: 'Repository description:',
|
|
861
|
+
default: `${path.basename(repoName)} service`
|
|
862
|
+
});
|
|
863
|
+
// Skip "Create on GitHub?" for discovered repos - they already exist!
|
|
864
|
+
let repoCreateOnGitHub = false;
|
|
865
|
+
if (!isDiscovered) {
|
|
866
|
+
repoCreateOnGitHub = await confirm({
|
|
867
|
+
message: 'Create this repository on GitHub?',
|
|
868
|
+
default: true
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
const repoAnswers = {
|
|
872
|
+
name: repoName,
|
|
873
|
+
description: repoDescription,
|
|
874
|
+
createOnGitHub: repoCreateOnGitHub
|
|
875
|
+
};
|
|
939
876
|
// Smart auto-generate ID from repository name (context-aware)
|
|
940
877
|
const smartId = generateRepoIdSmart(repoAnswers.name, configuredRepoNames);
|
|
941
878
|
const { id: suggestedId, wasModified } = ensureUniqueId(smartId, usedIds);
|
|
@@ -945,31 +882,27 @@ export class RepoStructureManager {
|
|
|
945
882
|
if (wasModified) {
|
|
946
883
|
console.log(chalk.yellow(` ā ļø ID conflict detected: "${smartId}" already used`));
|
|
947
884
|
console.log(chalk.gray(` ā Suggested unique ID: "${suggestedId}"`));
|
|
948
|
-
const
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
// Validate uniqueness
|
|
967
|
-
if (usedIds.has(input)) {
|
|
968
|
-
return 'Repository ID must be unique';
|
|
969
|
-
}
|
|
970
|
-
return true;
|
|
885
|
+
const confirmIdAnswer = await confirm({
|
|
886
|
+
message: `Use "${suggestedId}" as repository ID?`,
|
|
887
|
+
default: true
|
|
888
|
+
});
|
|
889
|
+
if (!confirmIdAnswer) {
|
|
890
|
+
const customId = await input({
|
|
891
|
+
message: 'Enter custom repository ID:',
|
|
892
|
+
default: suggestedId,
|
|
893
|
+
validate: (val) => {
|
|
894
|
+
// Validate format
|
|
895
|
+
const validation = validateRepoId(val);
|
|
896
|
+
if (!validation.valid) {
|
|
897
|
+
return validation.error || 'Invalid repository ID';
|
|
898
|
+
}
|
|
899
|
+
// Validate uniqueness
|
|
900
|
+
if (usedIds.has(val)) {
|
|
901
|
+
return 'Repository ID must be unique';
|
|
971
902
|
}
|
|
972
|
-
|
|
903
|
+
return true;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
973
906
|
id = customId;
|
|
974
907
|
}
|
|
975
908
|
}
|
|
@@ -983,18 +916,14 @@ export class RepoStructureManager {
|
|
|
983
916
|
let visibility = 'private';
|
|
984
917
|
if (repoAnswers.createOnGitHub) {
|
|
985
918
|
const visibilityPrompt = getVisibilityPrompt(repoAnswers.name);
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
})),
|
|
995
|
-
default: visibilityPrompt.default
|
|
996
|
-
}]);
|
|
997
|
-
visibility = result.visibility;
|
|
919
|
+
visibility = await select({
|
|
920
|
+
message: visibilityPrompt.question,
|
|
921
|
+
choices: visibilityPrompt.options.map(opt => ({
|
|
922
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}`,
|
|
923
|
+
value: opt.value
|
|
924
|
+
})),
|
|
925
|
+
default: visibilityPrompt.default
|
|
926
|
+
});
|
|
998
927
|
}
|
|
999
928
|
config.repositories.push({
|
|
1000
929
|
id: id,
|
|
@@ -1044,61 +973,52 @@ export class RepoStructureManager {
|
|
|
1044
973
|
async configureMonorepo(urlType = 'ssh', platform = 'github', provider) {
|
|
1045
974
|
console.log(chalk.cyan('\nš Monorepo Configuration\n'));
|
|
1046
975
|
console.log(chalk.gray('Single repository with multiple projects/packages.\n'));
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
},
|
|
1067
|
-
{
|
|
1068
|
-
type: 'input',
|
|
1069
|
-
name: 'projects',
|
|
1070
|
-
message: 'Project names (comma-separated, e.g., frontend,backend,shared):',
|
|
1071
|
-
validate: (input) => {
|
|
1072
|
-
const projects = input.split(',').map(p => p.trim()).filter(Boolean);
|
|
1073
|
-
if (projects.length < 2) {
|
|
1074
|
-
return 'Monorepo should have at least 2 projects';
|
|
1075
|
-
}
|
|
1076
|
-
return true;
|
|
976
|
+
const monoOwner = await input({
|
|
977
|
+
message: 'GitHub owner/organization:',
|
|
978
|
+
validate: (val) => !!val.trim() || 'Owner is required'
|
|
979
|
+
});
|
|
980
|
+
const monoRepo = await input({
|
|
981
|
+
message: 'Repository name:',
|
|
982
|
+
default: path.basename(this.projectPath),
|
|
983
|
+
validate: (val) => !!val.trim() || 'Repository name is required'
|
|
984
|
+
});
|
|
985
|
+
const monoDescription = await input({
|
|
986
|
+
message: 'Repository description:',
|
|
987
|
+
default: 'Monorepo project'
|
|
988
|
+
});
|
|
989
|
+
const monoProjects = await input({
|
|
990
|
+
message: 'Project names (comma-separated, e.g., frontend,backend,shared):',
|
|
991
|
+
validate: (val) => {
|
|
992
|
+
const projects = val.split(',').map(p => p.trim()).filter(Boolean);
|
|
993
|
+
if (projects.length < 2) {
|
|
994
|
+
return 'Monorepo should have at least 2 projects';
|
|
1077
995
|
}
|
|
1078
|
-
|
|
1079
|
-
{
|
|
1080
|
-
type: 'confirm',
|
|
1081
|
-
name: 'createOnGitHub',
|
|
1082
|
-
message: 'Create repository on GitHub?',
|
|
1083
|
-
default: !existsSync(path.join(this.projectPath, '.git'))
|
|
996
|
+
return true;
|
|
1084
997
|
}
|
|
1085
|
-
|
|
998
|
+
});
|
|
999
|
+
const monoCreateOnGitHub = await confirm({
|
|
1000
|
+
message: 'Create repository on GitHub?',
|
|
1001
|
+
default: !existsSync(path.join(this.projectPath, '.git'))
|
|
1002
|
+
});
|
|
1003
|
+
const answers = {
|
|
1004
|
+
owner: monoOwner,
|
|
1005
|
+
repo: monoRepo,
|
|
1006
|
+
description: monoDescription,
|
|
1007
|
+
projects: monoProjects,
|
|
1008
|
+
createOnGitHub: monoCreateOnGitHub
|
|
1009
|
+
};
|
|
1086
1010
|
// Ask about visibility only if creating a new repository
|
|
1087
1011
|
let visibility = 'private';
|
|
1088
1012
|
if (answers.createOnGitHub) {
|
|
1089
1013
|
const visibilityPrompt = getVisibilityPrompt(answers.repo);
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
})),
|
|
1099
|
-
default: visibilityPrompt.default
|
|
1100
|
-
}]);
|
|
1101
|
-
visibility = result.visibility;
|
|
1014
|
+
visibility = await select({
|
|
1015
|
+
message: visibilityPrompt.question,
|
|
1016
|
+
choices: visibilityPrompt.options.map(opt => ({
|
|
1017
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}`,
|
|
1018
|
+
value: opt.value
|
|
1019
|
+
})),
|
|
1020
|
+
default: visibilityPrompt.default
|
|
1021
|
+
});
|
|
1102
1022
|
}
|
|
1103
1023
|
const projects = answers.projects.split(',').map((p) => p.trim());
|
|
1104
1024
|
return {
|