opencode-sdlc-plugin 0.3.2 → 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.
Files changed (48) hide show
  1. package/README.md +90 -17
  2. package/config/presets/event-modeling.json +19 -8
  3. package/config/presets/minimal.json +29 -16
  4. package/config/presets/standard.json +19 -8
  5. package/config/schemas/athena.schema.json +4 -4
  6. package/config/schemas/sdlc.schema.json +101 -5
  7. package/dist/cli/index.js +1431 -1336
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/index.d.ts +428 -66
  10. package/dist/index.js +6262 -2440
  11. package/dist/index.js.map +1 -1
  12. package/dist/plugin/index.js +5793 -2010
  13. package/dist/plugin/index.js.map +1 -1
  14. package/package.json +2 -1
  15. package/prompts/agents/adr.md +234 -0
  16. package/prompts/agents/architect.md +204 -0
  17. package/prompts/agents/design-facilitator.md +237 -0
  18. package/prompts/agents/discovery.md +260 -0
  19. package/prompts/agents/domain.md +148 -34
  20. package/prompts/agents/file-updater.md +132 -0
  21. package/prompts/agents/green.md +119 -40
  22. package/prompts/agents/gwt.md +352 -0
  23. package/prompts/agents/model-checker.md +332 -0
  24. package/prompts/agents/red.md +112 -21
  25. package/prompts/agents/story.md +196 -0
  26. package/prompts/agents/ux.md +239 -0
  27. package/prompts/agents/workflow-designer.md +386 -0
  28. package/prompts/modes/architect.md +219 -0
  29. package/prompts/modes/build.md +150 -0
  30. package/prompts/modes/model.md +211 -0
  31. package/prompts/modes/plan.md +186 -0
  32. package/prompts/modes/pm.md +269 -0
  33. package/prompts/modes/prd.md +238 -0
  34. package/commands/sdlc-adr.md +0 -265
  35. package/commands/sdlc-debug.md +0 -376
  36. package/commands/sdlc-design.md +0 -246
  37. package/commands/sdlc-dev.md +0 -544
  38. package/commands/sdlc-info.md +0 -325
  39. package/commands/sdlc-parallel.md +0 -283
  40. package/commands/sdlc-recall.md +0 -213
  41. package/commands/sdlc-remember.md +0 -136
  42. package/commands/sdlc-research.md +0 -343
  43. package/commands/sdlc-review.md +0 -265
  44. package/commands/sdlc-status.md +0 -297
  45. package/config/presets/copilot-only.json +0 -69
  46. package/config/presets/enterprise.json +0 -79
  47. package/config/presets/solo-quick.json +0 -70
  48. package/config/presets/strict-tdd.json +0 -79
package/dist/cli/index.js CHANGED
@@ -1,452 +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 chalk4 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
- import { mkdir, writeFile, readdir, copyFile, rm } from 'fs/promises';
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
- sisyphus: [
126
- "anthropic/claude-sonnet-4-5-thinking",
127
- "anthropic/claude-opus-4-5-thinking",
128
- "openai/gpt-5.1-high",
129
- "google/gemini-2.5-pro",
130
- "github-copilot/claude-sonnet-4.5",
131
- "github-copilot/gpt-5.1",
132
- "github-copilot/gemini-2.5-pro"
133
- ],
134
- oracle: [
135
- "openai/gpt-5.1-high",
136
- "anthropic/claude-opus-4-5-thinking",
137
- "anthropic/claude-sonnet-4-5-thinking",
138
- "google/gemini-2.5-pro",
139
- "github-copilot/gpt-5.1",
140
- "github-copilot/claude-opus-4.5",
141
- "github-copilot/claude-sonnet-4.5"
142
- ],
143
- librarian: [
144
- "anthropic/claude-sonnet-4-5",
145
- "openai/gpt-4o",
146
- "google/gemini-2.5-flash",
147
- "github-copilot/claude-haiku-4.5",
148
- "github-copilot/gpt-5-mini"
149
- ],
150
- frontend: [
151
- "anthropic/claude-sonnet-4-5",
152
- "google/gemini-2.5-pro",
153
- "openai/gpt-4o",
154
- "github-copilot/claude-sonnet-4.5",
155
- "github-copilot/gemini-2.5-pro"
156
- ],
157
- documentWriter: [
158
- "google/gemini-2.5-pro",
159
- "anthropic/claude-sonnet-4-5",
160
- "openai/gpt-4o",
161
- "github-copilot/gemini-2.5-pro",
162
- "github-copilot/claude-sonnet-4.5"
163
- ],
164
- multimodalLooker: [
165
- "google/gemini-2.5-flash",
166
- "openai/gpt-4o",
167
- "anthropic/claude-sonnet-4-5",
168
- "github-copilot/gemini-3-flash",
169
- "github-copilot/gpt-5-mini"
170
- ],
171
- explore: [
172
- "google/gemini-2.5-flash",
173
- "anthropic/claude-sonnet-4-5",
174
- "openai/gpt-4o",
175
- "github-copilot/claude-haiku-4.5",
176
- "github-copilot/gpt-5-mini",
177
- "github-copilot/gemini-3-flash"
178
- ]
179
- };
180
- const roleDefaults = suggestions[role] || [];
181
- const availableIds = availableModels.map((m) => m.id);
182
- for (const modelId of roleDefaults) {
183
- if (availableIds.includes(modelId)) {
184
- return modelId;
185
- }
186
- }
187
- return availableModels[0]?.id;
188
- }
189
- async function gatherModels(subscriptions, defaults, customModels) {
190
- const availableModels = getAvailableModels(subscriptions, customModels);
191
- if (availableModels.length === 0) {
192
- throw new Error(
193
- "No models available. Please enable at least one provider (Claude, OpenAI, Google, or GitHub Copilot)."
194
- );
195
- }
196
- const choices = createModelChoices(availableModels);
197
- const sisyphusDefault = getValidModelOrFallback(
198
- defaults?.sisyphus,
199
- "sisyphus",
200
- subscriptions,
201
- availableModels
202
- );
203
- const oracleDefault = getValidModelOrFallback(
204
- defaults?.oracle,
205
- "oracle",
206
- subscriptions,
207
- availableModels
208
- );
209
- const librarianDefault = getValidModelOrFallback(
210
- defaults?.librarian,
211
- "librarian",
212
- subscriptions,
213
- availableModels
214
- );
215
- const sisyphus = await select({
216
- message: "Model for Sisyphus (main orchestrator - implements stories)?",
217
- choices,
218
- default: sisyphusDefault
219
- });
220
- const oracle = await select({
221
- message: "Model for Oracle (debugging and complex reasoning)?",
222
- choices,
223
- default: oracleDefault
224
- });
225
- const librarian = await select({
226
- message: "Model for Librarian (research and documentation lookup)?",
227
- choices,
228
- default: librarianDefault
229
- });
230
- const frontend = getValidModelOrFallback(
231
- defaults?.frontend,
232
- "frontend",
233
- subscriptions,
234
- availableModels
235
- );
236
- const documentWriter = getValidModelOrFallback(
237
- defaults?.documentWriter,
238
- "documentWriter",
239
- subscriptions,
240
- availableModels
241
- );
242
- const multimodalLooker = getValidModelOrFallback(
243
- defaults?.multimodalLooker,
244
- "multimodalLooker",
245
- subscriptions,
246
- availableModels
247
- );
248
- return {
249
- sisyphus,
250
- oracle,
251
- librarian,
252
- frontend,
253
- documentWriter,
254
- multimodalLooker
255
- };
256
- }
257
- function getModelList(subscriptions, customModels) {
258
- return getAvailableModels(subscriptions, customModels);
259
- }
260
- function validatePresetModels(presetModels, subscriptions, customModels) {
261
- const warnings = [];
262
- const availableModels = getAvailableModels(subscriptions, customModels);
263
- const checkModel = (model, role) => {
264
- if (model && !isModelAvailable(model, availableModels)) {
265
- warnings.push(
266
- `Preset model for ${role} (${model}) is not available with your subscriptions. A fallback will be used.`
267
- );
268
- }
269
- };
270
- checkModel(presetModels.sisyphus, "Sisyphus");
271
- checkModel(presetModels.oracle, "Oracle");
272
- checkModel(presetModels.librarian, "Librarian");
273
- checkModel(presetModels.frontend, "Frontend");
274
- checkModel(presetModels.documentWriter, "Document Writer");
275
- checkModel(presetModels.multimodalLooker, "Multimodal Looker");
276
- return warnings;
277
- }
278
- var AVAILABLE_MODELS, COPILOT_FREE_MODELS, COPILOT_OPUS_MODELS;
279
- var init_models = __esm({
280
- "src/cli/questions/models.ts"() {
281
- init_esm_shims();
282
- init_debug_logger();
283
- AVAILABLE_MODELS = [
284
- // Anthropic models
285
- {
286
- id: "anthropic/claude-sonnet-4-5",
287
- name: "Claude Sonnet 4.5",
288
- provider: "anthropic",
289
- description: "Latest Sonnet - balanced performance and speed"
290
- },
291
- {
292
- id: "anthropic/claude-opus-4-5",
293
- name: "Claude Opus 4.5",
294
- provider: "anthropic",
295
- description: "Most capable Claude model"
296
- },
297
- {
298
- id: "anthropic/claude-sonnet-4-5-thinking",
299
- name: "Claude Sonnet 4.5 (Thinking)",
300
- provider: "anthropic",
301
- description: "Sonnet with extended thinking enabled"
302
- },
303
- {
304
- id: "anthropic/claude-opus-4-5-thinking",
305
- name: "Claude Opus 4.5 (Thinking)",
306
- provider: "anthropic",
307
- description: "Opus with extended thinking enabled"
308
- },
309
- // OpenAI models
310
- {
311
- id: "openai/gpt-4o",
312
- name: "GPT-4o",
313
- provider: "openai",
314
- description: "Fast multimodal model"
315
- },
316
- {
317
- id: "openai/gpt-5.1",
318
- name: "GPT-5.1",
319
- provider: "openai",
320
- description: "Latest GPT model"
321
- },
322
- {
323
- id: "openai/gpt-5.1-high",
324
- name: "GPT-5.1 High",
325
- provider: "openai",
326
- description: "GPT-5.1 with high reasoning effort"
327
- },
328
- // Google models
329
- {
330
- id: "google/gemini-2.5-pro",
331
- name: "Gemini 2.5 Pro",
332
- provider: "google",
333
- description: "Latest Gemini Pro model"
334
- },
335
- {
336
- id: "google/gemini-2.5-flash",
337
- name: "Gemini 2.5 Flash",
338
- provider: "google",
339
- description: "Fast Gemini model"
340
- },
341
- {
342
- id: "google/gemini-2.0-flash",
343
- name: "Gemini 2.0 Flash",
344
- provider: "google",
345
- description: "Previous generation fast model"
346
- },
347
- // GitHub Copilot models (routed through Copilot - smaller context, no thinking)
348
- // Free tier models
349
- {
350
- id: "github-copilot/gpt-4.1",
351
- name: "GPT-4.1 (via Copilot)",
352
- provider: "github-copilot",
353
- description: "GPT-4.1 through GitHub Copilot"
354
- },
355
- {
356
- id: "github-copilot/gpt-5-mini",
357
- name: "GPT-5 mini (via Copilot)",
358
- provider: "github-copilot",
359
- description: "Fast GPT-5 variant through GitHub Copilot"
360
- },
361
- {
362
- id: "github-copilot/claude-haiku-4.5",
363
- name: "Claude Haiku 4.5 (via Copilot)",
364
- provider: "github-copilot",
365
- description: "Fast Claude model through GitHub Copilot"
366
- },
367
- // Pro/Business/Enterprise models
368
- {
369
- id: "github-copilot/claude-sonnet-4",
370
- name: "Claude Sonnet 4 (via Copilot)",
371
- provider: "github-copilot",
372
- description: "Claude Sonnet 4 through GitHub Copilot"
373
- },
374
- {
375
- id: "github-copilot/claude-sonnet-4.5",
376
- name: "Claude Sonnet 4.5 (via Copilot)",
377
- provider: "github-copilot",
378
- description: "Latest Sonnet through GitHub Copilot - no thinking mode"
379
- },
380
- {
381
- id: "github-copilot/gpt-5",
382
- name: "GPT-5 (via Copilot)",
383
- provider: "github-copilot",
384
- description: "GPT-5 through GitHub Copilot"
385
- },
386
- {
387
- id: "github-copilot/gpt-5.1",
388
- name: "GPT-5.1 (via Copilot)",
389
- provider: "github-copilot",
390
- description: "GPT-5.1 through GitHub Copilot"
391
- },
392
- {
393
- id: "github-copilot/gpt-5.1-codex",
394
- name: "GPT-5.1-Codex (via Copilot)",
395
- provider: "github-copilot",
396
- description: "Code-optimized GPT-5.1 through GitHub Copilot"
397
- },
398
- {
399
- id: "github-copilot/gpt-5.2",
400
- name: "GPT-5.2 (via Copilot)",
401
- provider: "github-copilot",
402
- description: "Latest GPT through GitHub Copilot"
403
- },
404
- {
405
- id: "github-copilot/gemini-2.5-pro",
406
- name: "Gemini 2.5 Pro (via Copilot)",
407
- provider: "github-copilot",
408
- description: "Gemini 2.5 Pro through GitHub Copilot"
409
- },
410
- {
411
- id: "github-copilot/gemini-3-flash",
412
- name: "Gemini 3 Flash (via Copilot)",
413
- provider: "github-copilot",
414
- description: "Fast Gemini 3 through GitHub Copilot"
415
- },
416
- {
417
- id: "github-copilot/gemini-3-pro",
418
- name: "Gemini 3 Pro (via Copilot)",
419
- provider: "github-copilot",
420
- description: "Gemini 3 Pro through GitHub Copilot"
421
- },
422
- // Pro+/Enterprise only (Opus models)
423
- {
424
- id: "github-copilot/claude-opus-4.1",
425
- name: "Claude Opus 4.1 (via Copilot)",
426
- provider: "github-copilot",
427
- description: "Claude Opus 4.1 through GitHub Copilot - Pro+/Enterprise only"
428
- },
429
- {
430
- id: "github-copilot/claude-opus-4.5",
431
- name: "Claude Opus 4.5 (via Copilot)",
432
- provider: "github-copilot",
433
- description: "Most capable Claude through GitHub Copilot - Pro+/Enterprise only"
434
- }
435
- ];
436
- COPILOT_FREE_MODELS = [
437
- "github-copilot/gpt-4.1",
438
- "github-copilot/gpt-5-mini",
439
- "github-copilot/claude-haiku-4.5"
440
- ];
441
- COPILOT_OPUS_MODELS = ["github-copilot/claude-opus-4.1", "github-copilot/claude-opus-4.5"];
442
- }
443
- });
444
-
445
- // src/cli/index.ts
446
- init_esm_shims();
447
-
448
- // src/shared/constants.ts
449
- init_esm_shims();
450
18
  function getPackageVersion() {
451
19
  try {
452
20
  const currentDir = dirname(fileURLToPath(import.meta.url));
@@ -497,21 +65,15 @@ var MIN_VERSIONS = {
497
65
  node: "20.0.0",
498
66
  opencode: "1.0.132"
499
67
  };
500
-
501
- // src/cli/commands/doctor.ts
502
- init_esm_shims();
503
-
504
- // src/cli/utils/file-manager.ts
505
- init_esm_shims();
506
68
  var execAsync = promisify(exec);
507
69
  function getPackageRoot() {
508
70
  const currentFileDir = dirname(fileURLToPath(import.meta.url));
509
71
  const bundledRoot = join(currentFileDir, "..", "..");
510
- if (existsSync(join(bundledRoot, "commands"))) {
72
+ if (existsSync(join(bundledRoot, "prompts"))) {
511
73
  return bundledRoot;
512
74
  }
513
75
  const devRoot = join(currentFileDir, "..", "..", "..");
514
- if (existsSync(join(devRoot, "commands"))) {
76
+ if (existsSync(join(devRoot, "prompts"))) {
515
77
  return devRoot;
516
78
  }
517
79
  return bundledRoot;
@@ -548,12 +110,12 @@ var FileManager = class {
548
110
  /**
549
111
  * Read a JSON configuration file
550
112
  */
551
- readJsonFile(path2) {
552
- if (!existsSync(path2)) {
113
+ readJsonFile(path) {
114
+ if (!existsSync(path)) {
553
115
  return null;
554
116
  }
555
117
  try {
556
- const content = readFileSync(path2, "utf-8");
118
+ const content = readFileSync(path, "utf-8");
557
119
  return JSON.parse(content);
558
120
  } catch {
559
121
  return null;
@@ -562,16 +124,16 @@ var FileManager = class {
562
124
  /**
563
125
  * Write a JSON configuration file
564
126
  */
565
- async writeJsonFile(path2, data) {
566
- const dir = dirname(path2);
127
+ async writeJsonFile(path, data) {
128
+ const dir = dirname(path);
567
129
  await this.ensureDir(dir);
568
- await writeFile(path2, JSON.stringify(data, null, 2), "utf-8");
130
+ await writeFile(path, JSON.stringify(data, null, 2), "utf-8");
569
131
  }
570
132
  /**
571
133
  * Check if a file exists
572
134
  */
573
- exists(path2) {
574
- return existsSync(path2);
135
+ exists(path) {
136
+ return existsSync(path);
575
137
  }
576
138
  /**
577
139
  * Install npm dependencies in the config directory
@@ -616,26 +178,73 @@ var FileManager = class {
616
178
  }
617
179
  /**
618
180
  * Copy bridge commands from package to config directory
181
+ *
182
+ * @deprecated Bridge commands have been removed in v1.0.
183
+ * This method now returns an empty array for backwards compatibility.
184
+ * Use mode-based workflow instead.
619
185
  */
620
186
  async copyCommands() {
621
- const commandsDir = CONFIG_PATHS.commandsDir;
622
- await this.ensureDir(commandsDir);
187
+ return [];
188
+ }
189
+ /**
190
+ * Copy Marvin mode agent files to project .opencode/agent directory
191
+ *
192
+ * @param projectDir - The project directory to copy to
193
+ * @param enabledModes - Array of enabled modes to copy
194
+ * @returns Array of copied file names
195
+ */
196
+ async copyAgentModes(projectDir, enabledModes) {
197
+ const agentDir = join(projectDir, ".opencode", "agent");
198
+ await this.ensureDir(agentDir);
623
199
  const packageRoot = getPackageRoot();
624
- const sourceCommandsDir = join(packageRoot, "commands");
200
+ const sourceModesDir = join(packageRoot, "prompts", "modes");
625
201
  const copiedFiles = [];
626
- if (existsSync(sourceCommandsDir)) {
627
- const files = await readdir(sourceCommandsDir);
628
- for (const file of files) {
629
- if (file.endsWith(".md")) {
630
- const sourcePath = join(sourceCommandsDir, file);
631
- const destPath = join(commandsDir, file);
202
+ const modeFileMap = {
203
+ build: "build.md",
204
+ discover: "plan.md",
205
+ // Discover mode overrides Plan agent
206
+ model: "model.md",
207
+ prd: "prd.md",
208
+ architect: "architect.md",
209
+ pm: "pm.md"
210
+ };
211
+ if (existsSync(sourceModesDir)) {
212
+ for (const mode of enabledModes) {
213
+ const sourceFile = modeFileMap[mode];
214
+ if (!sourceFile) continue;
215
+ const sourcePath = join(sourceModesDir, sourceFile);
216
+ if (existsSync(sourcePath)) {
217
+ const destPath = join(agentDir, sourceFile);
632
218
  await copyFile(sourcePath, destPath);
633
- copiedFiles.push(file);
219
+ copiedFiles.push(sourceFile);
634
220
  }
635
221
  }
636
222
  }
637
223
  return copiedFiles;
638
224
  }
225
+ /**
226
+ * Remove agent mode files from project .opencode/agent directory
227
+ *
228
+ * @param projectDir - The project directory to remove from
229
+ * @returns Array of removed file names
230
+ */
231
+ async removeAgentModes(projectDir) {
232
+ const agentDir = join(projectDir, ".opencode", "agent");
233
+ const removedFiles = [];
234
+ if (!existsSync(agentDir)) {
235
+ return removedFiles;
236
+ }
237
+ const modeFiles = ["build.md", "plan.md", "model.md", "prd.md", "architect.md", "pm.md"];
238
+ const files = await readdir(agentDir);
239
+ for (const file of files) {
240
+ if (modeFiles.includes(file)) {
241
+ const filePath = join(agentDir, file);
242
+ await rm(filePath);
243
+ removedFiles.push(file);
244
+ }
245
+ }
246
+ return removedFiles;
247
+ }
639
248
  /**
640
249
  * Remove bridge commands from config directory
641
250
  */
@@ -700,12 +309,12 @@ var FileManager = class {
700
309
  /**
701
310
  * Backup a file before modifying
702
311
  */
703
- async backupFile(path2) {
704
- if (!existsSync(path2)) {
312
+ async backupFile(path) {
313
+ if (!existsSync(path)) {
705
314
  return null;
706
315
  }
707
- const backupPath = `${path2}.backup`;
708
- await copyFile(path2, backupPath);
316
+ const backupPath = `${path}.backup`;
317
+ await copyFile(path, backupPath);
709
318
  return backupPath;
710
319
  }
711
320
  /**
@@ -719,47 +328,44 @@ var FileManager = class {
719
328
  }
720
329
  }
721
330
  };
722
-
723
- // src/cli/utils/logger.ts
724
- init_esm_shims();
725
331
  var logger = {
726
332
  /**
727
333
  * Log an informational message
728
334
  */
729
335
  info: (message) => {
730
- console.log(chalk4.blue("i"), message);
336
+ console.log(chalk5.blue("i"), message);
731
337
  },
732
338
  /**
733
339
  * Log a success message
734
340
  */
735
341
  success: (message) => {
736
- console.log(chalk4.green("\u2713"), message);
342
+ console.log(chalk5.green("\u2713"), message);
737
343
  },
738
344
  /**
739
345
  * Log a warning message
740
346
  */
741
347
  warn: (message) => {
742
- console.log(chalk4.yellow("!"), message);
348
+ console.log(chalk5.yellow("!"), message);
743
349
  },
744
350
  /**
745
351
  * Log an error message
746
352
  */
747
353
  error: (message) => {
748
- console.log(chalk4.red("\u2716"), message);
354
+ console.log(chalk5.red("\u2716"), message);
749
355
  },
750
356
  /**
751
357
  * Log a debug message (only when DEBUG env var is set)
752
358
  */
753
359
  debug: (message) => {
754
360
  if (process.env.DEBUG) {
755
- console.log(chalk4.gray("[debug]"), message);
361
+ console.log(chalk5.gray("[debug]"), message);
756
362
  }
757
363
  },
758
364
  /**
759
365
  * Log a step in a process
760
366
  */
761
367
  step: (step, total, message) => {
762
- console.log(chalk4.cyan(`[${step}/${total}]`), message);
368
+ console.log(chalk5.cyan(`[${step}/${total}]`), message);
763
369
  },
764
370
  /**
765
371
  * Log a blank line
@@ -772,7 +378,7 @@ var logger = {
772
378
  */
773
379
  section: (title) => {
774
380
  console.log();
775
- console.log(chalk4.bold(title));
381
+ console.log(chalk5.bold(title));
776
382
  console.log();
777
383
  },
778
384
  /**
@@ -780,21 +386,21 @@ var logger = {
780
386
  */
781
387
  keyValue: (key, value, indent = 0) => {
782
388
  const padding = " ".repeat(indent);
783
- console.log(`${padding}${chalk4.gray(`${key}:`)} ${value}`);
389
+ console.log(`${padding}${chalk5.gray(`${key}:`)} ${value}`);
784
390
  },
785
391
  /**
786
392
  * Log a list item
787
393
  */
788
394
  listItem: (item, indent = 0) => {
789
395
  const padding = " ".repeat(indent);
790
- console.log(`${padding}${chalk4.gray("-")} ${item}`);
396
+ console.log(`${padding}${chalk5.gray("-")} ${item}`);
791
397
  },
792
398
  /**
793
399
  * Display the Sdlc banner
794
400
  */
795
401
  banner: () => {
796
402
  console.log(
797
- chalk4.cyan(`
403
+ chalk5.cyan(`
798
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
799
405
  \u2551 OPENCODE ATHENA \u2551
800
406
  \u2551 Strategic Wisdom Meets Practical Execution \u2551
@@ -810,7 +416,7 @@ var logger = {
810
416
  successBanner: (message) => {
811
417
  const line = "\u2550".repeat(message.length + 4);
812
418
  console.log(
813
- chalk4.green(`
419
+ chalk5.green(`
814
420
  \u2554${line}\u2557
815
421
  \u2551 ${message} \u2551
816
422
  \u255A${line}\u255D
@@ -818,9 +424,6 @@ var logger = {
818
424
  );
819
425
  }
820
426
  };
821
-
822
- // src/cli/utils/prerequisites.ts
823
- init_esm_shims();
824
427
  var execAsync2 = promisify(exec);
825
428
  function parseVersion(version) {
826
429
  const match = version.match(/^v?(\d+)\.(\d+)\.(\d+)/);
@@ -917,12 +520,12 @@ async function getInstalledPlugins() {
917
520
  return {};
918
521
  }
919
522
  }
920
- function validateJsonFile(path2) {
921
- if (!existsSync(path2)) {
523
+ function validateJsonFile(path) {
524
+ if (!existsSync(path)) {
922
525
  return { valid: false, error: "File does not exist" };
923
526
  }
924
527
  try {
925
- const content = readFileSync(path2, "utf-8");
528
+ const content = readFileSync(path, "utf-8");
926
529
  JSON.parse(content);
927
530
  return { valid: true };
928
531
  } catch (err) {
@@ -932,19 +535,16 @@ function validateJsonFile(path2) {
932
535
  };
933
536
  }
934
537
  }
935
-
936
- // src/cli/utils/validators.ts
937
- init_esm_shims();
938
-
939
- // src/shared/schemas.ts
940
- init_esm_shims();
538
+ var ProviderAuthMethodSchema = z.enum(["subscription", "api", "none"]);
941
539
  var SubscriptionSchema = z.object({
942
540
  claude: z.object({
943
541
  enabled: z.boolean(),
542
+ authMethod: ProviderAuthMethodSchema.default("subscription"),
944
543
  tier: z.enum(["max5x", "max20x", "pro", "none"])
945
544
  }),
946
545
  openai: z.object({
947
- enabled: z.boolean()
546
+ enabled: z.boolean(),
547
+ authMethod: ProviderAuthMethodSchema.default("subscription")
948
548
  }),
949
549
  google: z.object({
950
550
  enabled: z.boolean(),
@@ -956,6 +556,17 @@ var SubscriptionSchema = z.object({
956
556
  enabledModels: z.array(z.string()).optional()
957
557
  })
958
558
  });
559
+ var SdlcModeSchema = z.enum(["discover", "model", "architect", "pm", "build"]);
560
+ var ModesConfigSchema = z.object({
561
+ default: SdlcModeSchema.default("build").describe("Default mode on startup"),
562
+ eventModeling: z.boolean().default(true).describe("true=Model mode, false=PRD mode"),
563
+ enabled: z.array(SdlcModeSchema).default(["discover", "model", "architect", "pm", "build"]).describe("Array of enabled modes")
564
+ });
565
+ var MemoryBackendSchema = z.enum(["memento", "stateless"]);
566
+ var MemoryConfigSchema = z.object({
567
+ backend: MemoryBackendSchema.default("stateless").describe("Memory backend type"),
568
+ checkpointTtl: z.number().int().positive().default(86400).describe("How long to keep checkpoints in seconds")
569
+ });
959
570
  var GitHubStatusSchema = z.enum(["Backlog", "Ready", "In progress", "In review", "Done"]);
960
571
  var GitHubConfigSchema = z.object({
961
572
  owner: z.string().describe("GitHub repository owner (user or org)"),
@@ -975,7 +586,11 @@ var TddConfigSchema = z.object({
975
586
  mutationTesting: MutationTestingConfigSchema.default({
976
587
  enabled: false,
977
588
  requiredScore: 80
978
- })
589
+ }),
590
+ // New v1.0.0 fields
591
+ domainVetoEnabled: z.boolean().default(true).describe("Enable domain agent veto power over type changes"),
592
+ debateRounds: z.number().int().min(1).max(5).default(2).describe("Max debate rounds before escalation to user"),
593
+ requireVerification: z.boolean().default(true).describe("Require test output evidence before phase transitions")
979
594
  });
980
595
  var EventModelingConfigSchema = z.object({
981
596
  enabled: z.boolean().default(false).describe("Enable Event Modeling workflow"),
@@ -1040,7 +655,7 @@ var RoutingConfigSchema = z.object({
1040
655
  gemini: z.array(LLMProviderSchema).optional()
1041
656
  }),
1042
657
  agentOverrides: z.object({
1043
- sisyphus: AgentRoutingSchema.optional(),
658
+ marvin: AgentRoutingSchema.optional(),
1044
659
  oracle: AgentRoutingSchema.optional(),
1045
660
  librarian: AgentRoutingSchema.optional(),
1046
661
  frontend: AgentRoutingSchema.optional(),
@@ -1051,7 +666,9 @@ var RoutingConfigSchema = z.object({
1051
666
  autoFallback: z.boolean(),
1052
667
  retryPeriodMs: z.number().min(0),
1053
668
  notifyOnRateLimit: z.boolean()
1054
- })
669
+ }),
670
+ // New v1.0.0 field
671
+ classifierModel: z.string().default("anthropic/claude-haiku").describe("Model for sdlc_classify_request tool (use cheapest available)")
1055
672
  });
1056
673
  var ThinkingLevelSchema = z.enum(["off", "low", "medium", "high"]);
1057
674
  var AgentSettingsSchema = z.object({
@@ -1069,15 +686,75 @@ var CustomModelDefinitionSchema = z.object({
1069
686
  supportsTemperature: z.boolean().optional()
1070
687
  }).optional()
1071
688
  });
689
+ var SubagentModelSchema = z.union([
690
+ z.string().describe("Full model identifier (e.g., 'anthropic/claude-sonnet-4-20250514')"),
691
+ z.literal("inherit").describe("Inherit model from marvin")
692
+ ]);
693
+ z.enum([
694
+ // TDD agents
695
+ "red",
696
+ "green",
697
+ "domain",
698
+ "refactor",
699
+ // Event modeling agents
700
+ "discovery",
701
+ "workflow",
702
+ "gwt",
703
+ "model-checker",
704
+ // Architecture agents
705
+ "architect",
706
+ "adr",
707
+ "design-facilitator",
708
+ // Planning agents
709
+ "story",
710
+ "pm",
711
+ "analyst",
712
+ // Review agents
713
+ "reviewer",
714
+ "ux",
715
+ "mutation"
716
+ ]);
717
+ var SubagentModelsSchema = z.object({
718
+ // TDD agents
719
+ red: SubagentModelSchema.optional().describe("Model for TDD red phase agent"),
720
+ green: SubagentModelSchema.optional().describe("Model for TDD green phase agent"),
721
+ domain: SubagentModelSchema.optional().describe("Model for domain modeling agent"),
722
+ refactor: SubagentModelSchema.optional().describe("Model for refactoring agent"),
723
+ // Event modeling agents
724
+ discovery: SubagentModelSchema.optional().describe(
725
+ "Model for discovery/domain exploration agent"
726
+ ),
727
+ workflow: SubagentModelSchema.optional().describe("Model for workflow design agent"),
728
+ gwt: SubagentModelSchema.optional().describe("Model for GWT specification agent"),
729
+ "model-checker": SubagentModelSchema.optional().describe(
730
+ "Model for event model validation agent"
731
+ ),
732
+ // Architecture agents
733
+ architect: SubagentModelSchema.optional().describe("Model for architect agent"),
734
+ adr: SubagentModelSchema.optional().describe("Model for ADR creation agent"),
735
+ "design-facilitator": SubagentModelSchema.optional().describe(
736
+ "Model for design facilitation agent"
737
+ ),
738
+ // Planning agents
739
+ story: SubagentModelSchema.optional().describe("Model for story/issue planning agent"),
740
+ pm: SubagentModelSchema.optional().describe("Model for PM agent"),
741
+ analyst: SubagentModelSchema.optional().describe("Model for business analyst agent"),
742
+ // Review agents
743
+ reviewer: SubagentModelSchema.optional().describe("Model for code reviewer agent"),
744
+ ux: SubagentModelSchema.optional().describe("Model for UX review agent"),
745
+ mutation: SubagentModelSchema.optional().describe("Model for mutation testing agent")
746
+ });
1072
747
  var ModelsSchema = z.object({
1073
- sisyphus: z.string().describe("Model for main orchestrator agent"),
748
+ marvin: z.string().describe("Model for main orchestrator agent"),
1074
749
  oracle: z.string().describe("Model for debugging/reasoning agent"),
1075
750
  librarian: z.string().describe("Model for research/documentation agent"),
1076
751
  frontend: z.string().optional().describe("Model for UI/UX agent"),
1077
752
  documentWriter: z.string().optional().describe("Model for documentation generation agent"),
1078
753
  multimodalLooker: z.string().optional().describe("Model for image analysis agent"),
754
+ // New v1.0.0 field for subagent models
755
+ subagents: SubagentModelsSchema.optional().describe("Per-subagent model overrides"),
1079
756
  settings: z.object({
1080
- sisyphus: AgentSettingsSchema.optional(),
757
+ marvin: AgentSettingsSchema.optional(),
1081
758
  oracle: AgentSettingsSchema.optional(),
1082
759
  librarian: AgentSettingsSchema.optional(),
1083
760
  frontend: AgentSettingsSchema.optional(),
@@ -1092,6 +769,9 @@ var SdlcConfigSchema = z.object({
1092
769
  version: z.string(),
1093
770
  subscriptions: SubscriptionSchema,
1094
771
  models: ModelsSchema,
772
+ // New v1.0.0 configuration
773
+ modes: ModesConfigSchema.optional(),
774
+ memory: MemoryConfigSchema.optional(),
1095
775
  // New v0.3.0+ configuration (optional during migration)
1096
776
  github: GitHubConfigSchema.optional(),
1097
777
  tdd: TddConfigSchema.optional(),
@@ -1138,15 +818,15 @@ function validateSdlcConfig(config) {
1138
818
  }
1139
819
  return result;
1140
820
  }
1141
- function validateJsonConfig(path2) {
821
+ function validateJsonConfig(path) {
1142
822
  const result = { valid: true, errors: [], warnings: [] };
1143
- if (!existsSync(path2)) {
823
+ if (!existsSync(path)) {
1144
824
  result.valid = false;
1145
825
  result.errors.push("File does not exist");
1146
826
  return result;
1147
827
  }
1148
828
  try {
1149
- const content = readFileSync(path2, "utf-8");
829
+ const content = readFileSync(path, "utf-8");
1150
830
  JSON.parse(content);
1151
831
  } catch (err) {
1152
832
  result.valid = false;
@@ -1278,23 +958,6 @@ async function doctor(options) {
1278
958
  }
1279
959
  }
1280
960
  });
1281
- const commandsDirExists = fileManager.exists(CONFIG_PATHS.commandsDir);
1282
- results.push({
1283
- name: "Commands Directory",
1284
- status: commandsDirExists ? "pass" : "warn",
1285
- message: commandsDirExists ? "Exists" : "Not found",
1286
- fix: async () => {
1287
- const spinner = ora5("Creating commands directory...").start();
1288
- try {
1289
- await fileManager.ensureDir(CONFIG_PATHS.commandsDir);
1290
- await fileManager.copyCommands();
1291
- spinner.succeed("Commands directory created and populated");
1292
- } catch (err) {
1293
- spinner.fail("Failed to create commands directory");
1294
- throw err;
1295
- }
1296
- }
1297
- });
1298
961
  if (sdlcConfigValid.valid) {
1299
962
  const config = fileManager.readJsonFile(CONFIG_PATHS.globalSdlcConfig);
1300
963
  const configVersion = config.version || "0.0.0";
@@ -1374,16 +1037,16 @@ async function doctor(options) {
1374
1037
  switch (result.status) {
1375
1038
  case "pass":
1376
1039
  icon = "\u2713";
1377
- color = chalk4.green;
1040
+ color = chalk5.green;
1378
1041
  break;
1379
1042
  case "warn":
1380
1043
  icon = "!";
1381
- color = chalk4.yellow;
1044
+ color = chalk5.yellow;
1382
1045
  hasWarnings = true;
1383
1046
  break;
1384
1047
  case "fail":
1385
1048
  icon = "\u2716";
1386
- color = chalk4.red;
1049
+ color = chalk5.red;
1387
1050
  hasFailures = true;
1388
1051
  if (result.fix) {
1389
1052
  fixableIssues.push(result);
@@ -1411,7 +1074,7 @@ async function doctor(options) {
1411
1074
  logger.blank();
1412
1075
  logger.info("Run 'opencode-sdlc doctor' again to verify fixes.");
1413
1076
  } else if (fixableIssues.length > 0) {
1414
- logger.info(`Run ${chalk4.cyan("opencode-sdlc doctor --fix")} to attempt automatic fixes.`);
1077
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc doctor --fix")} to attempt automatic fixes.`);
1415
1078
  }
1416
1079
  } else if (hasWarnings) {
1417
1080
  logger.warn("Some checks have warnings, but Sdlc should work.");
@@ -1419,41 +1082,38 @@ async function doctor(options) {
1419
1082
  logger.success("All checks passed! OpenCode SDLC is healthy.");
1420
1083
  }
1421
1084
  }
1422
-
1423
- // src/cli/commands/info.ts
1424
- init_esm_shims();
1425
1085
  async function info() {
1426
1086
  logger.banner();
1427
1087
  const fileManager = new FileManager();
1428
1088
  const sdlcConfig = fileManager.readJsonFile(CONFIG_PATHS.globalSdlcConfig);
1429
1089
  if (!sdlcConfig) {
1430
1090
  logger.warn("OpenCode SDLC is not installed.");
1431
- logger.info(`Run ${chalk4.cyan("opencode-sdlc install")} to get started.`);
1091
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to get started.`);
1432
1092
  return;
1433
1093
  }
1434
1094
  logger.section("Version Information");
1435
1095
  logger.keyValue("Sdlc Version", sdlcConfig.version || VERSION);
1436
1096
  logger.section("Prerequisites");
1437
1097
  const prereqs = await checkPrerequisites();
1438
- const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ? chalk4.green("\u2713") : chalk4.yellow("!") : chalk4.red("\u2716");
1098
+ const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ? chalk5.green("\u2713") : chalk5.yellow("!") : chalk5.red("\u2716");
1439
1099
  logger.keyValue("Node.js", `${nodeStatus} ${prereqs.node.version || "not found"}`);
1440
- const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ? chalk4.green("\u2713") : chalk4.yellow("!") : chalk4.red("\u2716");
1100
+ const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ? chalk5.green("\u2713") : chalk5.yellow("!") : chalk5.red("\u2716");
1441
1101
  logger.keyValue("OpenCode", `${opencodeStatus} ${prereqs.opencode.version || "not found"}`);
1442
1102
  logger.section("Configured Providers");
1443
- const claudeStatus = sdlcConfig.subscriptions.claude.enabled ? chalk4.green("enabled") : chalk4.gray("disabled");
1103
+ const claudeStatus = sdlcConfig.subscriptions.claude.enabled ? chalk5.green("enabled") : chalk5.gray("disabled");
1444
1104
  logger.keyValue(
1445
1105
  "Claude",
1446
1106
  `${claudeStatus}${sdlcConfig.subscriptions.claude.tier !== "none" ? ` (${sdlcConfig.subscriptions.claude.tier})` : ""}`
1447
1107
  );
1448
- const openaiStatus = sdlcConfig.subscriptions.openai.enabled ? chalk4.green("enabled") : chalk4.gray("disabled");
1108
+ const openaiStatus = sdlcConfig.subscriptions.openai.enabled ? chalk5.green("enabled") : chalk5.gray("disabled");
1449
1109
  logger.keyValue("OpenAI", openaiStatus);
1450
- const googleStatus = sdlcConfig.subscriptions.google.enabled ? chalk4.green("enabled") : chalk4.gray("disabled");
1110
+ const googleStatus = sdlcConfig.subscriptions.google.enabled ? chalk5.green("enabled") : chalk5.gray("disabled");
1451
1111
  logger.keyValue(
1452
1112
  "Google",
1453
1113
  `${googleStatus}${sdlcConfig.subscriptions.google.authMethod !== "none" ? ` (${sdlcConfig.subscriptions.google.authMethod})` : ""}`
1454
1114
  );
1455
1115
  logger.section("Agent Models");
1456
- logger.keyValue("Sisyphus", sdlcConfig.models.sisyphus);
1116
+ logger.keyValue("Marvin", sdlcConfig.models.marvin);
1457
1117
  logger.keyValue("Oracle", sdlcConfig.models.oracle);
1458
1118
  logger.keyValue("Librarian", sdlcConfig.models.librarian);
1459
1119
  if (sdlcConfig.models.frontend) {
@@ -1565,18 +1225,18 @@ async function info() {
1565
1225
  }
1566
1226
  ];
1567
1227
  for (const feature of featureList) {
1568
- const status = feature.enabled ? chalk4.green("on") : chalk4.gray("off");
1228
+ const status = feature.enabled ? chalk5.green("on") : chalk5.gray("off");
1569
1229
  logger.keyValue(feature.name, status);
1570
1230
  }
1571
1231
  logger.section("MCP Servers");
1572
1232
  const mcps = sdlcConfig.mcps;
1573
- logger.keyValue("context7", mcps.context7 ? chalk4.green("on") : chalk4.gray("off"));
1574
- logger.keyValue("exa", mcps.exa ? chalk4.green("on") : chalk4.gray("off"));
1575
- logger.keyValue("grep_app", mcps.grepApp ? chalk4.green("on") : chalk4.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"));
1576
1236
  if ("memento" in mcps) {
1577
1237
  logger.keyValue(
1578
1238
  "memento",
1579
- mcps.memento ? chalk4.green("on") : chalk4.gray("off")
1239
+ mcps.memento ? chalk5.green("on") : chalk5.gray("off")
1580
1240
  );
1581
1241
  }
1582
1242
  logger.section("Installed Plugins");
@@ -1591,21 +1251,10 @@ async function info() {
1591
1251
  logger.section("Configuration Paths");
1592
1252
  logger.keyValue("Config Dir", CONFIG_PATHS.globalConfigDir);
1593
1253
  logger.keyValue("Sdlc Config", CONFIG_PATHS.globalSdlcConfig);
1594
- logger.keyValue("Commands Dir", CONFIG_PATHS.commandsDir);
1595
1254
  console.log();
1596
1255
  }
1597
1256
 
1598
- // src/cli/commands/install.ts
1599
- init_esm_shims();
1600
-
1601
- // src/cli/generators/config-generator.ts
1602
- init_esm_shims();
1603
-
1604
- // src/cli/generators/omo-config.ts
1605
- init_esm_shims();
1606
-
1607
1257
  // src/plugin/utils/model-params.ts
1608
- init_esm_shims();
1609
1258
  var MODEL_FAMILY_BASE_TEMPS = {
1610
1259
  "claude-thinking": 0.3,
1611
1260
  claude: 0.2,
@@ -1616,7 +1265,7 @@ var MODEL_FAMILY_BASE_TEMPS = {
1616
1265
  };
1617
1266
  var ROLE_TEMP_ADJUSTMENTS = {
1618
1267
  oracle: -0.1,
1619
- sisyphus: 0,
1268
+ marvin: 0,
1620
1269
  librarian: 0.1,
1621
1270
  frontend: 0.2,
1622
1271
  documentWriter: 0.1,
@@ -1625,7 +1274,7 @@ var ROLE_TEMP_ADJUSTMENTS = {
1625
1274
  };
1626
1275
  var ROLE_DEFAULT_THINKING = {
1627
1276
  oracle: "high",
1628
- sisyphus: "medium",
1277
+ marvin: "medium",
1629
1278
  librarian: "low",
1630
1279
  frontend: "low",
1631
1280
  documentWriter: "low",
@@ -1788,10 +1437,12 @@ function buildMinimalConfig(answers) {
1788
1437
  subscriptions: {
1789
1438
  claude: {
1790
1439
  enabled: subscriptions.hasClaude,
1440
+ authMethod: subscriptions.claudeAuth || "none",
1791
1441
  tier: subscriptions.claudeTier || "none"
1792
1442
  },
1793
1443
  openai: {
1794
- enabled: subscriptions.hasOpenAI
1444
+ enabled: subscriptions.hasOpenAI,
1445
+ authMethod: subscriptions.openaiAuth || "none"
1795
1446
  },
1796
1447
  google: {
1797
1448
  enabled: subscriptions.hasGoogle,
@@ -1804,7 +1455,7 @@ function buildMinimalConfig(answers) {
1804
1455
  }
1805
1456
  },
1806
1457
  models: {
1807
- sisyphus: models.sisyphus,
1458
+ marvin: models.marvin,
1808
1459
  oracle: models.oracle,
1809
1460
  librarian: models.librarian,
1810
1461
  frontend: models.frontend,
@@ -1864,13 +1515,13 @@ function generateOmoConfig(answers) {
1864
1515
  omoConfig.google_auth = false;
1865
1516
  }
1866
1517
  const agentConfigs = [
1867
- { role: "sisyphus", omoName: "Sisyphus", modelId: models.sisyphus },
1518
+ { role: "marvin", omoName: "Marvin", modelId: models.marvin },
1868
1519
  { role: "oracle", omoName: "oracle", modelId: models.oracle },
1869
1520
  { role: "librarian", omoName: "librarian", modelId: models.librarian },
1870
1521
  {
1871
1522
  role: "frontend",
1872
1523
  omoName: "frontend-ui-ux-engineer",
1873
- modelId: models.frontend || models.sisyphus
1524
+ modelId: models.frontend || models.marvin
1874
1525
  },
1875
1526
  {
1876
1527
  role: "documentWriter",
@@ -1929,9 +1580,6 @@ function generateOmoConfig(answers) {
1929
1580
  }
1930
1581
  return omoConfig;
1931
1582
  }
1932
-
1933
- // src/cli/generators/opencode-config.ts
1934
- init_esm_shims();
1935
1583
  async function generateOpencodeConfig(answers, configDir) {
1936
1584
  const configPath = join(configDir, "opencode.json");
1937
1585
  let existingConfig = {};
@@ -2117,12 +1765,6 @@ function getRequiredPlugins(answers) {
2117
1765
  }
2118
1766
  return plugins;
2119
1767
  }
2120
-
2121
- // src/cli/generators/sdlc-config.ts
2122
- init_esm_shims();
2123
-
2124
- // src/cli/questions/features.ts
2125
- init_esm_shims();
2126
1768
  var AVAILABLE_FEATURES = [
2127
1769
  {
2128
1770
  name: "BMAD Bridge Commands (/sdlc-dev, /sdlc-review, etc.)",
@@ -2157,8 +1799,7 @@ var AVAILABLE_FEATURES = [
2157
1799
  value: "auto-git-operations"
2158
1800
  }
2159
1801
  ];
2160
- var ALL_FEATURE_VALUES = AVAILABLE_FEATURES.map((f) => f.value);
2161
- var FEATURES_ENABLED_BY_DEFAULT = ALL_FEATURE_VALUES.filter((f) => f !== "auto-git-operations");
1802
+ AVAILABLE_FEATURES.map((f) => f.value);
2162
1803
  var AVAILABLE_MCPS = [
2163
1804
  {
2164
1805
  name: "context7 - Documentation lookup and context retrieval",
@@ -2177,35 +1818,7 @@ var AVAILABLE_MCPS = [
2177
1818
  value: "memento"
2178
1819
  }
2179
1820
  ];
2180
- var ALL_MCP_VALUES = AVAILABLE_MCPS.map((m) => m.value);
2181
- function createFeatureChoices(defaults) {
2182
- const enabledSet = new Set(defaults ?? FEATURES_ENABLED_BY_DEFAULT);
2183
- return AVAILABLE_FEATURES.map((feature) => ({
2184
- ...feature,
2185
- checked: enabledSet.has(feature.value)
2186
- }));
2187
- }
2188
- function createMcpChoices(defaults) {
2189
- const enabledSet = new Set(defaults ?? ALL_MCP_VALUES);
2190
- return AVAILABLE_MCPS.map((mcp) => ({
2191
- ...mcp,
2192
- checked: enabledSet.has(mcp.value)
2193
- }));
2194
- }
2195
- async function gatherFeatures(defaults) {
2196
- const enabledFeatures = await checkbox({
2197
- message: "Select features to enable:",
2198
- choices: createFeatureChoices(defaults?.enabledFeatures)
2199
- });
2200
- const mcps = await checkbox({
2201
- message: "Select MCP servers to enable:",
2202
- choices: createMcpChoices(defaults?.mcps)
2203
- });
2204
- return {
2205
- enabledFeatures,
2206
- mcps
2207
- };
2208
- }
1821
+ AVAILABLE_MCPS.map((m) => m.value);
2209
1822
  function featuresToFlags(enabledFeatures) {
2210
1823
  return {
2211
1824
  bmadBridge: enabledFeatures.includes("bmad-bridge"),
@@ -2273,10 +1886,12 @@ function generateSdlcConfig(answers) {
2273
1886
  subscriptions: {
2274
1887
  claude: {
2275
1888
  enabled: subscriptions.hasClaude,
1889
+ authMethod: subscriptions.claudeAuth,
2276
1890
  tier: subscriptions.claudeTier
2277
1891
  },
2278
1892
  openai: {
2279
- enabled: subscriptions.hasOpenAI
1893
+ enabled: subscriptions.hasOpenAI,
1894
+ authMethod: subscriptions.openaiAuth
2280
1895
  },
2281
1896
  google: {
2282
1897
  enabled: subscriptions.hasGoogle,
@@ -2288,12 +1903,23 @@ function generateSdlcConfig(answers) {
2288
1903
  }
2289
1904
  },
2290
1905
  models: {
2291
- sisyphus: models.sisyphus,
1906
+ marvin: models.marvin,
2292
1907
  oracle: models.oracle,
2293
1908
  librarian: models.librarian,
2294
1909
  frontend: models.frontend,
2295
1910
  documentWriter: models.documentWriter,
2296
- multimodalLooker: models.multimodalLooker
1911
+ multimodalLooker: models.multimodalLooker,
1912
+ // v1.0.0: subagent model configuration
1913
+ subagents: {
1914
+ red: "inherit",
1915
+ green: "inherit",
1916
+ domain: "inherit",
1917
+ refactor: "inherit",
1918
+ architect: "inherit",
1919
+ pm: "inherit",
1920
+ analyst: "inherit",
1921
+ reviewer: "inherit"
1922
+ }
2297
1923
  },
2298
1924
  // Features and MCPs - use new format if new features selected, otherwise legacy
2299
1925
  features: useNewFeatures ? generateNewFeatures(features.enabledFeatures) : featuresToFlags(features.enabledFeatures),
@@ -2318,7 +1944,9 @@ function generateSdlcConfig(answers) {
2318
1944
  autoFallback: advanced.autoFallback ?? false,
2319
1945
  retryPeriodMs: 3e5,
2320
1946
  notifyOnRateLimit: true
2321
- }
1947
+ },
1948
+ // v1.0.0: classifier model for mode detection
1949
+ classifierModel: "anthropic/claude-haiku"
2322
1950
  }
2323
1951
  };
2324
1952
  if (!useNewFeatures) {
@@ -2340,6 +1968,22 @@ function generateSdlcConfig(answers) {
2340
1968
  statuses: answers.github.statuses
2341
1969
  };
2342
1970
  }
1971
+ config.modes = answers.modes ? {
1972
+ default: answers.modes.default,
1973
+ eventModeling: answers.modes.eventModeling,
1974
+ enabled: answers.modes.enabled
1975
+ } : {
1976
+ default: "build",
1977
+ eventModeling: true,
1978
+ enabled: ["discover", "model", "architect", "pm", "build"]
1979
+ };
1980
+ config.memory = answers.memory ? {
1981
+ backend: answers.memory.backend,
1982
+ checkpointTtl: answers.memory.checkpointTtl
1983
+ } : {
1984
+ backend: "stateless",
1985
+ checkpointTtl: 86400
1986
+ };
2343
1987
  config.tdd = answers.tdd ? {
2344
1988
  enabled: answers.tdd.enabled,
2345
1989
  verbosity: answers.tdd.verbosity,
@@ -2347,7 +1991,11 @@ function generateSdlcConfig(answers) {
2347
1991
  mutationTesting: {
2348
1992
  enabled: answers.tdd.mutationTesting.enabled,
2349
1993
  requiredScore: answers.tdd.mutationTesting.requiredScore
2350
- }
1994
+ },
1995
+ // v1.0.0 fields
1996
+ domainVetoEnabled: answers.tdd.domainVetoEnabled ?? true,
1997
+ debateRounds: answers.tdd.debateRounds ?? 2,
1998
+ requireVerification: answers.tdd.requireVerification ?? true
2351
1999
  } : {
2352
2000
  enabled: true,
2353
2001
  verbosity: "brief",
@@ -2355,7 +2003,10 @@ function generateSdlcConfig(answers) {
2355
2003
  mutationTesting: {
2356
2004
  enabled: false,
2357
2005
  requiredScore: 80
2358
- }
2006
+ },
2007
+ domainVetoEnabled: true,
2008
+ debateRounds: 2,
2009
+ requireVerification: true
2359
2010
  };
2360
2011
  config.eventModeling = answers.eventModeling ? {
2361
2012
  enabled: answers.eventModeling.enabled,
@@ -2422,379 +2073,889 @@ var ConfigGenerator = class {
2422
2073
  return getRequiredPlugins(this.answers);
2423
2074
  }
2424
2075
  };
2425
-
2426
- // src/cli/questions/index.ts
2427
- init_esm_shims();
2428
-
2429
- // src/cli/questions/subscriptions.ts
2430
- init_esm_shims();
2431
- init_debug_logger();
2432
- async function gatherSubscriptions() {
2433
- const hasClaude = await confirm({
2434
- message: "Do you have a Claude Pro/Max subscription?",
2435
- default: false
2436
- });
2437
- debugLog("subscription.hasClaude", hasClaude);
2438
- let claudeTier = "none";
2439
- if (hasClaude) {
2440
- claudeTier = await select({
2441
- message: "Which Claude tier?",
2442
- choices: [
2443
- { name: "Max 5x - 5x more usage than Pro", value: "max5x" },
2444
- { name: "Max 20x - 20x more usage than Pro", value: "max20x" },
2445
- { name: "Pro - Standard Pro subscription", value: "pro" }
2446
- ]
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;
2092
+ }
2093
+ } catch {
2094
+ }
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
2447
2141
  });
2448
- debugLog("subscription.claudeTier", claudeTier);
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
+ };
2449
2152
  }
2450
- const hasOpenAI = await confirm({
2451
- message: "Do you have a ChatGPT Plus/Pro subscription?",
2452
- default: false
2453
- });
2454
- debugLog("subscription.hasOpenAI", hasOpenAI);
2455
- const hasGoogle = await confirm({
2456
- message: "Will you use Google/Gemini models?",
2457
- default: false
2458
- });
2459
- debugLog("subscription.hasGoogle", hasGoogle);
2460
- let googleAuth = "none";
2461
- if (hasGoogle) {
2462
- googleAuth = await select({
2463
- message: "Google authentication method?",
2464
- choices: [
2465
- {
2466
- name: "Google Workspace (Antigravity OAuth) - Recommended for Workspace users",
2467
- value: "antigravity"
2468
- },
2469
- {
2470
- name: "Personal Google Account",
2471
- value: "personal"
2472
- },
2473
- {
2474
- name: "API Key - Direct API access",
2475
- value: "api"
2476
- }
2477
- ]
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
2478
2163
  });
2479
- debugLog("subscription.googleAuth", googleAuth);
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
+ }));
2480
2170
  }
2481
- const hasGitHubCopilot = await confirm({
2482
- message: "Do you have GitHub Copilot access?",
2483
- default: false
2484
- });
2485
- debugLog("subscription.hasGitHubCopilot", hasGitHubCopilot);
2486
- let copilotPlan = "none";
2487
- if (hasGitHubCopilot) {
2488
- copilotPlan = await select({
2489
- message: "Which GitHub Copilot plan?",
2490
- choices: [
2491
- { name: "Enterprise - Full model access including Opus", value: "enterprise" },
2492
- { name: "Pro+ - Includes Claude Opus models", value: "pro-plus" },
2493
- { name: "Pro - Standard paid plan", value: "pro" },
2494
- { name: "Business - Organization plan", value: "business" },
2495
- { name: "Free - Limited model access", value: "free" }
2496
- ]
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
2497
2180
  });
2498
- debugLog("subscription.copilotPlan", copilotPlan);
2499
2181
  }
2500
- const result = {
2501
- hasClaude,
2502
- claudeTier,
2503
- hasOpenAI,
2504
- hasGoogle,
2505
- googleAuth,
2506
- hasGitHubCopilot,
2507
- copilotPlan
2508
- };
2509
- debugLog("subscriptions.final", result);
2510
- return result;
2511
- }
2512
-
2513
- // src/cli/questions/index.ts
2514
- init_models();
2515
-
2516
- // src/cli/questions/methodology.ts
2517
- init_esm_shims();
2518
- async function gatherMethodology(defaults) {
2519
- const defaultTrack = await select({
2520
- message: "Default SDLC track for new projects?",
2521
- choices: [
2522
- {
2523
- name: "Quick Flow - Fast implementation for small features and bug fixes",
2524
- value: "quick-flow"
2525
- },
2526
- {
2527
- name: "Enterprise - Extended planning with compliance and scale considerations",
2528
- value: "enterprise"
2529
- }
2530
- ],
2531
- default: defaults?.defaultTrack ?? "quick-flow"
2532
- });
2533
- const autoStatusUpdate = await confirm({
2534
- message: "Automatically update GitHub issue status when issues complete?",
2535
- default: defaults?.autoStatusUpdate ?? true
2536
- });
2537
- return {
2538
- defaultTrack,
2539
- autoStatusUpdate
2540
- };
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
2191
+ });
2192
+ }
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
+ });
2209
+ return {
2210
+ number: data.number,
2211
+ url: data.html_url
2212
+ };
2213
+ }
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
+ }
2378
+ }
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
+ }
2397
+ }
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;
2404
+ }
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
+ }
2421
+ }
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
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
+ }
2541
2598
  }
2542
2599
 
2543
- // src/cli/questions/advanced.ts
2544
- init_esm_shims();
2545
- async function gatherAdvanced(defaults) {
2546
- const parallelIssueLimit = await select({
2547
- message: "Maximum parallel issues?",
2548
- choices: [
2549
- { name: "1 (sequential - one issue at a time)", value: 1 },
2550
- { name: "2", value: 2 },
2551
- { name: "3 (recommended)", value: 3 },
2552
- { name: "5", value: 5 },
2553
- { name: "Unlimited (0)", value: 0 }
2554
- ],
2555
- default: defaults?.parallelIssueLimit ?? 0
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
2556
2606
  });
2557
- const experimentalDefaults = new Set(defaults?.experimental ?? []);
2558
- const experimental = await checkbox({
2559
- message: "Enable experimental features?",
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);
2655
+ }
2656
+ return {
2657
+ enabled: true,
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
2667
+ };
2668
+ }
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
+ }
2685
+ }
2686
+ const repoAction = await select({
2687
+ message: "Repository setup:",
2560
2688
  choices: [
2561
2689
  {
2562
- name: "Aggressive Truncation - More aggressive context management",
2563
- value: "aggressive-truncation",
2564
- checked: experimentalDefaults.has("aggressive-truncation")
2690
+ value: "existing",
2691
+ name: "Connect to existing GitHub repository",
2692
+ description: "Enter owner/repo for an existing repository"
2693
+ },
2694
+ {
2695
+ value: "create-private",
2696
+ name: "Create new private repository",
2697
+ description: "Create a new private repo on GitHub"
2565
2698
  },
2566
2699
  {
2567
- name: "Auto Resume - Automatically resume interrupted sessions",
2568
- value: "auto-resume",
2569
- checked: experimentalDefaults.has("auto-resume")
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"
2570
2708
  }
2571
2709
  ]
2572
2710
  });
2573
- const autoFallback = await confirm({
2574
- message: "Enable automatic provider fallback on rate limits?",
2575
- default: defaults?.autoFallback ?? false
2576
- });
2577
- return {
2578
- parallelIssueLimit,
2579
- experimental,
2580
- autoFallback
2581
- };
2582
- }
2583
-
2584
- // src/cli/questions/github.ts
2585
- init_esm_shims();
2586
- var DEFAULT_STATUSES = ["Backlog", "Ready", "In progress", "In review", "Done"];
2587
- async function gatherGitHub(defaults) {
2588
- const enabled = await confirm({
2589
- message: "Enable GitHub Issues integration for work tracking?",
2590
- default: true
2591
- });
2592
- if (!enabled) {
2593
- return {
2594
- enabled: false,
2595
- owner: "",
2596
- repo: "",
2597
- statuses: DEFAULT_STATUSES
2598
- };
2711
+ if (repoAction === "skip") {
2712
+ return { action: "skip" };
2713
+ }
2714
+ if (repoAction === "existing") {
2715
+ return await connectExistingRepo(client);
2599
2716
  }
2717
+ return await createNewRepo(client, userLogin, repoAction === "create-private", cwd, isGitRepo);
2718
+ }
2719
+ async function connectExistingRepo(client) {
2600
2720
  const owner = await input({
2601
- message: "GitHub repository owner (user or organization):",
2602
- default: defaults?.owner,
2603
- validate: (value) => {
2604
- if (!value.trim()) {
2605
- return "Owner is required for GitHub integration";
2606
- }
2607
- if (!/^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(value)) {
2608
- return "Invalid GitHub username/organization format";
2609
- }
2610
- return true;
2611
- }
2721
+ message: "Repository owner (user or org):",
2722
+ validate: (v) => v.trim() ? true : "Owner is required"
2612
2723
  });
2613
2724
  const repo = await input({
2614
- message: "GitHub repository name:",
2615
- default: defaults?.repo,
2616
- validate: (value) => {
2617
- if (!value.trim()) {
2618
- return "Repository name is required";
2619
- }
2620
- if (!/^[a-zA-Z0-9._-]+$/.test(value)) {
2621
- return "Invalid repository name format";
2622
- }
2725
+ message: "Repository name:",
2726
+ validate: (v) => v.trim() ? true : "Repository name is required"
2727
+ });
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 };
2741
+ }
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";
2623
2748
  return true;
2624
2749
  }
2625
2750
  });
2626
- const useProjectBoard = await confirm({
2627
- message: "Use a GitHub Project board for issue status tracking?",
2628
- default: defaults?.project !== void 0
2751
+ const description = await input({
2752
+ message: "Repository description (optional):",
2753
+ default: ""
2629
2754
  });
2630
- let project;
2631
- if (useProjectBoard) {
2632
- project = await number({
2633
- message: "GitHub Project number (from project URL):",
2634
- default: defaults?.project,
2635
- min: 1,
2636
- validate: (value) => {
2637
- if (value === void 0 || value < 1) {
2638
- return "Project number must be a positive integer";
2639
- }
2640
- return true;
2641
- }
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
2642
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'`));
2772
+ }
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'`));
2780
+ }
2781
+ }
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" };
2643
2793
  }
2644
- return {
2645
- enabled: true,
2646
- owner,
2647
- repo,
2648
- project,
2649
- statuses: defaults?.statuses ?? DEFAULT_STATUSES
2650
- };
2651
2794
  }
2652
-
2653
- // src/cli/questions/tdd.ts
2654
- init_esm_shims();
2655
- var DEFAULT_BYPASS_PATTERNS = ["*.config.*", "*.json", "*.md", "*.yaml", "*.yml"];
2656
- async function gatherTdd(defaults) {
2657
- const enabled = await confirm({
2658
- message: "Enable TDD cycle enforcement (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)?",
2659
- default: defaults?.enabled ?? true
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
2660
2799
  });
2661
- if (!enabled) {
2662
- return {
2663
- enabled: false,
2664
- verbosity: "silent",
2665
- bypassPatterns: DEFAULT_BYPASS_PATTERNS,
2666
- mutationTesting: {
2667
- enabled: false,
2668
- requiredScore: 80
2669
- }
2670
- };
2800
+ if (!wantProject) {
2801
+ return { action: "skip" };
2671
2802
  }
2672
- const verbosity = await select({
2673
- message: "TDD feedback verbosity level:",
2803
+ const projectAction = await select({
2804
+ message: "Project board setup:",
2674
2805
  choices: [
2675
2806
  {
2676
- name: "Silent - Only show errors",
2677
- value: "silent"
2807
+ value: "existing",
2808
+ name: "Link existing project",
2809
+ description: "Enter project number from URL"
2678
2810
  },
2679
2811
  {
2680
- name: "Brief - Short status messages (recommended)",
2681
- value: "brief"
2812
+ value: "create-blank",
2813
+ name: "Create new blank project",
2814
+ description: "Create a new project with default columns"
2682
2815
  },
2683
2816
  {
2684
- name: "Explain - Detailed explanations of TDD phases",
2685
- value: "explain"
2817
+ value: "skip",
2818
+ name: "Skip project board",
2819
+ description: "Configure manually later"
2686
2820
  }
2687
- ],
2688
- default: defaults?.verbosity ?? "brief"
2689
- });
2690
- const customBypass = await confirm({
2691
- message: "Customize file patterns that bypass TDD checks?",
2692
- default: false
2821
+ ]
2693
2822
  });
2694
- let bypassPatterns = defaults?.bypassPatterns ?? DEFAULT_BYPASS_PATTERNS;
2695
- if (customBypass) {
2696
- const patternsInput = await input({
2697
- message: "Enter bypass patterns (comma-separated, e.g., *.config.*, *.json):",
2698
- default: bypassPatterns.join(", ")
2699
- });
2700
- bypassPatterns = patternsInput.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
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);
2830
+ }
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 {
2701
2842
  }
2702
- const mutationEnabled = await confirm({
2703
- message: "Enable mutation testing for coverage verification?",
2704
- default: defaults?.mutationTesting?.enabled ?? false
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"
2705
2847
  });
2706
- let requiredScore = defaults?.mutationTesting?.requiredScore ?? 80;
2707
- if (mutationEnabled) {
2708
- const scoreChoice = await select({
2709
- message: "Minimum mutation score required:",
2710
- choices: [
2711
- { name: "60% - Minimal coverage", value: 60 },
2712
- { name: "70% - Standard coverage", value: 70 },
2713
- { name: "80% - Good coverage (recommended)", value: 80 },
2714
- { name: "90% - High coverage", value: 90 }
2715
- ],
2716
- default: requiredScore
2717
- });
2718
- requiredScore = scoreChoice;
2848
+ if (!projectNum) {
2849
+ return { action: "skip" };
2719
2850
  }
2720
- return {
2721
- enabled,
2722
- verbosity,
2723
- bypassPatterns,
2724
- mutationTesting: {
2725
- enabled: mutationEnabled,
2726
- requiredScore
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 };
2727
2856
  }
2728
- };
2857
+ } catch {
2858
+ }
2859
+ return { action: "existing", projectNumber: projectNum };
2729
2860
  }
2730
-
2731
- // src/cli/questions/event-modeling.ts
2732
- init_esm_shims();
2733
- var DEFAULT_OUTPUT_PATH = "docs/event-model";
2734
- async function gatherEventModeling(defaults) {
2735
- const enabled = await confirm({
2736
- message: "Enable Event Modeling workflow for event-sourced systems?",
2737
- default: defaults?.enabled ?? false
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
2738
2868
  });
2739
- if (!enabled) {
2740
- return {
2741
- enabled: false,
2742
- outputPath: defaults?.outputPath ?? DEFAULT_OUTPUT_PATH
2743
- };
2869
+ if (!createdProject) {
2870
+ return { action: "skip" };
2744
2871
  }
2745
- const outputPath = await input({
2746
- message: "Output path for event model files:",
2747
- default: defaults?.outputPath ?? DEFAULT_OUTPUT_PATH,
2748
- validate: (value) => {
2749
- if (!value.trim()) {
2750
- return "Output path is required";
2751
- }
2752
- if (value.startsWith("/") || value.startsWith("~")) {
2753
- return "Use a relative path from the project root";
2754
- }
2755
- return true;
2756
- }
2872
+ const projectNum = await number({
2873
+ message: "New project number:",
2874
+ min: 1
2757
2875
  });
2758
2876
  return {
2759
- enabled,
2760
- outputPath
2877
+ action: "create-blank",
2878
+ projectNumber: projectNum
2761
2879
  };
2762
2880
  }
2763
-
2764
- // src/cli/questions/git-workflow.ts
2765
- init_esm_shims();
2766
- async function gatherGitWorkflow(defaults) {
2767
- const workflow = await select({
2768
- message: "Git workflow style:",
2769
- 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(
2770
2906
  {
2771
- name: "Standard - Traditional branch-based workflow",
2772
- value: "standard"
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
2773
2915
  },
2774
- {
2775
- name: "git-spice - Stacked PRs with git-spice CLI",
2776
- value: "git-spice"
2777
- }
2778
- ],
2779
- default: defaults?.workflow ?? "standard"
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"
2780
2933
  });
2781
- const requireClean = await confirm({
2782
- message: "Require clean working directory before git operations?",
2783
- default: defaults?.requireClean ?? true
2934
+ const repo = await input({
2935
+ message: "GitHub repository name:",
2936
+ validate: (v) => v.trim() ? true : "Repository name is required"
2784
2937
  });
2785
- const worktrees = await confirm({
2786
- message: "Enable git worktrees for parallel work on multiple issues?",
2787
- default: defaults?.worktrees ?? false
2938
+ const useProject = await confirm({
2939
+ message: "Use a GitHub Project board?",
2940
+ default: false
2788
2941
  });
2942
+ let project;
2943
+ if (useProject) {
2944
+ project = await number({
2945
+ message: "Project number:",
2946
+ min: 1
2947
+ }) ?? void 0;
2948
+ }
2789
2949
  return {
2790
- workflow,
2791
- requireClean,
2792
- worktrees
2950
+ enabled: true,
2951
+ config: {
2952
+ owner,
2953
+ repo,
2954
+ project,
2955
+ statuses: DEFAULT_STATUSES
2956
+ }
2793
2957
  };
2794
2958
  }
2795
-
2796
- // src/cli/utils/config-loader.ts
2797
- init_esm_shims();
2798
2959
  function loadExistingConfigs() {
2799
2960
  const result = {
2800
2961
  sdlc: null,
@@ -2840,10 +3001,15 @@ function extractSubscriptions(sdlc) {
2840
3001
  const openai = subs.openai;
2841
3002
  const google = subs.google;
2842
3003
  const copilot = subs.githubCopilot;
3004
+ const claudeTier = claude?.tier || "none";
3005
+ const claudeAuth = claude?.authMethod || (claudeTier !== "none" ? "subscription" : "none");
3006
+ const openaiAuth = openai?.authMethod || (openai?.enabled === true ? "subscription" : "none");
2843
3007
  return {
2844
3008
  hasClaude: claude?.enabled === true,
2845
- claudeTier: claude?.tier || "none",
3009
+ claudeAuth,
3010
+ claudeTier,
2846
3011
  hasOpenAI: openai?.enabled === true,
3012
+ openaiAuth,
2847
3013
  hasGoogle: google?.enabled === true,
2848
3014
  googleAuth: google?.authMethod || "none",
2849
3015
  hasGitHubCopilot: copilot?.enabled === true,
@@ -2859,7 +3025,7 @@ function extractModels(sdlc) {
2859
3025
  const models = sdlc.models;
2860
3026
  if (!models) return null;
2861
3027
  return {
2862
- sisyphus: models.sisyphus || "",
3028
+ marvin: models.marvin || "",
2863
3029
  oracle: models.oracle || "",
2864
3030
  librarian: models.librarian || "",
2865
3031
  frontend: models.frontend,
@@ -2928,9 +3094,6 @@ function detectNewFeatures(existingSdlc) {
2928
3094
  }
2929
3095
  return newFeatures;
2930
3096
  }
2931
-
2932
- // src/cli/utils/config-merger.ts
2933
- init_esm_shims();
2934
3097
  function deepMerge(baseObj, newObj) {
2935
3098
  const result = { ...baseObj };
2936
3099
  for (const [key, value] of Object.entries(newObj)) {
@@ -2999,21 +3162,67 @@ function writeMergedConfigs(configs) {
2999
3162
  );
3000
3163
  }
3001
3164
  }
3002
-
3003
- // src/cli/utils/migrations/index.ts
3004
- init_esm_shims();
3005
-
3006
- // src/cli/utils/migrations/types.ts
3007
- init_esm_shims();
3008
-
3009
- // src/cli/utils/migrations/migrations.ts
3010
- init_esm_shims();
3011
3165
  var MIGRATIONS = [
3012
3166
  {
3013
3167
  fromVersion: "0.0.1",
3014
3168
  toVersion: "0.3.0",
3015
- description: "Initial SDLC plugin version",
3169
+ description: "Initial SDLC plugin version with Marvin agent and API key auth options",
3016
3170
  migrateSdlc: (config) => config
3171
+ },
3172
+ {
3173
+ fromVersion: "0.3.0",
3174
+ toVersion: "1.0.0",
3175
+ description: "Add v1.0 config: modes, memory, TDD updates, classifier model, subagent models",
3176
+ migrateSdlc: (config) => {
3177
+ const migrated = { ...config };
3178
+ if (!migrated.modes) {
3179
+ migrated.modes = {
3180
+ default: "build",
3181
+ eventModeling: true,
3182
+ enabled: ["discover", "model", "architect", "pm", "build"]
3183
+ };
3184
+ }
3185
+ if (!migrated.memory) {
3186
+ migrated.memory = {
3187
+ backend: "stateless",
3188
+ checkpointTtl: 86400
3189
+ };
3190
+ }
3191
+ if (migrated.tdd) {
3192
+ const tdd = migrated.tdd;
3193
+ if (tdd.domainVetoEnabled === void 0) {
3194
+ tdd.domainVetoEnabled = true;
3195
+ }
3196
+ if (tdd.debateRounds === void 0) {
3197
+ tdd.debateRounds = 2;
3198
+ }
3199
+ if (tdd.requireVerification === void 0) {
3200
+ tdd.requireVerification = true;
3201
+ }
3202
+ }
3203
+ if (migrated.routing) {
3204
+ const routing = migrated.routing;
3205
+ if (routing.classifierModel === void 0) {
3206
+ routing.classifierModel = "anthropic/claude-haiku";
3207
+ }
3208
+ }
3209
+ if (migrated.models) {
3210
+ const models = migrated.models;
3211
+ if (!models.subagents) {
3212
+ models.subagents = {
3213
+ red: "inherit",
3214
+ green: "inherit",
3215
+ domain: "inherit",
3216
+ refactor: "inherit",
3217
+ architect: "inherit",
3218
+ pm: "inherit",
3219
+ analyst: "inherit",
3220
+ reviewer: "inherit"
3221
+ };
3222
+ }
3223
+ }
3224
+ return migrated;
3225
+ }
3017
3226
  }
3018
3227
  ];
3019
3228
  function migrateLegacyFiles() {
@@ -3053,9 +3262,6 @@ function migrateLegacyFiles() {
3053
3262
  }
3054
3263
  return result;
3055
3264
  }
3056
-
3057
- // src/cli/utils/migrations/runner.ts
3058
- init_esm_shims();
3059
3265
  function migrateConfigs(sdlcConfig, omoConfig, fromVersion, opencodeConfig = {}) {
3060
3266
  const targetVersion = VERSION;
3061
3267
  const migrationsApplied = [];
@@ -3111,18 +3317,30 @@ function isBreakingMigration(migration) {
3111
3317
  const toMajor = semver.major(semver.coerce(migration.toVersion) || "0.0.0");
3112
3318
  return toMajor > fromMajor;
3113
3319
  }
3114
-
3115
- // src/cli/utils/preset-loader.ts
3116
- init_esm_shims();
3117
- var PRESET_NAMES = [
3118
- "minimal",
3119
- "standard",
3120
- "strict-tdd",
3121
- "event-modeling",
3122
- "enterprise",
3123
- "solo-quick",
3124
- "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
+ }
3125
3342
  ];
3343
+ var PRESET_NAMES = ["minimal", "standard", "event-modeling"];
3126
3344
  function getPresetsDir() {
3127
3345
  const currentFileDir = dirname(fileURLToPath(import.meta.url));
3128
3346
  const bundledRoot = join(currentFileDir, "..", "..");
@@ -3157,6 +3375,17 @@ function getPresetsDir() {
3157
3375
  function isValidPresetName(name) {
3158
3376
  return PRESET_NAMES.includes(name);
3159
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
+ }
3160
3389
  function loadPreset(name) {
3161
3390
  if (!isValidPresetName(name)) {
3162
3391
  const validNames = PRESET_NAMES.join(", ");
@@ -3185,36 +3414,18 @@ Error: ${error.message}`);
3185
3414
  throw error;
3186
3415
  }
3187
3416
  }
3188
- function listPresets() {
3189
- const presetsDir = getPresetsDir();
3190
- const summaries = [];
3191
- for (const name of PRESET_NAMES) {
3192
- const presetPath = join(presetsDir, `${name}.json`);
3193
- if (existsSync(presetPath)) {
3194
- try {
3195
- const content = readFileSync(presetPath, "utf-8");
3196
- const preset = JSON.parse(content);
3197
- summaries.push({
3198
- name,
3199
- description: preset.description || `${name} preset`,
3200
- path: presetPath
3201
- });
3202
- } catch {
3203
- summaries.push({
3204
- name,
3205
- description: `${name} preset (unable to read)`,
3206
- path: presetPath
3207
- });
3208
- }
3209
- }
3210
- }
3211
- return summaries;
3417
+ function listProfiles() {
3418
+ return PROFILES.map((profile) => ({
3419
+ name: profile.displayName,
3420
+ description: profile.description,
3421
+ path: profile.id
3422
+ }));
3212
3423
  }
3213
3424
  function presetToDefaults(preset) {
3214
3425
  const isLegacy = "bmad" in preset && preset.bmad !== void 0;
3215
3426
  return {
3216
3427
  models: {
3217
- sisyphus: preset.models.sisyphus,
3428
+ marvin: preset.models.marvin || "",
3218
3429
  oracle: preset.models.oracle,
3219
3430
  librarian: preset.models.librarian,
3220
3431
  frontend: preset.models.frontend,
@@ -3279,7 +3490,7 @@ function formatPresetSummary(preset, name) {
3279
3490
  lines.push(`Description: ${preset.description}`);
3280
3491
  lines.push("");
3281
3492
  lines.push("Models:");
3282
- lines.push(` Sisyphus: ${preset.models.sisyphus}`);
3493
+ lines.push(` Marvin: ${preset.models.marvin}`);
3283
3494
  lines.push(` Oracle: ${preset.models.oracle}`);
3284
3495
  lines.push(` Librarian: ${preset.models.librarian}`);
3285
3496
  if (preset.bmad) {
@@ -3339,9 +3550,9 @@ function detectInstallMode(options, configs) {
3339
3550
  async function runUpgradeFlow(configs, existingVersion, options) {
3340
3551
  const { sdlc, omo, opencode } = configs;
3341
3552
  logger.section("Upgrading Configuration");
3342
- console.log(chalk4.cyan(`
3553
+ console.log(chalk5.cyan(`
3343
3554
  Current version: ${existingVersion}`));
3344
- console.log(chalk4.cyan(`New version: ${VERSION}
3555
+ console.log(chalk5.cyan(`New version: ${VERSION}
3345
3556
  `));
3346
3557
  if (!options.yes) {
3347
3558
  const proceed = await confirm({
@@ -3365,22 +3576,22 @@ Current version: ${existingVersion}`));
3365
3576
  if (fileMigrationResult.stateFileMoved) moved.push("state file");
3366
3577
  if (fileMigrationResult.backupsMoved > 0)
3367
3578
  moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
3368
- console.log(chalk4.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
3579
+ console.log(chalk5.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
3369
3580
  }
3370
3581
  const migrationSpinner = ora5("Applying migrations...").start();
3371
3582
  const migrationResult = migrateConfigs(sdlc || {}, omo || {}, existingVersion, opencode || {});
3372
3583
  if (migrationResult.migrationsApplied.length > 0) {
3373
3584
  migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
3374
3585
  for (const migration of migrationResult.migrationsApplied) {
3375
- console.log(chalk4.gray(` \u2022 ${migration}`));
3586
+ console.log(chalk5.gray(` \u2022 ${migration}`));
3376
3587
  }
3377
3588
  } else {
3378
3589
  migrationSpinner.succeed("No migrations needed");
3379
3590
  }
3380
3591
  if (migrationResult.hasBreakingChanges && !options.yes) {
3381
- console.log(chalk4.yellow("\nBreaking changes detected:"));
3592
+ console.log(chalk5.yellow("\nBreaking changes detected:"));
3382
3593
  for (const warning of migrationResult.breakingChangeWarnings) {
3383
- console.log(chalk4.yellow(` \u26A0 ${warning}`));
3594
+ console.log(chalk5.yellow(` \u26A0 ${warning}`));
3384
3595
  }
3385
3596
  const continueUpgrade = await confirm({
3386
3597
  message: "Continue with upgrade despite breaking changes?",
@@ -3398,20 +3609,25 @@ Current version: ${existingVersion}`));
3398
3609
  const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
3399
3610
  logger.section("Preserved Configuration");
3400
3611
  if (existingSubscriptions) {
3401
- console.log(chalk4.bold("Subscriptions:"));
3402
- if (existingSubscriptions.hasClaude)
3403
- console.log(chalk4.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
3404
- if (existingSubscriptions.hasOpenAI) console.log(chalk4.green(" \u2713 OpenAI"));
3612
+ console.log(chalk5.bold("Subscriptions:"));
3613
+ if (existingSubscriptions.hasClaude) {
3614
+ const claudeInfo = existingSubscriptions.claudeAuth === "api" ? "API Key" : existingSubscriptions.claudeTier;
3615
+ console.log(chalk5.green(` \u2713 Claude (${claudeInfo})`));
3616
+ }
3617
+ if (existingSubscriptions.hasOpenAI) {
3618
+ const openaiInfo = existingSubscriptions.openaiAuth === "api" ? "API Key" : "Subscription";
3619
+ console.log(chalk5.green(` \u2713 OpenAI (${openaiInfo})`));
3620
+ }
3405
3621
  if (existingSubscriptions.hasGoogle)
3406
- console.log(chalk4.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
3622
+ console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
3407
3623
  if (existingSubscriptions.hasGitHubCopilot)
3408
- console.log(chalk4.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
3624
+ console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
3409
3625
  }
3410
3626
  if (existingModels) {
3411
- console.log(chalk4.bold("\nModel Assignments:"));
3412
- console.log(chalk4.green(` \u2713 Sisyphus: ${existingModels.sisyphus}`));
3413
- console.log(chalk4.green(` \u2713 Oracle: ${existingModels.oracle}`));
3414
- console.log(chalk4.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}`));
3415
3631
  }
3416
3632
  console.log();
3417
3633
  const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
@@ -3431,26 +3647,21 @@ Current version: ${existingVersion}`));
3431
3647
  }
3432
3648
  }
3433
3649
  }
3434
- let finalSubscriptions = existingSubscriptions;
3435
- if (!options.yes) {
3436
- const changeSubscriptions = await confirm({
3437
- message: "Do you want to modify your LLM subscriptions?",
3438
- default: false
3439
- });
3440
- if (changeSubscriptions) {
3441
- logger.section("LLM Subscriptions");
3442
- finalSubscriptions = await gatherSubscriptions();
3443
- }
3444
- }
3445
- if (!finalSubscriptions) {
3446
- logger.error("Could not extract subscription information from existing config.");
3447
- logger.info("Please run with --reconfigure to set up from scratch.");
3448
- process.exit(1);
3449
- }
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
+ };
3450
3661
  const fullAnswers = {
3451
3662
  subscriptions: finalSubscriptions,
3452
3663
  models: existingModels || {
3453
- sisyphus: "",
3664
+ marvin: "",
3454
3665
  oracle: "",
3455
3666
  librarian: ""
3456
3667
  },
@@ -3486,17 +3697,14 @@ Current version: ${existingVersion}`));
3486
3697
  await fileManager.installDependencies(packages);
3487
3698
  installSpinner.succeed(`Installed ${packages.length} package(s)`);
3488
3699
  }
3489
- const commandsSpinner = ora5("Updating bridge commands...").start();
3490
- const copiedCommands = await fileManager.copyCommands();
3491
- commandsSpinner.succeed(`Updated ${copiedCommands.length} bridge commands`);
3492
3700
  logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
3493
3701
  if (backups.sdlcBackup || backups.omoBackup || backups.opencodeBackup) {
3494
- console.log(chalk4.gray("\nBackups saved:"));
3495
- if (backups.sdlcBackup) console.log(chalk4.gray(` \u2022 ${backups.sdlcBackup}`));
3496
- if (backups.omoBackup) console.log(chalk4.gray(` \u2022 ${backups.omoBackup}`));
3497
- if (backups.opencodeBackup) console.log(chalk4.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}`));
3498
3706
  }
3499
- console.log(chalk4.gray("\nRestart OpenCode to use the upgraded configuration."));
3707
+ console.log(chalk5.gray("\nRestart OpenCode to use the upgraded configuration."));
3500
3708
  console.log();
3501
3709
  }
3502
3710
  async function install(options) {
@@ -3530,192 +3738,94 @@ async function install(options) {
3530
3738
  if (modeResult.mode === "reconfigure") {
3531
3739
  logger.info("Reconfiguring from scratch (--reconfigure flag)");
3532
3740
  }
3533
- let preset = null;
3534
- let presetDefaults = null;
3535
- let presetName = null;
3536
- if (options.preset && options.preset !== "none") {
3537
- if (!isValidPresetName(options.preset)) {
3538
- logger.error(`Invalid preset: "${options.preset}"`);
3539
- logger.info(`Valid presets: ${PRESET_NAMES.join(", ")}`);
3540
- process.exit(1);
3541
- }
3542
- try {
3543
- preset = loadPreset(options.preset);
3544
- presetDefaults = presetToDefaults(preset);
3545
- presetName = options.preset;
3546
- logger.success(`Loaded preset: ${options.preset}`);
3547
- } catch (error) {
3548
- logger.error(error instanceof Error ? error.message : String(error));
3549
- process.exit(1);
3550
- }
3551
- }
3552
- if (!preset && !options.yes) {
3553
- const selectedPreset = await askForPreset();
3554
- if (selectedPreset) {
3555
- preset = selectedPreset.preset;
3556
- presetDefaults = presetToDefaults(preset);
3557
- presetName = selectedPreset.name;
3558
- }
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)");
3750
+ } else {
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");
3559
3821
  }
3560
- logger.section("LLM Subscriptions");
3561
- const subscriptions = await gatherSubscriptions();
3562
- if (!options.yes) {
3563
- logger.section("Confirm Provider Selection");
3564
- console.log(chalk4.bold("You selected:\n"));
3565
- console.log(
3566
- ` Claude: ${subscriptions.hasClaude ? chalk4.green("\u2713 Yes") : chalk4.gray("\u2717 No")}`
3567
- );
3568
- if (subscriptions.hasClaude) {
3569
- console.log(` Tier: ${subscriptions.claudeTier}`);
3570
- }
3571
- console.log(
3572
- ` OpenAI: ${subscriptions.hasOpenAI ? chalk4.green("\u2713 Yes") : chalk4.gray("\u2717 No")}`
3573
- );
3574
- console.log(
3575
- ` Google: ${subscriptions.hasGoogle ? chalk4.green("\u2713 Yes") : chalk4.gray("\u2717 No")}`
3576
- );
3577
- if (subscriptions.hasGoogle) {
3578
- console.log(` Auth Method: ${subscriptions.googleAuth}`);
3579
- }
3580
- console.log(
3581
- ` GitHub Copilot: ${subscriptions.hasGitHubCopilot ? chalk4.green("\u2713 Yes") : chalk4.gray("\u2717 No")}`
3582
- );
3583
- if (subscriptions.hasGitHubCopilot) {
3584
- console.log(` Plan: ${subscriptions.copilotPlan}`);
3585
- }
3586
- console.log();
3587
- const confirmed = await confirm({
3588
- message: "Is this correct?",
3589
- default: true
3590
- });
3591
- if (!confirmed) {
3592
- logger.info("Please restart installation with correct provider selections.");
3593
- process.exit(0);
3594
- }
3822
+ if (githubResult.projectCreated) {
3823
+ logger.success("Linked project board");
3595
3824
  }
3596
- let shouldCustomize = true;
3597
- if (preset && presetDefaults && presetName) {
3598
- const modelWarnings = validatePresetModels(presetDefaults.models, subscriptions);
3599
- if (modelWarnings.length > 0) {
3600
- console.log(chalk4.yellow("\nPreset model compatibility warnings:"));
3601
- for (const warning of modelWarnings) {
3602
- console.log(chalk4.yellow(` - ${warning}`));
3603
- }
3604
- console.log();
3605
- }
3606
- console.log(chalk4.bold("\nPreset Configuration:\n"));
3607
- console.log(chalk4.gray(formatPresetSummary(preset, presetName)));
3608
- console.log();
3609
- if (options.yes) {
3610
- shouldCustomize = false;
3611
- logger.info("Using preset defaults (--yes flag)");
3612
- } else {
3613
- shouldCustomize = await confirm({
3614
- message: "Would you like to customize these settings?",
3615
- default: false
3616
- });
3617
- }
3618
- }
3619
- let models;
3620
- let methodology;
3621
- let features;
3622
- let advanced;
3623
- let github;
3624
- let tdd;
3625
- let eventModeling;
3626
- let gitWorkflow;
3627
- if (!shouldCustomize && presetDefaults) {
3628
- logger.section("Applying Preset Configuration");
3629
- const availableModels = await Promise.resolve().then(() => (init_models(), models_exports)).then(
3630
- (m) => m.getAvailableModels(subscriptions)
3631
- );
3632
- if (availableModels.length === 0) {
3633
- logger.error(
3634
- "No models available. Please enable at least one provider (Claude, OpenAI, or Google)."
3635
- );
3636
- process.exit(1);
3637
- }
3638
- models = {
3639
- sisyphus: getValidModelOrFirst(presetDefaults.models.sisyphus, availableModels),
3640
- oracle: getValidModelOrFirst(presetDefaults.models.oracle, availableModels),
3641
- librarian: getValidModelOrFirst(presetDefaults.models.librarian, availableModels),
3642
- frontend: getValidModelOrFirst(presetDefaults.models.frontend, availableModels),
3643
- documentWriter: getValidModelOrFirst(presetDefaults.models.documentWriter, availableModels),
3644
- multimodalLooker: getValidModelOrFirst(
3645
- presetDefaults.models.multimodalLooker,
3646
- availableModels
3647
- )
3648
- };
3649
- methodology = presetDefaults.methodology;
3650
- features = presetDefaults.features;
3651
- advanced = presetDefaults.advanced;
3652
- logger.section("GitHub Integration");
3653
- github = await gatherGitHub();
3654
- if (preset?.tdd) {
3655
- tdd = {
3656
- enabled: preset.tdd.enabled,
3657
- verbosity: preset.tdd.verbosity,
3658
- bypassPatterns: preset.tdd.bypassPatterns,
3659
- mutationTesting: preset.tdd.mutationTesting
3660
- };
3661
- }
3662
- if (preset?.eventModeling) {
3663
- eventModeling = {
3664
- enabled: preset.eventModeling.enabled,
3665
- outputPath: preset.eventModeling.outputPath
3666
- };
3667
- }
3668
- if (preset?.git) {
3669
- gitWorkflow = {
3670
- workflow: preset.git.workflow,
3671
- requireClean: preset.git.requireClean,
3672
- worktrees: preset.git.worktrees
3673
- };
3674
- }
3675
- logger.success("Preset configuration applied");
3676
- } else {
3677
- logger.section("Model Selection");
3678
- models = await gatherModels(subscriptions, presetDefaults?.models);
3679
- logger.section("Methodology Preferences");
3680
- methodology = await gatherMethodology(presetDefaults?.methodology);
3681
- logger.section("GitHub Integration");
3682
- github = await gatherGitHub();
3683
- logger.section("TDD Configuration");
3684
- tdd = await gatherTdd(
3685
- preset?.tdd ? {
3686
- enabled: preset.tdd.enabled,
3687
- verbosity: preset.tdd.verbosity,
3688
- bypassPatterns: preset.tdd.bypassPatterns,
3689
- mutationTesting: preset.tdd.mutationTesting
3690
- } : void 0
3691
- );
3692
- logger.section("Event Modeling");
3693
- eventModeling = await gatherEventModeling(
3694
- preset?.eventModeling ? {
3695
- enabled: preset.eventModeling.enabled,
3696
- outputPath: preset.eventModeling.outputPath
3697
- } : void 0
3698
- );
3699
- logger.section("Git Workflow");
3700
- gitWorkflow = await gatherGitWorkflow(
3701
- preset?.git ? {
3702
- workflow: preset.git.workflow,
3703
- requireClean: preset.git.requireClean,
3704
- worktrees: preset.git.worktrees
3705
- } : void 0
3706
- );
3707
- logger.section("Feature Selection");
3708
- features = await gatherFeatures(presetDefaults?.features);
3709
- if (options.advanced) {
3710
- logger.section("Advanced Configuration");
3711
- advanced = await gatherAdvanced(presetDefaults?.advanced);
3712
- } else {
3713
- advanced = presetDefaults?.advanced ?? {
3714
- parallelIssueLimit: 0,
3715
- experimental: []
3716
- };
3717
- }
3825
+ if (githubResult.rulesetsCreated) {
3826
+ logger.success("Created branch protection rules");
3718
3827
  }
3828
+ logger.success("Profile configuration applied");
3719
3829
  logger.section("Generating Configuration");
3720
3830
  const answers = {
3721
3831
  subscriptions,
@@ -3727,14 +3837,17 @@ async function install(options) {
3727
3837
  github,
3728
3838
  tdd,
3729
3839
  eventModeling,
3730
- gitWorkflow
3840
+ gitWorkflow,
3841
+ // v1.0.0 additions
3842
+ modes,
3843
+ memory
3731
3844
  };
3732
3845
  const generator = new ConfigGenerator(answers);
3733
3846
  const files = await generator.generate();
3734
- console.log(chalk4.bold("Files to be created/modified:\n"));
3847
+ console.log(chalk5.bold("Files to be created/modified:\n"));
3735
3848
  for (const file of files) {
3736
- const action = file.exists ? chalk4.yellow("update") : chalk4.green("create");
3737
- console.log(chalk4.gray(` [${action}] ${file.path}`));
3849
+ const action = file.exists ? chalk5.yellow("update") : chalk5.green("create");
3850
+ console.log(chalk5.gray(` [${action}] ${file.path}`));
3738
3851
  }
3739
3852
  console.log();
3740
3853
  if (!options.yes) {
@@ -3757,84 +3870,64 @@ async function install(options) {
3757
3870
  installSpinner.text = `Installing dependencies: ${packages.join(", ")}...`;
3758
3871
  await fileManager.installDependencies(packages);
3759
3872
  }
3760
- installSpinner.text = "Installing commands...";
3761
- const copiedCommands = await fileManager.copyCommands();
3762
- installSpinner.succeed("Installation complete!");
3763
- if (copiedCommands.length > 0) {
3764
- logger.info(`Installed ${copiedCommands.length} bridge commands`);
3873
+ if (modes?.enabled && modes.enabled.length > 0) {
3874
+ installSpinner.text = "Installing Marvin mode agents...";
3875
+ const projectDir = process.cwd();
3876
+ const copiedAgents = await fileManager.copyAgentModes(projectDir, modes.enabled);
3877
+ if (copiedAgents.length > 0) {
3878
+ logger.info(`Installed ${copiedAgents.length} Marvin mode agents to .opencode/agent/`);
3879
+ }
3765
3880
  }
3881
+ installSpinner.succeed("Installation complete!");
3766
3882
  } catch (error) {
3767
3883
  installSpinner.fail("Installation failed");
3768
3884
  logger.error(error instanceof Error ? error.message : String(error));
3769
3885
  process.exit(1);
3770
3886
  }
3771
- printNextSteps(subscriptions);
3887
+ printNextSteps();
3772
3888
  }
3773
- async function askForPreset() {
3774
- const presets = listPresets();
3775
- const choices = [
3776
- { name: "No preset - Configure everything manually", value: "none" },
3777
- ...presets.map((p) => ({
3778
- name: `${p.name} - ${p.description}`,
3779
- value: p.name
3780
- }))
3781
- ];
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
+ }));
3782
3898
  const selected = await select({
3783
- message: "Start from a preset?",
3899
+ message: "Select a development profile:",
3784
3900
  choices,
3785
- default: "standard"
3901
+ default: "event-modeling"
3786
3902
  });
3787
- if (selected === "none") {
3788
- return null;
3789
- }
3790
- try {
3791
- const preset = loadPreset(selected);
3792
- return { preset, name: selected };
3793
- } catch (error) {
3794
- logger.warn(`Failed to load preset: ${error instanceof Error ? error.message : String(error)}`);
3795
- return null;
3903
+ const profile = PROFILES.find((p) => p.id === selected);
3904
+ if (!profile) {
3905
+ throw new Error(`Profile not found: ${selected}`);
3796
3906
  }
3907
+ const preset = loadProfile(selected);
3908
+ return { preset, profile };
3797
3909
  }
3798
- function getValidModelOrFirst(modelId, availableModels) {
3799
- if (modelId && availableModels.some((m) => m.id === modelId)) {
3800
- return modelId;
3801
- }
3802
- return availableModels[0]?.id ?? "";
3803
- }
3804
- function printNextSteps(subscriptions) {
3805
- const steps = [];
3806
- if (subscriptions.hasClaude) {
3807
- steps.push(`Run: ${chalk4.cyan("opencode auth login")} -> Select Anthropic -> Claude Pro/Max`);
3808
- }
3809
- if (subscriptions.hasOpenAI) {
3810
- steps.push(`Run: ${chalk4.cyan("opencode auth login")} -> Select OpenAI -> ChatGPT Plus/Pro`);
3811
- }
3812
- if (subscriptions.hasGoogle) {
3813
- steps.push(`Run: ${chalk4.cyan("opencode auth login")} -> Select Google -> OAuth with Google`);
3814
- }
3815
- if (subscriptions.hasGitHubCopilot) {
3816
- steps.push(`Run: ${chalk4.cyan("opencode auth login")} -> Select GitHub Copilot`);
3817
- }
3818
- logger.successBanner("OPENCODE ATHENA INSTALLED SUCCESSFULLY!");
3819
- console.log(chalk4.bold("Next Steps:\n"));
3820
- steps.forEach((step, i) => {
3821
- console.log(` ${i + 1}. ${step}`);
3822
- });
3823
- console.log(chalk4.bold("\nThen start OpenCode and try:\n"));
3824
- console.log(` ${chalk4.cyan("/sdlc-dev")} Implement the next issue with Sisyphus`);
3825
- console.log(` ${chalk4.cyan("/sdlc-status")} Check issue status`);
3826
- console.log(` ${chalk4.cyan("/sdlc-info")} View toolkit configuration`);
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")}`);
3827
3915
  console.log(
3828
- chalk4.gray("\nDocumentation: https://github.com/ZebulonRouseFrantzich/opencode-sdlc")
3916
+ " (Select providers you have access to: Anthropic, OpenAI, Google, GitHub Copilot)"
3829
3917
  );
3830
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"));
3929
+ console.log();
3831
3930
  }
3832
-
3833
- // src/cli/commands/providers.ts
3834
- init_esm_shims();
3835
-
3836
- // src/cli/utils/auth-detector.ts
3837
- init_esm_shims();
3838
3931
  async function detectAuthStatus() {
3839
3932
  const opencodeConfigPath = join(homedir(), ".opencode.json");
3840
3933
  if (!existsSync(opencodeConfigPath)) {
@@ -3907,7 +4000,7 @@ async function showStatus() {
3907
4000
  const spinner = ora5("Checking authentication status...").start();
3908
4001
  const authStatus = await detectAuthStatus();
3909
4002
  spinner.stop();
3910
- console.log(chalk4.bold("\nConfigured Providers:\n"));
4003
+ console.log(chalk5.bold("\nConfigured Providers:\n"));
3911
4004
  displayProvider(
3912
4005
  "Claude",
3913
4006
  subscriptions.hasClaude,
@@ -3924,32 +4017,32 @@ async function showStatus() {
3924
4017
  );
3925
4018
  const models = configs.sdlc.models;
3926
4019
  if (models) {
3927
- console.log(chalk4.bold("\nCurrent Model Assignments:\n"));
3928
- console.log(` ${chalk4.cyan("Sisyphus:")} ${models.sisyphus || "not set"}`);
3929
- console.log(` ${chalk4.cyan("Oracle:")} ${models.oracle || "not set"}`);
3930
- console.log(` ${chalk4.cyan("Librarian:")} ${models.librarian || "not set"}`);
3931
- console.log(` ${chalk4.cyan("Frontend:")} ${models.frontend || "not set"}`);
3932
- console.log(` ${chalk4.cyan("Doc Writer:")} ${models.documentWriter || "not set"}`);
3933
- console.log(` ${chalk4.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"}`);
3934
4027
  }
3935
4028
  if (!authStatus.anthropic && subscriptions.hasClaude) {
3936
- console.log(chalk4.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"));
3937
4030
  }
3938
4031
  if (!authStatus.openai && subscriptions.hasOpenAI) {
3939
- console.log(chalk4.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
4032
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
3940
4033
  }
3941
4034
  if (!authStatus.google && subscriptions.hasGoogle) {
3942
- console.log(chalk4.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
4035
+ console.log(chalk5.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
3943
4036
  }
3944
4037
  if (!authStatus.githubCopilot && subscriptions.hasGitHubCopilot) {
3945
- console.log(chalk4.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"));
3946
4039
  }
3947
4040
  console.log();
3948
4041
  }
3949
4042
  function displayProvider(name, enabled, tier, authenticated) {
3950
- const status = enabled ? chalk4.green("\u2713 Enabled") : chalk4.gray("\u2717 Disabled");
3951
- const auth = enabled ? authenticated ? chalk4.green("\u2713 Authenticated") : chalk4.yellow("\u26A0 Not authenticated") : chalk4.gray("(disabled)");
3952
- const tierText = tier && tier !== "none" ? chalk4.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})`) : "";
3953
4046
  console.log(` ${status.padEnd(30)} ${name}${tierText}`);
3954
4047
  console.log(` ${" ".repeat(30)} Auth: ${auth}`);
3955
4048
  }
@@ -4011,7 +4104,7 @@ async function addProvider() {
4011
4104
  });
4012
4105
  }
4013
4106
  logger.success(`Provider added: ${provider}`);
4014
- console.log(chalk4.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
4107
+ console.log(chalk5.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
4015
4108
  }
4016
4109
  async function removeProvider() {
4017
4110
  logger.banner();
@@ -4028,9 +4121,6 @@ async function syncWithAuth() {
4028
4121
  logger.section("Sync with OpenCode Auth");
4029
4122
  logger.info("Not yet implemented.");
4030
4123
  }
4031
-
4032
- // src/cli/commands/uninstall.ts
4033
- init_esm_shims();
4034
4124
  async function uninstall(options) {
4035
4125
  logger.banner();
4036
4126
  logger.warn("This will remove OpenCode SDLC from your system.");
@@ -4045,16 +4135,16 @@ async function uninstall(options) {
4045
4135
  }
4046
4136
  logger.section("Uninstalling OpenCode SDLC");
4047
4137
  const fileManager = new FileManager();
4048
- const commandsSpinner = ora5("Removing bridge commands...").start();
4138
+ const commandsSpinner = ora5("Cleaning up legacy bridge commands...").start();
4049
4139
  try {
4050
4140
  const removedCommands = await fileManager.removeCommands();
4051
4141
  if (removedCommands.length > 0) {
4052
- commandsSpinner.succeed(`Removed ${removedCommands.length} bridge command(s)`);
4142
+ commandsSpinner.succeed(`Removed ${removedCommands.length} legacy command(s)`);
4053
4143
  } else {
4054
- commandsSpinner.info("No bridge commands found");
4144
+ commandsSpinner.info("No legacy bridge commands found");
4055
4145
  }
4056
4146
  } catch (err) {
4057
- commandsSpinner.fail("Failed to remove bridge commands");
4147
+ commandsSpinner.fail("Failed to remove legacy commands");
4058
4148
  logger.error(err instanceof Error ? err.message : String(err));
4059
4149
  }
4060
4150
  if (!options.keepConfig) {
@@ -4105,13 +4195,10 @@ async function uninstall(options) {
4105
4195
  logger.success("OpenCode SDLC has been uninstalled.");
4106
4196
  if (options.keepConfig) {
4107
4197
  logger.info("Configuration files were preserved.");
4108
- logger.info(`Run ${chalk4.cyan("opencode-sdlc install")} to reinstall with existing config.`);
4198
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc install")} to reinstall with existing config.`);
4109
4199
  }
4110
4200
  console.log();
4111
4201
  }
4112
-
4113
- // src/cli/commands/upgrade.ts
4114
- init_esm_shims();
4115
4202
  var execAsync3 = promisify(exec);
4116
4203
  function detectReleaseChannel(version) {
4117
4204
  if (version.includes("-beta")) return "beta";
@@ -4141,7 +4228,7 @@ async function upgrade(options) {
4141
4228
  const configs = loadExistingConfigs();
4142
4229
  if (!configs.sdlc) {
4143
4230
  logger.error("No existing Sdlc installation found.");
4144
- logger.info(`Run ${chalk4.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.`);
4145
4232
  process.exit(1);
4146
4233
  }
4147
4234
  const existingVersion = configs.sdlcVersion || "0.0.1";
@@ -4176,7 +4263,7 @@ async function upgrade(options) {
4176
4263
  logger.section("Package Versions");
4177
4264
  const updatesAvailable = updates.filter((u) => u.updateAvailable);
4178
4265
  for (const pkg of updates) {
4179
- const status = pkg.updateAvailable ? chalk4.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk4.green(pkg.current);
4266
+ const status = pkg.updateAvailable ? chalk5.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk5.green(pkg.current);
4180
4267
  logger.keyValue(pkg.name, status);
4181
4268
  }
4182
4269
  console.log();
@@ -4194,7 +4281,7 @@ async function upgrade(options) {
4194
4281
  }
4195
4282
  if (options.check) {
4196
4283
  console.log();
4197
- logger.info(`Run ${chalk4.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
4284
+ logger.info(`Run ${chalk5.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
4198
4285
  return;
4199
4286
  }
4200
4287
  const actionCount = updatesAvailable.length + (existingVersion !== VERSION ? 1 : 0);
@@ -4213,9 +4300,9 @@ async function upgrade(options) {
4213
4300
  }
4214
4301
  }
4215
4302
  logger.section("Upgrading Configuration");
4216
- console.log(chalk4.cyan(`
4303
+ console.log(chalk5.cyan(`
4217
4304
  Current version: ${existingVersion}`));
4218
- console.log(chalk4.cyan(`New version: ${VERSION}
4305
+ console.log(chalk5.cyan(`New version: ${VERSION}
4219
4306
  `));
4220
4307
  const backupSpinner = ora5("Creating backup...").start();
4221
4308
  const backups = createBackups();
@@ -4229,7 +4316,7 @@ Current version: ${existingVersion}`));
4229
4316
  if (fileMigrationResult.stateFileMoved) moved.push("state file");
4230
4317
  if (fileMigrationResult.backupsMoved > 0)
4231
4318
  moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
4232
- console.log(chalk4.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
4319
+ console.log(chalk5.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
4233
4320
  }
4234
4321
  const migrationSpinner = ora5("Applying migrations...").start();
4235
4322
  const migrationResult = migrateConfigs(
@@ -4241,15 +4328,15 @@ Current version: ${existingVersion}`));
4241
4328
  if (migrationResult.migrationsApplied.length > 0) {
4242
4329
  migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
4243
4330
  for (const migration of migrationResult.migrationsApplied) {
4244
- console.log(chalk4.gray(` \u2022 ${migration}`));
4331
+ console.log(chalk5.gray(` \u2022 ${migration}`));
4245
4332
  }
4246
4333
  } else {
4247
4334
  migrationSpinner.succeed("No migrations needed");
4248
4335
  }
4249
4336
  if (migrationResult.hasBreakingChanges && !options.yes) {
4250
- console.log(chalk4.yellow("\nBreaking changes detected:"));
4337
+ console.log(chalk5.yellow("\nBreaking changes detected:"));
4251
4338
  for (const warning of migrationResult.breakingChangeWarnings) {
4252
- console.log(chalk4.yellow(` \u26A0 ${warning}`));
4339
+ console.log(chalk5.yellow(` \u26A0 ${warning}`));
4253
4340
  }
4254
4341
  const continueUpgrade = await confirm({
4255
4342
  message: "Continue with upgrade despite breaking changes?",
@@ -4267,20 +4354,20 @@ Current version: ${existingVersion}`));
4267
4354
  const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
4268
4355
  logger.section("Preserved Configuration");
4269
4356
  if (existingSubscriptions) {
4270
- console.log(chalk4.bold("Subscriptions:"));
4357
+ console.log(chalk5.bold("Subscriptions:"));
4271
4358
  if (existingSubscriptions.hasClaude)
4272
- console.log(chalk4.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
4273
- if (existingSubscriptions.hasOpenAI) console.log(chalk4.green(" \u2713 OpenAI"));
4359
+ console.log(chalk5.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
4360
+ if (existingSubscriptions.hasOpenAI) console.log(chalk5.green(" \u2713 OpenAI"));
4274
4361
  if (existingSubscriptions.hasGoogle)
4275
- console.log(chalk4.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
4362
+ console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
4276
4363
  if (existingSubscriptions.hasGitHubCopilot)
4277
- console.log(chalk4.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
4364
+ console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
4278
4365
  }
4279
4366
  if (existingModels) {
4280
- console.log(chalk4.bold("\nModel Assignments:"));
4281
- console.log(chalk4.green(` \u2713 Sisyphus: ${existingModels.sisyphus}`));
4282
- console.log(chalk4.green(` \u2713 Oracle: ${existingModels.oracle}`));
4283
- console.log(chalk4.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}`));
4284
4371
  }
4285
4372
  console.log();
4286
4373
  const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
@@ -4307,7 +4394,7 @@ Current version: ${existingVersion}`));
4307
4394
  const fullAnswers = {
4308
4395
  subscriptions: existingSubscriptions,
4309
4396
  models: existingModels || {
4310
- sisyphus: "",
4397
+ marvin: "",
4311
4398
  oracle: "",
4312
4399
  librarian: ""
4313
4400
  },
@@ -4390,37 +4477,45 @@ Current version: ${existingVersion}`));
4390
4477
  }
4391
4478
  }
4392
4479
  }
4393
- const commandsSpinner = ora5("Updating bridge commands...").start();
4394
4480
  try {
4395
- const copiedCommands = await fileManager.copyCommands();
4396
- commandsSpinner.succeed(`Updated ${copiedCommands.length} bridge commands`);
4397
- } catch (_err) {
4398
- commandsSpinner.warn("Could not update bridge commands");
4481
+ await fileManager.removeCommands();
4482
+ } catch {
4483
+ }
4484
+ const modesConfig = migrationResult.sdlcConfig.modes;
4485
+ if (modesConfig?.enabled && modesConfig.enabled.length > 0) {
4486
+ const agentSpinner = ora5("Updating Marvin mode agents...").start();
4487
+ try {
4488
+ const projectDir = process.cwd();
4489
+ const copiedAgents = await fileManager.copyAgentModes(projectDir, modesConfig.enabled);
4490
+ if (copiedAgents.length > 0) {
4491
+ agentSpinner.succeed(`Updated ${copiedAgents.length} Marvin mode agents`);
4492
+ } else {
4493
+ agentSpinner.succeed("Marvin mode agents up to date");
4494
+ }
4495
+ } catch (_err) {
4496
+ agentSpinner.warn("Could not update Marvin mode agents");
4497
+ }
4399
4498
  }
4400
4499
  logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
4401
4500
  if (backups.sdlcBackup || backups.omoBackup || backups.opencodeBackup) {
4402
- console.log(chalk4.gray("\nBackups saved:"));
4403
- if (backups.sdlcBackup) console.log(chalk4.gray(` \u2022 ${backups.sdlcBackup}`));
4404
- if (backups.omoBackup) console.log(chalk4.gray(` \u2022 ${backups.omoBackup}`));
4405
- if (backups.opencodeBackup) console.log(chalk4.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}`));
4406
4505
  }
4407
- console.log(chalk4.gray("\nRestart OpenCode to use the upgraded configuration."));
4506
+ console.log(chalk5.gray("\nRestart OpenCode to use the upgraded configuration."));
4408
4507
  console.log();
4409
4508
  }
4410
4509
 
4411
4510
  // src/cli/index.ts
4412
4511
  var program = new Command();
4413
4512
  program.name("opencode-sdlc").description(
4414
- `${chalk4.cyan(DISPLAY_NAME)} - ${TAGLINE}
4513
+ `${chalk5.cyan(DISPLAY_NAME)} - ${TAGLINE}
4415
4514
  TDD-driven development toolkit with GitHub Issues integration for OpenCode`
4416
4515
  ).version(VERSION);
4417
- program.command("install").description("Install and configure OpenCode SDLC").option(
4418
- "-p, --preset <preset>",
4419
- "Use a preset configuration (minimal, standard, strict-tdd, event-modeling, enterprise, solo-quick, copilot-only)",
4420
- "standard"
4421
- ).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) => {
4422
- if (options.listPresets) {
4423
- 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();
4424
4519
  return;
4425
4520
  }
4426
4521
  await install(options);
@@ -4432,16 +4527,16 @@ program.command("info").description("Show current configuration and status").act
4432
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) => {
4433
4528
  await providers({ action });
4434
4529
  });
4435
- function displayPresets() {
4436
- console.log(chalk4.bold.cyan("\nAvailable Presets:\n"));
4437
- const presets = listPresets();
4438
- for (const preset of presets) {
4439
- console.log(chalk4.bold(` ${preset.name}`));
4440
- console.log(chalk4.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}`));
4441
4536
  console.log();
4442
4537
  }
4443
- console.log(chalk4.gray("Usage: opencode-sdlc install --preset <name>"));
4444
- console.log(chalk4.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"));
4445
4540
  }
4446
4541
  program.parse();
4447
4542
  //# sourceMappingURL=index.js.map