specweave 1.0.15 → 1.0.16

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 (35) hide show
  1. package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
  2. package/dist/src/cli/helpers/init/external-import.js +108 -0
  3. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  4. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts +1 -1
  5. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  6. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +3 -12
  7. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  8. package/dist/src/cli/helpers/issue-tracker/github.d.ts +2 -5
  9. package/dist/src/cli/helpers/issue-tracker/github.d.ts.map +1 -1
  10. package/dist/src/cli/helpers/issue-tracker/github.js +9 -81
  11. package/dist/src/cli/helpers/issue-tracker/github.js.map +1 -1
  12. package/dist/src/core/repo-structure/multi-repo-configurator.d.ts +4 -3
  13. package/dist/src/core/repo-structure/multi-repo-configurator.d.ts.map +1 -1
  14. package/dist/src/core/repo-structure/multi-repo-configurator.js +103 -434
  15. package/dist/src/core/repo-structure/multi-repo-configurator.js.map +1 -1
  16. package/dist/src/core/repo-structure/prompt-consolidator.d.ts +1 -1
  17. package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
  18. package/dist/src/core/repo-structure/prompt-consolidator.js +14 -18
  19. package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
  20. package/dist/src/core/repo-structure/repo-structure-manager.d.ts +6 -2
  21. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  22. package/dist/src/core/repo-structure/repo-structure-manager.js +13 -39
  23. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  24. package/dist/src/core/repo-structure/setup-state-manager.d.ts +4 -0
  25. package/dist/src/core/repo-structure/setup-state-manager.d.ts.map +1 -1
  26. package/dist/src/core/repo-structure/setup-state-manager.js.map +1 -1
  27. package/package.json +1 -1
  28. package/dist/src/cli/commands/validate-parent-repo.d.ts +0 -8
  29. package/dist/src/cli/commands/validate-parent-repo.d.ts.map +0 -1
  30. package/dist/src/cli/commands/validate-parent-repo.js +0 -15
  31. package/dist/src/cli/commands/validate-parent-repo.js.map +0 -1
  32. package/dist/src/core/cicd/parent-repo-validator.d.ts +0 -42
  33. package/dist/src/core/cicd/parent-repo-validator.d.ts.map +0 -1
  34. package/dist/src/core/cicd/parent-repo-validator.js +0 -201
  35. package/dist/src/core/cicd/parent-repo-validator.js.map +0 -1
@@ -2,14 +2,16 @@
2
2
  * Multi-Repository Configuration
3
3
  *
4
4
  * Extracted from repo-structure-manager.ts to reduce file size and prevent crashes.
5
- * Handles configuration of multi-repository setups including parent repos and discovery.
5
+ * Handles configuration of multi-repository setups with repository discovery.
6
+ *
7
+ * v1.0.13: REMOVED parent repo concept - all repos are equal, first is default.
6
8
  */
7
9
  import path from 'path';
8
10
  import chalk from 'chalk';
9
11
  import { select, input, confirm, number } from '@inquirer/prompts';
10
12
  import { Octokit } from '@octokit/rest';
11
13
  import { normalizeRepoName, suggestRepoName } from './repo-id-generator.js';
12
- import { getParentRepoBenefits, getVisibilityPrompt, } from './prompt-consolidator.js';
14
+ import { getVisibilityPrompt, } from './prompt-consolidator.js';
13
15
  import { detectRepositoryHints } from './folder-detector.js';
14
16
  import { discoverRepositories } from './repo-bulk-discovery.js';
15
17
  /**
@@ -25,19 +27,16 @@ async function saveSetupState(stateManager, state) {
25
27
  }
26
28
  /**
27
29
  * Configure multi-repository architecture
30
+ * v1.0.13: All repos are equal - no parent concept. First repo is default.
28
31
  * @param options - Configuration options
29
32
  */
30
33
  export async function configureMultiRepo(options) {
31
- const { projectPath, githubToken, stateManager, useParent = true, isLocalParent = false, urlType = 'ssh', platform = 'github', provider } = options;
34
+ const { projectPath, githubToken, stateManager, urlType = 'ssh', platform = 'github', provider } = options;
32
35
  console.log(chalk.cyan('\n Multi-Repository Configuration\n'));
33
36
  console.log(chalk.gray('This creates separate repositories for each service/component.\n'));
34
- // Show parent repo benefits if using parent approach
35
- if (useParent) {
36
- console.log(chalk.blue(getParentRepoBenefits()));
37
- console.log('');
38
- }
37
+ console.log(chalk.gray('All repositories are equal - first one will be the default for issues.\n'));
39
38
  const config = {
40
- architecture: useParent ? 'parent' : 'multi-repo',
39
+ architecture: 'multi-repo',
41
40
  urlType,
42
41
  platform,
43
42
  provider,
@@ -46,19 +45,17 @@ export async function configureMultiRepo(options) {
46
45
  // Save state: architecture selected
47
46
  await saveSetupState(stateManager, {
48
47
  version: '1.0.0',
49
- architecture: useParent ? 'parent' : 'multi-repo',
50
- parentRepo: undefined,
48
+ architecture: 'multi-repo',
51
49
  repos: [],
52
50
  currentStep: 'architecture-selected',
53
51
  timestamp: new Date().toISOString(),
54
52
  envCreated: false
55
53
  });
56
- // ========== NEW FLOW: Ask discovery strategy FIRST (before parent questions!) ==========
57
- // Step 1: Ask how to configure implementation repositories (BEFORE parent questions)
54
+ // Ask discovery strategy
58
55
  let discoveryStrategy = 'manual';
59
56
  let discoveredRepos = [];
60
57
  let owner = '';
61
- if (!isLocalParent && githubToken && useParent) {
58
+ if (githubToken) {
62
59
  console.log(chalk.cyan('\n Repository Discovery\n'));
63
60
  console.log(chalk.gray('You\'re setting up multiple repositories. We can discover them automatically!\n'));
64
61
  const configMethod = await select({
@@ -68,8 +65,7 @@ export async function configureMultiRepo(options) {
68
65
  name: [
69
66
  chalk.bold.green(' Bulk Discovery (RECOMMENDED)'),
70
67
  chalk.gray(' Automatically discover repos from ' + provider.config.name),
71
- chalk.gray(' - Select parent from discovered list'),
72
- chalk.gray(' - Auto-configure implementation repos'),
68
+ chalk.gray(' - Auto-configure all repos'),
73
69
  chalk.gray(' - Supports: all, pattern, regex filtering'),
74
70
  ''
75
71
  ].join('\n'),
@@ -90,9 +86,9 @@ export async function configureMultiRepo(options) {
90
86
  });
91
87
  discoveryStrategy = configMethod;
92
88
  }
93
- // Step 2: If bulk discovery, discover repos FIRST, then ask which is parent
89
+ // Bulk discovery flow
94
90
  if (discoveryStrategy === 'bulk-discovery') {
95
- // Get owner FIRST (needed for discovery)
91
+ // Get owner first (needed for discovery)
96
92
  console.log(chalk.cyan('\n Repository Owner\n'));
97
93
  owner = await input({
98
94
  message: `${provider.config.name} owner/organization:`,
@@ -122,424 +118,108 @@ export async function configureMultiRepo(options) {
122
118
  }
123
119
  if (discoveryResult && discoveryResult.strategy !== 'manual') {
124
120
  discoveredRepos = discoveryResult.repositories;
125
- // Now ask: Which repo is the parent?
126
- console.log(chalk.cyan('\n Select Parent Repository\n'));
127
- console.log(chalk.gray('Choose which repository will be the parent (contains .specweave/ structure)\n'));
128
- const parentChoices = [
129
- ...discoveredRepos.map((repo, index) => ({
130
- name: `${chalk.bold(repo.name)}\n${chalk.gray(repo.description || '(no description)')}`,
131
- value: index.toString(),
132
- short: repo.name
133
- })),
134
- {
135
- name: `${chalk.yellow(' Enter parent manually')} ${chalk.gray('(not in discovered list)')}`,
136
- value: 'manual',
137
- short: 'Enter manually'
138
- }
139
- ];
140
- const parentSelection = await select({
141
- message: 'Which repository is the parent?',
142
- choices: parentChoices,
143
- pageSize: 15
144
- });
145
- if (parentSelection === 'manual') {
146
- // User wants to enter parent manually - fall back to old flow
147
- discoveryStrategy = 'manual';
148
- discoveredRepos = []; // Clear discovered repos
149
- }
150
- else {
151
- // User selected a parent from discovered list
152
- const parentIndex = parseInt(parentSelection);
153
- const selectedParent = discoveredRepos[parentIndex];
154
- // Fetch full repo details from GitHub API
155
- let description = selectedParent.description || 'SpecWeave parent repository - specs, docs, and architecture';
156
- let existingVisibility = selectedParent.private ? 'private' : 'public';
157
- try {
158
- const response = await fetch(`https://api.github.com/repos/${owner}/${selectedParent.name}`, {
159
- headers: {
160
- 'Authorization': `Bearer ${githubToken}`,
161
- 'Accept': 'application/vnd.github+json'
162
- }
163
- });
164
- if (response.ok) {
165
- const data = await response.json();
166
- description = data.description || description;
167
- existingVisibility = data.private ? 'private' : 'public';
168
- }
169
- }
170
- catch {
171
- // Use defaults if fetch fails
172
- }
173
- // Set parent config
174
- config.parentRepo = {
175
- name: selectedParent.name,
176
- owner: owner,
177
- description: description,
178
- visibility: existingVisibility,
179
- createOnGitHub: false // Already exists!
180
- };
181
- // Remove parent from discovered repos (implementation repos = discovered - parent)
182
- discoveredRepos.splice(parentIndex, 1);
183
- console.log(chalk.green(`\n Using existing repository: ${owner}/${selectedParent.name}\n`));
184
- console.log(chalk.gray(` Implementation repositories: ${discoveredRepos.length}\n`));
185
- // Save state: parent repo configured
186
- await saveSetupState(stateManager, {
187
- version: '1.0.0',
188
- architecture: useParent ? 'parent' : 'multi-repo',
189
- parentRepo: config.parentRepo,
190
- repos: [],
191
- currentStep: 'parent-repo-configured',
192
- timestamp: new Date().toISOString(),
193
- envCreated: false
121
+ console.log(chalk.green(`\n Discovered ${discoveredRepos.length} repositories\n`));
122
+ // Configure all discovered repos - first is default
123
+ for (let i = 0; i < discoveredRepos.length; i++) {
124
+ const repo = discoveredRepos[i];
125
+ const repoId = normalizeRepoName(repo.name);
126
+ config.repositories.push({
127
+ id: repoId,
128
+ name: repo.name,
129
+ owner: repo.owner,
130
+ description: repo.description || `${repo.name} service`,
131
+ path: repo.name,
132
+ visibility: repo.private ? 'private' : 'public',
133
+ createOnGitHub: false,
134
+ isNested: false
194
135
  });
195
- // Skip to repository configuration (lines 794+)
196
- // We'll continue below with discoveredRepos
136
+ console.log(chalk.green(` ${i === 0 ? '(default) ' : ''}${repo.name} ${chalk.gray(`(id: ${repoId})`)}`));
197
137
  }
138
+ // Save state
139
+ await saveSetupState(stateManager, {
140
+ version: '1.0.0',
141
+ architecture: 'multi-repo',
142
+ repos: config.repositories.map(r => ({
143
+ id: r.id,
144
+ repo: r.name,
145
+ owner: r.owner,
146
+ path: r.path,
147
+ visibility: r.visibility,
148
+ displayName: r.name,
149
+ created: false
150
+ })),
151
+ currentStep: 'repos-configured',
152
+ timestamp: new Date().toISOString(),
153
+ envCreated: false
154
+ });
155
+ return config;
198
156
  }
199
157
  else {
200
- // User selected manual - fall back to old flow
158
+ // User selected manual - fall back to manual flow
201
159
  discoveryStrategy = 'manual';
202
160
  }
203
161
  }
204
- // Step 3: Manual flow (existing logic) - only runs if discoveryStrategy === 'manual'
205
- if (discoveryStrategy === 'manual' && useParent) {
206
- let parentAnswers;
207
- if (isLocalParent) {
208
- // Local parent: Skip GitHub questions, just ask for folder name
209
- console.log(chalk.blue('\n Local Parent Folder Setup'));
210
- console.log(chalk.gray('This folder will contain .specweave/ but will NOT be pushed to GitHub.\n'));
211
- const parentNameAnswer = await input({
212
- message: 'Parent folder name:',
213
- default: `${path.basename(projectPath)}`,
214
- validate: (val) => {
215
- if (!val.trim())
216
- return 'Folder name is required';
217
- return true;
218
- }
219
- });
220
- const ownerForLocalAnswer = await input({
221
- message: `${provider.config.name} owner/organization for IMPLEMENTATION repos:`,
222
- validate: async (val) => {
223
- if (!val.trim())
224
- return 'Owner is required';
225
- // Validate owner exists on the platform
226
- if (githubToken) {
227
- const result = await provider.validateOwner(val, githubToken);
228
- if (!result.valid) {
229
- return result.error || `Invalid ${provider.config.name} owner`;
230
- }
231
- }
232
- return true;
233
- }
234
- });
235
- parentAnswers = {
236
- parentName: parentNameAnswer,
237
- owner: ownerForLocalAnswer
238
- };
239
- // Set defaults for local parent
240
- parentAnswers.description = 'Local parent folder (not synced to GitHub)';
241
- parentAnswers.createOnGitHub = false; // Never create GitHub repo for local parent
242
- }
243
- else {
244
- // GitHub parent: First ask if using existing or creating new
245
- const parentChoice = await select({
246
- message: 'Parent repository setup:',
247
- choices: [
248
- {
249
- name: `${chalk.green('Use existing parent repository')}\n${chalk.gray('Connect to an existing GitHub repo that already has .specweave/ structure')}`,
250
- value: 'existing'
251
- },
252
- {
253
- name: `${chalk.blue('Create new parent repository')}\n${chalk.gray('Create a new GitHub repo for specs, docs, and architecture')}`,
254
- value: 'new'
255
- }
256
- ],
257
- default: 'new'
258
- });
259
- if (parentChoice === 'existing') {
260
- // Using existing parent repository
261
- console.log(chalk.cyan('\n Existing Parent Repository\n'));
262
- // Ask for owner first
263
- const existingOwner = await input({
264
- message: `${provider.config.name} owner/organization:`,
265
- validate: async (val) => {
266
- if (!val.trim())
267
- return 'Owner is required';
268
- // Validate owner exists on the platform
269
- if (githubToken) {
270
- const result = await provider.validateOwner(val, githubToken);
271
- if (!result.valid) {
272
- return result.error || `Invalid ${provider.config.name} owner`;
273
- }
274
- }
275
- return true;
276
- }
277
- });
278
- const ownerPrompt = { owner: existingOwner };
279
- // Ask for existing repo name
280
- const existingParentName = await input({
281
- message: 'Existing parent repository name:',
282
- default: `${path.basename(projectPath)}-parent`,
283
- validate: async (val) => {
284
- if (!val.trim())
285
- return 'Repository name is required';
286
- // Validate repository EXISTS on the platform
287
- if (githubToken && ownerPrompt.owner) {
288
- const result = await provider.validateRepository(ownerPrompt.owner, val, githubToken);
289
- if (!result.exists) {
290
- return `Repository ${ownerPrompt.owner}/${val} not found on ${provider.config.name}. Please check the name or choose 'Create new'.`;
291
- }
292
- }
293
- return true;
294
- }
295
- });
296
- const repoPrompt = { parentName: existingParentName };
297
- // Fetch description and visibility from GitHub API (or use defaults)
298
- let description = 'SpecWeave parent repository - specs, docs, and architecture';
299
- let existingVisibility = 'private';
162
+ // Manual flow
163
+ // Ask for owner
164
+ if (!owner) {
165
+ console.log(chalk.cyan('\n Repository Owner\n'));
166
+ owner = await input({
167
+ message: `${provider.config.name} owner/organization:`,
168
+ validate: async (val) => {
169
+ if (!val.trim())
170
+ return 'Owner is required';
171
+ // Validate owner exists on the platform
300
172
  if (githubToken) {
301
- try {
302
- const response = await fetch(`https://api.github.com/repos/${ownerPrompt.owner}/${repoPrompt.parentName}`, {
303
- headers: {
304
- 'Authorization': `Bearer ${githubToken}`,
305
- 'Accept': 'application/vnd.github+json'
306
- }
307
- });
308
- if (response.ok) {
309
- const data = await response.json();
310
- description = data.description || description;
311
- existingVisibility = data.private ? 'private' : 'public';
312
- }
313
- }
314
- catch {
315
- // Use defaults if fetch fails
173
+ const result = await provider.validateOwner(val, githubToken);
174
+ if (!result.valid) {
175
+ return result.error || `Invalid ${provider.config.name} owner`;
316
176
  }
317
177
  }
318
- parentAnswers = {
319
- owner: ownerPrompt.owner,
320
- parentName: repoPrompt.parentName,
321
- description: description,
322
- createOnGitHub: false, // Don't create, it already exists!
323
- visibility: existingVisibility // Use existing visibility from GitHub
324
- };
325
- console.log(chalk.green(`\n Using existing repository: ${ownerPrompt.owner}/${repoPrompt.parentName}\n`));
326
- }
327
- else {
328
- // Creating new parent repository
329
- console.log(chalk.cyan('\n New Parent Repository\n'));
330
- // Ask for owner (separate prompt to avoid validator issues)
331
- const newOwner = await input({
332
- message: `${provider.config.name} owner/organization for ALL repos:`,
333
- validate: async (val) => {
334
- if (!val.trim())
335
- return 'Owner is required';
336
- // Validate owner exists on the platform
337
- if (githubToken) {
338
- const result = await provider.validateOwner(val, githubToken);
339
- if (!result.valid) {
340
- return result.error || `Invalid ${provider.config.name} owner`;
341
- }
342
- }
343
- return true;
344
- }
345
- });
346
- const ownerPrompt = { owner: newOwner };
347
- // Now ask remaining questions, using the owner value
348
- const newParentName = await input({
349
- message: 'Parent repository name:',
350
- default: `${path.basename(projectPath)}-parent`,
351
- validate: async (val) => {
352
- if (!val.trim())
353
- return 'Repository name is required';
354
- // Validate repository DOESN'T exist
355
- if (githubToken && ownerPrompt.owner) {
356
- const result = await provider.validateRepository(ownerPrompt.owner, val, githubToken);
357
- if (result.exists) {
358
- return `Repository ${ownerPrompt.owner}/${val} already exists at ${result.url}. Please choose 'Use existing' or pick a different name.`;
359
- }
360
- }
361
- return true;
362
- }
363
- });
364
- const newParentDesc = await input({
365
- message: 'Parent repository description:',
366
- default: 'SpecWeave parent repository - specs, docs, and architecture'
367
- });
368
- const createParentOnGitHub = await confirm({
369
- message: 'Create parent repository on GitHub?',
370
- default: true
371
- });
372
- // Merge the answers
373
- parentAnswers = {
374
- ...ownerPrompt,
375
- parentName: newParentName,
376
- description: newParentDesc,
377
- createOnGitHub: createParentOnGitHub
378
- };
178
+ return true;
379
179
  }
380
- }
381
- // Ask about visibility for parent repo (only if creating NEW repo on GitHub)
382
- let parentVisibility = 'private';
383
- if (!isLocalParent && parentAnswers.createOnGitHub) {
384
- // Only prompt for visibility when creating a NEW repository
385
- const parentVisibilityPrompt = getVisibilityPrompt(parentAnswers.parentName);
386
- parentVisibility = await select({
387
- message: parentVisibilityPrompt.question,
388
- choices: parentVisibilityPrompt.options.map(opt => ({
389
- name: `${opt.label}\n${chalk.gray(opt.description)}`,
390
- value: opt.value
391
- })),
392
- default: parentVisibilityPrompt.default
393
- });
394
- }
395
- else if (!isLocalParent && parentAnswers.visibility) {
396
- // Use existing repository's visibility (fetched from GitHub API)
397
- parentVisibility = parentAnswers.visibility;
398
- }
399
- config.parentRepo = {
400
- name: parentAnswers.parentName,
401
- owner: parentAnswers.owner,
402
- description: parentAnswers.description,
403
- visibility: parentVisibility,
404
- createOnGitHub: parentAnswers.createOnGitHub
405
- };
406
- // Save state: parent repo configured
407
- await saveSetupState(stateManager, {
408
- version: '1.0.0',
409
- architecture: useParent ? 'parent' : 'multi-repo',
410
- parentRepo: config.parentRepo,
411
- repos: [],
412
- currentStep: 'parent-repo-configured',
413
- timestamp: new Date().toISOString(),
414
- envCreated: false
415
180
  });
416
181
  }
417
- // Step 4: Repository count and discovery (skip count question if using bulk discovery)
418
- let repoCount;
419
- let bulkDiscoveryStrategy = discoveryStrategy === 'bulk-discovery' ? 'pattern' : 'manual';
420
- if (discoveryStrategy === 'bulk-discovery' && discoveredRepos.length > 0) {
421
- // Bulk discovery: repos already discovered, skip count question
422
- repoCount = discoveredRepos.length;
423
- console.log(chalk.green(`\n Total repositories: ${repoCount + 1} (1 parent + ${repoCount} implementation)\n`));
182
+ // Auto-detect existing folders
183
+ const hints = await detectRepositoryHints(projectPath);
184
+ if (hints.detectedFolders.length > 0) {
185
+ console.log(chalk.green(`\n Detected ${hints.detectedFolders.length} service folder(s):`));
186
+ hints.detectedFolders.forEach(f => console.log(chalk.gray(` - ${f}`)));
187
+ console.log('');
424
188
  }
425
- else {
426
- // Manual entry: ask for count
427
- // Auto-detect existing folders
428
- const hints = await detectRepositoryHints(projectPath);
429
- if (hints.detectedFolders.length > 0) {
430
- console.log(chalk.green(`\n Detected ${hints.detectedFolders.length} service folder(s):`));
431
- hints.detectedFolders.forEach(f => console.log(chalk.gray(` - ${f}`)));
432
- console.log('');
433
- }
434
- // Show repository count clarification BEFORE asking
435
- if (useParent && config.parentRepo) {
436
- console.log(chalk.cyan('\n Repository Count\n'));
437
- console.log(chalk.gray('You will create:'));
438
- if (isLocalParent) {
439
- console.log(chalk.white(' - 1 parent FOLDER (local only, .specweave/ gitignored)'));
440
- console.log(chalk.white(' - N implementation repositories (your services/apps on GitHub)'));
441
- }
442
- else {
443
- console.log(chalk.white(' - 1 parent repository (specs, docs, increments)'));
444
- console.log(chalk.white(' - N implementation repositories (your services/apps)'));
445
- }
446
- console.log(chalk.gray('\nNext question asks for: IMPLEMENTATION repositories ONLY (not counting parent)\n'));
189
+ // Ask how many repositories
190
+ const repoCountAnswer = await number({
191
+ message: 'How many repositories?',
192
+ default: hints.suggestedCount >= 2 ? hints.suggestedCount : 2,
193
+ validate: (val) => {
194
+ if (val === undefined || val < 2)
195
+ return 'Need at least 2 repositories';
196
+ if (val > 10)
197
+ return 'Maximum 10 repositories supported';
198
+ return true;
447
199
  }
448
- // Ask how many implementation repositories
449
- const repoCountAnswer = await number({
450
- message: useParent
451
- ? ' How many IMPLEMENTATION repositories? (not counting parent)'
452
- : 'How many repositories?',
453
- default: hints.suggestedCount, // Use auto-detected count
454
- validate: (val) => {
455
- if (val === undefined || val < 1)
456
- return useParent
457
- ? 'Need at least 1 implementation repository'
458
- : 'Need at least 2 repositories';
459
- if (val > 10)
460
- return 'Maximum 10 repositories supported';
461
- return true;
462
- }
463
- });
464
- repoCount = repoCountAnswer ?? hints.suggestedCount;
465
- // Show summary AFTER for confirmation
466
- if (useParent && config.parentRepo) {
467
- if (isLocalParent) {
468
- console.log(chalk.green(`\n Total repositories to create: ${repoCount} implementation repos`));
469
- console.log(chalk.gray(` (Parent folder is local only, not counted)\n`));
470
- }
471
- else {
472
- const totalRepos = 1 + repoCount;
473
- console.log(chalk.green(`\n Total repositories to create: ${totalRepos} (1 parent + ${repoCount} implementation)\n`));
474
- }
475
- }
476
- // Bulk repository discovery for manual flow (old behavior)
477
- if (githubToken && config.parentRepo && discoveryStrategy === 'manual') {
478
- const octokit = new Octokit({ auth: githubToken });
479
- const discoveryOwner = config.parentRepo.owner;
480
- const isOrg = await provider.isOrganization(discoveryOwner, githubToken);
481
- // Retry loop for pattern adjustment
482
- let discoveryResult = null;
483
- while (discoveryResult === null) {
484
- discoveryResult = await discoverRepositories(octokit, discoveryOwner, isOrg, repoCount);
485
- // If null, user selected "go back and adjust pattern", loop will retry
486
- // If user selected "manual", discoveryResult will be { repositories: [], strategy: 'manual' }
487
- }
488
- if (discoveryResult) {
489
- bulkDiscoveryStrategy = discoveryResult.strategy;
490
- if (discoveryResult.strategy !== 'manual') {
491
- discoveredRepos = discoveryResult.repositories;
492
- // Update repoCount to match discovered repos
493
- if (discoveredRepos.length !== repoCount) {
494
- console.log(chalk.yellow(`\n Adjusting repository count from ${repoCount} to ${discoveredRepos.length} (based on discovery)\n`));
495
- repoCount = discoveredRepos.length;
496
- }
497
- }
498
- }
499
- }
500
- }
200
+ });
201
+ const repoCount = repoCountAnswer ?? 2;
501
202
  // Configure each repository
502
203
  console.log(chalk.cyan('\n Configure Each Repository:\n'));
204
+ console.log(chalk.gray('First repository will be the default for issue tracking.\n'));
503
205
  const usedIds = new Set();
504
- const configuredRepoNames = []; // Track configured repo names for smart ID generation
505
206
  for (let i = 0; i < repoCount; i++) {
506
- const discoveredRepo = discoveredRepos[i]; // May be undefined if manual
507
- const isDiscovered = bulkDiscoveryStrategy !== 'manual' && discoveredRepo;
508
- console.log(chalk.white(`\nRepository ${i + 1} of ${repoCount}:`));
509
- // Smart suggestion for ALL repos (not just first one!)
207
+ console.log(chalk.white(`\nRepository ${i + 1} of ${repoCount}${i === 0 ? chalk.yellow(' (default)') : ''}:`));
208
+ // Smart suggestion
510
209
  const projectName = path.basename(projectPath);
511
- const suggestedName = isDiscovered ? discoveredRepo.name : suggestRepoName(projectName, i, repoCount);
512
- // If discovered, auto-use without confirmation (repos exist on GitHub)
513
- if (isDiscovered) {
514
- // Use normalized repo name as ID (repo names are unique in GitHub)
515
- const repoId = normalizeRepoName(discoveredRepo.name);
516
- console.log(chalk.green(` Using: ${chalk.bold(discoveredRepo.name)} ${chalk.gray(`(id: ${repoId})`)}`));
517
- usedIds.add(repoId);
518
- configuredRepoNames.push(discoveredRepo.name);
519
- config.repositories.push({
520
- id: repoId,
521
- name: discoveredRepo.name,
522
- owner: discoveredRepo.owner,
523
- description: discoveredRepo.description || `${discoveredRepo.name} service`,
524
- path: discoveredRepo.name,
525
- visibility: discoveredRepo.private ? 'private' : 'public',
526
- createOnGitHub: false,
527
- isNested: useParent
528
- });
529
- continue;
530
- }
531
- // Manual entry (original behavior)
210
+ const suggestedName = suggestRepoName(projectName, i, repoCount);
211
+ // Manual entry
532
212
  const repoName = await input({
533
213
  message: 'Repository name:',
534
214
  default: suggestedName,
535
215
  validate: async (val) => {
536
216
  if (!val.trim())
537
217
  return 'Repository name is required';
538
- // Validate repository doesn't exist (skip for discovered repos)
539
- if (!isDiscovered && githubToken && config.parentRepo) {
540
- const result = await provider.validateRepository(config.parentRepo.owner, val, githubToken);
218
+ // Validate repository doesn't exist
219
+ if (githubToken) {
220
+ const result = await provider.validateRepository(owner, val, githubToken);
541
221
  if (result.exists) {
542
- return `Repository ${config.parentRepo.owner}/${val} already exists at ${result.url}`;
222
+ return `Repository ${owner}/${val} already exists at ${result.url}`;
543
223
  }
544
224
  }
545
225
  return true;
@@ -549,28 +229,18 @@ export async function configureMultiRepo(options) {
549
229
  message: 'Repository description:',
550
230
  default: `${path.basename(repoName)} service`
551
231
  });
552
- // Skip "Create on GitHub?" for discovered repos - they already exist!
553
- let repoCreateOnGitHub = false;
554
- if (!isDiscovered) {
555
- repoCreateOnGitHub = await confirm({
556
- message: 'Create this repository on GitHub?',
557
- default: true
558
- });
559
- }
560
- const repoAnswers = {
561
- name: repoName,
562
- description: repoDescription,
563
- createOnGitHub: repoCreateOnGitHub
564
- };
232
+ const repoCreateOnGitHub = await confirm({
233
+ message: 'Create this repository on GitHub?',
234
+ default: true
235
+ });
565
236
  // Use normalized repo name as ID (repo names are unique)
566
- const id = normalizeRepoName(repoAnswers.name);
237
+ const id = normalizeRepoName(repoName);
567
238
  console.log(chalk.green(` Repository ID: ${chalk.bold(id)}`));
568
239
  usedIds.add(id);
569
- configuredRepoNames.push(repoAnswers.name);
570
240
  // Ask about visibility only if creating a new repository
571
241
  let visibility = 'private';
572
- if (repoAnswers.createOnGitHub) {
573
- const visibilityPrompt = getVisibilityPrompt(repoAnswers.name);
242
+ if (repoCreateOnGitHub) {
243
+ const visibilityPrompt = getVisibilityPrompt(repoName);
574
244
  visibility = await select({
575
245
  message: visibilityPrompt.question,
576
246
  choices: visibilityPrompt.options.map(opt => ({
@@ -582,19 +252,18 @@ export async function configureMultiRepo(options) {
582
252
  }
583
253
  config.repositories.push({
584
254
  id: id,
585
- name: repoAnswers.name,
586
- owner: config.parentRepo?.owner || '',
587
- description: repoAnswers.description,
588
- path: repoAnswers.name, // Use full repo name as folder (not short ID)
255
+ name: repoName,
256
+ owner: owner,
257
+ description: repoDescription,
258
+ path: repoName,
589
259
  visibility: visibility,
590
- createOnGitHub: repoAnswers.createOnGitHub,
591
- isNested: useParent
260
+ createOnGitHub: repoCreateOnGitHub,
261
+ isNested: false
592
262
  });
593
263
  // Save state after each repo
594
264
  await saveSetupState(stateManager, {
595
265
  version: '1.0.0',
596
- architecture: useParent ? 'parent' : 'multi-repo',
597
- parentRepo: config.parentRepo,
266
+ architecture: 'multi-repo',
598
267
  repos: config.repositories.map(r => ({
599
268
  id: r.id,
600
269
  repo: r.name,