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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,889 @@ 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
2308
+ */
2309
+ async createRepository(options) {
2310
+ const { data } = await this.octokit.repos.createForAuthenticatedUser({
2311
+ name: options.name,
2312
+ description: options.description,
2313
+ private: options.private ?? false,
2314
+ auto_init: options.autoInit ?? false
2315
+ });
2316
+ return {
2317
+ id: data.id,
2318
+ name: data.name,
2319
+ fullName: data.full_name,
2320
+ url: data.html_url,
2321
+ private: data.private,
2322
+ defaultBranch: data.default_branch || "main"
2323
+ };
2324
+ }
2325
+ /**
2326
+ * Get repository details
2327
+ */
2328
+ async getRepository(owner, repo) {
2329
+ const params = this.getRepoParams(owner, repo);
2330
+ const { data } = await this.octokit.repos.get(params);
2331
+ return {
2332
+ id: data.id,
2333
+ name: data.name,
2334
+ fullName: data.full_name,
2335
+ url: data.html_url,
2336
+ private: data.private,
2337
+ defaultBranch: data.default_branch || "main"
2338
+ };
2339
+ }
2340
+ /**
2341
+ * List repositories for the authenticated user
2342
+ */
2343
+ async listUserRepos(options = {}) {
2344
+ const { data } = await this.octokit.repos.listForAuthenticatedUser({
2345
+ type: options.type || "owner",
2346
+ per_page: options.limit || 30,
2347
+ sort: "updated"
2348
+ });
2349
+ return data.map((repo) => ({
2350
+ id: repo.id,
2351
+ name: repo.name,
2352
+ fullName: repo.full_name,
2353
+ url: repo.html_url,
2354
+ private: repo.private,
2355
+ defaultBranch: repo.default_branch || "main"
2356
+ }));
2357
+ }
2358
+ // ==========================================================================
2359
+ // Projects (GraphQL required for Projects V2)
2360
+ // ==========================================================================
2361
+ /**
2362
+ * List projects for the authenticated user
2363
+ * Note: Projects V2 requires GraphQL API
2364
+ */
2365
+ async listUserProjects(limit = 20) {
2366
+ const query = `
2367
+ query($first: Int!) {
2368
+ viewer {
2369
+ projectsV2(first: $first) {
2370
+ nodes {
2371
+ id
2372
+ number
2373
+ title
2374
+ url
2375
+ }
2376
+ }
2377
+ }
2837
2378
  }
2838
- if (!/^[a-zA-Z0-9._-]+$/.test(value)) {
2839
- return "Invalid repository name format";
2379
+ `;
2380
+ const result = await this.octokit.graphql(query, { first: limit });
2381
+ return result.viewer.projectsV2.nodes;
2382
+ }
2383
+ /**
2384
+ * Get project by number for a user
2385
+ */
2386
+ async getUserProject(userLogin, projectNumber) {
2387
+ const query = `
2388
+ query($login: String!, $number: Int!) {
2389
+ user(login: $login) {
2390
+ projectV2(number: $number) {
2391
+ id
2392
+ number
2393
+ title
2394
+ url
2395
+ }
2396
+ }
2840
2397
  }
2841
- return true;
2398
+ `;
2399
+ try {
2400
+ const result = await this.octokit.graphql(query, { login: userLogin, number: projectNumber });
2401
+ return result.user.projectV2;
2402
+ } catch {
2403
+ return null;
2842
2404
  }
2843
- });
2844
- const useProjectBoard = await confirm({
2845
- message: "Use a GitHub Project board for issue status tracking?",
2846
- default: defaults?.project !== void 0
2847
- });
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";
2405
+ }
2406
+ /**
2407
+ * Link a repository to a project
2408
+ * Note: This creates a linked repository in the project
2409
+ */
2410
+ async linkRepoToProject(projectId, repoOwner, repoName) {
2411
+ const { data: repoData } = await this.octokit.repos.get({
2412
+ owner: repoOwner,
2413
+ repo: repoName
2414
+ });
2415
+ const mutation = `
2416
+ mutation($projectId: ID!, $repositoryId: ID!) {
2417
+ linkProjectV2ToRepository(input: { projectId: $projectId, repositoryId: $repositoryId }) {
2418
+ repository {
2419
+ id
2420
+ }
2857
2421
  }
2858
- return true;
2859
2422
  }
2423
+ `;
2424
+ await this.octokit.graphql(mutation, {
2425
+ projectId,
2426
+ repositoryId: repoData.node_id
2427
+ });
2428
+ }
2429
+ /**
2430
+ * Add an issue to a project
2431
+ */
2432
+ async addIssueToProject(projectId, issueNodeId) {
2433
+ const mutation = `
2434
+ mutation($projectId: ID!, $contentId: ID!) {
2435
+ addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
2436
+ item {
2437
+ id
2438
+ }
2439
+ }
2440
+ }
2441
+ `;
2442
+ const result = await this.octokit.graphql(mutation, {
2443
+ projectId,
2444
+ contentId: issueNodeId
2445
+ });
2446
+ return result.addProjectV2ItemById.item.id;
2447
+ }
2448
+ // ==========================================================================
2449
+ // Branch Protection / Rulesets
2450
+ // ==========================================================================
2451
+ /**
2452
+ * Create a branch ruleset with common protection rules
2453
+ */
2454
+ async createBranchRuleset(options, owner, repo) {
2455
+ const params = this.getRepoParams(owner, repo);
2456
+ const rules = [];
2457
+ if (options.requirePullRequest) {
2458
+ rules.push({
2459
+ type: "pull_request",
2460
+ parameters: {
2461
+ dismiss_stale_reviews_on_push: options.dismissStaleReviews ?? false,
2462
+ require_code_owner_review: options.requireCodeOwnerReview ?? false,
2463
+ require_last_push_approval: false,
2464
+ required_approving_review_count: options.requiredApprovals ?? 1,
2465
+ required_review_thread_resolution: false
2466
+ }
2467
+ });
2468
+ }
2469
+ if (options.requireSignedCommits) {
2470
+ rules.push({ type: "required_signatures" });
2471
+ }
2472
+ if (options.requireLinearHistory) {
2473
+ rules.push({ type: "required_linear_history" });
2474
+ }
2475
+ if (options.preventDeletion) {
2476
+ rules.push({ type: "deletion" });
2477
+ }
2478
+ if (options.preventForcePush) {
2479
+ rules.push({ type: "non_fast_forward" });
2480
+ }
2481
+ const { data } = await this.octokit.repos.createRepoRuleset({
2482
+ ...params,
2483
+ name: options.name,
2484
+ enforcement: options.enforcement,
2485
+ conditions: {
2486
+ ref_name: {
2487
+ include: options.targetBranches,
2488
+ exclude: []
2489
+ }
2490
+ },
2491
+ rules
2860
2492
  });
2493
+ return { id: data.id };
2494
+ }
2495
+ // ==========================================================================
2496
+ // Utility Methods
2497
+ // ==========================================================================
2498
+ /**
2499
+ * Get the authenticated user's login
2500
+ */
2501
+ async getAuthenticatedUser() {
2502
+ const { data } = await this.octokit.users.getAuthenticated();
2503
+ return { login: data.login, id: data.id };
2504
+ }
2505
+ /**
2506
+ * Check if a repository exists
2507
+ */
2508
+ async repoExists(owner, repo) {
2509
+ try {
2510
+ await this.octokit.repos.get({ owner, repo });
2511
+ return true;
2512
+ } catch {
2513
+ return false;
2514
+ }
2515
+ }
2516
+ };
2517
+ function createGitHubClientIfAuthenticated(options = {}) {
2518
+ if (!isGitHubAuthenticated(options.token)) {
2519
+ return null;
2520
+ }
2521
+ try {
2522
+ return new GitHubClient(options);
2523
+ } catch {
2524
+ return null;
2525
+ }
2526
+ }
2527
+ function parseGitHubUrl(url) {
2528
+ const httpsMatch = url.match(/github\.com[/:]([^/]+)\/([^/.]+)(?:\.git)?/);
2529
+ if (httpsMatch) {
2530
+ return {
2531
+ owner: httpsMatch[1],
2532
+ repo: httpsMatch[2],
2533
+ url
2534
+ };
2535
+ }
2536
+ const sshMatch = url.match(/git@github\.com:([^/]+)\/([^/.]+)(?:\.git)?/);
2537
+ if (sshMatch) {
2538
+ return {
2539
+ owner: sshMatch[1],
2540
+ repo: sshMatch[2],
2541
+ url
2542
+ };
2543
+ }
2544
+ return null;
2545
+ }
2546
+ function getOriginRemoteUrl(cwd) {
2547
+ try {
2548
+ const url = execSync("git remote get-url origin", {
2549
+ encoding: "utf-8",
2550
+ cwd,
2551
+ stdio: ["pipe", "pipe", "pipe"]
2552
+ }).trim();
2553
+ return url || null;
2554
+ } catch {
2555
+ return null;
2556
+ }
2557
+ }
2558
+ function detectGitHubRepo(cwd) {
2559
+ const url = getOriginRemoteUrl(cwd);
2560
+ if (!url) return null;
2561
+ return parseGitHubUrl(url);
2562
+ }
2563
+ function isGitRepository(cwd) {
2564
+ try {
2565
+ execSync("git rev-parse --git-dir", {
2566
+ encoding: "utf-8",
2567
+ cwd,
2568
+ stdio: ["pipe", "pipe", "pipe"]
2569
+ });
2570
+ return true;
2571
+ } catch {
2572
+ return false;
2573
+ }
2574
+ }
2575
+ function initGitRepo(cwd) {
2576
+ try {
2577
+ execSync("git init", {
2578
+ encoding: "utf-8",
2579
+ cwd,
2580
+ stdio: ["pipe", "pipe", "pipe"]
2581
+ });
2582
+ return true;
2583
+ } catch {
2584
+ return false;
2585
+ }
2586
+ }
2587
+ function addGitRemote(name, url, cwd) {
2588
+ try {
2589
+ execSync(`git remote add ${name} ${url}`, {
2590
+ encoding: "utf-8",
2591
+ cwd,
2592
+ stdio: ["pipe", "pipe", "pipe"]
2593
+ });
2594
+ return true;
2595
+ } catch {
2596
+ return false;
2597
+ }
2598
+ }
2599
+
2600
+ // src/cli/questions/github-smart.ts
2601
+ var DEFAULT_STATUSES = ["Backlog", "Ready", "In progress", "In review", "Done"];
2602
+ async function setupSmartGitHub(cwd) {
2603
+ const wantGitHub = await confirm({
2604
+ message: "Enable GitHub integration for issue tracking?",
2605
+ default: true
2606
+ });
2607
+ if (!wantGitHub) {
2608
+ return { enabled: false };
2609
+ }
2610
+ if (!isGitHubAuthenticated()) {
2611
+ console.log(chalk5.yellow("\n\u26A0 GitHub authentication not found."));
2612
+ console.log(" Run 'gh auth login' or set GITHUB_TOKEN environment variable.\n");
2613
+ const continueWithoutAuth = await confirm({
2614
+ message: "Continue with manual configuration (no auto-detection)?",
2615
+ default: true
2616
+ });
2617
+ if (!continueWithoutAuth) {
2618
+ return { enabled: false };
2619
+ }
2620
+ return manualGitHubConfig();
2621
+ }
2622
+ const client = createGitHubClientIfAuthenticated();
2623
+ if (!client) {
2624
+ console.log(chalk5.yellow("\n\u26A0 Could not create GitHub client.\n"));
2625
+ return manualGitHubConfig();
2626
+ }
2627
+ let userLogin;
2628
+ try {
2629
+ const user = await client.getAuthenticatedUser();
2630
+ userLogin = user.login;
2631
+ console.log(chalk5.green(`
2632
+ \u2713 Authenticated as ${userLogin}
2633
+ `));
2634
+ } catch (error) {
2635
+ console.log(chalk5.yellow("\n\u26A0 Could not verify GitHub authentication.\n"));
2636
+ return manualGitHubConfig();
2637
+ }
2638
+ const repoResult = await setupRepository(client, userLogin, cwd);
2639
+ if (repoResult.action === "skip") {
2640
+ return { enabled: false };
2641
+ }
2642
+ const { owner, repo } = repoResult;
2643
+ if (!owner || !repo) {
2644
+ return { enabled: false };
2645
+ }
2646
+ client.setRepo(owner, repo);
2647
+ const projectResult = await setupProjectBoard(client, userLogin);
2648
+ let rulesetsCreated = false;
2649
+ const wantRulesets = await confirm({
2650
+ message: "Configure branch protection rules for main branch?",
2651
+ default: false
2652
+ });
2653
+ if (wantRulesets) {
2654
+ rulesetsCreated = await setupBranchRulesets(client, owner, repo);
2861
2655
  }
2862
2656
  return {
2863
2657
  enabled: true,
2864
- owner,
2865
- repo,
2866
- project,
2867
- statuses: defaults?.statuses ?? DEFAULT_STATUSES
2658
+ config: {
2659
+ owner,
2660
+ repo,
2661
+ project: projectResult.projectNumber,
2662
+ statuses: DEFAULT_STATUSES
2663
+ },
2664
+ repoCreated: repoResult.action === "create-public" || repoResult.action === "create-private",
2665
+ projectCreated: projectResult.action === "create-blank",
2666
+ rulesetsCreated
2868
2667
  };
2869
2668
  }
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
2878
- });
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
- };
2669
+ async function setupRepository(client, userLogin, cwd) {
2670
+ const isGitRepo = isGitRepository(cwd);
2671
+ const existingRemote = isGitRepo ? detectGitHubRepo(cwd) : null;
2672
+ if (existingRemote) {
2673
+ console.log(chalk5.dim(` Detected remote: ${existingRemote.owner}/${existingRemote.repo}`));
2674
+ const useExisting = await confirm({
2675
+ message: `Use existing repository ${existingRemote.owner}/${existingRemote.repo}?`,
2676
+ default: true
2677
+ });
2678
+ if (useExisting) {
2679
+ return {
2680
+ action: "existing",
2681
+ owner: existingRemote.owner,
2682
+ repo: existingRemote.repo
2683
+ };
2684
+ }
2892
2685
  }
2893
- const verbosity = await select({
2894
- message: "TDD feedback verbosity level:",
2686
+ const repoAction = await select({
2687
+ message: "Repository setup:",
2895
2688
  choices: [
2896
2689
  {
2897
- name: "Silent - Only show errors",
2898
- value: "silent"
2690
+ value: "existing",
2691
+ name: "Connect to existing GitHub repository",
2692
+ description: "Enter owner/repo for an existing repository"
2899
2693
  },
2900
2694
  {
2901
- name: "Brief - Short status messages (recommended)",
2902
- value: "brief"
2695
+ value: "create-private",
2696
+ name: "Create new private repository",
2697
+ description: "Create a new private repo on GitHub"
2903
2698
  },
2904
2699
  {
2905
- name: "Explain - Detailed explanations of TDD phases",
2906
- value: "explain"
2700
+ value: "create-public",
2701
+ name: "Create new public repository",
2702
+ description: "Create a new public repo on GitHub"
2703
+ },
2704
+ {
2705
+ value: "skip",
2706
+ name: "Skip GitHub integration",
2707
+ description: "Configure manually later"
2907
2708
  }
2908
- ],
2909
- default: defaults?.verbosity ?? "brief"
2910
- });
2911
- const customBypass = await confirm({
2912
- message: "Customize file patterns that bypass TDD checks?",
2913
- default: false
2709
+ ]
2914
2710
  });
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);
2711
+ if (repoAction === "skip") {
2712
+ return { action: "skip" };
2922
2713
  }
2923
- const mutationEnabled = await confirm({
2924
- message: "Enable mutation testing for coverage verification?",
2925
- default: defaults?.mutationTesting?.enabled ?? false
2926
- });
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
2938
- });
2939
- requiredScore = scoreChoice;
2714
+ if (repoAction === "existing") {
2715
+ return await connectExistingRepo(client);
2940
2716
  }
2941
- const domainVetoEnabled = await confirm({
2942
- message: "Enable domain agent veto power? (Can block cycle for type violations)",
2943
- default: defaults?.domainVetoEnabled ?? true
2944
- });
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
2717
+ return await createNewRepo(client, userLogin, repoAction === "create-private", cwd, isGitRepo);
2718
+ }
2719
+ async function connectExistingRepo(client) {
2720
+ const owner = await input({
2721
+ message: "Repository owner (user or org):",
2722
+ validate: (v) => v.trim() ? true : "Owner is required"
2954
2723
  });
2955
- const requireVerification = await confirm({
2956
- message: "Require test output evidence before phase transitions?",
2957
- default: defaults?.requireVerification ?? true
2724
+ const repo = await input({
2725
+ message: "Repository name:",
2726
+ validate: (v) => v.trim() ? true : "Repository name is required"
2958
2727
  });
2959
- return {
2960
- enabled,
2961
- verbosity,
2962
- bypassPatterns,
2963
- mutationTesting: {
2964
- enabled: mutationEnabled,
2965
- requiredScore
2966
- },
2967
- domainVetoEnabled,
2968
- debateRounds,
2969
- requireVerification
2970
- };
2728
+ try {
2729
+ const exists = await client.repoExists(owner, repo);
2730
+ if (!exists) {
2731
+ console.log(chalk5.yellow(`
2732
+ \u26A0 Repository ${owner}/${repo} not found.
2733
+ `));
2734
+ return { action: "skip" };
2735
+ }
2736
+ console.log(chalk5.green(`\u2713 Found repository ${owner}/${repo}`));
2737
+ } catch {
2738
+ console.log(chalk5.yellow("\n\u26A0 Could not verify repository.\n"));
2739
+ }
2740
+ return { action: "existing", owner, repo };
2971
2741
  }
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
2742
+ async function createNewRepo(client, userLogin, isPrivate, cwd, isGitRepo) {
2743
+ const repoName = await input({
2744
+ message: "New repository name:",
2745
+ validate: (v) => {
2746
+ if (!v.trim()) return "Repository name is required";
2747
+ if (!/^[a-zA-Z0-9._-]+$/.test(v)) return "Invalid repository name";
2748
+ return true;
2749
+ }
2980
2750
  });
2981
- if (!enabled) {
2982
- return {
2983
- enabled: false,
2984
- outputPath: defaults?.outputPath ?? DEFAULT_OUTPUT_PATH
2985
- };
2986
- }
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";
2751
+ const description = await input({
2752
+ message: "Repository description (optional):",
2753
+ default: ""
2754
+ });
2755
+ try {
2756
+ console.log(chalk5.dim(`
2757
+ Creating ${isPrivate ? "private" : "public"} repository...`));
2758
+ const newRepo = await client.createRepository({
2759
+ name: repoName,
2760
+ description: description || void 0,
2761
+ private: isPrivate,
2762
+ autoInit: !isGitRepo
2763
+ // Only auto-init if no local git repo
2764
+ });
2765
+ console.log(chalk5.green(`\u2713 Created repository: ${newRepo.url}
2766
+ `));
2767
+ if (isGitRepo) {
2768
+ const remoteUrl = `git@github.com:${userLogin}/${repoName}.git`;
2769
+ const added = addGitRemote("origin", remoteUrl, cwd);
2770
+ if (added) {
2771
+ console.log(chalk5.green(`\u2713 Added git remote 'origin'`));
2993
2772
  }
2994
- if (value.startsWith("/") || value.startsWith("~")) {
2995
- return "Use a relative path from the project root";
2773
+ } else {
2774
+ const initialized = initGitRepo(cwd);
2775
+ if (initialized) {
2776
+ console.log(chalk5.green("\u2713 Initialized git repository"));
2777
+ const remoteUrl = `git@github.com:${userLogin}/${repoName}.git`;
2778
+ addGitRemote("origin", remoteUrl, cwd);
2779
+ console.log(chalk5.green(`\u2713 Added git remote 'origin'`));
2996
2780
  }
2997
- return true;
2998
2781
  }
2999
- });
3000
- return {
3001
- enabled,
3002
- outputPath
3003
- };
2782
+ return {
2783
+ action: isPrivate ? "create-private" : "create-public",
2784
+ owner: userLogin,
2785
+ repo: repoName
2786
+ };
2787
+ } catch (error) {
2788
+ const message = error instanceof Error ? error.message : "Unknown error";
2789
+ console.log(chalk5.red(`
2790
+ \u2717 Failed to create repository: ${message}
2791
+ `));
2792
+ return { action: "skip" };
2793
+ }
3004
2794
  }
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:",
2795
+ async function setupProjectBoard(client, userLogin) {
2796
+ const wantProject = await confirm({
2797
+ message: "Use a GitHub Project board for issue status tracking?",
2798
+ default: true
2799
+ });
2800
+ if (!wantProject) {
2801
+ return { action: "skip" };
2802
+ }
2803
+ const projectAction = await select({
2804
+ message: "Project board setup:",
3011
2805
  choices: [
3012
2806
  {
3013
- name: "Standard - Traditional branch-based workflow",
3014
- value: "standard"
2807
+ value: "existing",
2808
+ name: "Link existing project",
2809
+ description: "Enter project number from URL"
2810
+ },
2811
+ {
2812
+ value: "create-blank",
2813
+ name: "Create new blank project",
2814
+ description: "Create a new project with default columns"
3015
2815
  },
3016
2816
  {
3017
- name: "git-spice - Stacked PRs with git-spice CLI",
3018
- value: "git-spice"
2817
+ value: "skip",
2818
+ name: "Skip project board",
2819
+ description: "Configure manually later"
3019
2820
  }
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
2821
+ ]
3030
2822
  });
3031
- return {
3032
- workflow,
3033
- requireClean,
3034
- worktrees
3035
- };
2823
+ if (projectAction === "skip") {
2824
+ return { action: "skip" };
2825
+ }
2826
+ if (projectAction === "existing") {
2827
+ return await linkExistingProject(client, userLogin);
2828
+ }
2829
+ return await createNewProject(client, userLogin);
3036
2830
  }
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"
2831
+ async function linkExistingProject(client, userLogin) {
2832
+ try {
2833
+ const projects = await client.listUserProjects(10);
2834
+ if (projects.length > 0) {
2835
+ console.log(chalk5.dim("\n Your recent projects:"));
2836
+ for (const p of projects) {
2837
+ console.log(chalk5.dim(` #${p.number}: ${p.title}`));
2838
+ }
2839
+ console.log();
2840
+ }
2841
+ } catch {
3065
2842
  }
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)",
3082
- default: true
2843
+ const projectNum = await number({
2844
+ message: "Project number (from project URL):",
2845
+ min: 1,
2846
+ validate: (v) => v && v >= 1 ? true : "Project number required"
3083
2847
  });
3084
- let finalEnabled = enabled;
3085
- if (!useEventModeling) {
3086
- finalEnabled = enabled.filter((m) => m !== "model");
2848
+ if (!projectNum) {
2849
+ return { action: "skip" };
3087
2850
  }
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
- };
2851
+ try {
2852
+ const project = await client.getUserProject(userLogin, projectNum);
2853
+ if (project) {
2854
+ console.log(chalk5.green(`\u2713 Found project: ${project.title}`));
2855
+ return { action: "existing", projectNumber: projectNum };
2856
+ }
2857
+ } catch {
2858
+ }
2859
+ return { action: "existing", projectNumber: projectNum };
2860
+ }
2861
+ async function createNewProject(client, userLogin) {
2862
+ console.log(chalk5.yellow("\n\u26A0 Project creation requires manual setup via GitHub UI."));
2863
+ console.log(` Visit: https://github.com/users/${userLogin}/projects?type=new
2864
+ `);
2865
+ const createdProject = await confirm({
2866
+ message: "Did you create a project? Enter its number?",
2867
+ default: false
3094
2868
  });
3095
- const defaultMode = await select({
3096
- message: "Which mode should be active when you start OpenCode?",
3097
- choices: defaultModeChoices,
3098
- default: "build"
2869
+ if (!createdProject) {
2870
+ return { action: "skip" };
2871
+ }
2872
+ const projectNum = await number({
2873
+ message: "New project number:",
2874
+ min: 1
3099
2875
  });
3100
2876
  return {
3101
- default: defaultMode,
3102
- eventModeling: useEventModeling,
3103
- enabled: finalEnabled
2877
+ action: "create-blank",
2878
+ projectNumber: projectNum
3104
2879
  };
3105
2880
  }
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: [
2881
+ async function setupBranchRulesets(client, owner, repo) {
2882
+ console.log(chalk5.dim("\n Configuring branch protection for 'main'...\n"));
2883
+ const requirePR = await confirm({
2884
+ message: "Require pull requests for changes?",
2885
+ default: true
2886
+ });
2887
+ let requiredApprovals = 0;
2888
+ if (requirePR) {
2889
+ requiredApprovals = await number({
2890
+ message: "Required approvals (0 for none):",
2891
+ default: 0,
2892
+ min: 0,
2893
+ max: 10
2894
+ }) ?? 0;
2895
+ }
2896
+ const requireSignedCommits = await confirm({
2897
+ message: "Require signed commits?",
2898
+ default: false
2899
+ });
2900
+ const preventForcePush = await confirm({
2901
+ message: "Prevent force pushes?",
2902
+ default: true
2903
+ });
2904
+ try {
2905
+ await client.createBranchRuleset(
3116
2906
  {
3117
- name: "Stateless - No persistent memory (simpler, no setup required)",
3118
- value: "stateless"
2907
+ name: "main-protection",
2908
+ enforcement: "active",
2909
+ targetBranches: ["refs/heads/main", "refs/heads/master"],
2910
+ requirePullRequest: requirePR,
2911
+ requiredApprovals,
2912
+ requireSignedCommits,
2913
+ preventForcePush,
2914
+ preventDeletion: true
3119
2915
  },
3120
- {
3121
- name: "Memento - Persistent memory via Memento MCP (requires setup)",
3122
- value: "memento"
3123
- }
3124
- ],
3125
- default: "stateless"
2916
+ owner,
2917
+ repo
2918
+ );
2919
+ console.log(chalk5.green("\n\u2713 Created branch ruleset 'main-protection'\n"));
2920
+ return true;
2921
+ } catch (error) {
2922
+ const message = error instanceof Error ? error.message : "Unknown error";
2923
+ console.log(chalk5.yellow(`
2924
+ \u26A0 Could not create ruleset: ${message}`));
2925
+ console.log(" You may need admin permissions or GitHub Pro/Team.\n");
2926
+ return false;
2927
+ }
2928
+ }
2929
+ async function manualGitHubConfig() {
2930
+ const owner = await input({
2931
+ message: "GitHub repository owner:",
2932
+ validate: (v) => v.trim() ? true : "Owner is required"
3126
2933
  });
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;
3157
- }
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"));
2934
+ const repo = await input({
2935
+ message: "GitHub repository name:",
2936
+ validate: (v) => v.trim() ? true : "Repository name is required"
2937
+ });
2938
+ const useProject = await confirm({
2939
+ message: "Use a GitHub Project board?",
2940
+ default: false
2941
+ });
2942
+ let project;
2943
+ if (useProject) {
2944
+ project = await number({
2945
+ message: "Project number:",
2946
+ min: 1
2947
+ }) ?? void 0;
3160
2948
  }
3161
2949
  return {
3162
- backend,
3163
- checkpointTtl
2950
+ enabled: true,
2951
+ config: {
2952
+ owner,
2953
+ repo,
2954
+ project,
2955
+ statuses: DEFAULT_STATUSES
2956
+ }
3164
2957
  };
3165
2958
  }
3166
-
3167
- // src/cli/utils/config-loader.ts
3168
- init_esm_shims();
3169
2959
  function loadExistingConfigs() {
3170
2960
  const result = {
3171
2961
  sdlc: null,
@@ -3304,9 +3094,6 @@ function detectNewFeatures(existingSdlc) {
3304
3094
  }
3305
3095
  return newFeatures;
3306
3096
  }
3307
-
3308
- // src/cli/utils/config-merger.ts
3309
- init_esm_shims();
3310
3097
  function deepMerge(baseObj, newObj) {
3311
3098
  const result = { ...baseObj };
3312
3099
  for (const [key, value] of Object.entries(newObj)) {
@@ -3375,15 +3162,6 @@ function writeMergedConfigs(configs) {
3375
3162
  );
3376
3163
  }
3377
3164
  }
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
3165
  var MIGRATIONS = [
3388
3166
  {
3389
3167
  fromVersion: "0.0.1",
@@ -3484,9 +3262,6 @@ function migrateLegacyFiles() {
3484
3262
  }
3485
3263
  return result;
3486
3264
  }
3487
-
3488
- // src/cli/utils/migrations/runner.ts
3489
- init_esm_shims();
3490
3265
  function migrateConfigs(sdlcConfig, omoConfig, fromVersion, opencodeConfig = {}) {
3491
3266
  const targetVersion = VERSION;
3492
3267
  const migrationsApplied = [];
@@ -3542,18 +3317,30 @@ function isBreakingMigration(migration) {
3542
3317
  const toMajor = semver.major(semver.coerce(migration.toVersion) || "0.0.0");
3543
3318
  return toMajor > fromMajor;
3544
3319
  }
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"
3320
+ var PROFILES = [
3321
+ {
3322
+ id: "event-modeling",
3323
+ fileName: "event-modeling",
3324
+ displayName: "Event Modeling",
3325
+ description: "Full TDD + Event Modeling + all Marvin modes. For line-of-business applications with complex state management.",
3326
+ recommendation: "Recommended for LOB apps"
3327
+ },
3328
+ {
3329
+ id: "prd",
3330
+ fileName: "standard",
3331
+ displayName: "PRD-Driven",
3332
+ description: "TDD + PRD-style specifications. For libraries, CLI tools, and APIs without long-term state tracking.",
3333
+ recommendation: "Recommended for libraries/tools"
3334
+ },
3335
+ {
3336
+ id: "tdd-only",
3337
+ fileName: "minimal",
3338
+ displayName: "TDD-Only",
3339
+ description: "Just TDD cycle enforcement with no planning methodology. Minimal setup.",
3340
+ recommendation: "For users who only want TDD"
3341
+ }
3556
3342
  ];
3343
+ var PRESET_NAMES = ["minimal", "standard", "event-modeling"];
3557
3344
  function getPresetsDir() {
3558
3345
  const currentFileDir = dirname(fileURLToPath(import.meta.url));
3559
3346
  const bundledRoot = join(currentFileDir, "..", "..");
@@ -3588,6 +3375,17 @@ function getPresetsDir() {
3588
3375
  function isValidPresetName(name) {
3589
3376
  return PRESET_NAMES.includes(name);
3590
3377
  }
3378
+ function getProfileById(id) {
3379
+ return PROFILES.find((p) => p.id === id);
3380
+ }
3381
+ function loadProfile(profileId) {
3382
+ const profile = getProfileById(profileId);
3383
+ if (!profile) {
3384
+ const validIds = PROFILES.map((p) => p.id).join(", ");
3385
+ throw new Error(`Invalid profile: "${profileId}". Valid profiles are: ${validIds}`);
3386
+ }
3387
+ return loadPreset(profile.fileName);
3388
+ }
3591
3389
  function loadPreset(name) {
3592
3390
  if (!isValidPresetName(name)) {
3593
3391
  const validNames = PRESET_NAMES.join(", ");
@@ -3616,30 +3414,12 @@ Error: ${error.message}`);
3616
3414
  throw error;
3617
3415
  }
3618
3416
  }
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;
3417
+ function listProfiles() {
3418
+ return PROFILES.map((profile) => ({
3419
+ name: profile.displayName,
3420
+ description: profile.description,
3421
+ path: profile.id
3422
+ }));
3643
3423
  }
3644
3424
  function presetToDefaults(preset) {
3645
3425
  const isLegacy = "bmad" in preset && preset.bmad !== void 0;
@@ -3770,9 +3550,9 @@ function detectInstallMode(options, configs) {
3770
3550
  async function runUpgradeFlow(configs, existingVersion, options) {
3771
3551
  const { sdlc, omo, opencode } = configs;
3772
3552
  logger.section("Upgrading Configuration");
3773
- console.log(chalk6.cyan(`
3553
+ console.log(chalk5.cyan(`
3774
3554
  Current version: ${existingVersion}`));
3775
- console.log(chalk6.cyan(`New version: ${VERSION}
3555
+ console.log(chalk5.cyan(`New version: ${VERSION}
3776
3556
  `));
3777
3557
  if (!options.yes) {
3778
3558
  const proceed = await confirm({
@@ -3796,22 +3576,22 @@ Current version: ${existingVersion}`));
3796
3576
  if (fileMigrationResult.stateFileMoved) moved.push("state file");
3797
3577
  if (fileMigrationResult.backupsMoved > 0)
3798
3578
  moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
3799
- console.log(chalk6.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
3579
+ console.log(chalk5.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
3800
3580
  }
3801
3581
  const migrationSpinner = ora5("Applying migrations...").start();
3802
3582
  const migrationResult = migrateConfigs(sdlc || {}, omo || {}, existingVersion, opencode || {});
3803
3583
  if (migrationResult.migrationsApplied.length > 0) {
3804
3584
  migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
3805
3585
  for (const migration of migrationResult.migrationsApplied) {
3806
- console.log(chalk6.gray(` \u2022 ${migration}`));
3586
+ console.log(chalk5.gray(` \u2022 ${migration}`));
3807
3587
  }
3808
3588
  } else {
3809
3589
  migrationSpinner.succeed("No migrations needed");
3810
3590
  }
3811
3591
  if (migrationResult.hasBreakingChanges && !options.yes) {
3812
- console.log(chalk6.yellow("\nBreaking changes detected:"));
3592
+ console.log(chalk5.yellow("\nBreaking changes detected:"));
3813
3593
  for (const warning of migrationResult.breakingChangeWarnings) {
3814
- console.log(chalk6.yellow(` \u26A0 ${warning}`));
3594
+ console.log(chalk5.yellow(` \u26A0 ${warning}`));
3815
3595
  }
3816
3596
  const continueUpgrade = await confirm({
3817
3597
  message: "Continue with upgrade despite breaking changes?",
@@ -3829,25 +3609,25 @@ Current version: ${existingVersion}`));
3829
3609
  const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
3830
3610
  logger.section("Preserved Configuration");
3831
3611
  if (existingSubscriptions) {
3832
- console.log(chalk6.bold("Subscriptions:"));
3612
+ console.log(chalk5.bold("Subscriptions:"));
3833
3613
  if (existingSubscriptions.hasClaude) {
3834
3614
  const claudeInfo = existingSubscriptions.claudeAuth === "api" ? "API Key" : existingSubscriptions.claudeTier;
3835
- console.log(chalk6.green(` \u2713 Claude (${claudeInfo})`));
3615
+ console.log(chalk5.green(` \u2713 Claude (${claudeInfo})`));
3836
3616
  }
3837
3617
  if (existingSubscriptions.hasOpenAI) {
3838
3618
  const openaiInfo = existingSubscriptions.openaiAuth === "api" ? "API Key" : "Subscription";
3839
- console.log(chalk6.green(` \u2713 OpenAI (${openaiInfo})`));
3619
+ console.log(chalk5.green(` \u2713 OpenAI (${openaiInfo})`));
3840
3620
  }
3841
3621
  if (existingSubscriptions.hasGoogle)
3842
- console.log(chalk6.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
3622
+ console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
3843
3623
  if (existingSubscriptions.hasGitHubCopilot)
3844
- console.log(chalk6.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
3624
+ console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
3845
3625
  }
3846
3626
  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}`));
3627
+ console.log(chalk5.bold("\nModel Assignments:"));
3628
+ console.log(chalk5.green(` \u2713 Marvin: ${existingModels.marvin}`));
3629
+ console.log(chalk5.green(` \u2713 Oracle: ${existingModels.oracle}`));
3630
+ console.log(chalk5.green(` \u2713 Librarian: ${existingModels.librarian}`));
3851
3631
  }
3852
3632
  console.log();
3853
3633
  const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
@@ -3867,22 +3647,17 @@ Current version: ${existingVersion}`));
3867
3647
  }
3868
3648
  }
3869
3649
  }
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
- }
3650
+ const finalSubscriptions = existingSubscriptions || {
3651
+ hasClaude: true,
3652
+ claudeAuth: "subscription",
3653
+ claudeTier: "max5x",
3654
+ hasOpenAI: true,
3655
+ openaiAuth: "subscription",
3656
+ hasGoogle: true,
3657
+ googleAuth: "antigravity",
3658
+ hasGitHubCopilot: true,
3659
+ copilotPlan: "enterprise"
3660
+ };
3886
3661
  const fullAnswers = {
3887
3662
  subscriptions: finalSubscriptions,
3888
3663
  models: existingModels || {
@@ -3924,12 +3699,12 @@ Current version: ${existingVersion}`));
3924
3699
  }
3925
3700
  logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
3926
3701
  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}`));
3702
+ console.log(chalk5.gray("\nBackups saved:"));
3703
+ if (backups.sdlcBackup) console.log(chalk5.gray(` \u2022 ${backups.sdlcBackup}`));
3704
+ if (backups.omoBackup) console.log(chalk5.gray(` \u2022 ${backups.omoBackup}`));
3705
+ if (backups.opencodeBackup) console.log(chalk5.gray(` \u2022 ${backups.opencodeBackup}`));
3931
3706
  }
3932
- console.log(chalk6.gray("\nRestart OpenCode to use the upgraded configuration."));
3707
+ console.log(chalk5.gray("\nRestart OpenCode to use the upgraded configuration."));
3933
3708
  console.log();
3934
3709
  }
3935
3710
  async function install(options) {
@@ -3963,231 +3738,94 @@ async function install(options) {
3963
3738
  if (modeResult.mode === "reconfigure") {
3964
3739
  logger.info("Reconfiguring from scratch (--reconfigure flag)");
3965
3740
  }
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
- }
3741
+ logger.section("Development Profile");
3742
+ let preset;
3743
+ let presetDefaults;
3744
+ let profileName;
3745
+ if (options.yes) {
3746
+ preset = loadProfile("event-modeling");
3747
+ presetDefaults = presetToDefaults(preset);
3748
+ profileName = "Event Modeling";
3749
+ logger.info("Using default profile: Event Modeling (--yes flag)");
4065
3750
  } 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
- };
3751
+ const { preset: selectedPreset, profile } = await askForProfile();
3752
+ preset = selectedPreset;
3753
+ presetDefaults = presetToDefaults(preset);
3754
+ profileName = profile.displayName;
3755
+ logger.success(`Selected profile: ${profileName}`);
3756
+ }
3757
+ console.log(chalk5.bold("\nProfile Configuration:\n"));
3758
+ console.log(chalk5.gray(formatPresetSummary(preset, profileName)));
3759
+ console.log();
3760
+ const subscriptions = {
3761
+ hasClaude: true,
3762
+ claudeAuth: "subscription",
3763
+ claudeTier: "max5x",
3764
+ hasOpenAI: true,
3765
+ openaiAuth: "subscription",
3766
+ hasGoogle: true,
3767
+ googleAuth: "antigravity",
3768
+ hasGitHubCopilot: true,
3769
+ copilotPlan: "enterprise"
3770
+ };
3771
+ logger.section("Applying Profile Configuration");
3772
+ const models = {
3773
+ marvin: presetDefaults.models.marvin,
3774
+ oracle: presetDefaults.models.oracle,
3775
+ librarian: presetDefaults.models.librarian,
3776
+ frontend: presetDefaults.models.frontend,
3777
+ documentWriter: presetDefaults.models.documentWriter,
3778
+ multimodalLooker: presetDefaults.models.multimodalLooker
3779
+ };
3780
+ const methodology = presetDefaults.methodology;
3781
+ const features = presetDefaults.features;
3782
+ const advanced = presetDefaults.advanced;
3783
+ const tdd = preset.tdd ? {
3784
+ enabled: preset.tdd.enabled,
3785
+ verbosity: preset.tdd.verbosity,
3786
+ bypassPatterns: preset.tdd.bypassPatterns,
3787
+ mutationTesting: preset.tdd.mutationTesting,
3788
+ domainVetoEnabled: true,
3789
+ debateRounds: 2,
3790
+ requireVerification: true
3791
+ } : void 0;
3792
+ const eventModeling = preset.eventModeling ? {
3793
+ enabled: preset.eventModeling.enabled,
3794
+ outputPath: preset.eventModeling.outputPath
3795
+ } : void 0;
3796
+ const gitWorkflow = preset.git ? {
3797
+ workflow: preset.git.workflow,
3798
+ requireClean: preset.git.requireClean,
3799
+ worktrees: preset.git.worktrees
3800
+ } : void 0;
3801
+ const modes = preset.modes ? {
3802
+ default: preset.modes.default,
3803
+ eventModeling: preset.modes.eventModeling,
3804
+ enabled: preset.modes.enabled
3805
+ } : void 0;
3806
+ const memory = preset.memory ? {
3807
+ backend: preset.memory.backend,
3808
+ checkpointTtl: preset.memory.checkpointTtl
3809
+ } : void 0;
3810
+ logger.section("GitHub Integration");
3811
+ const githubResult = await setupSmartGitHub(process.cwd());
3812
+ const github = githubResult.enabled && githubResult.config ? {
3813
+ enabled: true,
3814
+ owner: githubResult.config.owner,
3815
+ repo: githubResult.config.repo,
3816
+ project: githubResult.config.project,
3817
+ statuses: githubResult.config.statuses || []
3818
+ } : { enabled: false, owner: "", repo: "", statuses: [] };
3819
+ if (githubResult.repoCreated) {
3820
+ logger.success("Created new GitHub repository");
4077
3821
  }
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
- }
3822
+ if (githubResult.projectCreated) {
3823
+ logger.success("Linked project board");
4190
3824
  }
3825
+ if (githubResult.rulesetsCreated) {
3826
+ logger.success("Created branch protection rules");
3827
+ }
3828
+ logger.success("Profile configuration applied");
4191
3829
  logger.section("Generating Configuration");
4192
3830
  const answers = {
4193
3831
  subscriptions,
@@ -4206,10 +3844,10 @@ async function install(options) {
4206
3844
  };
4207
3845
  const generator = new ConfigGenerator(answers);
4208
3846
  const files = await generator.generate();
4209
- console.log(chalk6.bold("Files to be created/modified:\n"));
3847
+ console.log(chalk5.bold("Files to be created/modified:\n"));
4210
3848
  for (const file of files) {
4211
- const action = file.exists ? chalk6.yellow("update") : chalk6.green("create");
4212
- console.log(chalk6.gray(` [${action}] ${file.path}`));
3849
+ const action = file.exists ? chalk5.yellow("update") : chalk5.green("create");
3850
+ console.log(chalk5.gray(` [${action}] ${file.path}`));
4213
3851
  }
4214
3852
  console.log();
4215
3853
  if (!options.yes) {
@@ -4246,85 +3884,50 @@ async function install(options) {
4246
3884
  logger.error(error instanceof Error ? error.message : String(error));
4247
3885
  process.exit(1);
4248
3886
  }
4249
- printNextSteps(subscriptions);
3887
+ printNextSteps();
4250
3888
  }
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
- ];
3889
+ async function askForProfile() {
3890
+ console.log(
3891
+ 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")
3892
+ );
3893
+ const choices = PROFILES.map((p) => ({
3894
+ name: `${p.displayName} (${p.recommendation})`,
3895
+ value: p.id,
3896
+ description: p.description
3897
+ }));
4260
3898
  const selected = await select({
4261
- message: "Choose a configuration profile:",
3899
+ message: "Select a development profile:",
4262
3900
  choices,
4263
3901
  default: "event-modeling"
4264
3902
  });
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;
3903
+ const profile = PROFILES.find((p) => p.id === selected);
3904
+ if (!profile) {
3905
+ throw new Error(`Profile not found: ${selected}`);
4274
3906
  }
3907
+ const preset = loadProfile(selected);
3908
+ return { preset, profile };
4275
3909
  }
4276
- function getValidModelOrFirst(modelId, availableModels) {
4277
- if (modelId && availableModels.some((m) => m.id === modelId)) {
4278
- return modelId;
4279
- }
4280
- return availableModels[0]?.id ?? "";
4281
- }
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"));
3910
+ function printNextSteps() {
3911
+ logger.successBanner("OPENCODE SDLC INSTALLED SUCCESSFULLY!");
3912
+ console.log(chalk5.bold("Next Steps:\n"));
3913
+ console.log(" 1. Authenticate with your LLM providers:");
3914
+ console.log(` ${chalk5.cyan("opencode auth login")}`);
3915
+ console.log(
3916
+ " (Select providers you have access to: Anthropic, OpenAI, Google, GitHub Copilot)"
3917
+ );
3918
+ console.log();
3919
+ console.log(" 2. Start OpenCode in your project directory:");
3920
+ console.log(` ${chalk5.cyan("opencode")}`);
3921
+ console.log(chalk5.bold("\nThe SDLC plugin will automatically:\n"));
3922
+ console.log(chalk5.gray(" \u2022 Enforce TDD cycles (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)"));
3923
+ console.log(chalk5.gray(" \u2022 Track work via GitHub Issues"));
3924
+ console.log(chalk5.gray(" \u2022 Apply file ownership rules"));
3925
+ console.log(chalk5.gray(" \u2022 Route to available models at runtime"));
3926
+ console.log(chalk5.gray("\nUse Tab to switch between Marvin modes (Build, Discover, etc.)"));
3927
+ console.log(chalk5.gray("\nAdvanced: Edit ~/.config/opencode/sdlc.json to customize models."));
3928
+ console.log(chalk5.gray("Documentation: https://github.com/jwilger/opencode-sdlc-plugin"));
4320
3929
  console.log();
4321
3930
  }
4322
-
4323
- // src/cli/commands/providers.ts
4324
- init_esm_shims();
4325
-
4326
- // src/cli/utils/auth-detector.ts
4327
- init_esm_shims();
4328
3931
  async function detectAuthStatus() {
4329
3932
  const opencodeConfigPath = join(homedir(), ".opencode.json");
4330
3933
  if (!existsSync(opencodeConfigPath)) {
@@ -4397,7 +4000,7 @@ async function showStatus() {
4397
4000
  const spinner = ora5("Checking authentication status...").start();
4398
4001
  const authStatus = await detectAuthStatus();
4399
4002
  spinner.stop();
4400
- console.log(chalk6.bold("\nConfigured Providers:\n"));
4003
+ console.log(chalk5.bold("\nConfigured Providers:\n"));
4401
4004
  displayProvider(
4402
4005
  "Claude",
4403
4006
  subscriptions.hasClaude,
@@ -4414,32 +4017,32 @@ async function showStatus() {
4414
4017
  );
4415
4018
  const models = configs.sdlc.models;
4416
4019
  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"}`);
4020
+ console.log(chalk5.bold("\nCurrent Model Assignments:\n"));
4021
+ console.log(` ${chalk5.cyan("Marvin:")} ${models.marvin || "not set"}`);
4022
+ console.log(` ${chalk5.cyan("Oracle:")} ${models.oracle || "not set"}`);
4023
+ console.log(` ${chalk5.cyan("Librarian:")} ${models.librarian || "not set"}`);
4024
+ console.log(` ${chalk5.cyan("Frontend:")} ${models.frontend || "not set"}`);
4025
+ console.log(` ${chalk5.cyan("Doc Writer:")} ${models.documentWriter || "not set"}`);
4026
+ console.log(` ${chalk5.cyan("Multimodal:")} ${models.multimodalLooker || "not set"}`);
4424
4027
  }
4425
4028
  if (!authStatus.anthropic && subscriptions.hasClaude) {
4426
- console.log(chalk6.yellow("\n\u26A0 Tip: Run 'opencode auth login' to authenticate Claude"));
4029
+ console.log(chalk5.yellow("\n\u26A0 Tip: Run 'opencode auth login' to authenticate Claude"));
4427
4030
  }
4428
4031
  if (!authStatus.openai && subscriptions.hasOpenAI) {
4429
- console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
4032
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
4430
4033
  }
4431
4034
  if (!authStatus.google && subscriptions.hasGoogle) {
4432
- console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
4035
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
4433
4036
  }
4434
4037
  if (!authStatus.githubCopilot && subscriptions.hasGitHubCopilot) {
4435
- console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate GitHub Copilot"));
4038
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate GitHub Copilot"));
4436
4039
  }
4437
4040
  console.log();
4438
4041
  }
4439
4042
  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})`) : "";
4043
+ const status = enabled ? chalk5.green("\u2713 Enabled") : chalk5.gray("\u2717 Disabled");
4044
+ const auth = enabled ? authenticated ? chalk5.green("\u2713 Authenticated") : chalk5.yellow("\u26A0 Not authenticated") : chalk5.gray("(disabled)");
4045
+ const tierText = tier && tier !== "none" ? chalk5.gray(` (${tier})`) : "";
4443
4046
  console.log(` ${status.padEnd(30)} ${name}${tierText}`);
4444
4047
  console.log(` ${" ".repeat(30)} Auth: ${auth}`);
4445
4048
  }
@@ -4501,7 +4104,7 @@ async function addProvider() {
4501
4104
  });
4502
4105
  }
4503
4106
  logger.success(`Provider added: ${provider}`);
4504
- console.log(chalk6.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
4107
+ console.log(chalk5.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
4505
4108
  }
4506
4109
  async function removeProvider() {
4507
4110
  logger.banner();
@@ -4518,9 +4121,6 @@ async function syncWithAuth() {
4518
4121
  logger.section("Sync with OpenCode Auth");
4519
4122
  logger.info("Not yet implemented.");
4520
4123
  }
4521
-
4522
- // src/cli/commands/uninstall.ts
4523
- init_esm_shims();
4524
4124
  async function uninstall(options) {
4525
4125
  logger.banner();
4526
4126
  logger.warn("This will remove OpenCode SDLC from your system.");
@@ -4595,13 +4195,10 @@ async function uninstall(options) {
4595
4195
  logger.success("OpenCode SDLC has been uninstalled.");
4596
4196
  if (options.keepConfig) {
4597
4197
  logger.info("Configuration files were preserved.");
4598
- logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to reinstall with existing config.`);
4198
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to reinstall with existing config.`);
4599
4199
  }
4600
4200
  console.log();
4601
4201
  }
4602
-
4603
- // src/cli/commands/upgrade.ts
4604
- init_esm_shims();
4605
4202
  var execAsync3 = promisify(exec);
4606
4203
  function detectReleaseChannel(version) {
4607
4204
  if (version.includes("-beta")) return "beta";
@@ -4631,7 +4228,7 @@ async function upgrade(options) {
4631
4228
  const configs = loadExistingConfigs();
4632
4229
  if (!configs.sdlc) {
4633
4230
  logger.error("No existing Sdlc installation found.");
4634
- logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to install for the first time.`);
4231
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to install for the first time.`);
4635
4232
  process.exit(1);
4636
4233
  }
4637
4234
  const existingVersion = configs.sdlcVersion || "0.0.1";
@@ -4666,7 +4263,7 @@ async function upgrade(options) {
4666
4263
  logger.section("Package Versions");
4667
4264
  const updatesAvailable = updates.filter((u) => u.updateAvailable);
4668
4265
  for (const pkg of updates) {
4669
- const status = pkg.updateAvailable ? chalk6.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk6.green(pkg.current);
4266
+ const status = pkg.updateAvailable ? chalk5.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk5.green(pkg.current);
4670
4267
  logger.keyValue(pkg.name, status);
4671
4268
  }
4672
4269
  console.log();
@@ -4684,7 +4281,7 @@ async function upgrade(options) {
4684
4281
  }
4685
4282
  if (options.check) {
4686
4283
  console.log();
4687
- logger.info(`Run ${chalk6.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
4284
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
4688
4285
  return;
4689
4286
  }
4690
4287
  const actionCount = updatesAvailable.length + (existingVersion !== VERSION ? 1 : 0);
@@ -4703,9 +4300,9 @@ async function upgrade(options) {
4703
4300
  }
4704
4301
  }
4705
4302
  logger.section("Upgrading Configuration");
4706
- console.log(chalk6.cyan(`
4303
+ console.log(chalk5.cyan(`
4707
4304
  Current version: ${existingVersion}`));
4708
- console.log(chalk6.cyan(`New version: ${VERSION}
4305
+ console.log(chalk5.cyan(`New version: ${VERSION}
4709
4306
  `));
4710
4307
  const backupSpinner = ora5("Creating backup...").start();
4711
4308
  const backups = createBackups();
@@ -4719,7 +4316,7 @@ Current version: ${existingVersion}`));
4719
4316
  if (fileMigrationResult.stateFileMoved) moved.push("state file");
4720
4317
  if (fileMigrationResult.backupsMoved > 0)
4721
4318
  moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
4722
- console.log(chalk6.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
4319
+ console.log(chalk5.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
4723
4320
  }
4724
4321
  const migrationSpinner = ora5("Applying migrations...").start();
4725
4322
  const migrationResult = migrateConfigs(
@@ -4731,15 +4328,15 @@ Current version: ${existingVersion}`));
4731
4328
  if (migrationResult.migrationsApplied.length > 0) {
4732
4329
  migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
4733
4330
  for (const migration of migrationResult.migrationsApplied) {
4734
- console.log(chalk6.gray(` \u2022 ${migration}`));
4331
+ console.log(chalk5.gray(` \u2022 ${migration}`));
4735
4332
  }
4736
4333
  } else {
4737
4334
  migrationSpinner.succeed("No migrations needed");
4738
4335
  }
4739
4336
  if (migrationResult.hasBreakingChanges && !options.yes) {
4740
- console.log(chalk6.yellow("\nBreaking changes detected:"));
4337
+ console.log(chalk5.yellow("\nBreaking changes detected:"));
4741
4338
  for (const warning of migrationResult.breakingChangeWarnings) {
4742
- console.log(chalk6.yellow(` \u26A0 ${warning}`));
4339
+ console.log(chalk5.yellow(` \u26A0 ${warning}`));
4743
4340
  }
4744
4341
  const continueUpgrade = await confirm({
4745
4342
  message: "Continue with upgrade despite breaking changes?",
@@ -4757,20 +4354,20 @@ Current version: ${existingVersion}`));
4757
4354
  const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
4758
4355
  logger.section("Preserved Configuration");
4759
4356
  if (existingSubscriptions) {
4760
- console.log(chalk6.bold("Subscriptions:"));
4357
+ console.log(chalk5.bold("Subscriptions:"));
4761
4358
  if (existingSubscriptions.hasClaude)
4762
- console.log(chalk6.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
4763
- if (existingSubscriptions.hasOpenAI) console.log(chalk6.green(" \u2713 OpenAI"));
4359
+ console.log(chalk5.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
4360
+ if (existingSubscriptions.hasOpenAI) console.log(chalk5.green(" \u2713 OpenAI"));
4764
4361
  if (existingSubscriptions.hasGoogle)
4765
- console.log(chalk6.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
4362
+ console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
4766
4363
  if (existingSubscriptions.hasGitHubCopilot)
4767
- console.log(chalk6.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
4364
+ console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
4768
4365
  }
4769
4366
  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}`));
4367
+ console.log(chalk5.bold("\nModel Assignments:"));
4368
+ console.log(chalk5.green(` \u2713 Marvin: ${existingModels.marvin}`));
4369
+ console.log(chalk5.green(` \u2713 Oracle: ${existingModels.oracle}`));
4370
+ console.log(chalk5.green(` \u2713 Librarian: ${existingModels.librarian}`));
4774
4371
  }
4775
4372
  console.log();
4776
4373
  const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
@@ -4901,28 +4498,24 @@ Current version: ${existingVersion}`));
4901
4498
  }
4902
4499
  logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
4903
4500
  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}`));
4501
+ console.log(chalk5.gray("\nBackups saved:"));
4502
+ if (backups.sdlcBackup) console.log(chalk5.gray(` \u2022 ${backups.sdlcBackup}`));
4503
+ if (backups.omoBackup) console.log(chalk5.gray(` \u2022 ${backups.omoBackup}`));
4504
+ if (backups.opencodeBackup) console.log(chalk5.gray(` \u2022 ${backups.opencodeBackup}`));
4908
4505
  }
4909
- console.log(chalk6.gray("\nRestart OpenCode to use the upgraded configuration."));
4506
+ console.log(chalk5.gray("\nRestart OpenCode to use the upgraded configuration."));
4910
4507
  console.log();
4911
4508
  }
4912
4509
 
4913
4510
  // src/cli/index.ts
4914
4511
  var program = new Command();
4915
4512
  program.name("opencode-sdlc").description(
4916
- `${chalk6.cyan(DISPLAY_NAME)} - ${TAGLINE}
4513
+ `${chalk5.cyan(DISPLAY_NAME)} - ${TAGLINE}
4917
4514
  TDD-driven development toolkit with GitHub Issues integration for OpenCode`
4918
4515
  ).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();
4516
+ 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) => {
4517
+ if (options.listProfiles) {
4518
+ displayProfiles();
4926
4519
  return;
4927
4520
  }
4928
4521
  await install(options);
@@ -4934,16 +4527,16 @@ program.command("info").description("Show current configuration and status").act
4934
4527
  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
4528
  await providers({ action });
4936
4529
  });
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}`));
4530
+ function displayProfiles() {
4531
+ console.log(chalk5.bold.cyan("\nAvailable Development Profiles:\n"));
4532
+ const profiles = listProfiles();
4533
+ for (const profile of profiles) {
4534
+ console.log(chalk5.bold(` ${profile.name}`));
4535
+ console.log(chalk5.gray(` ${profile.description}`));
4943
4536
  console.log();
4944
4537
  }
4945
- console.log(chalk6.gray("Usage: opencode-sdlc install --preset <name>"));
4946
- console.log(chalk6.gray(" opencode-sdlc install --preset standard --yes\n"));
4538
+ console.log(chalk5.gray("Usage: opencode-sdlc install"));
4539
+ console.log(chalk5.gray(" opencode-sdlc install --yes (uses Event Modeling profile)\n"));
4947
4540
  }
4948
4541
  program.parse();
4949
4542
  //# sourceMappingURL=index.js.map