opencode-sdlc-plugin 1.0.0-alpha.0 → 1.1.1

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/dist/cli/index.js CHANGED
@@ -1,482 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { join, dirname } from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { confirm, select, input, number, checkbox } from '@inquirer/prompts';
5
- import chalk6 from 'chalk';
2
+ import chalk5 from 'chalk';
6
3
  import { Command } from 'commander';
7
4
  import { existsSync, readFileSync, mkdirSync, copyFileSync, unlinkSync, readdirSync, renameSync, writeFileSync } from 'fs';
8
5
  import { homedir } from 'os';
6
+ import { join, dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
9
8
  import { exec, execSync } from 'child_process';
10
9
  import ora5 from 'ora';
11
10
  import { mkdir, writeFile, copyFile, readdir, rm } from 'fs/promises';
12
11
  import { promisify } from 'util';
13
12
  import { z } from 'zod';
13
+ import { confirm, select, input, number } from '@inquirer/prompts';
14
+ import { Octokit } from '@octokit/rest';
14
15
  import * as semver from 'semver';
15
16
  import semver__default from 'semver';
16
17
 
17
- var __defProp = Object.defineProperty;
18
- var __getOwnPropNames = Object.getOwnPropertyNames;
19
- var __esm = (fn, res) => function __init() {
20
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
21
- };
22
- var __export = (target, all) => {
23
- for (var name in all)
24
- __defProp(target, name, { get: all[name], enumerable: true });
25
- };
26
- var init_esm_shims = __esm({
27
- "node_modules/tsup/assets/esm_shims.js"() {
28
- }
29
- });
30
-
31
- // src/cli/utils/debug-logger.ts
32
- function debugLog(context, data) {
33
- if (!isDebugEnabled()) return;
34
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
35
- console.error(`[ATHENA_DEBUG ${timestamp}] ${context}:`, JSON.stringify(data, null, 2));
36
- }
37
- var isDebugEnabled;
38
- var init_debug_logger = __esm({
39
- "src/cli/utils/debug-logger.ts"() {
40
- init_esm_shims();
41
- isDebugEnabled = () => {
42
- return process.env.ATHENA_DEBUG === "1" || process.env.ATHENA_DEBUG === "true";
43
- };
44
- }
45
- });
46
-
47
- // src/cli/questions/models.ts
48
- var models_exports = {};
49
- __export(models_exports, {
50
- gatherModels: () => gatherModels,
51
- getAvailableModels: () => getAvailableModels,
52
- getModelList: () => getModelList,
53
- mergeCustomModels: () => mergeCustomModels,
54
- validatePresetModels: () => validatePresetModels
55
- });
56
- function customModelToChoice(custom) {
57
- return {
58
- id: custom.id,
59
- name: custom.name,
60
- provider: custom.provider,
61
- description: custom.description
62
- };
63
- }
64
- function mergeCustomModels(builtInModels, customModels) {
65
- if (!customModels || customModels.length === 0) {
66
- return builtInModels;
67
- }
68
- const modelMap = /* @__PURE__ */ new Map();
69
- for (const model of builtInModels) {
70
- modelMap.set(model.id, model);
71
- }
72
- for (const custom of customModels) {
73
- modelMap.set(custom.id, customModelToChoice(custom));
74
- }
75
- return Array.from(modelMap.values());
76
- }
77
- function isModelAvailableForCopilotPlan(modelId, plan) {
78
- if (plan === "none") return false;
79
- if (COPILOT_OPUS_MODELS.includes(modelId)) {
80
- return plan === "pro-plus" || plan === "enterprise";
81
- }
82
- if (plan === "free") {
83
- return COPILOT_FREE_MODELS.includes(modelId);
84
- }
85
- return true;
86
- }
87
- function getAvailableModels(subscriptions, customModels) {
88
- const allModels = mergeCustomModels(AVAILABLE_MODELS, customModels);
89
- debugLog("models.allModels.count", allModels.length);
90
- debugLog("models.subscriptions", subscriptions);
91
- const filtered = allModels.filter((model) => {
92
- if (model.provider === "anthropic" && !subscriptions.hasClaude) return false;
93
- if (model.provider === "openai" && !subscriptions.hasOpenAI) return false;
94
- if (model.provider === "google" && !subscriptions.hasGoogle) return false;
95
- if (model.provider === "github-copilot") {
96
- if (!subscriptions.hasGitHubCopilot) return false;
97
- if (!isModelAvailableForCopilotPlan(model.id, subscriptions.copilotPlan)) return false;
98
- if (subscriptions.copilotEnabledModels && !subscriptions.copilotEnabledModels.includes(model.id)) {
99
- return false;
100
- }
101
- }
102
- return true;
103
- });
104
- debugLog("models.filtered.count", filtered.length);
105
- debugLog("models.filtered.providers", [...new Set(filtered.map((m) => m.provider))]);
106
- return filtered;
107
- }
108
- function isModelAvailable(modelId, availableModels) {
109
- return availableModels.some((m) => m.id === modelId);
110
- }
111
- function getValidModelOrFallback(presetModel, role, subscriptions, availableModels) {
112
- if (presetModel && isModelAvailable(presetModel, availableModels)) {
113
- return presetModel;
114
- }
115
- return getSuggestedModel(role, subscriptions, availableModels);
116
- }
117
- function createModelChoices(models) {
118
- return models.map((model) => ({
119
- name: `${model.name} - ${model.description}`,
120
- value: model.id
121
- }));
122
- }
123
- function getSuggestedModel(role, _subscriptions, availableModels) {
124
- const suggestions = {
125
- marvin: [
126
- "openai/gpt-5.2-codex",
127
- "anthropic/claude-sonnet-4-5-thinking",
128
- "anthropic/claude-opus-4-5-thinking",
129
- "openai/gpt-5.1-high",
130
- "google/gemini-3-pro-preview",
131
- "github-copilot/gpt-5.2-codex",
132
- "github-copilot/claude-sonnet-4.5",
133
- "github-copilot/gpt-5.1"
134
- ],
135
- oracle: [
136
- "openai/gpt-5.2",
137
- "openai/gpt-5.1-high",
138
- "anthropic/claude-opus-4-5-thinking",
139
- "anthropic/claude-sonnet-4-5-thinking",
140
- "google/gemini-3-pro-preview",
141
- "github-copilot/gpt-5.2",
142
- "github-copilot/gpt-5.1",
143
- "github-copilot/claude-opus-4.5"
144
- ],
145
- librarian: [
146
- "anthropic/claude-sonnet-4-5",
147
- "openai/gpt-4o",
148
- "google/gemini-2.5-flash",
149
- "google/gemini-3-flash-preview",
150
- "github-copilot/claude-haiku-4.5",
151
- "github-copilot/gpt-5-mini"
152
- ],
153
- frontend: [
154
- "anthropic/claude-sonnet-4-5",
155
- "openai/gpt-5.2-codex",
156
- "google/gemini-3-pro-preview",
157
- "openai/gpt-4o",
158
- "github-copilot/claude-sonnet-4.5",
159
- "github-copilot/gpt-5.2-codex"
160
- ],
161
- documentWriter: [
162
- "google/gemini-3-pro-preview",
163
- "google/gemini-2.5-pro",
164
- "anthropic/claude-sonnet-4-5",
165
- "openai/gpt-4o",
166
- "github-copilot/gemini-3-pro",
167
- "github-copilot/claude-sonnet-4.5"
168
- ],
169
- multimodalLooker: [
170
- "google/gemini-3-flash-preview",
171
- "google/gemini-2.5-flash",
172
- "openai/gpt-4o",
173
- "anthropic/claude-sonnet-4-5",
174
- "github-copilot/gemini-3-flash-preview",
175
- "github-copilot/gpt-5-mini"
176
- ],
177
- explore: [
178
- "google/gemini-3-flash-preview",
179
- "google/gemini-2.5-flash",
180
- "anthropic/claude-sonnet-4-5",
181
- "openai/gpt-4o",
182
- "github-copilot/gemini-3-flash-preview",
183
- "github-copilot/claude-haiku-4.5"
184
- ]
185
- };
186
- const roleDefaults = suggestions[role] || [];
187
- const availableIds = availableModels.map((m) => m.id);
188
- for (const modelId of roleDefaults) {
189
- if (availableIds.includes(modelId)) {
190
- return modelId;
191
- }
192
- }
193
- return availableModels[0]?.id;
194
- }
195
- async function gatherModels(subscriptions, defaults, customModels) {
196
- const availableModels = getAvailableModels(subscriptions, customModels);
197
- if (availableModels.length === 0) {
198
- throw new Error(
199
- "No models available. Please enable at least one provider (Claude, OpenAI, Google, or GitHub Copilot)."
200
- );
201
- }
202
- const choices = createModelChoices(availableModels);
203
- const marvinDefault = getValidModelOrFallback(
204
- defaults?.marvin,
205
- "marvin",
206
- subscriptions,
207
- availableModels
208
- );
209
- const oracleDefault = getValidModelOrFallback(
210
- defaults?.oracle,
211
- "oracle",
212
- subscriptions,
213
- availableModels
214
- );
215
- const librarianDefault = getValidModelOrFallback(
216
- defaults?.librarian,
217
- "librarian",
218
- subscriptions,
219
- availableModels
220
- );
221
- const marvin = await select({
222
- message: "Model for Marvin (main orchestrator - implements stories)?",
223
- choices,
224
- default: marvinDefault
225
- });
226
- const oracle = await select({
227
- message: "Model for Oracle (debugging and complex reasoning)?",
228
- choices,
229
- default: oracleDefault
230
- });
231
- const librarian = await select({
232
- message: "Model for Librarian (research and documentation lookup)?",
233
- choices,
234
- default: librarianDefault
235
- });
236
- const frontend = getValidModelOrFallback(
237
- defaults?.frontend,
238
- "frontend",
239
- subscriptions,
240
- availableModels
241
- );
242
- const documentWriter = getValidModelOrFallback(
243
- defaults?.documentWriter,
244
- "documentWriter",
245
- subscriptions,
246
- availableModels
247
- );
248
- const multimodalLooker = getValidModelOrFallback(
249
- defaults?.multimodalLooker,
250
- "multimodalLooker",
251
- subscriptions,
252
- availableModels
253
- );
254
- return {
255
- marvin,
256
- oracle,
257
- librarian,
258
- frontend,
259
- documentWriter,
260
- multimodalLooker
261
- };
262
- }
263
- function getModelList(subscriptions, customModels) {
264
- return getAvailableModels(subscriptions, customModels);
265
- }
266
- function validatePresetModels(presetModels, subscriptions, customModels) {
267
- const warnings = [];
268
- const availableModels = getAvailableModels(subscriptions, customModels);
269
- const checkModel = (model, role) => {
270
- if (model && !isModelAvailable(model, availableModels)) {
271
- warnings.push(
272
- `Preset model for ${role} (${model}) is not available with your subscriptions. A fallback will be used.`
273
- );
274
- }
275
- };
276
- checkModel(presetModels.marvin, "Marvin");
277
- checkModel(presetModels.oracle, "Oracle");
278
- checkModel(presetModels.librarian, "Librarian");
279
- checkModel(presetModels.frontend, "Frontend");
280
- checkModel(presetModels.documentWriter, "Document Writer");
281
- checkModel(presetModels.multimodalLooker, "Multimodal Looker");
282
- return warnings;
283
- }
284
- var AVAILABLE_MODELS, COPILOT_FREE_MODELS, COPILOT_OPUS_MODELS;
285
- var init_models = __esm({
286
- "src/cli/questions/models.ts"() {
287
- init_esm_shims();
288
- init_debug_logger();
289
- AVAILABLE_MODELS = [
290
- // Anthropic models
291
- {
292
- id: "anthropic/claude-sonnet-4-5",
293
- name: "Claude Sonnet 4.5",
294
- provider: "anthropic",
295
- description: "Latest Sonnet - balanced performance and speed"
296
- },
297
- {
298
- id: "anthropic/claude-opus-4-5",
299
- name: "Claude Opus 4.5",
300
- provider: "anthropic",
301
- description: "Most capable Claude model"
302
- },
303
- {
304
- id: "anthropic/claude-sonnet-4-5-thinking",
305
- name: "Claude Sonnet 4.5 (Thinking)",
306
- provider: "anthropic",
307
- description: "Sonnet with extended thinking enabled"
308
- },
309
- {
310
- id: "anthropic/claude-opus-4-5-thinking",
311
- name: "Claude Opus 4.5 (Thinking)",
312
- provider: "anthropic",
313
- description: "Opus with extended thinking enabled"
314
- },
315
- // OpenAI models
316
- {
317
- id: "openai/gpt-4o",
318
- name: "GPT-4o",
319
- provider: "openai",
320
- description: "Fast multimodal model"
321
- },
322
- {
323
- id: "openai/gpt-5.1",
324
- name: "GPT-5.1",
325
- provider: "openai",
326
- description: "GPT-5.1 base model"
327
- },
328
- {
329
- id: "openai/gpt-5.1-high",
330
- name: "GPT-5.1 High",
331
- provider: "openai",
332
- description: "GPT-5.1 with high reasoning effort"
333
- },
334
- {
335
- id: "openai/gpt-5.2",
336
- name: "GPT-5.2",
337
- provider: "openai",
338
- description: "Latest GPT model"
339
- },
340
- {
341
- id: "openai/gpt-5.2-codex",
342
- name: "GPT-5.2 Codex",
343
- provider: "openai",
344
- description: "Code-optimized GPT-5.2"
345
- },
346
- // Google models
347
- {
348
- id: "google/gemini-2.5-pro",
349
- name: "Gemini 2.5 Pro",
350
- provider: "google",
351
- description: "Gemini 2.5 Pro model"
352
- },
353
- {
354
- id: "google/gemini-2.5-flash",
355
- name: "Gemini 2.5 Flash",
356
- provider: "google",
357
- description: "Fast Gemini model"
358
- },
359
- {
360
- id: "google/gemini-3-flash-preview",
361
- name: "Gemini 3 Flash (Preview)",
362
- provider: "google",
363
- description: "Gemini 3 Flash preview - fast and efficient"
364
- },
365
- {
366
- id: "google/gemini-3-pro-preview",
367
- name: "Gemini 3 Pro (Preview)",
368
- provider: "google",
369
- description: "Latest Gemini 3 Pro preview"
370
- },
371
- // GitHub Copilot models (routed through Copilot - smaller context, no thinking)
372
- // Free tier models
373
- {
374
- id: "github-copilot/gpt-4.1",
375
- name: "GPT-4.1 (via Copilot)",
376
- provider: "github-copilot",
377
- description: "GPT-4.1 through GitHub Copilot"
378
- },
379
- {
380
- id: "github-copilot/gpt-5-mini",
381
- name: "GPT-5 mini (via Copilot)",
382
- provider: "github-copilot",
383
- description: "Fast GPT-5 variant through GitHub Copilot"
384
- },
385
- {
386
- id: "github-copilot/claude-haiku-4.5",
387
- name: "Claude Haiku 4.5 (via Copilot)",
388
- provider: "github-copilot",
389
- description: "Fast Claude model through GitHub Copilot"
390
- },
391
- // Pro/Business/Enterprise models
392
- {
393
- id: "github-copilot/claude-sonnet-4",
394
- name: "Claude Sonnet 4 (via Copilot)",
395
- provider: "github-copilot",
396
- description: "Claude Sonnet 4 through GitHub Copilot"
397
- },
398
- {
399
- id: "github-copilot/claude-sonnet-4.5",
400
- name: "Claude Sonnet 4.5 (via Copilot)",
401
- provider: "github-copilot",
402
- description: "Latest Sonnet through GitHub Copilot - no thinking mode"
403
- },
404
- {
405
- id: "github-copilot/gpt-5",
406
- name: "GPT-5 (via Copilot)",
407
- provider: "github-copilot",
408
- description: "GPT-5 through GitHub Copilot"
409
- },
410
- {
411
- id: "github-copilot/gpt-5.1",
412
- name: "GPT-5.1 (via Copilot)",
413
- provider: "github-copilot",
414
- description: "GPT-5.1 through GitHub Copilot"
415
- },
416
- {
417
- id: "github-copilot/gpt-5.1-codex",
418
- name: "GPT-5.1-Codex (via Copilot)",
419
- provider: "github-copilot",
420
- description: "Code-optimized GPT-5.1 through GitHub Copilot"
421
- },
422
- {
423
- id: "github-copilot/gpt-5.2",
424
- name: "GPT-5.2 (via Copilot)",
425
- provider: "github-copilot",
426
- description: "Latest GPT through GitHub Copilot"
427
- },
428
- {
429
- id: "github-copilot/gpt-5.2-codex",
430
- name: "GPT-5.2 Codex (via Copilot)",
431
- provider: "github-copilot",
432
- description: "Code-optimized GPT-5.2 through GitHub Copilot"
433
- },
434
- {
435
- id: "github-copilot/gemini-2.5-pro",
436
- name: "Gemini 2.5 Pro (via Copilot)",
437
- provider: "github-copilot",
438
- description: "Gemini 2.5 Pro through GitHub Copilot"
439
- },
440
- {
441
- id: "github-copilot/gemini-3-flash",
442
- name: "Gemini 3 Flash (via Copilot)",
443
- provider: "github-copilot",
444
- description: "Fast Gemini 3 through GitHub Copilot"
445
- },
446
- {
447
- id: "github-copilot/gemini-3-pro",
448
- name: "Gemini 3 Pro (via Copilot)",
449
- provider: "github-copilot",
450
- description: "Gemini 3 Pro through GitHub Copilot"
451
- },
452
- // Pro+/Enterprise only (Opus models)
453
- {
454
- id: "github-copilot/claude-opus-4.1",
455
- name: "Claude Opus 4.1 (via Copilot)",
456
- provider: "github-copilot",
457
- description: "Claude Opus 4.1 through GitHub Copilot - Pro+/Enterprise only"
458
- },
459
- {
460
- id: "github-copilot/claude-opus-4.5",
461
- name: "Claude Opus 4.5 (via Copilot)",
462
- provider: "github-copilot",
463
- description: "Most capable Claude through GitHub Copilot - Pro+/Enterprise only"
464
- }
465
- ];
466
- COPILOT_FREE_MODELS = [
467
- "github-copilot/gpt-4.1",
468
- "github-copilot/gpt-5-mini",
469
- "github-copilot/claude-haiku-4.5"
470
- ];
471
- COPILOT_OPUS_MODELS = ["github-copilot/claude-opus-4.1", "github-copilot/claude-opus-4.5"];
472
- }
473
- });
474
-
475
- // src/cli/index.ts
476
- init_esm_shims();
477
-
478
- // src/shared/constants.ts
479
- init_esm_shims();
480
18
  function getPackageVersion() {
481
19
  try {
482
20
  const currentDir = dirname(fileURLToPath(import.meta.url));
@@ -527,12 +65,6 @@ var MIN_VERSIONS = {
527
65
  node: "20.0.0",
528
66
  opencode: "1.0.132"
529
67
  };
530
-
531
- // src/cli/commands/doctor.ts
532
- init_esm_shims();
533
-
534
- // src/cli/utils/file-manager.ts
535
- init_esm_shims();
536
68
  var execAsync = promisify(exec);
537
69
  function getPackageRoot() {
538
70
  const currentFileDir = dirname(fileURLToPath(import.meta.url));
@@ -578,12 +110,12 @@ var FileManager = class {
578
110
  /**
579
111
  * Read a JSON configuration file
580
112
  */
581
- readJsonFile(path2) {
582
- if (!existsSync(path2)) {
113
+ readJsonFile(path) {
114
+ if (!existsSync(path)) {
583
115
  return null;
584
116
  }
585
117
  try {
586
- const content = readFileSync(path2, "utf-8");
118
+ const content = readFileSync(path, "utf-8");
587
119
  return JSON.parse(content);
588
120
  } catch {
589
121
  return null;
@@ -592,16 +124,16 @@ var FileManager = class {
592
124
  /**
593
125
  * Write a JSON configuration file
594
126
  */
595
- async writeJsonFile(path2, data) {
596
- const dir = dirname(path2);
127
+ async writeJsonFile(path, data) {
128
+ const dir = dirname(path);
597
129
  await this.ensureDir(dir);
598
- await writeFile(path2, JSON.stringify(data, null, 2), "utf-8");
130
+ await writeFile(path, JSON.stringify(data, null, 2), "utf-8");
599
131
  }
600
132
  /**
601
133
  * Check if a file exists
602
134
  */
603
- exists(path2) {
604
- return existsSync(path2);
135
+ exists(path) {
136
+ return existsSync(path);
605
137
  }
606
138
  /**
607
139
  * Install npm dependencies in the config directory
@@ -777,12 +309,12 @@ var FileManager = class {
777
309
  /**
778
310
  * Backup a file before modifying
779
311
  */
780
- async backupFile(path2) {
781
- if (!existsSync(path2)) {
312
+ async backupFile(path) {
313
+ if (!existsSync(path)) {
782
314
  return null;
783
315
  }
784
- const backupPath = `${path2}.backup`;
785
- await copyFile(path2, backupPath);
316
+ const backupPath = `${path}.backup`;
317
+ await copyFile(path, backupPath);
786
318
  return backupPath;
787
319
  }
788
320
  /**
@@ -796,47 +328,44 @@ var FileManager = class {
796
328
  }
797
329
  }
798
330
  };
799
-
800
- // src/cli/utils/logger.ts
801
- init_esm_shims();
802
331
  var logger = {
803
332
  /**
804
333
  * Log an informational message
805
334
  */
806
335
  info: (message) => {
807
- console.log(chalk6.blue("i"), message);
336
+ console.log(chalk5.blue("i"), message);
808
337
  },
809
338
  /**
810
339
  * Log a success message
811
340
  */
812
341
  success: (message) => {
813
- console.log(chalk6.green("\u2713"), message);
342
+ console.log(chalk5.green("\u2713"), message);
814
343
  },
815
344
  /**
816
345
  * Log a warning message
817
346
  */
818
347
  warn: (message) => {
819
- console.log(chalk6.yellow("!"), message);
348
+ console.log(chalk5.yellow("!"), message);
820
349
  },
821
350
  /**
822
351
  * Log an error message
823
352
  */
824
353
  error: (message) => {
825
- console.log(chalk6.red("\u2716"), message);
354
+ console.log(chalk5.red("\u2716"), message);
826
355
  },
827
356
  /**
828
357
  * Log a debug message (only when DEBUG env var is set)
829
358
  */
830
359
  debug: (message) => {
831
360
  if (process.env.DEBUG) {
832
- console.log(chalk6.gray("[debug]"), message);
361
+ console.log(chalk5.gray("[debug]"), message);
833
362
  }
834
363
  },
835
364
  /**
836
365
  * Log a step in a process
837
366
  */
838
367
  step: (step, total, message) => {
839
- console.log(chalk6.cyan(`[${step}/${total}]`), message);
368
+ console.log(chalk5.cyan(`[${step}/${total}]`), message);
840
369
  },
841
370
  /**
842
371
  * Log a blank line
@@ -849,7 +378,7 @@ var logger = {
849
378
  */
850
379
  section: (title) => {
851
380
  console.log();
852
- console.log(chalk6.bold(title));
381
+ console.log(chalk5.bold(title));
853
382
  console.log();
854
383
  },
855
384
  /**
@@ -857,21 +386,21 @@ var logger = {
857
386
  */
858
387
  keyValue: (key, value, indent = 0) => {
859
388
  const padding = " ".repeat(indent);
860
- console.log(`${padding}${chalk6.gray(`${key}:`)} ${value}`);
389
+ console.log(`${padding}${chalk5.gray(`${key}:`)} ${value}`);
861
390
  },
862
391
  /**
863
392
  * Log a list item
864
393
  */
865
394
  listItem: (item, indent = 0) => {
866
395
  const padding = " ".repeat(indent);
867
- console.log(`${padding}${chalk6.gray("-")} ${item}`);
396
+ console.log(`${padding}${chalk5.gray("-")} ${item}`);
868
397
  },
869
398
  /**
870
399
  * Display the Sdlc banner
871
400
  */
872
401
  banner: () => {
873
402
  console.log(
874
- chalk6.cyan(`
403
+ chalk5.cyan(`
875
404
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
876
405
  \u2551 OPENCODE ATHENA \u2551
877
406
  \u2551 Strategic Wisdom Meets Practical Execution \u2551
@@ -887,7 +416,7 @@ var logger = {
887
416
  successBanner: (message) => {
888
417
  const line = "\u2550".repeat(message.length + 4);
889
418
  console.log(
890
- chalk6.green(`
419
+ chalk5.green(`
891
420
  \u2554${line}\u2557
892
421
  \u2551 ${message} \u2551
893
422
  \u255A${line}\u255D
@@ -895,9 +424,6 @@ var logger = {
895
424
  );
896
425
  }
897
426
  };
898
-
899
- // src/cli/utils/prerequisites.ts
900
- init_esm_shims();
901
427
  var execAsync2 = promisify(exec);
902
428
  function parseVersion(version) {
903
429
  const match = version.match(/^v?(\d+)\.(\d+)\.(\d+)/);
@@ -994,12 +520,12 @@ async function getInstalledPlugins() {
994
520
  return {};
995
521
  }
996
522
  }
997
- function validateJsonFile(path2) {
998
- if (!existsSync(path2)) {
523
+ function validateJsonFile(path) {
524
+ if (!existsSync(path)) {
999
525
  return { valid: false, error: "File does not exist" };
1000
526
  }
1001
527
  try {
1002
- const content = readFileSync(path2, "utf-8");
528
+ const content = readFileSync(path, "utf-8");
1003
529
  JSON.parse(content);
1004
530
  return { valid: true };
1005
531
  } catch (err) {
@@ -1009,12 +535,6 @@ function validateJsonFile(path2) {
1009
535
  };
1010
536
  }
1011
537
  }
1012
-
1013
- // src/cli/utils/validators.ts
1014
- init_esm_shims();
1015
-
1016
- // src/shared/schemas.ts
1017
- init_esm_shims();
1018
538
  var ProviderAuthMethodSchema = z.enum(["subscription", "api", "none"]);
1019
539
  var SubscriptionSchema = z.object({
1020
540
  claude: z.object({
@@ -1298,15 +818,15 @@ function validateSdlcConfig(config) {
1298
818
  }
1299
819
  return result;
1300
820
  }
1301
- function validateJsonConfig(path2) {
821
+ function validateJsonConfig(path) {
1302
822
  const result = { valid: true, errors: [], warnings: [] };
1303
- if (!existsSync(path2)) {
823
+ if (!existsSync(path)) {
1304
824
  result.valid = false;
1305
825
  result.errors.push("File does not exist");
1306
826
  return result;
1307
827
  }
1308
828
  try {
1309
- const content = readFileSync(path2, "utf-8");
829
+ const content = readFileSync(path, "utf-8");
1310
830
  JSON.parse(content);
1311
831
  } catch (err) {
1312
832
  result.valid = false;
@@ -1517,16 +1037,16 @@ async function doctor(options) {
1517
1037
  switch (result.status) {
1518
1038
  case "pass":
1519
1039
  icon = "\u2713";
1520
- color = chalk6.green;
1040
+ color = chalk5.green;
1521
1041
  break;
1522
1042
  case "warn":
1523
1043
  icon = "!";
1524
- color = chalk6.yellow;
1044
+ color = chalk5.yellow;
1525
1045
  hasWarnings = true;
1526
1046
  break;
1527
1047
  case "fail":
1528
1048
  icon = "\u2716";
1529
- color = chalk6.red;
1049
+ color = chalk5.red;
1530
1050
  hasFailures = true;
1531
1051
  if (result.fix) {
1532
1052
  fixableIssues.push(result);
@@ -1554,7 +1074,7 @@ async function doctor(options) {
1554
1074
  logger.blank();
1555
1075
  logger.info("Run 'opencode-sdlc doctor' again to verify fixes.");
1556
1076
  } else if (fixableIssues.length > 0) {
1557
- logger.info(`Run ${chalk6.cyan("opencode-sdlc doctor --fix")} to attempt automatic fixes.`);
1077
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc doctor --fix")} to attempt automatic fixes.`);
1558
1078
  }
1559
1079
  } else if (hasWarnings) {
1560
1080
  logger.warn("Some checks have warnings, but Sdlc should work.");
@@ -1562,35 +1082,32 @@ async function doctor(options) {
1562
1082
  logger.success("All checks passed! OpenCode SDLC is healthy.");
1563
1083
  }
1564
1084
  }
1565
-
1566
- // src/cli/commands/info.ts
1567
- init_esm_shims();
1568
1085
  async function info() {
1569
1086
  logger.banner();
1570
1087
  const fileManager = new FileManager();
1571
1088
  const sdlcConfig = fileManager.readJsonFile(CONFIG_PATHS.globalSdlcConfig);
1572
1089
  if (!sdlcConfig) {
1573
1090
  logger.warn("OpenCode SDLC is not installed.");
1574
- logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to get started.`);
1091
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to get started.`);
1575
1092
  return;
1576
1093
  }
1577
1094
  logger.section("Version Information");
1578
1095
  logger.keyValue("Sdlc Version", sdlcConfig.version || VERSION);
1579
1096
  logger.section("Prerequisites");
1580
1097
  const prereqs = await checkPrerequisites();
1581
- const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ? chalk6.green("\u2713") : chalk6.yellow("!") : chalk6.red("\u2716");
1098
+ const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ? chalk5.green("\u2713") : chalk5.yellow("!") : chalk5.red("\u2716");
1582
1099
  logger.keyValue("Node.js", `${nodeStatus} ${prereqs.node.version || "not found"}`);
1583
- const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ? chalk6.green("\u2713") : chalk6.yellow("!") : chalk6.red("\u2716");
1100
+ const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ? chalk5.green("\u2713") : chalk5.yellow("!") : chalk5.red("\u2716");
1584
1101
  logger.keyValue("OpenCode", `${opencodeStatus} ${prereqs.opencode.version || "not found"}`);
1585
1102
  logger.section("Configured Providers");
1586
- const claudeStatus = sdlcConfig.subscriptions.claude.enabled ? chalk6.green("enabled") : chalk6.gray("disabled");
1103
+ const claudeStatus = sdlcConfig.subscriptions.claude.enabled ? chalk5.green("enabled") : chalk5.gray("disabled");
1587
1104
  logger.keyValue(
1588
1105
  "Claude",
1589
1106
  `${claudeStatus}${sdlcConfig.subscriptions.claude.tier !== "none" ? ` (${sdlcConfig.subscriptions.claude.tier})` : ""}`
1590
1107
  );
1591
- const openaiStatus = sdlcConfig.subscriptions.openai.enabled ? chalk6.green("enabled") : chalk6.gray("disabled");
1108
+ const openaiStatus = sdlcConfig.subscriptions.openai.enabled ? chalk5.green("enabled") : chalk5.gray("disabled");
1592
1109
  logger.keyValue("OpenAI", openaiStatus);
1593
- const googleStatus = sdlcConfig.subscriptions.google.enabled ? chalk6.green("enabled") : chalk6.gray("disabled");
1110
+ const googleStatus = sdlcConfig.subscriptions.google.enabled ? chalk5.green("enabled") : chalk5.gray("disabled");
1594
1111
  logger.keyValue(
1595
1112
  "Google",
1596
1113
  `${googleStatus}${sdlcConfig.subscriptions.google.authMethod !== "none" ? ` (${sdlcConfig.subscriptions.google.authMethod})` : ""}`
@@ -1708,18 +1225,18 @@ async function info() {
1708
1225
  }
1709
1226
  ];
1710
1227
  for (const feature of featureList) {
1711
- const status = feature.enabled ? chalk6.green("on") : chalk6.gray("off");
1228
+ const status = feature.enabled ? chalk5.green("on") : chalk5.gray("off");
1712
1229
  logger.keyValue(feature.name, status);
1713
1230
  }
1714
1231
  logger.section("MCP Servers");
1715
1232
  const mcps = sdlcConfig.mcps;
1716
- logger.keyValue("context7", mcps.context7 ? chalk6.green("on") : chalk6.gray("off"));
1717
- logger.keyValue("exa", mcps.exa ? chalk6.green("on") : chalk6.gray("off"));
1718
- logger.keyValue("grep_app", mcps.grepApp ? chalk6.green("on") : chalk6.gray("off"));
1233
+ logger.keyValue("context7", mcps.context7 ? chalk5.green("on") : chalk5.gray("off"));
1234
+ logger.keyValue("exa", mcps.exa ? chalk5.green("on") : chalk5.gray("off"));
1235
+ logger.keyValue("grep_app", mcps.grepApp ? chalk5.green("on") : chalk5.gray("off"));
1719
1236
  if ("memento" in mcps) {
1720
1237
  logger.keyValue(
1721
1238
  "memento",
1722
- mcps.memento ? chalk6.green("on") : chalk6.gray("off")
1239
+ mcps.memento ? chalk5.green("on") : chalk5.gray("off")
1723
1240
  );
1724
1241
  }
1725
1242
  logger.section("Installed Plugins");
@@ -1737,17 +1254,7 @@ async function info() {
1737
1254
  console.log();
1738
1255
  }
1739
1256
 
1740
- // src/cli/commands/install.ts
1741
- init_esm_shims();
1742
-
1743
- // src/cli/generators/config-generator.ts
1744
- init_esm_shims();
1745
-
1746
- // src/cli/generators/omo-config.ts
1747
- init_esm_shims();
1748
-
1749
1257
  // src/plugin/utils/model-params.ts
1750
- init_esm_shims();
1751
1258
  var MODEL_FAMILY_BASE_TEMPS = {
1752
1259
  "claude-thinking": 0.3,
1753
1260
  claude: 0.2,
@@ -2073,9 +1580,6 @@ function generateOmoConfig(answers) {
2073
1580
  }
2074
1581
  return omoConfig;
2075
1582
  }
2076
-
2077
- // src/cli/generators/opencode-config.ts
2078
- init_esm_shims();
2079
1583
  async function generateOpencodeConfig(answers, configDir) {
2080
1584
  const configPath = join(configDir, "opencode.json");
2081
1585
  let existingConfig = {};
@@ -2261,12 +1765,6 @@ function getRequiredPlugins(answers) {
2261
1765
  }
2262
1766
  return plugins;
2263
1767
  }
2264
-
2265
- // src/cli/generators/sdlc-config.ts
2266
- init_esm_shims();
2267
-
2268
- // src/cli/questions/features.ts
2269
- init_esm_shims();
2270
1768
  var AVAILABLE_FEATURES = [
2271
1769
  {
2272
1770
  name: "BMAD Bridge Commands (/sdlc-dev, /sdlc-review, etc.)",
@@ -2301,8 +1799,7 @@ var AVAILABLE_FEATURES = [
2301
1799
  value: "auto-git-operations"
2302
1800
  }
2303
1801
  ];
2304
- var ALL_FEATURE_VALUES = AVAILABLE_FEATURES.map((f) => f.value);
2305
- var FEATURES_ENABLED_BY_DEFAULT = ALL_FEATURE_VALUES.filter((f) => f !== "auto-git-operations");
1802
+ AVAILABLE_FEATURES.map((f) => f.value);
2306
1803
  var AVAILABLE_MCPS = [
2307
1804
  {
2308
1805
  name: "context7 - Documentation lookup and context retrieval",
@@ -2321,35 +1818,7 @@ var AVAILABLE_MCPS = [
2321
1818
  value: "memento"
2322
1819
  }
2323
1820
  ];
2324
- var ALL_MCP_VALUES = AVAILABLE_MCPS.map((m) => m.value);
2325
- function createFeatureChoices(defaults) {
2326
- const enabledSet = new Set(defaults ?? FEATURES_ENABLED_BY_DEFAULT);
2327
- return AVAILABLE_FEATURES.map((feature) => ({
2328
- ...feature,
2329
- checked: enabledSet.has(feature.value)
2330
- }));
2331
- }
2332
- function createMcpChoices(defaults) {
2333
- const enabledSet = new Set(defaults ?? ALL_MCP_VALUES);
2334
- return AVAILABLE_MCPS.map((mcp) => ({
2335
- ...mcp,
2336
- checked: enabledSet.has(mcp.value)
2337
- }));
2338
- }
2339
- async function gatherFeatures(defaults) {
2340
- const enabledFeatures = await checkbox({
2341
- message: "Select features to enable:",
2342
- choices: createFeatureChoices(defaults?.enabledFeatures)
2343
- });
2344
- const mcps = await checkbox({
2345
- message: "Select MCP servers to enable:",
2346
- choices: createMcpChoices(defaults?.mcps)
2347
- });
2348
- return {
2349
- enabledFeatures,
2350
- mcps
2351
- };
2352
- }
1821
+ AVAILABLE_MCPS.map((m) => m.value);
2353
1822
  function featuresToFlags(enabledFeatures) {
2354
1823
  return {
2355
1824
  bmadBridge: enabledFeatures.includes("bmad-bridge"),
@@ -2604,568 +2073,1239 @@ var ConfigGenerator = class {
2604
2073
  return getRequiredPlugins(this.answers);
2605
2074
  }
2606
2075
  };
2607
-
2608
- // src/cli/questions/index.ts
2609
- init_esm_shims();
2610
-
2611
- // src/cli/questions/subscriptions.ts
2612
- init_esm_shims();
2613
- init_debug_logger();
2614
- async function gatherSubscriptions() {
2615
- const hasClaude = await confirm({
2616
- message: "Will you use Anthropic/Claude models?",
2617
- default: false
2618
- });
2619
- debugLog("subscription.hasClaude", hasClaude);
2620
- let claudeAuth = "none";
2621
- let claudeTier = "none";
2622
- if (hasClaude) {
2623
- claudeAuth = await select({
2624
- message: "How will you authenticate with Anthropic?",
2625
- choices: [
2626
- {
2627
- name: "API Key - Direct API access (pay per use)",
2628
- value: "api"
2629
- },
2630
- {
2631
- name: "Claude Pro/Max Subscription - OAuth login",
2632
- value: "subscription"
2633
- }
2634
- ]
2635
- });
2636
- debugLog("subscription.claudeAuth", claudeAuth);
2637
- if (claudeAuth === "subscription") {
2638
- claudeTier = await select({
2639
- message: "Which Claude subscription tier?",
2640
- choices: [
2641
- { name: "Max 5x - 5x more usage than Pro", value: "max5x" },
2642
- { name: "Max 20x - 20x more usage than Pro", value: "max20x" },
2643
- { name: "Pro - Standard Pro subscription", value: "pro" }
2644
- ]
2645
- });
2646
- debugLog("subscription.claudeTier", claudeTier);
2076
+ function resolveGitHubToken(explicitToken) {
2077
+ if (explicitToken) {
2078
+ return explicitToken;
2079
+ }
2080
+ const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
2081
+ if (envToken) {
2082
+ return envToken;
2083
+ }
2084
+ try {
2085
+ const ghToken = execSync("gh auth token", {
2086
+ encoding: "utf-8",
2087
+ timeout: 5e3,
2088
+ stdio: ["pipe", "pipe", "pipe"]
2089
+ }).trim();
2090
+ if (ghToken) {
2091
+ return ghToken;
2647
2092
  }
2093
+ } catch {
2648
2094
  }
2649
- const hasOpenAI = await confirm({
2650
- message: "Will you use OpenAI models?",
2651
- default: false
2652
- });
2653
- debugLog("subscription.hasOpenAI", hasOpenAI);
2654
- let openaiAuth = "none";
2655
- if (hasOpenAI) {
2656
- openaiAuth = await select({
2657
- message: "How will you authenticate with OpenAI?",
2658
- choices: [
2659
- {
2660
- name: "API Key - Direct API access (pay per use)",
2661
- value: "api"
2662
- },
2663
- {
2664
- name: "ChatGPT Plus/Pro Subscription - OAuth login",
2665
- value: "subscription"
2666
- }
2667
- ]
2095
+ return null;
2096
+ }
2097
+ function isGitHubAuthenticated(token) {
2098
+ return resolveGitHubToken(token) !== null;
2099
+ }
2100
+ var GitHubClient = class {
2101
+ octokit;
2102
+ owner;
2103
+ repo;
2104
+ constructor(options = {}) {
2105
+ const token = resolveGitHubToken(options.token);
2106
+ if (!token) {
2107
+ throw new Error(
2108
+ "GitHub authentication not found. Set GITHUB_TOKEN environment variable or run 'gh auth login'."
2109
+ );
2110
+ }
2111
+ this.octokit = new Octokit({ auth: token });
2112
+ this.owner = options.owner;
2113
+ this.repo = options.repo;
2114
+ }
2115
+ /**
2116
+ * Set the default owner/repo for operations
2117
+ */
2118
+ setRepo(owner, repo) {
2119
+ this.owner = owner;
2120
+ this.repo = repo;
2121
+ }
2122
+ getRepoParams(owner, repo) {
2123
+ const o = owner || this.owner;
2124
+ const r = repo || this.repo;
2125
+ if (!o || !r) {
2126
+ throw new Error("Repository owner and name are required");
2127
+ }
2128
+ return { owner: o, repo: r };
2129
+ }
2130
+ // ==========================================================================
2131
+ // Issues
2132
+ // ==========================================================================
2133
+ /**
2134
+ * Get a single issue by number
2135
+ */
2136
+ async getIssue(issueNumber, owner, repo) {
2137
+ const params = this.getRepoParams(owner, repo);
2138
+ const { data } = await this.octokit.issues.get({
2139
+ ...params,
2140
+ issue_number: issueNumber
2141
+ });
2142
+ return {
2143
+ number: data.number,
2144
+ title: data.title,
2145
+ body: data.body ?? null,
2146
+ url: data.html_url,
2147
+ state: data.state,
2148
+ labels: (data.labels || []).map(
2149
+ (label) => typeof label === "string" ? { name: label } : { name: label.name || "" }
2150
+ )
2151
+ };
2152
+ }
2153
+ /**
2154
+ * List issues in a repository
2155
+ */
2156
+ async listIssues(options = {}, owner, repo) {
2157
+ const params = this.getRepoParams(owner, repo);
2158
+ const { data } = await this.octokit.issues.listForRepo({
2159
+ ...params,
2160
+ state: options.state || "open",
2161
+ labels: options.labels?.join(","),
2162
+ per_page: options.limit || 30
2668
2163
  });
2669
- debugLog("subscription.openaiAuth", openaiAuth);
2164
+ return data.filter((issue) => !issue.pull_request).map((issue) => ({
2165
+ number: issue.number,
2166
+ title: issue.title,
2167
+ url: issue.html_url,
2168
+ state: issue.state
2169
+ }));
2670
2170
  }
2671
- const hasGoogle = await confirm({
2672
- message: "Will you use Google/Gemini models?",
2673
- default: false
2674
- });
2675
- debugLog("subscription.hasGoogle", hasGoogle);
2676
- let googleAuth = "none";
2677
- if (hasGoogle) {
2678
- googleAuth = await select({
2679
- message: "Google authentication method?",
2680
- choices: [
2681
- {
2682
- name: "Google Workspace (Antigravity OAuth) - Recommended for Workspace users",
2683
- value: "antigravity"
2684
- },
2685
- {
2686
- name: "Personal Google Account",
2687
- value: "personal"
2688
- },
2689
- {
2690
- name: "API Key - Direct API access",
2691
- value: "api"
2692
- }
2693
- ]
2171
+ /**
2172
+ * Update an issue's body
2173
+ */
2174
+ async updateIssueBody(issueNumber, body, owner, repo) {
2175
+ const params = this.getRepoParams(owner, repo);
2176
+ await this.octokit.issues.update({
2177
+ ...params,
2178
+ issue_number: issueNumber,
2179
+ body
2694
2180
  });
2695
- debugLog("subscription.googleAuth", googleAuth);
2696
2181
  }
2697
- const hasGitHubCopilot = await confirm({
2698
- message: "Do you have GitHub Copilot access?",
2699
- default: false
2700
- });
2701
- debugLog("subscription.hasGitHubCopilot", hasGitHubCopilot);
2702
- let copilotPlan = "none";
2703
- if (hasGitHubCopilot) {
2704
- copilotPlan = await select({
2705
- message: "Which GitHub Copilot plan?",
2706
- choices: [
2707
- { name: "Enterprise - Full model access including Opus", value: "enterprise" },
2708
- { name: "Pro+ - Includes Claude Opus models", value: "pro-plus" },
2709
- { name: "Pro - Standard paid plan", value: "pro" },
2710
- { name: "Business - Organization plan", value: "business" },
2711
- { name: "Free - Limited model access", value: "free" }
2712
- ]
2182
+ /**
2183
+ * Add labels to an issue
2184
+ */
2185
+ async addLabels(issueNumber, labels, owner, repo) {
2186
+ const params = this.getRepoParams(owner, repo);
2187
+ await this.octokit.issues.addLabels({
2188
+ ...params,
2189
+ issue_number: issueNumber,
2190
+ labels
2713
2191
  });
2714
- debugLog("subscription.copilotPlan", copilotPlan);
2715
2192
  }
2716
- const result = {
2717
- hasClaude,
2718
- claudeAuth,
2719
- claudeTier,
2720
- hasOpenAI,
2721
- openaiAuth,
2722
- hasGoogle,
2723
- googleAuth,
2724
- hasGitHubCopilot,
2725
- copilotPlan
2726
- };
2727
- debugLog("subscriptions.final", result);
2728
- return result;
2729
- }
2730
-
2731
- // src/cli/questions/index.ts
2732
- init_models();
2733
-
2734
- // src/cli/questions/methodology.ts
2735
- init_esm_shims();
2736
- async function gatherMethodology(defaults) {
2737
- const defaultTrack = await select({
2738
- message: "Default SDLC track for new projects?",
2739
- choices: [
2740
- {
2741
- name: "Quick Flow - Fast implementation for small features and bug fixes",
2742
- value: "quick-flow"
2743
- },
2744
- {
2745
- name: "Enterprise - Extended planning with compliance and scale considerations",
2746
- value: "enterprise"
2747
- }
2748
- ],
2749
- default: defaults?.defaultTrack ?? "quick-flow"
2750
- });
2751
- const autoStatusUpdate = await confirm({
2752
- message: "Automatically update GitHub issue status when issues complete?",
2753
- default: defaults?.autoStatusUpdate ?? true
2754
- });
2755
- return {
2756
- defaultTrack,
2757
- autoStatusUpdate
2758
- };
2759
- }
2760
-
2761
- // src/cli/questions/advanced.ts
2762
- init_esm_shims();
2763
- async function gatherAdvanced(defaults) {
2764
- const parallelIssueLimit = await select({
2765
- message: "Maximum parallel issues?",
2766
- choices: [
2767
- { name: "1 (sequential - one issue at a time)", value: 1 },
2768
- { name: "2", value: 2 },
2769
- { name: "3 (recommended)", value: 3 },
2770
- { name: "5", value: 5 },
2771
- { name: "Unlimited (0)", value: 0 }
2772
- ],
2773
- default: defaults?.parallelIssueLimit ?? 0
2774
- });
2775
- const experimentalDefaults = new Set(defaults?.experimental ?? []);
2776
- const experimental = await checkbox({
2777
- message: "Enable experimental features?",
2778
- choices: [
2779
- {
2780
- name: "Aggressive Truncation - More aggressive context management",
2781
- value: "aggressive-truncation",
2782
- checked: experimentalDefaults.has("aggressive-truncation")
2783
- },
2784
- {
2785
- name: "Auto Resume - Automatically resume interrupted sessions",
2786
- value: "auto-resume",
2787
- checked: experimentalDefaults.has("auto-resume")
2788
- }
2789
- ]
2790
- });
2791
- const autoFallback = await confirm({
2792
- message: "Enable automatic provider fallback on rate limits?",
2793
- default: defaults?.autoFallback ?? false
2794
- });
2795
- return {
2796
- parallelIssueLimit,
2797
- experimental,
2798
- autoFallback
2799
- };
2800
- }
2801
-
2802
- // src/cli/questions/github.ts
2803
- init_esm_shims();
2804
- var DEFAULT_STATUSES = ["Backlog", "Ready", "In progress", "In review", "Done"];
2805
- async function gatherGitHub(defaults) {
2806
- const enabled = await confirm({
2807
- message: "Enable GitHub Issues integration for work tracking?",
2808
- default: true
2809
- });
2810
- if (!enabled) {
2193
+ // ==========================================================================
2194
+ // Pull Requests
2195
+ // ==========================================================================
2196
+ /**
2197
+ * Create a pull request
2198
+ */
2199
+ async createPullRequest(options, owner, repo) {
2200
+ const params = this.getRepoParams(owner, repo);
2201
+ const { data } = await this.octokit.pulls.create({
2202
+ ...params,
2203
+ title: options.title,
2204
+ body: options.body,
2205
+ head: options.head,
2206
+ base: options.base || "main",
2207
+ draft: options.draft
2208
+ });
2811
2209
  return {
2812
- enabled: false,
2813
- owner: "",
2814
- repo: "",
2815
- statuses: DEFAULT_STATUSES
2210
+ number: data.number,
2211
+ url: data.html_url
2816
2212
  };
2817
2213
  }
2818
- const owner = await input({
2819
- message: "GitHub repository owner (user or organization):",
2820
- default: defaults?.owner,
2821
- validate: (value) => {
2822
- if (!value.trim()) {
2823
- return "Owner is required for GitHub integration";
2824
- }
2825
- if (!/^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(value)) {
2826
- return "Invalid GitHub username/organization format";
2827
- }
2828
- return true;
2829
- }
2830
- });
2831
- const repo = await input({
2832
- message: "GitHub repository name:",
2833
- default: defaults?.repo,
2834
- validate: (value) => {
2835
- if (!value.trim()) {
2836
- return "Repository name is required";
2214
+ /**
2215
+ * Get pull request details
2216
+ */
2217
+ async getPullRequest(prNumber, owner, repo) {
2218
+ const params = this.getRepoParams(owner, repo);
2219
+ const { data } = await this.octokit.pulls.get({
2220
+ ...params,
2221
+ pull_number: prNumber
2222
+ });
2223
+ return {
2224
+ number: data.number,
2225
+ title: data.title,
2226
+ body: data.body,
2227
+ url: data.html_url,
2228
+ state: data.merged ? "merged" : data.state,
2229
+ mergeable: data.mergeable,
2230
+ mergeableState: data.mergeable_state,
2231
+ draft: data.draft || false
2232
+ };
2233
+ }
2234
+ /**
2235
+ * Get pull request status including checks and reviews
2236
+ */
2237
+ async getPullRequestStatus(prNumber, owner, repo) {
2238
+ const params = this.getRepoParams(owner, repo);
2239
+ const pr = await this.getPullRequest(prNumber, owner, repo);
2240
+ const { data: prData } = await this.octokit.pulls.get({
2241
+ ...params,
2242
+ pull_number: prNumber
2243
+ });
2244
+ let checks = [];
2245
+ try {
2246
+ const { data: checksData } = await this.octokit.checks.listForRef({
2247
+ ...params,
2248
+ ref: prData.head.sha
2249
+ });
2250
+ checks = checksData.check_runs.map((check) => ({
2251
+ name: check.name,
2252
+ status: check.status === "completed" ? check.conclusion === "success" ? "pass" : check.conclusion === "skipped" ? "skipped" : "fail" : "pending",
2253
+ conclusion: check.conclusion
2254
+ }));
2255
+ } catch {
2256
+ }
2257
+ const { data: reviewsData } = await this.octokit.pulls.listReviews({
2258
+ ...params,
2259
+ pull_number: prNumber
2260
+ });
2261
+ const reviews = reviewsData.map((review) => ({
2262
+ reviewer: review.user?.login || "unknown",
2263
+ state: review.state
2264
+ }));
2265
+ return {
2266
+ number: pr.number,
2267
+ url: pr.url,
2268
+ state: pr.state,
2269
+ mergeable: pr.mergeable,
2270
+ checks,
2271
+ reviews
2272
+ };
2273
+ }
2274
+ /**
2275
+ * Get pull request diff
2276
+ */
2277
+ async getPullRequestDiff(prNumber, owner, repo) {
2278
+ const params = this.getRepoParams(owner, repo);
2279
+ const { data } = await this.octokit.pulls.get({
2280
+ ...params,
2281
+ pull_number: prNumber,
2282
+ mediaType: { format: "diff" }
2283
+ });
2284
+ return data;
2285
+ }
2286
+ /**
2287
+ * Merge a pull request
2288
+ */
2289
+ async mergePullRequest(options, owner, repo) {
2290
+ const params = this.getRepoParams(owner, repo);
2291
+ const { data } = await this.octokit.pulls.merge({
2292
+ ...params,
2293
+ pull_number: options.prNumber,
2294
+ merge_method: options.mergeMethod || "squash",
2295
+ commit_title: options.commitTitle,
2296
+ commit_message: options.commitMessage
2297
+ });
2298
+ return {
2299
+ sha: data.sha,
2300
+ merged: data.merged
2301
+ };
2302
+ }
2303
+ // ==========================================================================
2304
+ // Repositories
2305
+ // ==========================================================================
2306
+ /**
2307
+ * Create a new repository (personal or in an organization)
2308
+ */
2309
+ async createRepository(options) {
2310
+ let data;
2311
+ if (options.org) {
2312
+ const response = await this.octokit.repos.createInOrg({
2313
+ org: options.org,
2314
+ name: options.name,
2315
+ description: options.description,
2316
+ private: options.private ?? false,
2317
+ auto_init: options.autoInit ?? false
2318
+ });
2319
+ data = response.data;
2320
+ } else {
2321
+ const response = await this.octokit.repos.createForAuthenticatedUser({
2322
+ name: options.name,
2323
+ description: options.description,
2324
+ private: options.private ?? false,
2325
+ auto_init: options.autoInit ?? false
2326
+ });
2327
+ data = response.data;
2328
+ }
2329
+ return {
2330
+ id: data.id,
2331
+ name: data.name,
2332
+ fullName: data.full_name,
2333
+ url: data.html_url,
2334
+ private: data.private,
2335
+ defaultBranch: data.default_branch || "main"
2336
+ };
2337
+ }
2338
+ /**
2339
+ * List organizations the authenticated user belongs to
2340
+ */
2341
+ async listOrganizations() {
2342
+ const query = `
2343
+ query {
2344
+ viewer {
2345
+ organizations(first: 100) {
2346
+ nodes {
2347
+ id
2348
+ databaseId
2349
+ login
2350
+ name
2351
+ }
2352
+ }
2353
+ }
2354
+ }
2355
+ `;
2356
+ const result = await this.octokit.graphql(query);
2357
+ return result.viewer.organizations.nodes.map((org) => ({
2358
+ id: String(org.databaseId),
2359
+ nodeId: org.id,
2360
+ login: org.login,
2361
+ name: org.name
2362
+ }));
2363
+ }
2364
+ /**
2365
+ * Get repository details
2366
+ */
2367
+ async getRepository(owner, repo) {
2368
+ const params = this.getRepoParams(owner, repo);
2369
+ const { data } = await this.octokit.repos.get(params);
2370
+ return {
2371
+ id: data.id,
2372
+ name: data.name,
2373
+ fullName: data.full_name,
2374
+ url: data.html_url,
2375
+ private: data.private,
2376
+ defaultBranch: data.default_branch || "main"
2377
+ };
2378
+ }
2379
+ /**
2380
+ * List repositories for the authenticated user
2381
+ */
2382
+ async listUserRepos(options = {}) {
2383
+ const { data } = await this.octokit.repos.listForAuthenticatedUser({
2384
+ type: options.type || "owner",
2385
+ per_page: options.limit || 30,
2386
+ sort: "updated"
2387
+ });
2388
+ return data.map((repo) => ({
2389
+ id: repo.id,
2390
+ name: repo.name,
2391
+ fullName: repo.full_name,
2392
+ url: repo.html_url,
2393
+ private: repo.private,
2394
+ defaultBranch: repo.default_branch || "main"
2395
+ }));
2396
+ }
2397
+ // ==========================================================================
2398
+ // Projects (GraphQL required for Projects V2)
2399
+ // ==========================================================================
2400
+ /**
2401
+ * List projects for the authenticated user
2402
+ * Note: Projects V2 requires GraphQL API
2403
+ */
2404
+ async listUserProjects(limit = 20) {
2405
+ const query = `
2406
+ query($first: Int!) {
2407
+ viewer {
2408
+ projectsV2(first: $first) {
2409
+ nodes {
2410
+ id
2411
+ number
2412
+ title
2413
+ url
2414
+ }
2415
+ }
2416
+ }
2417
+ }
2418
+ `;
2419
+ const result = await this.octokit.graphql(query, { first: limit });
2420
+ return result.viewer.projectsV2.nodes;
2421
+ }
2422
+ /**
2423
+ * Get project by number for a user
2424
+ */
2425
+ async getUserProject(userLogin, projectNumber) {
2426
+ const query = `
2427
+ query($login: String!, $number: Int!) {
2428
+ user(login: $login) {
2429
+ projectV2(number: $number) {
2430
+ id
2431
+ number
2432
+ title
2433
+ url
2434
+ }
2435
+ }
2436
+ }
2437
+ `;
2438
+ try {
2439
+ const result = await this.octokit.graphql(query, { login: userLogin, number: projectNumber });
2440
+ return result.user.projectV2;
2441
+ } catch {
2442
+ return null;
2443
+ }
2444
+ }
2445
+ /**
2446
+ * Get project by number for an organization
2447
+ */
2448
+ async getOrgProject(orgLogin, projectNumber) {
2449
+ const query = `
2450
+ query($login: String!, $number: Int!) {
2451
+ organization(login: $login) {
2452
+ projectV2(number: $number) {
2453
+ id
2454
+ number
2455
+ title
2456
+ url
2457
+ }
2458
+ }
2459
+ }
2460
+ `;
2461
+ try {
2462
+ const result = await this.octokit.graphql(query, { login: orgLogin, number: projectNumber });
2463
+ return result.organization.projectV2;
2464
+ } catch {
2465
+ return null;
2466
+ }
2467
+ }
2468
+ /**
2469
+ * List projects for an organization
2470
+ */
2471
+ async listOrgProjects(orgLogin, limit = 20) {
2472
+ const query = `
2473
+ query($login: String!, $first: Int!) {
2474
+ organization(login: $login) {
2475
+ projectsV2(first: $first) {
2476
+ nodes {
2477
+ id
2478
+ number
2479
+ title
2480
+ url
2481
+ }
2482
+ }
2483
+ }
2484
+ }
2485
+ `;
2486
+ try {
2487
+ const result = await this.octokit.graphql(query, { login: orgLogin, first: limit });
2488
+ return result.organization.projectsV2.nodes;
2489
+ } catch {
2490
+ return [];
2491
+ }
2492
+ }
2493
+ /**
2494
+ * Create a new project
2495
+ */
2496
+ async createProject(options) {
2497
+ let ownerId;
2498
+ if (options.isOrg) {
2499
+ const query = `
2500
+ query($login: String!) {
2501
+ organization(login: $login) {
2502
+ id
2503
+ }
2504
+ }
2505
+ `;
2506
+ const result2 = await this.octokit.graphql(query, { login: options.ownerLogin });
2507
+ ownerId = result2.organization.id;
2508
+ } else {
2509
+ const query = `
2510
+ query($login: String!) {
2511
+ user(login: $login) {
2512
+ id
2513
+ }
2514
+ }
2515
+ `;
2516
+ const result2 = await this.octokit.graphql(query, { login: options.ownerLogin });
2517
+ ownerId = result2.user.id;
2518
+ }
2519
+ const mutation = `
2520
+ mutation($ownerId: ID!, $title: String!) {
2521
+ createProjectV2(input: { ownerId: $ownerId, title: $title }) {
2522
+ projectV2 {
2523
+ id
2524
+ number
2525
+ title
2526
+ url
2527
+ }
2528
+ }
2529
+ }
2530
+ `;
2531
+ const result = await this.octokit.graphql(mutation, { ownerId, title: options.title });
2532
+ return result.createProjectV2.projectV2;
2533
+ }
2534
+ /**
2535
+ * Copy an existing project
2536
+ */
2537
+ async copyProject(options) {
2538
+ let sourceProject;
2539
+ if (options.sourceIsOrg) {
2540
+ sourceProject = await this.getOrgProject(
2541
+ options.sourceOwnerLogin,
2542
+ options.sourceProjectNumber
2543
+ );
2544
+ } else {
2545
+ sourceProject = await this.getUserProject(
2546
+ options.sourceOwnerLogin,
2547
+ options.sourceProjectNumber
2548
+ );
2549
+ }
2550
+ if (!sourceProject) {
2551
+ throw new Error(
2552
+ `Source project #${options.sourceProjectNumber} not found for ${options.sourceOwnerLogin}`
2553
+ );
2554
+ }
2555
+ let targetOwnerId;
2556
+ if (options.targetIsOrg) {
2557
+ const query = `
2558
+ query($login: String!) {
2559
+ organization(login: $login) {
2560
+ id
2561
+ }
2562
+ }
2563
+ `;
2564
+ const result2 = await this.octokit.graphql(query, { login: options.targetOwnerLogin });
2565
+ targetOwnerId = result2.organization.id;
2566
+ } else {
2567
+ const query = `
2568
+ query($login: String!) {
2569
+ user(login: $login) {
2570
+ id
2571
+ }
2572
+ }
2573
+ `;
2574
+ const result2 = await this.octokit.graphql(query, { login: options.targetOwnerLogin });
2575
+ targetOwnerId = result2.user.id;
2576
+ }
2577
+ const mutation = `
2578
+ mutation($projectId: ID!, $ownerId: ID!, $title: String!, $includeDraftIssues: Boolean) {
2579
+ copyProjectV2(input: {
2580
+ projectId: $projectId,
2581
+ ownerId: $ownerId,
2582
+ title: $title,
2583
+ includeDraftIssues: $includeDraftIssues
2584
+ }) {
2585
+ projectV2 {
2586
+ id
2587
+ number
2588
+ title
2589
+ url
2590
+ }
2591
+ }
2592
+ }
2593
+ `;
2594
+ const result = await this.octokit.graphql(mutation, {
2595
+ projectId: sourceProject.id,
2596
+ ownerId: targetOwnerId,
2597
+ title: options.title,
2598
+ includeDraftIssues: options.includeDraftIssues ?? false
2599
+ });
2600
+ return result.copyProjectV2.projectV2;
2601
+ }
2602
+ /**
2603
+ * Link a repository to a project
2604
+ * Note: This creates a linked repository in the project
2605
+ */
2606
+ async linkRepoToProject(projectId, repoOwner, repoName) {
2607
+ const { data: repoData } = await this.octokit.repos.get({
2608
+ owner: repoOwner,
2609
+ repo: repoName
2610
+ });
2611
+ const mutation = `
2612
+ mutation($projectId: ID!, $repositoryId: ID!) {
2613
+ linkProjectV2ToRepository(input: { projectId: $projectId, repositoryId: $repositoryId }) {
2614
+ repository {
2615
+ id
2616
+ }
2617
+ }
2837
2618
  }
2838
- if (!/^[a-zA-Z0-9._-]+$/.test(value)) {
2839
- return "Invalid repository name format";
2619
+ `;
2620
+ await this.octokit.graphql(mutation, {
2621
+ projectId,
2622
+ repositoryId: repoData.node_id
2623
+ });
2624
+ }
2625
+ /**
2626
+ * Add an issue to a project
2627
+ */
2628
+ async addIssueToProject(projectId, issueNodeId) {
2629
+ const mutation = `
2630
+ mutation($projectId: ID!, $contentId: ID!) {
2631
+ addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
2632
+ item {
2633
+ id
2634
+ }
2635
+ }
2636
+ }
2637
+ `;
2638
+ const result = await this.octokit.graphql(mutation, {
2639
+ projectId,
2640
+ contentId: issueNodeId
2641
+ });
2642
+ return result.addProjectV2ItemById.item.id;
2643
+ }
2644
+ // ==========================================================================
2645
+ // Branch Protection / Rulesets
2646
+ // ==========================================================================
2647
+ /**
2648
+ * Create a branch ruleset with common protection rules
2649
+ */
2650
+ async createBranchRuleset(options, owner, repo) {
2651
+ const params = this.getRepoParams(owner, repo);
2652
+ const rules = [];
2653
+ if (options.requirePullRequest) {
2654
+ rules.push({
2655
+ type: "pull_request",
2656
+ parameters: {
2657
+ dismiss_stale_reviews_on_push: options.dismissStaleReviews ?? false,
2658
+ require_code_owner_review: options.requireCodeOwnerReview ?? false,
2659
+ require_last_push_approval: false,
2660
+ required_approving_review_count: options.requiredApprovals ?? 1,
2661
+ required_review_thread_resolution: false
2662
+ }
2663
+ });
2664
+ }
2665
+ if (options.requireSignedCommits) {
2666
+ rules.push({ type: "required_signatures" });
2667
+ }
2668
+ if (options.requireLinearHistory) {
2669
+ rules.push({ type: "required_linear_history" });
2670
+ }
2671
+ if (options.preventDeletion) {
2672
+ rules.push({ type: "deletion" });
2673
+ }
2674
+ if (options.preventForcePush) {
2675
+ rules.push({ type: "non_fast_forward" });
2676
+ }
2677
+ const { data } = await this.octokit.repos.createRepoRuleset({
2678
+ ...params,
2679
+ name: options.name,
2680
+ enforcement: options.enforcement,
2681
+ conditions: {
2682
+ ref_name: {
2683
+ include: options.targetBranches,
2684
+ exclude: []
2685
+ }
2686
+ },
2687
+ rules
2688
+ });
2689
+ return { id: data.id };
2690
+ }
2691
+ // ==========================================================================
2692
+ // Utility Methods
2693
+ // ==========================================================================
2694
+ /**
2695
+ * Get the authenticated user's login
2696
+ */
2697
+ async getAuthenticatedUser() {
2698
+ const { data } = await this.octokit.users.getAuthenticated();
2699
+ return { login: data.login, id: data.id };
2700
+ }
2701
+ /**
2702
+ * Check if a repository exists
2703
+ */
2704
+ async repoExists(owner, repo) {
2705
+ try {
2706
+ await this.octokit.repos.get({ owner, repo });
2707
+ return true;
2708
+ } catch {
2709
+ return false;
2710
+ }
2711
+ }
2712
+ };
2713
+ function createGitHubClientIfAuthenticated(options = {}) {
2714
+ if (!isGitHubAuthenticated(options.token)) {
2715
+ return null;
2716
+ }
2717
+ try {
2718
+ return new GitHubClient(options);
2719
+ } catch {
2720
+ return null;
2721
+ }
2722
+ }
2723
+ function parseGitHubUrl(url) {
2724
+ const httpsMatch = url.match(/github\.com[/:]([^/]+)\/([^/.]+)(?:\.git)?/);
2725
+ if (httpsMatch) {
2726
+ return {
2727
+ owner: httpsMatch[1],
2728
+ repo: httpsMatch[2],
2729
+ url
2730
+ };
2731
+ }
2732
+ const sshMatch = url.match(/git@github\.com:([^/]+)\/([^/.]+)(?:\.git)?/);
2733
+ if (sshMatch) {
2734
+ return {
2735
+ owner: sshMatch[1],
2736
+ repo: sshMatch[2],
2737
+ url
2738
+ };
2739
+ }
2740
+ return null;
2741
+ }
2742
+ function getOriginRemoteUrl(cwd) {
2743
+ try {
2744
+ const url = execSync("git remote get-url origin", {
2745
+ encoding: "utf-8",
2746
+ cwd,
2747
+ stdio: ["pipe", "pipe", "pipe"]
2748
+ }).trim();
2749
+ return url || null;
2750
+ } catch {
2751
+ return null;
2752
+ }
2753
+ }
2754
+ function detectGitHubRepo(cwd) {
2755
+ const url = getOriginRemoteUrl(cwd);
2756
+ if (!url) return null;
2757
+ return parseGitHubUrl(url);
2758
+ }
2759
+ function isGitRepository(cwd) {
2760
+ try {
2761
+ execSync("git rev-parse --git-dir", {
2762
+ encoding: "utf-8",
2763
+ cwd,
2764
+ stdio: ["pipe", "pipe", "pipe"]
2765
+ });
2766
+ return true;
2767
+ } catch {
2768
+ return false;
2769
+ }
2770
+ }
2771
+ function initGitRepo(cwd) {
2772
+ try {
2773
+ execSync("git init", {
2774
+ encoding: "utf-8",
2775
+ cwd,
2776
+ stdio: ["pipe", "pipe", "pipe"]
2777
+ });
2778
+ return true;
2779
+ } catch {
2780
+ return false;
2781
+ }
2782
+ }
2783
+ function addGitRemote(name, url, cwd) {
2784
+ try {
2785
+ execSync(`git remote add ${name} ${url}`, {
2786
+ encoding: "utf-8",
2787
+ cwd,
2788
+ stdio: ["pipe", "pipe", "pipe"]
2789
+ });
2790
+ return true;
2791
+ } catch {
2792
+ return false;
2793
+ }
2794
+ }
2795
+
2796
+ // src/cli/questions/github-smart.ts
2797
+ var DEFAULT_STATUSES = ["Backlog", "Ready", "In progress", "In review", "Done"];
2798
+ async function setupSmartGitHub(cwd) {
2799
+ const wantGitHub = await confirm({
2800
+ message: "Enable GitHub integration for issue tracking?",
2801
+ default: true
2802
+ });
2803
+ if (!wantGitHub) {
2804
+ return { enabled: false };
2805
+ }
2806
+ if (!isGitHubAuthenticated()) {
2807
+ console.log(chalk5.yellow("\n\u26A0 GitHub authentication not found."));
2808
+ console.log(" Run 'gh auth login' or set GITHUB_TOKEN environment variable.\n");
2809
+ const continueWithoutAuth = await confirm({
2810
+ message: "Continue with manual configuration (no auto-detection)?",
2811
+ default: true
2812
+ });
2813
+ if (!continueWithoutAuth) {
2814
+ return { enabled: false };
2815
+ }
2816
+ return manualGitHubConfig();
2817
+ }
2818
+ const client = createGitHubClientIfAuthenticated();
2819
+ if (!client) {
2820
+ console.log(chalk5.yellow("\n\u26A0 Could not create GitHub client.\n"));
2821
+ return manualGitHubConfig();
2822
+ }
2823
+ let userLogin;
2824
+ let orgs = [];
2825
+ try {
2826
+ const user = await client.getAuthenticatedUser();
2827
+ userLogin = user.login;
2828
+ console.log(chalk5.green(`
2829
+ \u2713 Authenticated as ${userLogin}`));
2830
+ orgs = await client.listOrganizations();
2831
+ if (orgs.length > 0) {
2832
+ console.log(chalk5.dim(` Organizations: ${orgs.map((o) => o.login).join(", ")}`));
2833
+ }
2834
+ console.log();
2835
+ } catch (error) {
2836
+ console.log(chalk5.yellow("\n\u26A0 Could not verify GitHub authentication.\n"));
2837
+ return manualGitHubConfig();
2838
+ }
2839
+ const repoResult = await setupRepository(client, userLogin, orgs, cwd);
2840
+ if (repoResult.action === "skip") {
2841
+ return { enabled: false };
2842
+ }
2843
+ const { owner, repo } = repoResult;
2844
+ if (!owner || !repo) {
2845
+ return { enabled: false };
2846
+ }
2847
+ client.setRepo(owner, repo);
2848
+ const ownerIsOrg = orgs.some((o) => o.login === owner);
2849
+ const projectResult = await setupProjectBoard(client, userLogin, owner, ownerIsOrg, orgs);
2850
+ let rulesetsCreated = false;
2851
+ const wantRulesets = await confirm({
2852
+ message: "Configure branch protection rules for main branch?",
2853
+ default: false
2854
+ });
2855
+ if (wantRulesets) {
2856
+ rulesetsCreated = await setupBranchRulesets(client, owner, repo);
2857
+ }
2858
+ return {
2859
+ enabled: true,
2860
+ config: {
2861
+ owner,
2862
+ repo,
2863
+ project: projectResult.projectNumber,
2864
+ statuses: DEFAULT_STATUSES
2865
+ },
2866
+ repoCreated: repoResult.action === "create-public" || repoResult.action === "create-private",
2867
+ projectCreated: projectResult.action === "create-blank" || projectResult.action === "copy",
2868
+ rulesetsCreated
2869
+ };
2870
+ }
2871
+ async function selectOwner(userLogin, orgs, prompt) {
2872
+ const choices = [
2873
+ {
2874
+ value: { login: userLogin, isOrg: false, name: userLogin },
2875
+ name: `${userLogin} (personal)`,
2876
+ description: "Your personal account"
2877
+ },
2878
+ ...orgs.map((org) => ({
2879
+ value: { login: org.login, isOrg: true, name: org.name || org.login },
2880
+ name: org.login,
2881
+ description: org.name || "Organization"
2882
+ }))
2883
+ ];
2884
+ if (choices.length === 1) {
2885
+ return choices[0].value;
2886
+ }
2887
+ return await select({
2888
+ message: prompt,
2889
+ choices
2890
+ });
2891
+ }
2892
+ async function setupRepository(client, userLogin, orgs, cwd) {
2893
+ const isGitRepo = isGitRepository(cwd);
2894
+ const existingRemote = isGitRepo ? detectGitHubRepo(cwd) : null;
2895
+ if (existingRemote) {
2896
+ console.log(chalk5.dim(` Detected remote: ${existingRemote.owner}/${existingRemote.repo}`));
2897
+ const useExisting = await confirm({
2898
+ message: `Use existing repository ${existingRemote.owner}/${existingRemote.repo}?`,
2899
+ default: true
2900
+ });
2901
+ if (useExisting) {
2902
+ return {
2903
+ action: "existing",
2904
+ owner: existingRemote.owner,
2905
+ repo: existingRemote.repo
2906
+ };
2907
+ }
2908
+ }
2909
+ const repoAction = await select({
2910
+ message: "Repository setup:",
2911
+ choices: [
2912
+ {
2913
+ value: "existing",
2914
+ name: "Connect to existing GitHub repository",
2915
+ description: "Enter owner/repo for an existing repository"
2916
+ },
2917
+ {
2918
+ value: "create-private",
2919
+ name: "Create new private repository",
2920
+ description: "Create a new private repo on GitHub"
2921
+ },
2922
+ {
2923
+ value: "create-public",
2924
+ name: "Create new public repository",
2925
+ description: "Create a new public repo on GitHub"
2926
+ },
2927
+ {
2928
+ value: "skip",
2929
+ name: "Skip GitHub integration",
2930
+ description: "Configure manually later"
2840
2931
  }
2932
+ ]
2933
+ });
2934
+ if (repoAction === "skip") {
2935
+ return { action: "skip" };
2936
+ }
2937
+ if (repoAction === "existing") {
2938
+ return await connectExistingRepo(client);
2939
+ }
2940
+ return await createNewRepo(
2941
+ client,
2942
+ userLogin,
2943
+ orgs,
2944
+ repoAction === "create-private",
2945
+ cwd,
2946
+ isGitRepo
2947
+ );
2948
+ }
2949
+ async function connectExistingRepo(client) {
2950
+ const ownerRepo = await input({
2951
+ message: "Repository (owner/repo or just repo for personal):",
2952
+ validate: (v) => v.trim() ? true : "Repository is required"
2953
+ });
2954
+ let owner;
2955
+ let repo;
2956
+ if (ownerRepo.includes("/")) {
2957
+ [owner, repo] = ownerRepo.split("/", 2);
2958
+ } else {
2959
+ try {
2960
+ const user = await client.getAuthenticatedUser();
2961
+ owner = user.login;
2962
+ repo = ownerRepo;
2963
+ } catch {
2964
+ console.log(chalk5.yellow("\n\u26A0 Could not determine owner. Please use owner/repo format.\n"));
2965
+ return { action: "skip" };
2966
+ }
2967
+ }
2968
+ try {
2969
+ const exists = await client.repoExists(owner, repo);
2970
+ if (!exists) {
2971
+ console.log(chalk5.yellow(`
2972
+ \u26A0 Repository ${owner}/${repo} not found.
2973
+ `));
2974
+ return { action: "skip" };
2975
+ }
2976
+ console.log(chalk5.green(`\u2713 Found repository ${owner}/${repo}`));
2977
+ } catch {
2978
+ console.log(chalk5.yellow("\n\u26A0 Could not verify repository.\n"));
2979
+ }
2980
+ return { action: "existing", owner, repo };
2981
+ }
2982
+ async function createNewRepo(client, userLogin, orgs, isPrivate, cwd, isGitRepo) {
2983
+ const ownerChoice = await selectOwner(userLogin, orgs, "Create repository under:");
2984
+ const repoName = await input({
2985
+ message: "New repository name:",
2986
+ validate: (v) => {
2987
+ if (!v.trim()) return "Repository name is required";
2988
+ if (!/^[a-zA-Z0-9._-]+$/.test(v)) return "Invalid repository name";
2841
2989
  return true;
2842
2990
  }
2843
2991
  });
2844
- const useProjectBoard = await confirm({
2845
- message: "Use a GitHub Project board for issue status tracking?",
2846
- default: defaults?.project !== void 0
2992
+ const description = await input({
2993
+ message: "Repository description (optional):",
2994
+ default: ""
2847
2995
  });
2848
- let project;
2849
- if (useProjectBoard) {
2850
- project = await number({
2851
- message: "GitHub Project number (from project URL):",
2852
- default: defaults?.project,
2853
- min: 1,
2854
- validate: (value) => {
2855
- if (value === void 0 || value < 1) {
2856
- return "Project number must be a positive integer";
2857
- }
2858
- return true;
2996
+ try {
2997
+ console.log(
2998
+ chalk5.dim(
2999
+ `
3000
+ Creating ${isPrivate ? "private" : "public"} repository in ${ownerChoice.login}...`
3001
+ )
3002
+ );
3003
+ const newRepo = await client.createRepository({
3004
+ name: repoName,
3005
+ description: description || void 0,
3006
+ private: isPrivate,
3007
+ autoInit: !isGitRepo,
3008
+ // Only auto-init if no local git repo
3009
+ org: ownerChoice.isOrg ? ownerChoice.login : void 0
3010
+ });
3011
+ console.log(chalk5.green(`\u2713 Created repository: ${newRepo.url}
3012
+ `));
3013
+ const remoteUrl = `git@github.com:${ownerChoice.login}/${repoName}.git`;
3014
+ if (isGitRepo) {
3015
+ const added = addGitRemote("origin", remoteUrl, cwd);
3016
+ if (added) {
3017
+ console.log(chalk5.green("\u2713 Added git remote 'origin'"));
2859
3018
  }
2860
- });
3019
+ } else {
3020
+ const initialized = initGitRepo(cwd);
3021
+ if (initialized) {
3022
+ console.log(chalk5.green("\u2713 Initialized git repository"));
3023
+ addGitRemote("origin", remoteUrl, cwd);
3024
+ console.log(chalk5.green("\u2713 Added git remote 'origin'"));
3025
+ }
3026
+ }
3027
+ return {
3028
+ action: isPrivate ? "create-private" : "create-public",
3029
+ owner: ownerChoice.login,
3030
+ repo: repoName
3031
+ };
3032
+ } catch (error) {
3033
+ const message = error instanceof Error ? error.message : "Unknown error";
3034
+ console.log(chalk5.red(`
3035
+ \u2717 Failed to create repository: ${message}
3036
+ `));
3037
+ return { action: "skip" };
2861
3038
  }
2862
- return {
2863
- enabled: true,
2864
- owner,
2865
- repo,
2866
- project,
2867
- statuses: defaults?.statuses ?? DEFAULT_STATUSES
2868
- };
2869
3039
  }
2870
-
2871
- // src/cli/questions/tdd.ts
2872
- init_esm_shims();
2873
- var DEFAULT_BYPASS_PATTERNS = ["*.config.*", "*.json", "*.md", "*.yaml", "*.yml"];
2874
- async function gatherTdd(defaults) {
2875
- const enabled = await confirm({
2876
- message: "Enable TDD cycle enforcement (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)?",
2877
- default: defaults?.enabled ?? true
3040
+ async function setupProjectBoard(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3041
+ const wantProject = await confirm({
3042
+ message: "Use a GitHub Project board for issue status tracking?",
3043
+ default: true
2878
3044
  });
2879
- if (!enabled) {
2880
- return {
2881
- enabled: false,
2882
- verbosity: "silent",
2883
- bypassPatterns: DEFAULT_BYPASS_PATTERNS,
2884
- mutationTesting: {
2885
- enabled: false,
2886
- requiredScore: 80
2887
- },
2888
- domainVetoEnabled: true,
2889
- debateRounds: 2,
2890
- requireVerification: true
2891
- };
3045
+ if (!wantProject) {
3046
+ return { action: "skip" };
2892
3047
  }
2893
- const verbosity = await select({
2894
- message: "TDD feedback verbosity level:",
3048
+ const projectAction = await select({
3049
+ message: "Project board setup:",
2895
3050
  choices: [
2896
3051
  {
2897
- name: "Silent - Only show errors",
2898
- value: "silent"
3052
+ value: "existing",
3053
+ name: "Link existing project",
3054
+ description: "Select from your projects or enter project number"
3055
+ },
3056
+ {
3057
+ value: "create-blank",
3058
+ name: "Create new blank project",
3059
+ description: "Create a new empty project"
2899
3060
  },
2900
3061
  {
2901
- name: "Brief - Short status messages (recommended)",
2902
- value: "brief"
3062
+ value: "copy",
3063
+ name: "Copy from existing project",
3064
+ description: "Copy structure from another project (yours or a template)"
2903
3065
  },
2904
3066
  {
2905
- name: "Explain - Detailed explanations of TDD phases",
2906
- value: "explain"
3067
+ value: "skip",
3068
+ name: "Skip project board",
3069
+ description: "Configure manually later"
2907
3070
  }
2908
- ],
2909
- default: defaults?.verbosity ?? "brief"
3071
+ ]
2910
3072
  });
2911
- const customBypass = await confirm({
2912
- message: "Customize file patterns that bypass TDD checks?",
2913
- default: false
3073
+ if (projectAction === "skip") {
3074
+ return { action: "skip" };
3075
+ }
3076
+ if (projectAction === "existing") {
3077
+ return await linkExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs);
3078
+ }
3079
+ if (projectAction === "copy") {
3080
+ return await copyExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs);
3081
+ }
3082
+ return await createNewProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs);
3083
+ }
3084
+ async function linkExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3085
+ await showAvailableProjects(client, userLogin, orgs);
3086
+ const projectOwner = await input({
3087
+ message: "Project owner (user or org, leave empty for repo owner):",
3088
+ default: repoOwner
2914
3089
  });
2915
- let bypassPatterns = defaults?.bypassPatterns ?? DEFAULT_BYPASS_PATTERNS;
2916
- if (customBypass) {
2917
- const patternsInput = await input({
2918
- message: "Enter bypass patterns (comma-separated, e.g., *.config.*, *.json):",
2919
- default: bypassPatterns.join(", ")
2920
- });
2921
- bypassPatterns = patternsInput.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
3090
+ const projectNum = await number({
3091
+ message: "Project number (from project URL):",
3092
+ min: 1,
3093
+ validate: (v) => v && v >= 1 ? true : "Project number required"
3094
+ });
3095
+ if (!projectNum) {
3096
+ return { action: "skip" };
3097
+ }
3098
+ const ownerIsOrg = projectOwner === repoOwner ? repoOwnerIsOrg : orgs.some((o) => o.login === projectOwner);
3099
+ try {
3100
+ const project = ownerIsOrg ? await client.getOrgProject(projectOwner, projectNum) : await client.getUserProject(projectOwner, projectNum);
3101
+ if (project) {
3102
+ console.log(chalk5.green(`\u2713 Found project: ${project.title}`));
3103
+ return { action: "existing", projectNumber: projectNum };
3104
+ }
3105
+ console.log(chalk5.yellow(`
3106
+ \u26A0 Project #${projectNum} not found for ${projectOwner}
3107
+ `));
3108
+ } catch (error) {
3109
+ const message = error instanceof Error ? error.message : "Unknown error";
3110
+ console.log(chalk5.yellow(`
3111
+ \u26A0 Could not verify project: ${message}
3112
+ `));
2922
3113
  }
2923
- const mutationEnabled = await confirm({
2924
- message: "Enable mutation testing for coverage verification?",
2925
- default: defaults?.mutationTesting?.enabled ?? false
3114
+ return { action: "existing", projectNumber: projectNum };
3115
+ }
3116
+ async function createNewProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3117
+ const ownerChoice = await selectOwner(userLogin, orgs, "Create project under:");
3118
+ const projectTitle = await input({
3119
+ message: "Project title:",
3120
+ default: "Project Board",
3121
+ validate: (v) => v.trim() ? true : "Title is required"
2926
3122
  });
2927
- let requiredScore = defaults?.mutationTesting?.requiredScore ?? 80;
2928
- if (mutationEnabled) {
2929
- const scoreChoice = await select({
2930
- message: "Minimum mutation score required:",
2931
- choices: [
2932
- { name: "60% - Minimal coverage", value: 60 },
2933
- { name: "70% - Standard coverage", value: 70 },
2934
- { name: "80% - Good coverage (recommended)", value: 80 },
2935
- { name: "90% - High coverage", value: 90 }
2936
- ],
2937
- default: requiredScore
3123
+ try {
3124
+ console.log(chalk5.dim(`
3125
+ Creating project "${projectTitle}" in ${ownerChoice.login}...`));
3126
+ const project = await client.createProject({
3127
+ title: projectTitle,
3128
+ ownerLogin: ownerChoice.login,
3129
+ isOrg: ownerChoice.isOrg
2938
3130
  });
2939
- requiredScore = scoreChoice;
3131
+ console.log(chalk5.green(`\u2713 Created project: ${project.title} (#${project.number})`));
3132
+ console.log(chalk5.dim(` URL: ${project.url}
3133
+ `));
3134
+ return {
3135
+ action: "create-blank",
3136
+ projectNumber: project.number,
3137
+ projectTitle: project.title
3138
+ };
3139
+ } catch (error) {
3140
+ const message = error instanceof Error ? error.message : "Unknown error";
3141
+ console.log(chalk5.red(`
3142
+ \u2717 Failed to create project: ${message}
3143
+ `));
3144
+ return { action: "skip" };
2940
3145
  }
2941
- const domainVetoEnabled = await confirm({
2942
- message: "Enable domain agent veto power? (Can block cycle for type violations)",
2943
- default: defaults?.domainVetoEnabled ?? true
3146
+ }
3147
+ async function copyExistingProject(client, userLogin, repoOwner, repoOwnerIsOrg, orgs) {
3148
+ console.log(chalk5.dim("\n Available projects to copy from:\n"));
3149
+ await showAvailableProjects(client, userLogin, orgs);
3150
+ const sourceOwner = await input({
3151
+ message: "Source project owner (user or org):",
3152
+ default: userLogin
2944
3153
  });
2945
- const debateRounds = await select({
2946
- message: "Max debate rounds before escalation to user:",
2947
- choices: [
2948
- { name: "1 round - Quick decisions", value: 1 },
2949
- { name: "2 rounds - Balanced (recommended)", value: 2 },
2950
- { name: "3 rounds - Thorough discussion", value: 3 },
2951
- { name: "5 rounds - Extended debate", value: 5 }
2952
- ],
2953
- default: defaults?.debateRounds ?? 2
3154
+ const sourceProjectNum = await number({
3155
+ message: "Source project number to copy:",
3156
+ min: 1,
3157
+ validate: (v) => v && v >= 1 ? true : "Project number required"
2954
3158
  });
2955
- const requireVerification = await confirm({
2956
- message: "Require test output evidence before phase transitions?",
2957
- default: defaults?.requireVerification ?? true
3159
+ if (!sourceProjectNum) {
3160
+ return { action: "skip" };
3161
+ }
3162
+ const targetOwnerChoice = await selectOwner(userLogin, orgs, "Create copied project under:");
3163
+ const projectTitle = await input({
3164
+ message: "Title for new project:",
3165
+ default: "Project Board (copy)",
3166
+ validate: (v) => v.trim() ? true : "Title is required"
2958
3167
  });
2959
- return {
2960
- enabled,
2961
- verbosity,
2962
- bypassPatterns,
2963
- mutationTesting: {
2964
- enabled: mutationEnabled,
2965
- requiredScore
2966
- },
2967
- domainVetoEnabled,
2968
- debateRounds,
2969
- requireVerification
2970
- };
2971
- }
2972
-
2973
- // src/cli/questions/event-modeling.ts
2974
- init_esm_shims();
2975
- var DEFAULT_OUTPUT_PATH = "docs/event-model";
2976
- async function gatherEventModeling(defaults) {
2977
- const enabled = await confirm({
2978
- message: "Enable Event Modeling workflow for event-sourced systems?",
2979
- default: defaults?.enabled ?? false
3168
+ const includeDrafts = await confirm({
3169
+ message: "Include draft issues from source project?",
3170
+ default: false
2980
3171
  });
2981
- if (!enabled) {
3172
+ const sourceIsOrg = sourceOwner !== userLogin && orgs.some((o) => o.login === sourceOwner);
3173
+ try {
3174
+ console.log(
3175
+ chalk5.dim(
3176
+ `
3177
+ Copying project #${sourceProjectNum} from ${sourceOwner} to ${targetOwnerChoice.login}...`
3178
+ )
3179
+ );
3180
+ const project = await client.copyProject({
3181
+ sourceProjectNumber: sourceProjectNum,
3182
+ sourceOwnerLogin: sourceOwner,
3183
+ sourceIsOrg,
3184
+ targetOwnerLogin: targetOwnerChoice.login,
3185
+ targetIsOrg: targetOwnerChoice.isOrg,
3186
+ title: projectTitle,
3187
+ includeDraftIssues: includeDrafts
3188
+ });
3189
+ console.log(chalk5.green(`\u2713 Copied project: ${project.title} (#${project.number})`));
3190
+ console.log(chalk5.dim(` URL: ${project.url}
3191
+ `));
2982
3192
  return {
2983
- enabled: false,
2984
- outputPath: defaults?.outputPath ?? DEFAULT_OUTPUT_PATH
3193
+ action: "copy",
3194
+ projectNumber: project.number,
3195
+ projectTitle: project.title
2985
3196
  };
3197
+ } catch (error) {
3198
+ const message = error instanceof Error ? error.message : "Unknown error";
3199
+ console.log(chalk5.red(`
3200
+ \u2717 Failed to copy project: ${message}
3201
+ `));
3202
+ return { action: "skip" };
2986
3203
  }
2987
- const outputPath = await input({
2988
- message: "Output path for event model files:",
2989
- default: defaults?.outputPath ?? DEFAULT_OUTPUT_PATH,
2990
- validate: (value) => {
2991
- if (!value.trim()) {
2992
- return "Output path is required";
2993
- }
2994
- if (value.startsWith("/") || value.startsWith("~")) {
2995
- return "Use a relative path from the project root";
3204
+ }
3205
+ async function showAvailableProjects(client, userLogin, orgs) {
3206
+ try {
3207
+ const userProjects = await client.listUserProjects(10);
3208
+ if (userProjects.length > 0) {
3209
+ console.log(chalk5.dim(` ${userLogin} (personal):`));
3210
+ for (const p of userProjects) {
3211
+ console.log(chalk5.dim(` #${p.number}: ${p.title}`));
2996
3212
  }
2997
- return true;
2998
3213
  }
2999
- });
3000
- return {
3001
- enabled,
3002
- outputPath
3003
- };
3004
- }
3005
-
3006
- // src/cli/questions/git-workflow.ts
3007
- init_esm_shims();
3008
- async function gatherGitWorkflow(defaults) {
3009
- const workflow = await select({
3010
- message: "Git workflow style:",
3011
- choices: [
3012
- {
3013
- name: "Standard - Traditional branch-based workflow",
3014
- value: "standard"
3015
- },
3016
- {
3017
- name: "git-spice - Stacked PRs with git-spice CLI",
3018
- value: "git-spice"
3214
+ } catch {
3215
+ }
3216
+ for (const org of orgs.slice(0, 3)) {
3217
+ try {
3218
+ const orgProjects = await client.listOrgProjects(org.login, 5);
3219
+ if (orgProjects.length > 0) {
3220
+ console.log(chalk5.dim(` ${org.login}:`));
3221
+ for (const p of orgProjects) {
3222
+ console.log(chalk5.dim(` #${p.number}: ${p.title}`));
3223
+ }
3019
3224
  }
3020
- ],
3021
- default: defaults?.workflow ?? "standard"
3022
- });
3023
- const requireClean = await confirm({
3024
- message: "Require clean working directory before git operations?",
3025
- default: defaults?.requireClean ?? true
3026
- });
3027
- const worktrees = await confirm({
3028
- message: "Enable git worktrees for parallel work on multiple issues?",
3029
- default: defaults?.worktrees ?? false
3030
- });
3031
- return {
3032
- workflow,
3033
- requireClean,
3034
- worktrees
3035
- };
3036
- }
3037
-
3038
- // src/cli/questions/modes.ts
3039
- init_esm_shims();
3040
- var MODE_OPTIONS = [
3041
- {
3042
- value: "build",
3043
- name: "Build",
3044
- description: "TDD implementation (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)"
3045
- },
3046
- {
3047
- value: "discover",
3048
- name: "Discover",
3049
- description: "Problem space exploration, stakeholder mapping"
3050
- },
3051
- {
3052
- value: "model",
3053
- name: "Model",
3054
- description: "Event Modeling (commands, events, views)"
3055
- },
3056
- {
3057
- value: "architect",
3058
- name: "Architect",
3059
- description: "System design, ADRs, technology decisions"
3060
- },
3061
- {
3062
- value: "pm",
3063
- name: "PM",
3064
- description: "Project management, git workflow, PRs"
3225
+ } catch {
3226
+ }
3065
3227
  }
3066
- ];
3067
- async function gatherModes(defaults) {
3068
- console.log(chalk6.gray("\nMarvin operates in different modes depending on your work focus."));
3069
- console.log(chalk6.gray("Build mode (TDD) is always enabled.\n"));
3070
- const enabledChoices = MODE_OPTIONS.filter((m) => m.value !== "build").map((m) => ({
3071
- name: `${m.name} - ${m.description}`,
3072
- value: m.value,
3073
- checked: true
3074
- }));
3075
- const selectedModes = await checkbox({
3076
- message: "Which additional modes do you want to enable?",
3077
- choices: enabledChoices
3078
- });
3079
- const enabled = ["build", ...selectedModes];
3080
- const useEventModeling = await confirm({
3081
- message: "Use Event Modeling for system design? (No = use PRD-style specifications)",
3228
+ console.log();
3229
+ }
3230
+ async function setupBranchRulesets(client, owner, repo) {
3231
+ console.log(chalk5.dim("\n Configuring branch protection for 'main'...\n"));
3232
+ const requirePR = await confirm({
3233
+ message: "Require pull requests for changes?",
3082
3234
  default: true
3083
3235
  });
3084
- let finalEnabled = enabled;
3085
- if (!useEventModeling) {
3086
- finalEnabled = enabled.filter((m) => m !== "model");
3087
- }
3088
- const defaultModeChoices = finalEnabled.map((mode) => {
3089
- const option = MODE_OPTIONS.find((m) => m.value === mode);
3090
- return {
3091
- name: `${option?.name || mode} - ${option?.description || ""}`,
3092
- value: mode
3093
- };
3236
+ let requiredApprovals = 0;
3237
+ if (requirePR) {
3238
+ requiredApprovals = await number({
3239
+ message: "Required approvals (0 for none):",
3240
+ default: 0,
3241
+ min: 0,
3242
+ max: 10
3243
+ }) ?? 0;
3244
+ }
3245
+ const requireSignedCommits = await confirm({
3246
+ message: "Require signed commits?",
3247
+ default: false
3094
3248
  });
3095
- const defaultMode = await select({
3096
- message: "Which mode should be active when you start OpenCode?",
3097
- choices: defaultModeChoices,
3098
- default: "build"
3249
+ const preventForcePush = await confirm({
3250
+ message: "Prevent force pushes?",
3251
+ default: true
3099
3252
  });
3100
- return {
3101
- default: defaultMode,
3102
- eventModeling: useEventModeling,
3103
- enabled: finalEnabled
3104
- };
3105
- }
3106
-
3107
- // src/cli/questions/memory.ts
3108
- init_esm_shims();
3109
- async function gatherMemory(defaults) {
3110
- console.log(
3111
- chalk6.gray("\nMemory backend determines how Marvin remembers context across sessions.\n")
3112
- );
3113
- const backend = await select({
3114
- message: "Choose memory backend:",
3115
- choices: [
3253
+ try {
3254
+ await client.createBranchRuleset(
3116
3255
  {
3117
- name: "Stateless - No persistent memory (simpler, no setup required)",
3118
- value: "stateless"
3256
+ name: "main-protection",
3257
+ enforcement: "active",
3258
+ targetBranches: ["refs/heads/main", "refs/heads/master"],
3259
+ requirePullRequest: requirePR,
3260
+ requiredApprovals,
3261
+ requireSignedCommits,
3262
+ preventForcePush,
3263
+ preventDeletion: true
3119
3264
  },
3120
- {
3121
- name: "Memento - Persistent memory via Memento MCP (requires setup)",
3122
- value: "memento"
3123
- }
3124
- ],
3125
- default: "stateless"
3126
- });
3127
- let checkpointTtl = defaults?.checkpointTtl ?? 86400;
3128
- if (backend === "memento") {
3129
- console.log(chalk6.gray("\nMemento stores checkpoints of conversation state."));
3130
- console.log(chalk6.gray("Set how long to keep checkpoints before they expire.\n"));
3131
- const ttlChoice = await select({
3132
- message: "Checkpoint retention period:",
3133
- choices: [
3134
- { name: "1 hour (3600 seconds)", value: 3600 },
3135
- { name: "6 hours (21600 seconds)", value: 21600 },
3136
- { name: "24 hours (86400 seconds) - Recommended", value: 86400 },
3137
- { name: "7 days (604800 seconds)", value: 604800 },
3138
- { name: "Custom", value: -1 }
3139
- ],
3140
- default: 86400
3141
- });
3142
- if (ttlChoice === -1) {
3143
- const customTtl = await input({
3144
- message: "Enter checkpoint TTL in seconds:",
3145
- default: String(checkpointTtl),
3146
- validate: (value) => {
3147
- const num = Number.parseInt(value, 10);
3148
- if (Number.isNaN(num) || num < 60) {
3149
- return "Please enter a number >= 60 seconds";
3150
- }
3151
- return true;
3152
- }
3153
- });
3154
- checkpointTtl = Number.parseInt(customTtl, 10);
3155
- } else {
3156
- checkpointTtl = ttlChoice;
3265
+ owner,
3266
+ repo
3267
+ );
3268
+ console.log(chalk5.green("\n\u2713 Created branch ruleset 'main-protection'\n"));
3269
+ return true;
3270
+ } catch (error) {
3271
+ const message = error instanceof Error ? error.message : "Unknown error";
3272
+ console.log(chalk5.yellow(`
3273
+ \u26A0 Could not create ruleset: ${message}`));
3274
+ console.log(" You may need admin permissions or GitHub Pro/Team.\n");
3275
+ return false;
3276
+ }
3277
+ }
3278
+ async function manualGitHubConfig() {
3279
+ const ownerRepo = await input({
3280
+ message: "GitHub repository (owner/repo):",
3281
+ validate: (v) => {
3282
+ if (!v.trim()) return "Repository is required";
3283
+ if (!v.includes("/")) return "Please use owner/repo format";
3284
+ return true;
3157
3285
  }
3158
- console.log(chalk6.yellow("\nNote: You'll need to configure Memento MCP separately."));
3159
- console.log(chalk6.gray("See: https://github.com/gannonh/memento\n"));
3286
+ });
3287
+ const [owner, repo] = ownerRepo.split("/", 2);
3288
+ const useProject = await confirm({
3289
+ message: "Use a GitHub Project board?",
3290
+ default: false
3291
+ });
3292
+ let project;
3293
+ if (useProject) {
3294
+ project = await number({
3295
+ message: "Project number:",
3296
+ min: 1
3297
+ }) ?? void 0;
3160
3298
  }
3161
3299
  return {
3162
- backend,
3163
- checkpointTtl
3300
+ enabled: true,
3301
+ config: {
3302
+ owner,
3303
+ repo,
3304
+ project,
3305
+ statuses: DEFAULT_STATUSES
3306
+ }
3164
3307
  };
3165
3308
  }
3166
-
3167
- // src/cli/utils/config-loader.ts
3168
- init_esm_shims();
3169
3309
  function loadExistingConfigs() {
3170
3310
  const result = {
3171
3311
  sdlc: null,
@@ -3304,9 +3444,6 @@ function detectNewFeatures(existingSdlc) {
3304
3444
  }
3305
3445
  return newFeatures;
3306
3446
  }
3307
-
3308
- // src/cli/utils/config-merger.ts
3309
- init_esm_shims();
3310
3447
  function deepMerge(baseObj, newObj) {
3311
3448
  const result = { ...baseObj };
3312
3449
  for (const [key, value] of Object.entries(newObj)) {
@@ -3375,15 +3512,6 @@ function writeMergedConfigs(configs) {
3375
3512
  );
3376
3513
  }
3377
3514
  }
3378
-
3379
- // src/cli/utils/migrations/index.ts
3380
- init_esm_shims();
3381
-
3382
- // src/cli/utils/migrations/types.ts
3383
- init_esm_shims();
3384
-
3385
- // src/cli/utils/migrations/migrations.ts
3386
- init_esm_shims();
3387
3515
  var MIGRATIONS = [
3388
3516
  {
3389
3517
  fromVersion: "0.0.1",
@@ -3484,9 +3612,6 @@ function migrateLegacyFiles() {
3484
3612
  }
3485
3613
  return result;
3486
3614
  }
3487
-
3488
- // src/cli/utils/migrations/runner.ts
3489
- init_esm_shims();
3490
3615
  function migrateConfigs(sdlcConfig, omoConfig, fromVersion, opencodeConfig = {}) {
3491
3616
  const targetVersion = VERSION;
3492
3617
  const migrationsApplied = [];
@@ -3542,18 +3667,30 @@ function isBreakingMigration(migration) {
3542
3667
  const toMajor = semver.major(semver.coerce(migration.toVersion) || "0.0.0");
3543
3668
  return toMajor > fromMajor;
3544
3669
  }
3545
-
3546
- // src/cli/utils/preset-loader.ts
3547
- init_esm_shims();
3548
- var PRESET_NAMES = [
3549
- "minimal",
3550
- "standard",
3551
- "strict-tdd",
3552
- "event-modeling",
3553
- "enterprise",
3554
- "solo-quick",
3555
- "copilot-only"
3670
+ var PROFILES = [
3671
+ {
3672
+ id: "event-modeling",
3673
+ fileName: "event-modeling",
3674
+ displayName: "Event Modeling",
3675
+ description: "Full TDD + Event Modeling + all Marvin modes. For line-of-business applications with complex state management.",
3676
+ recommendation: "Recommended for LOB apps"
3677
+ },
3678
+ {
3679
+ id: "prd",
3680
+ fileName: "standard",
3681
+ displayName: "PRD-Driven",
3682
+ description: "TDD + PRD-style specifications. For libraries, CLI tools, and APIs without long-term state tracking.",
3683
+ recommendation: "Recommended for libraries/tools"
3684
+ },
3685
+ {
3686
+ id: "tdd-only",
3687
+ fileName: "minimal",
3688
+ displayName: "TDD-Only",
3689
+ description: "Just TDD cycle enforcement with no planning methodology. Minimal setup.",
3690
+ recommendation: "For users who only want TDD"
3691
+ }
3556
3692
  ];
3693
+ var PRESET_NAMES = ["minimal", "standard", "event-modeling"];
3557
3694
  function getPresetsDir() {
3558
3695
  const currentFileDir = dirname(fileURLToPath(import.meta.url));
3559
3696
  const bundledRoot = join(currentFileDir, "..", "..");
@@ -3588,6 +3725,17 @@ function getPresetsDir() {
3588
3725
  function isValidPresetName(name) {
3589
3726
  return PRESET_NAMES.includes(name);
3590
3727
  }
3728
+ function getProfileById(id) {
3729
+ return PROFILES.find((p) => p.id === id);
3730
+ }
3731
+ function loadProfile(profileId) {
3732
+ const profile = getProfileById(profileId);
3733
+ if (!profile) {
3734
+ const validIds = PROFILES.map((p) => p.id).join(", ");
3735
+ throw new Error(`Invalid profile: "${profileId}". Valid profiles are: ${validIds}`);
3736
+ }
3737
+ return loadPreset(profile.fileName);
3738
+ }
3591
3739
  function loadPreset(name) {
3592
3740
  if (!isValidPresetName(name)) {
3593
3741
  const validNames = PRESET_NAMES.join(", ");
@@ -3616,30 +3764,12 @@ Error: ${error.message}`);
3616
3764
  throw error;
3617
3765
  }
3618
3766
  }
3619
- function listPresets() {
3620
- const presetsDir = getPresetsDir();
3621
- const summaries = [];
3622
- for (const name of PRESET_NAMES) {
3623
- const presetPath = join(presetsDir, `${name}.json`);
3624
- if (existsSync(presetPath)) {
3625
- try {
3626
- const content = readFileSync(presetPath, "utf-8");
3627
- const preset = JSON.parse(content);
3628
- summaries.push({
3629
- name,
3630
- description: preset.description || `${name} preset`,
3631
- path: presetPath
3632
- });
3633
- } catch {
3634
- summaries.push({
3635
- name,
3636
- description: `${name} preset (unable to read)`,
3637
- path: presetPath
3638
- });
3639
- }
3640
- }
3641
- }
3642
- return summaries;
3767
+ function listProfiles() {
3768
+ return PROFILES.map((profile) => ({
3769
+ name: profile.displayName,
3770
+ description: profile.description,
3771
+ path: profile.id
3772
+ }));
3643
3773
  }
3644
3774
  function presetToDefaults(preset) {
3645
3775
  const isLegacy = "bmad" in preset && preset.bmad !== void 0;
@@ -3770,9 +3900,9 @@ function detectInstallMode(options, configs) {
3770
3900
  async function runUpgradeFlow(configs, existingVersion, options) {
3771
3901
  const { sdlc, omo, opencode } = configs;
3772
3902
  logger.section("Upgrading Configuration");
3773
- console.log(chalk6.cyan(`
3903
+ console.log(chalk5.cyan(`
3774
3904
  Current version: ${existingVersion}`));
3775
- console.log(chalk6.cyan(`New version: ${VERSION}
3905
+ console.log(chalk5.cyan(`New version: ${VERSION}
3776
3906
  `));
3777
3907
  if (!options.yes) {
3778
3908
  const proceed = await confirm({
@@ -3796,22 +3926,22 @@ Current version: ${existingVersion}`));
3796
3926
  if (fileMigrationResult.stateFileMoved) moved.push("state file");
3797
3927
  if (fileMigrationResult.backupsMoved > 0)
3798
3928
  moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
3799
- console.log(chalk6.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
3929
+ console.log(chalk5.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
3800
3930
  }
3801
3931
  const migrationSpinner = ora5("Applying migrations...").start();
3802
3932
  const migrationResult = migrateConfigs(sdlc || {}, omo || {}, existingVersion, opencode || {});
3803
3933
  if (migrationResult.migrationsApplied.length > 0) {
3804
3934
  migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
3805
3935
  for (const migration of migrationResult.migrationsApplied) {
3806
- console.log(chalk6.gray(` \u2022 ${migration}`));
3936
+ console.log(chalk5.gray(` \u2022 ${migration}`));
3807
3937
  }
3808
3938
  } else {
3809
3939
  migrationSpinner.succeed("No migrations needed");
3810
3940
  }
3811
3941
  if (migrationResult.hasBreakingChanges && !options.yes) {
3812
- console.log(chalk6.yellow("\nBreaking changes detected:"));
3942
+ console.log(chalk5.yellow("\nBreaking changes detected:"));
3813
3943
  for (const warning of migrationResult.breakingChangeWarnings) {
3814
- console.log(chalk6.yellow(` \u26A0 ${warning}`));
3944
+ console.log(chalk5.yellow(` \u26A0 ${warning}`));
3815
3945
  }
3816
3946
  const continueUpgrade = await confirm({
3817
3947
  message: "Continue with upgrade despite breaking changes?",
@@ -3829,25 +3959,25 @@ Current version: ${existingVersion}`));
3829
3959
  const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
3830
3960
  logger.section("Preserved Configuration");
3831
3961
  if (existingSubscriptions) {
3832
- console.log(chalk6.bold("Subscriptions:"));
3962
+ console.log(chalk5.bold("Subscriptions:"));
3833
3963
  if (existingSubscriptions.hasClaude) {
3834
3964
  const claudeInfo = existingSubscriptions.claudeAuth === "api" ? "API Key" : existingSubscriptions.claudeTier;
3835
- console.log(chalk6.green(` \u2713 Claude (${claudeInfo})`));
3965
+ console.log(chalk5.green(` \u2713 Claude (${claudeInfo})`));
3836
3966
  }
3837
3967
  if (existingSubscriptions.hasOpenAI) {
3838
3968
  const openaiInfo = existingSubscriptions.openaiAuth === "api" ? "API Key" : "Subscription";
3839
- console.log(chalk6.green(` \u2713 OpenAI (${openaiInfo})`));
3969
+ console.log(chalk5.green(` \u2713 OpenAI (${openaiInfo})`));
3840
3970
  }
3841
3971
  if (existingSubscriptions.hasGoogle)
3842
- console.log(chalk6.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
3972
+ console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
3843
3973
  if (existingSubscriptions.hasGitHubCopilot)
3844
- console.log(chalk6.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
3974
+ console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
3845
3975
  }
3846
3976
  if (existingModels) {
3847
- console.log(chalk6.bold("\nModel Assignments:"));
3848
- console.log(chalk6.green(` \u2713 Marvin: ${existingModels.marvin}`));
3849
- console.log(chalk6.green(` \u2713 Oracle: ${existingModels.oracle}`));
3850
- console.log(chalk6.green(` \u2713 Librarian: ${existingModels.librarian}`));
3977
+ console.log(chalk5.bold("\nModel Assignments:"));
3978
+ console.log(chalk5.green(` \u2713 Marvin: ${existingModels.marvin}`));
3979
+ console.log(chalk5.green(` \u2713 Oracle: ${existingModels.oracle}`));
3980
+ console.log(chalk5.green(` \u2713 Librarian: ${existingModels.librarian}`));
3851
3981
  }
3852
3982
  console.log();
3853
3983
  const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
@@ -3867,22 +3997,17 @@ Current version: ${existingVersion}`));
3867
3997
  }
3868
3998
  }
3869
3999
  }
3870
- let finalSubscriptions = existingSubscriptions;
3871
- if (!options.yes) {
3872
- const changeSubscriptions = await confirm({
3873
- message: "Do you want to modify your LLM subscriptions?",
3874
- default: false
3875
- });
3876
- if (changeSubscriptions) {
3877
- logger.section("LLM Subscriptions");
3878
- finalSubscriptions = await gatherSubscriptions();
3879
- }
3880
- }
3881
- if (!finalSubscriptions) {
3882
- logger.error("Could not extract subscription information from existing config.");
3883
- logger.info("Please run with --reconfigure to set up from scratch.");
3884
- process.exit(1);
3885
- }
4000
+ const finalSubscriptions = existingSubscriptions || {
4001
+ hasClaude: true,
4002
+ claudeAuth: "subscription",
4003
+ claudeTier: "max5x",
4004
+ hasOpenAI: true,
4005
+ openaiAuth: "subscription",
4006
+ hasGoogle: true,
4007
+ googleAuth: "antigravity",
4008
+ hasGitHubCopilot: true,
4009
+ copilotPlan: "enterprise"
4010
+ };
3886
4011
  const fullAnswers = {
3887
4012
  subscriptions: finalSubscriptions,
3888
4013
  models: existingModels || {
@@ -3924,12 +4049,12 @@ Current version: ${existingVersion}`));
3924
4049
  }
3925
4050
  logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
3926
4051
  if (backups.sdlcBackup || backups.omoBackup || backups.opencodeBackup) {
3927
- console.log(chalk6.gray("\nBackups saved:"));
3928
- if (backups.sdlcBackup) console.log(chalk6.gray(` \u2022 ${backups.sdlcBackup}`));
3929
- if (backups.omoBackup) console.log(chalk6.gray(` \u2022 ${backups.omoBackup}`));
3930
- if (backups.opencodeBackup) console.log(chalk6.gray(` \u2022 ${backups.opencodeBackup}`));
4052
+ console.log(chalk5.gray("\nBackups saved:"));
4053
+ if (backups.sdlcBackup) console.log(chalk5.gray(` \u2022 ${backups.sdlcBackup}`));
4054
+ if (backups.omoBackup) console.log(chalk5.gray(` \u2022 ${backups.omoBackup}`));
4055
+ if (backups.opencodeBackup) console.log(chalk5.gray(` \u2022 ${backups.opencodeBackup}`));
3931
4056
  }
3932
- console.log(chalk6.gray("\nRestart OpenCode to use the upgraded configuration."));
4057
+ console.log(chalk5.gray("\nRestart OpenCode to use the upgraded configuration."));
3933
4058
  console.log();
3934
4059
  }
3935
4060
  async function install(options) {
@@ -3963,231 +4088,94 @@ async function install(options) {
3963
4088
  if (modeResult.mode === "reconfigure") {
3964
4089
  logger.info("Reconfiguring from scratch (--reconfigure flag)");
3965
4090
  }
3966
- let preset = null;
3967
- let presetDefaults = null;
3968
- let presetName = null;
3969
- if (options.preset && options.preset !== "none") {
3970
- if (!isValidPresetName(options.preset)) {
3971
- logger.error(`Invalid preset: "${options.preset}"`);
3972
- logger.info(`Valid presets: ${PRESET_NAMES.join(", ")}`);
3973
- process.exit(1);
3974
- }
3975
- try {
3976
- preset = loadPreset(options.preset);
3977
- presetDefaults = presetToDefaults(preset);
3978
- presetName = options.preset;
3979
- logger.success(`Loaded preset: ${options.preset}`);
3980
- } catch (error) {
3981
- logger.error(error instanceof Error ? error.message : String(error));
3982
- process.exit(1);
3983
- }
3984
- }
3985
- if (!preset && !options.yes) {
3986
- logger.section("Configuration Profile");
3987
- const selectedPreset = await askForPreset();
3988
- if (selectedPreset) {
3989
- preset = selectedPreset.preset;
3990
- presetDefaults = presetToDefaults(preset);
3991
- presetName = selectedPreset.name;
3992
- }
3993
- }
3994
- let shouldCustomize = true;
3995
- if (preset && presetDefaults && presetName && !options.yes) {
3996
- console.log(chalk6.bold("\nProfile Configuration:\n"));
3997
- console.log(chalk6.gray(formatPresetSummary(preset, presetName)));
3998
- console.log();
3999
- shouldCustomize = await confirm({
4000
- message: "Would you like to customize these settings?",
4001
- default: false
4002
- });
4003
- } else if (options.yes && preset) {
4004
- shouldCustomize = false;
4005
- logger.info("Using preset defaults (--yes flag)");
4006
- }
4007
- let subscriptions;
4008
- if (shouldCustomize || !preset) {
4009
- logger.section("LLM Subscriptions");
4010
- subscriptions = await gatherSubscriptions();
4011
- if (!options.yes) {
4012
- logger.section("Confirm Provider Selection");
4013
- console.log(chalk6.bold("You selected:\n"));
4014
- console.log(
4015
- ` Claude: ${subscriptions.hasClaude ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
4016
- );
4017
- if (subscriptions.hasClaude) {
4018
- console.log(
4019
- ` Auth Method: ${subscriptions.claudeAuth === "api" ? "API Key" : "Subscription"}`
4020
- );
4021
- if (subscriptions.claudeAuth === "subscription") {
4022
- console.log(` Tier: ${subscriptions.claudeTier}`);
4023
- }
4024
- }
4025
- console.log(
4026
- ` OpenAI: ${subscriptions.hasOpenAI ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
4027
- );
4028
- if (subscriptions.hasOpenAI) {
4029
- console.log(
4030
- ` Auth Method: ${subscriptions.openaiAuth === "api" ? "API Key" : "Subscription"}`
4031
- );
4032
- }
4033
- console.log(
4034
- ` Google: ${subscriptions.hasGoogle ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
4035
- );
4036
- if (subscriptions.hasGoogle) {
4037
- console.log(` Auth Method: ${subscriptions.googleAuth}`);
4038
- }
4039
- console.log(
4040
- ` GitHub Copilot: ${subscriptions.hasGitHubCopilot ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
4041
- );
4042
- if (subscriptions.hasGitHubCopilot) {
4043
- console.log(` Plan: ${subscriptions.copilotPlan}`);
4044
- }
4045
- console.log();
4046
- const confirmed = await confirm({
4047
- message: "Is this correct?",
4048
- default: true
4049
- });
4050
- if (!confirmed) {
4051
- logger.info("Please restart installation with correct provider selections.");
4052
- process.exit(0);
4053
- }
4054
- }
4055
- if (preset && presetDefaults) {
4056
- const modelWarnings = validatePresetModels(presetDefaults.models, subscriptions);
4057
- if (modelWarnings.length > 0) {
4058
- console.log(chalk6.yellow("\nPreset model compatibility warnings:"));
4059
- for (const warning of modelWarnings) {
4060
- console.log(chalk6.yellow(` - ${warning}`));
4061
- }
4062
- console.log();
4063
- }
4064
- }
4091
+ logger.section("Development Profile");
4092
+ let preset;
4093
+ let presetDefaults;
4094
+ let profileName;
4095
+ if (options.yes) {
4096
+ preset = loadProfile("event-modeling");
4097
+ presetDefaults = presetToDefaults(preset);
4098
+ profileName = "Event Modeling";
4099
+ logger.info("Using default profile: Event Modeling (--yes flag)");
4065
4100
  } else {
4066
- subscriptions = {
4067
- hasClaude: preset.subscriptions.claude.enabled,
4068
- claudeAuth: "subscription",
4069
- claudeTier: preset.subscriptions.claude.tier,
4070
- hasOpenAI: preset.subscriptions.openai.enabled,
4071
- openaiAuth: "subscription",
4072
- hasGoogle: preset.subscriptions.google.enabled,
4073
- googleAuth: preset.subscriptions.google.authMethod,
4074
- hasGitHubCopilot: preset.subscriptions.githubCopilot?.enabled ?? false,
4075
- copilotPlan: preset.subscriptions.githubCopilot?.plan ?? "none"
4076
- };
4101
+ const { preset: selectedPreset, profile } = await askForProfile();
4102
+ preset = selectedPreset;
4103
+ presetDefaults = presetToDefaults(preset);
4104
+ profileName = profile.displayName;
4105
+ logger.success(`Selected profile: ${profileName}`);
4106
+ }
4107
+ console.log(chalk5.bold("\nProfile Configuration:\n"));
4108
+ console.log(chalk5.gray(formatPresetSummary(preset, profileName)));
4109
+ console.log();
4110
+ const subscriptions = {
4111
+ hasClaude: true,
4112
+ claudeAuth: "subscription",
4113
+ claudeTier: "max5x",
4114
+ hasOpenAI: true,
4115
+ openaiAuth: "subscription",
4116
+ hasGoogle: true,
4117
+ googleAuth: "antigravity",
4118
+ hasGitHubCopilot: true,
4119
+ copilotPlan: "enterprise"
4120
+ };
4121
+ logger.section("Applying Profile Configuration");
4122
+ const models = {
4123
+ marvin: presetDefaults.models.marvin,
4124
+ oracle: presetDefaults.models.oracle,
4125
+ librarian: presetDefaults.models.librarian,
4126
+ frontend: presetDefaults.models.frontend,
4127
+ documentWriter: presetDefaults.models.documentWriter,
4128
+ multimodalLooker: presetDefaults.models.multimodalLooker
4129
+ };
4130
+ const methodology = presetDefaults.methodology;
4131
+ const features = presetDefaults.features;
4132
+ const advanced = presetDefaults.advanced;
4133
+ const tdd = preset.tdd ? {
4134
+ enabled: preset.tdd.enabled,
4135
+ verbosity: preset.tdd.verbosity,
4136
+ bypassPatterns: preset.tdd.bypassPatterns,
4137
+ mutationTesting: preset.tdd.mutationTesting,
4138
+ domainVetoEnabled: true,
4139
+ debateRounds: 2,
4140
+ requireVerification: true
4141
+ } : void 0;
4142
+ const eventModeling = preset.eventModeling ? {
4143
+ enabled: preset.eventModeling.enabled,
4144
+ outputPath: preset.eventModeling.outputPath
4145
+ } : void 0;
4146
+ const gitWorkflow = preset.git ? {
4147
+ workflow: preset.git.workflow,
4148
+ requireClean: preset.git.requireClean,
4149
+ worktrees: preset.git.worktrees
4150
+ } : void 0;
4151
+ const modes = preset.modes ? {
4152
+ default: preset.modes.default,
4153
+ eventModeling: preset.modes.eventModeling,
4154
+ enabled: preset.modes.enabled
4155
+ } : void 0;
4156
+ const memory = preset.memory ? {
4157
+ backend: preset.memory.backend,
4158
+ checkpointTtl: preset.memory.checkpointTtl
4159
+ } : void 0;
4160
+ logger.section("GitHub Integration");
4161
+ const githubResult = await setupSmartGitHub(process.cwd());
4162
+ const github = githubResult.enabled && githubResult.config ? {
4163
+ enabled: true,
4164
+ owner: githubResult.config.owner,
4165
+ repo: githubResult.config.repo,
4166
+ project: githubResult.config.project,
4167
+ statuses: githubResult.config.statuses || []
4168
+ } : { enabled: false, owner: "", repo: "", statuses: [] };
4169
+ if (githubResult.repoCreated) {
4170
+ logger.success("Created new GitHub repository");
4077
4171
  }
4078
- let models;
4079
- let methodology;
4080
- let features;
4081
- let advanced;
4082
- let github;
4083
- let tdd;
4084
- let eventModeling;
4085
- let gitWorkflow;
4086
- let modes;
4087
- let memory;
4088
- if (!shouldCustomize && presetDefaults) {
4089
- logger.section("Applying Preset Configuration");
4090
- const availableModels = await Promise.resolve().then(() => (init_models(), models_exports)).then(
4091
- (m) => m.getAvailableModels(subscriptions)
4092
- );
4093
- if (availableModels.length === 0) {
4094
- logger.error(
4095
- "No models available. Please enable at least one provider (Claude, OpenAI, or Google)."
4096
- );
4097
- process.exit(1);
4098
- }
4099
- models = {
4100
- marvin: getValidModelOrFirst(presetDefaults.models.marvin, availableModels),
4101
- oracle: getValidModelOrFirst(presetDefaults.models.oracle, availableModels),
4102
- librarian: getValidModelOrFirst(presetDefaults.models.librarian, availableModels),
4103
- frontend: getValidModelOrFirst(presetDefaults.models.frontend, availableModels),
4104
- documentWriter: getValidModelOrFirst(presetDefaults.models.documentWriter, availableModels),
4105
- multimodalLooker: getValidModelOrFirst(
4106
- presetDefaults.models.multimodalLooker,
4107
- availableModels
4108
- )
4109
- };
4110
- methodology = presetDefaults.methodology;
4111
- features = presetDefaults.features;
4112
- advanced = presetDefaults.advanced;
4113
- logger.section("GitHub Integration");
4114
- github = await gatherGitHub();
4115
- logger.section("Marvin Modes");
4116
- modes = await gatherModes();
4117
- logger.section("Memory Configuration");
4118
- memory = await gatherMemory();
4119
- if (preset?.tdd) {
4120
- tdd = {
4121
- enabled: preset.tdd.enabled,
4122
- verbosity: preset.tdd.verbosity,
4123
- bypassPatterns: preset.tdd.bypassPatterns,
4124
- mutationTesting: preset.tdd.mutationTesting,
4125
- domainVetoEnabled: true,
4126
- debateRounds: 2,
4127
- requireVerification: true
4128
- };
4129
- }
4130
- if (preset?.eventModeling) {
4131
- eventModeling = {
4132
- enabled: preset.eventModeling.enabled,
4133
- outputPath: preset.eventModeling.outputPath
4134
- };
4135
- }
4136
- if (preset?.git) {
4137
- gitWorkflow = {
4138
- workflow: preset.git.workflow,
4139
- requireClean: preset.git.requireClean,
4140
- worktrees: preset.git.worktrees
4141
- };
4142
- }
4143
- logger.success("Preset configuration applied");
4144
- } else {
4145
- logger.section("Marvin Modes");
4146
- modes = await gatherModes();
4147
- logger.section("Memory Configuration");
4148
- memory = await gatherMemory();
4149
- logger.section("Model Selection");
4150
- models = await gatherModels(subscriptions, presetDefaults?.models);
4151
- logger.section("Methodology Preferences");
4152
- methodology = await gatherMethodology(presetDefaults?.methodology);
4153
- logger.section("GitHub Integration");
4154
- github = await gatherGitHub();
4155
- logger.section("TDD Configuration");
4156
- tdd = await gatherTdd(
4157
- preset?.tdd ? {
4158
- enabled: preset.tdd.enabled,
4159
- verbosity: preset.tdd.verbosity,
4160
- bypassPatterns: preset.tdd.bypassPatterns,
4161
- mutationTesting: preset.tdd.mutationTesting
4162
- } : void 0
4163
- );
4164
- logger.section("Event Modeling");
4165
- eventModeling = await gatherEventModeling(
4166
- preset?.eventModeling ? {
4167
- enabled: preset.eventModeling.enabled,
4168
- outputPath: preset.eventModeling.outputPath
4169
- } : void 0
4170
- );
4171
- logger.section("Git Workflow");
4172
- gitWorkflow = await gatherGitWorkflow(
4173
- preset?.git ? {
4174
- workflow: preset.git.workflow,
4175
- requireClean: preset.git.requireClean,
4176
- worktrees: preset.git.worktrees
4177
- } : void 0
4178
- );
4179
- logger.section("Feature Selection");
4180
- features = await gatherFeatures(presetDefaults?.features);
4181
- if (options.advanced) {
4182
- logger.section("Advanced Configuration");
4183
- advanced = await gatherAdvanced(presetDefaults?.advanced);
4184
- } else {
4185
- advanced = presetDefaults?.advanced ?? {
4186
- parallelIssueLimit: 0,
4187
- experimental: []
4188
- };
4189
- }
4172
+ if (githubResult.projectCreated) {
4173
+ logger.success("Linked project board");
4190
4174
  }
4175
+ if (githubResult.rulesetsCreated) {
4176
+ logger.success("Created branch protection rules");
4177
+ }
4178
+ logger.success("Profile configuration applied");
4191
4179
  logger.section("Generating Configuration");
4192
4180
  const answers = {
4193
4181
  subscriptions,
@@ -4206,10 +4194,10 @@ async function install(options) {
4206
4194
  };
4207
4195
  const generator = new ConfigGenerator(answers);
4208
4196
  const files = await generator.generate();
4209
- console.log(chalk6.bold("Files to be created/modified:\n"));
4197
+ console.log(chalk5.bold("Files to be created/modified:\n"));
4210
4198
  for (const file of files) {
4211
- const action = file.exists ? chalk6.yellow("update") : chalk6.green("create");
4212
- console.log(chalk6.gray(` [${action}] ${file.path}`));
4199
+ const action = file.exists ? chalk5.yellow("update") : chalk5.green("create");
4200
+ console.log(chalk5.gray(` [${action}] ${file.path}`));
4213
4201
  }
4214
4202
  console.log();
4215
4203
  if (!options.yes) {
@@ -4246,85 +4234,50 @@ async function install(options) {
4246
4234
  logger.error(error instanceof Error ? error.message : String(error));
4247
4235
  process.exit(1);
4248
4236
  }
4249
- printNextSteps(subscriptions);
4237
+ printNextSteps();
4250
4238
  }
4251
- async function askForPreset() {
4252
- const presets = listPresets();
4253
- const choices = [
4254
- ...presets.map((p) => ({
4255
- name: `${p.name} - ${p.description}`,
4256
- value: p.name
4257
- })),
4258
- { name: "No preset - Configure everything manually", value: "none" }
4259
- ];
4239
+ async function askForProfile() {
4240
+ console.log(
4241
+ chalk5.gray("\nChoose a profile based on your project type. Each profile is opinionated\n") + chalk5.gray("and includes sensible defaults - you can customize later if needed.\n")
4242
+ );
4243
+ const choices = PROFILES.map((p) => ({
4244
+ name: `${p.displayName} (${p.recommendation})`,
4245
+ value: p.id,
4246
+ description: p.description
4247
+ }));
4260
4248
  const selected = await select({
4261
- message: "Choose a configuration profile:",
4249
+ message: "Select a development profile:",
4262
4250
  choices,
4263
4251
  default: "event-modeling"
4264
4252
  });
4265
- if (selected === "none") {
4266
- return null;
4267
- }
4268
- try {
4269
- const preset = loadPreset(selected);
4270
- return { preset, name: selected };
4271
- } catch (error) {
4272
- logger.warn(`Failed to load preset: ${error instanceof Error ? error.message : String(error)}`);
4273
- return null;
4274
- }
4275
- }
4276
- function getValidModelOrFirst(modelId, availableModels) {
4277
- if (modelId && availableModels.some((m) => m.id === modelId)) {
4278
- return modelId;
4253
+ const profile = PROFILES.find((p) => p.id === selected);
4254
+ if (!profile) {
4255
+ throw new Error(`Profile not found: ${selected}`);
4279
4256
  }
4280
- return availableModels[0]?.id ?? "";
4257
+ const preset = loadProfile(selected);
4258
+ return { preset, profile };
4281
4259
  }
4282
- function printNextSteps(subscriptions) {
4283
- const steps = [];
4284
- if (subscriptions.hasClaude) {
4285
- if (subscriptions.claudeAuth === "api") {
4286
- steps.push(`Set ${chalk6.cyan("ANTHROPIC_API_KEY")} environment variable with your API key`);
4287
- } else {
4288
- steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select Anthropic -> Claude Pro/Max`);
4289
- }
4290
- }
4291
- if (subscriptions.hasOpenAI) {
4292
- if (subscriptions.openaiAuth === "api") {
4293
- steps.push(`Set ${chalk6.cyan("OPENAI_API_KEY")} environment variable with your API key`);
4294
- } else {
4295
- steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select OpenAI -> ChatGPT Plus/Pro`);
4296
- }
4297
- }
4298
- if (subscriptions.hasGoogle) {
4299
- if (subscriptions.googleAuth === "api") {
4300
- steps.push(`Set ${chalk6.cyan("GOOGLE_API_KEY")} environment variable with your API key`);
4301
- } else {
4302
- steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select Google -> OAuth with Google`);
4303
- }
4304
- }
4305
- if (subscriptions.hasGitHubCopilot) {
4306
- steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select GitHub Copilot`);
4307
- }
4308
- logger.successBanner("OPENCODE ATHENA INSTALLED SUCCESSFULLY!");
4309
- console.log(chalk6.bold("Next Steps:\n"));
4310
- steps.forEach((step, i) => {
4311
- console.log(` ${i + 1}. ${step}`);
4312
- });
4313
- console.log(chalk6.bold("\nThen start OpenCode in your project directory.\n"));
4314
- console.log(chalk6.gray("The SDLC plugin will automatically:"));
4315
- console.log(chalk6.gray(" \u2022 Enforce TDD cycles (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)"));
4316
- console.log(chalk6.gray(" \u2022 Track work via GitHub Issues"));
4317
- console.log(chalk6.gray(" \u2022 Apply file ownership rules"));
4318
- console.log(chalk6.gray("\nUse Tab to switch between Marvin modes (Build, Discover, etc.)"));
4319
- console.log(chalk6.gray("\nDocumentation: https://github.com/jwilger/opencode-sdlc-plugin"));
4260
+ function printNextSteps() {
4261
+ logger.successBanner("OPENCODE SDLC INSTALLED SUCCESSFULLY!");
4262
+ console.log(chalk5.bold("Next Steps:\n"));
4263
+ console.log(" 1. Authenticate with your LLM providers:");
4264
+ console.log(` ${chalk5.cyan("opencode auth login")}`);
4265
+ console.log(
4266
+ " (Select providers you have access to: Anthropic, OpenAI, Google, GitHub Copilot)"
4267
+ );
4268
+ console.log();
4269
+ console.log(" 2. Start OpenCode in your project directory:");
4270
+ console.log(` ${chalk5.cyan("opencode")}`);
4271
+ console.log(chalk5.bold("\nThe SDLC plugin will automatically:\n"));
4272
+ console.log(chalk5.gray(" \u2022 Enforce TDD cycles (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)"));
4273
+ console.log(chalk5.gray(" \u2022 Track work via GitHub Issues"));
4274
+ console.log(chalk5.gray(" \u2022 Apply file ownership rules"));
4275
+ console.log(chalk5.gray(" \u2022 Route to available models at runtime"));
4276
+ console.log(chalk5.gray("\nUse Tab to switch between Marvin modes (Build, Discover, etc.)"));
4277
+ console.log(chalk5.gray("\nAdvanced: Edit ~/.config/opencode/sdlc.json to customize models."));
4278
+ console.log(chalk5.gray("Documentation: https://github.com/jwilger/opencode-sdlc-plugin"));
4320
4279
  console.log();
4321
4280
  }
4322
-
4323
- // src/cli/commands/providers.ts
4324
- init_esm_shims();
4325
-
4326
- // src/cli/utils/auth-detector.ts
4327
- init_esm_shims();
4328
4281
  async function detectAuthStatus() {
4329
4282
  const opencodeConfigPath = join(homedir(), ".opencode.json");
4330
4283
  if (!existsSync(opencodeConfigPath)) {
@@ -4397,7 +4350,7 @@ async function showStatus() {
4397
4350
  const spinner = ora5("Checking authentication status...").start();
4398
4351
  const authStatus = await detectAuthStatus();
4399
4352
  spinner.stop();
4400
- console.log(chalk6.bold("\nConfigured Providers:\n"));
4353
+ console.log(chalk5.bold("\nConfigured Providers:\n"));
4401
4354
  displayProvider(
4402
4355
  "Claude",
4403
4356
  subscriptions.hasClaude,
@@ -4414,32 +4367,32 @@ async function showStatus() {
4414
4367
  );
4415
4368
  const models = configs.sdlc.models;
4416
4369
  if (models) {
4417
- console.log(chalk6.bold("\nCurrent Model Assignments:\n"));
4418
- console.log(` ${chalk6.cyan("Marvin:")} ${models.marvin || "not set"}`);
4419
- console.log(` ${chalk6.cyan("Oracle:")} ${models.oracle || "not set"}`);
4420
- console.log(` ${chalk6.cyan("Librarian:")} ${models.librarian || "not set"}`);
4421
- console.log(` ${chalk6.cyan("Frontend:")} ${models.frontend || "not set"}`);
4422
- console.log(` ${chalk6.cyan("Doc Writer:")} ${models.documentWriter || "not set"}`);
4423
- console.log(` ${chalk6.cyan("Multimodal:")} ${models.multimodalLooker || "not set"}`);
4370
+ console.log(chalk5.bold("\nCurrent Model Assignments:\n"));
4371
+ console.log(` ${chalk5.cyan("Marvin:")} ${models.marvin || "not set"}`);
4372
+ console.log(` ${chalk5.cyan("Oracle:")} ${models.oracle || "not set"}`);
4373
+ console.log(` ${chalk5.cyan("Librarian:")} ${models.librarian || "not set"}`);
4374
+ console.log(` ${chalk5.cyan("Frontend:")} ${models.frontend || "not set"}`);
4375
+ console.log(` ${chalk5.cyan("Doc Writer:")} ${models.documentWriter || "not set"}`);
4376
+ console.log(` ${chalk5.cyan("Multimodal:")} ${models.multimodalLooker || "not set"}`);
4424
4377
  }
4425
4378
  if (!authStatus.anthropic && subscriptions.hasClaude) {
4426
- console.log(chalk6.yellow("\n\u26A0 Tip: Run 'opencode auth login' to authenticate Claude"));
4379
+ console.log(chalk5.yellow("\n\u26A0 Tip: Run 'opencode auth login' to authenticate Claude"));
4427
4380
  }
4428
4381
  if (!authStatus.openai && subscriptions.hasOpenAI) {
4429
- console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
4382
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
4430
4383
  }
4431
4384
  if (!authStatus.google && subscriptions.hasGoogle) {
4432
- console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
4385
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
4433
4386
  }
4434
4387
  if (!authStatus.githubCopilot && subscriptions.hasGitHubCopilot) {
4435
- console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate GitHub Copilot"));
4388
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate GitHub Copilot"));
4436
4389
  }
4437
4390
  console.log();
4438
4391
  }
4439
4392
  function displayProvider(name, enabled, tier, authenticated) {
4440
- const status = enabled ? chalk6.green("\u2713 Enabled") : chalk6.gray("\u2717 Disabled");
4441
- const auth = enabled ? authenticated ? chalk6.green("\u2713 Authenticated") : chalk6.yellow("\u26A0 Not authenticated") : chalk6.gray("(disabled)");
4442
- const tierText = tier && tier !== "none" ? chalk6.gray(` (${tier})`) : "";
4393
+ const status = enabled ? chalk5.green("\u2713 Enabled") : chalk5.gray("\u2717 Disabled");
4394
+ const auth = enabled ? authenticated ? chalk5.green("\u2713 Authenticated") : chalk5.yellow("\u26A0 Not authenticated") : chalk5.gray("(disabled)");
4395
+ const tierText = tier && tier !== "none" ? chalk5.gray(` (${tier})`) : "";
4443
4396
  console.log(` ${status.padEnd(30)} ${name}${tierText}`);
4444
4397
  console.log(` ${" ".repeat(30)} Auth: ${auth}`);
4445
4398
  }
@@ -4501,7 +4454,7 @@ async function addProvider() {
4501
4454
  });
4502
4455
  }
4503
4456
  logger.success(`Provider added: ${provider}`);
4504
- console.log(chalk6.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
4457
+ console.log(chalk5.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
4505
4458
  }
4506
4459
  async function removeProvider() {
4507
4460
  logger.banner();
@@ -4518,9 +4471,6 @@ async function syncWithAuth() {
4518
4471
  logger.section("Sync with OpenCode Auth");
4519
4472
  logger.info("Not yet implemented.");
4520
4473
  }
4521
-
4522
- // src/cli/commands/uninstall.ts
4523
- init_esm_shims();
4524
4474
  async function uninstall(options) {
4525
4475
  logger.banner();
4526
4476
  logger.warn("This will remove OpenCode SDLC from your system.");
@@ -4595,13 +4545,10 @@ async function uninstall(options) {
4595
4545
  logger.success("OpenCode SDLC has been uninstalled.");
4596
4546
  if (options.keepConfig) {
4597
4547
  logger.info("Configuration files were preserved.");
4598
- logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to reinstall with existing config.`);
4548
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to reinstall with existing config.`);
4599
4549
  }
4600
4550
  console.log();
4601
4551
  }
4602
-
4603
- // src/cli/commands/upgrade.ts
4604
- init_esm_shims();
4605
4552
  var execAsync3 = promisify(exec);
4606
4553
  function detectReleaseChannel(version) {
4607
4554
  if (version.includes("-beta")) return "beta";
@@ -4631,7 +4578,7 @@ async function upgrade(options) {
4631
4578
  const configs = loadExistingConfigs();
4632
4579
  if (!configs.sdlc) {
4633
4580
  logger.error("No existing Sdlc installation found.");
4634
- logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to install for the first time.`);
4581
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to install for the first time.`);
4635
4582
  process.exit(1);
4636
4583
  }
4637
4584
  const existingVersion = configs.sdlcVersion || "0.0.1";
@@ -4666,7 +4613,7 @@ async function upgrade(options) {
4666
4613
  logger.section("Package Versions");
4667
4614
  const updatesAvailable = updates.filter((u) => u.updateAvailable);
4668
4615
  for (const pkg of updates) {
4669
- const status = pkg.updateAvailable ? chalk6.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk6.green(pkg.current);
4616
+ const status = pkg.updateAvailable ? chalk5.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk5.green(pkg.current);
4670
4617
  logger.keyValue(pkg.name, status);
4671
4618
  }
4672
4619
  console.log();
@@ -4684,7 +4631,7 @@ async function upgrade(options) {
4684
4631
  }
4685
4632
  if (options.check) {
4686
4633
  console.log();
4687
- logger.info(`Run ${chalk6.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
4634
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
4688
4635
  return;
4689
4636
  }
4690
4637
  const actionCount = updatesAvailable.length + (existingVersion !== VERSION ? 1 : 0);
@@ -4703,9 +4650,9 @@ async function upgrade(options) {
4703
4650
  }
4704
4651
  }
4705
4652
  logger.section("Upgrading Configuration");
4706
- console.log(chalk6.cyan(`
4653
+ console.log(chalk5.cyan(`
4707
4654
  Current version: ${existingVersion}`));
4708
- console.log(chalk6.cyan(`New version: ${VERSION}
4655
+ console.log(chalk5.cyan(`New version: ${VERSION}
4709
4656
  `));
4710
4657
  const backupSpinner = ora5("Creating backup...").start();
4711
4658
  const backups = createBackups();
@@ -4719,7 +4666,7 @@ Current version: ${existingVersion}`));
4719
4666
  if (fileMigrationResult.stateFileMoved) moved.push("state file");
4720
4667
  if (fileMigrationResult.backupsMoved > 0)
4721
4668
  moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
4722
- console.log(chalk6.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
4669
+ console.log(chalk5.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
4723
4670
  }
4724
4671
  const migrationSpinner = ora5("Applying migrations...").start();
4725
4672
  const migrationResult = migrateConfigs(
@@ -4731,15 +4678,15 @@ Current version: ${existingVersion}`));
4731
4678
  if (migrationResult.migrationsApplied.length > 0) {
4732
4679
  migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
4733
4680
  for (const migration of migrationResult.migrationsApplied) {
4734
- console.log(chalk6.gray(` \u2022 ${migration}`));
4681
+ console.log(chalk5.gray(` \u2022 ${migration}`));
4735
4682
  }
4736
4683
  } else {
4737
4684
  migrationSpinner.succeed("No migrations needed");
4738
4685
  }
4739
4686
  if (migrationResult.hasBreakingChanges && !options.yes) {
4740
- console.log(chalk6.yellow("\nBreaking changes detected:"));
4687
+ console.log(chalk5.yellow("\nBreaking changes detected:"));
4741
4688
  for (const warning of migrationResult.breakingChangeWarnings) {
4742
- console.log(chalk6.yellow(` \u26A0 ${warning}`));
4689
+ console.log(chalk5.yellow(` \u26A0 ${warning}`));
4743
4690
  }
4744
4691
  const continueUpgrade = await confirm({
4745
4692
  message: "Continue with upgrade despite breaking changes?",
@@ -4757,20 +4704,20 @@ Current version: ${existingVersion}`));
4757
4704
  const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
4758
4705
  logger.section("Preserved Configuration");
4759
4706
  if (existingSubscriptions) {
4760
- console.log(chalk6.bold("Subscriptions:"));
4707
+ console.log(chalk5.bold("Subscriptions:"));
4761
4708
  if (existingSubscriptions.hasClaude)
4762
- console.log(chalk6.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
4763
- if (existingSubscriptions.hasOpenAI) console.log(chalk6.green(" \u2713 OpenAI"));
4709
+ console.log(chalk5.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
4710
+ if (existingSubscriptions.hasOpenAI) console.log(chalk5.green(" \u2713 OpenAI"));
4764
4711
  if (existingSubscriptions.hasGoogle)
4765
- console.log(chalk6.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
4712
+ console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
4766
4713
  if (existingSubscriptions.hasGitHubCopilot)
4767
- console.log(chalk6.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
4714
+ console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
4768
4715
  }
4769
4716
  if (existingModels) {
4770
- console.log(chalk6.bold("\nModel Assignments:"));
4771
- console.log(chalk6.green(` \u2713 Marvin: ${existingModels.marvin}`));
4772
- console.log(chalk6.green(` \u2713 Oracle: ${existingModels.oracle}`));
4773
- console.log(chalk6.green(` \u2713 Librarian: ${existingModels.librarian}`));
4717
+ console.log(chalk5.bold("\nModel Assignments:"));
4718
+ console.log(chalk5.green(` \u2713 Marvin: ${existingModels.marvin}`));
4719
+ console.log(chalk5.green(` \u2713 Oracle: ${existingModels.oracle}`));
4720
+ console.log(chalk5.green(` \u2713 Librarian: ${existingModels.librarian}`));
4774
4721
  }
4775
4722
  console.log();
4776
4723
  const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
@@ -4901,28 +4848,24 @@ Current version: ${existingVersion}`));
4901
4848
  }
4902
4849
  logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
4903
4850
  if (backups.sdlcBackup || backups.omoBackup || backups.opencodeBackup) {
4904
- console.log(chalk6.gray("\nBackups saved:"));
4905
- if (backups.sdlcBackup) console.log(chalk6.gray(` \u2022 ${backups.sdlcBackup}`));
4906
- if (backups.omoBackup) console.log(chalk6.gray(` \u2022 ${backups.omoBackup}`));
4907
- if (backups.opencodeBackup) console.log(chalk6.gray(` \u2022 ${backups.opencodeBackup}`));
4851
+ console.log(chalk5.gray("\nBackups saved:"));
4852
+ if (backups.sdlcBackup) console.log(chalk5.gray(` \u2022 ${backups.sdlcBackup}`));
4853
+ if (backups.omoBackup) console.log(chalk5.gray(` \u2022 ${backups.omoBackup}`));
4854
+ if (backups.opencodeBackup) console.log(chalk5.gray(` \u2022 ${backups.opencodeBackup}`));
4908
4855
  }
4909
- console.log(chalk6.gray("\nRestart OpenCode to use the upgraded configuration."));
4856
+ console.log(chalk5.gray("\nRestart OpenCode to use the upgraded configuration."));
4910
4857
  console.log();
4911
4858
  }
4912
4859
 
4913
4860
  // src/cli/index.ts
4914
4861
  var program = new Command();
4915
4862
  program.name("opencode-sdlc").description(
4916
- `${chalk6.cyan(DISPLAY_NAME)} - ${TAGLINE}
4863
+ `${chalk5.cyan(DISPLAY_NAME)} - ${TAGLINE}
4917
4864
  TDD-driven development toolkit with GitHub Issues integration for OpenCode`
4918
4865
  ).version(VERSION);
4919
- program.command("install").description("Install and configure OpenCode SDLC").option(
4920
- "-p, --preset <preset>",
4921
- "Use a preset configuration (minimal, standard, strict-tdd, event-modeling, enterprise, solo-quick, copilot-only)",
4922
- "standard"
4923
- ).option("-y, --yes", "Skip confirmation prompts", false).option("--advanced", "Show advanced configuration options", false).option("--global", "Install globally (default)", true).option("--local", "Install to current project only", false).option("--list-presets", "List available presets and exit", false).option("--reconfigure", "Force full reconfiguration (ignore existing config)", false).action(async (options) => {
4924
- if (options.listPresets) {
4925
- displayPresets();
4866
+ program.command("install").description("Install and configure OpenCode SDLC").option("-y, --yes", "Skip confirmation prompts (uses Event Modeling profile)", false).option("--advanced", "Show advanced configuration options", false).option("--global", "Install globally (default)", true).option("--local", "Install to current project only", false).option("--list-profiles", "List available profiles and exit", false).option("--reconfigure", "Force full reconfiguration (ignore existing config)", false).action(async (options) => {
4867
+ if (options.listProfiles) {
4868
+ displayProfiles();
4926
4869
  return;
4927
4870
  }
4928
4871
  await install(options);
@@ -4934,16 +4877,16 @@ program.command("info").description("Show current configuration and status").act
4934
4877
  program.command("providers [action]").description("Manage LLM provider subscriptions (status/add/remove/refresh/sync)").option("-v, --verbose", "Show detailed provider information", false).action(async (action) => {
4935
4878
  await providers({ action });
4936
4879
  });
4937
- function displayPresets() {
4938
- console.log(chalk6.bold.cyan("\nAvailable Presets:\n"));
4939
- const presets = listPresets();
4940
- for (const preset of presets) {
4941
- console.log(chalk6.bold(` ${preset.name}`));
4942
- console.log(chalk6.gray(` ${preset.description}`));
4880
+ function displayProfiles() {
4881
+ console.log(chalk5.bold.cyan("\nAvailable Development Profiles:\n"));
4882
+ const profiles = listProfiles();
4883
+ for (const profile of profiles) {
4884
+ console.log(chalk5.bold(` ${profile.name}`));
4885
+ console.log(chalk5.gray(` ${profile.description}`));
4943
4886
  console.log();
4944
4887
  }
4945
- console.log(chalk6.gray("Usage: opencode-sdlc install --preset <name>"));
4946
- console.log(chalk6.gray(" opencode-sdlc install --preset standard --yes\n"));
4888
+ console.log(chalk5.gray("Usage: opencode-sdlc install"));
4889
+ console.log(chalk5.gray(" opencode-sdlc install --yes (uses Event Modeling profile)\n"));
4947
4890
  }
4948
4891
  program.parse();
4949
4892
  //# sourceMappingURL=index.js.map