opencode-sdlc-plugin 0.3.2 → 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -17
- package/config/presets/copilot-only.json +12 -5
- package/config/presets/enterprise.json +7 -7
- package/config/presets/event-modeling.json +6 -6
- package/config/presets/minimal.json +2 -2
- package/config/presets/solo-quick.json +2 -2
- package/config/presets/standard.json +6 -6
- package/config/presets/strict-tdd.json +7 -7
- package/config/schemas/athena.schema.json +4 -4
- package/config/schemas/sdlc.schema.json +101 -5
- package/dist/cli/index.js +781 -279
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +427 -65
- package/dist/index.js +5239 -1924
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.js +5206 -1930
- package/dist/plugin/index.js.map +1 -1
- package/package.json +1 -1
- package/prompts/agents/adr.md +234 -0
- package/prompts/agents/architect.md +204 -0
- package/prompts/agents/design-facilitator.md +237 -0
- package/prompts/agents/discovery.md +260 -0
- package/prompts/agents/domain.md +148 -34
- package/prompts/agents/file-updater.md +132 -0
- package/prompts/agents/green.md +119 -40
- package/prompts/agents/gwt.md +352 -0
- package/prompts/agents/model-checker.md +332 -0
- package/prompts/agents/red.md +112 -21
- package/prompts/agents/story.md +196 -0
- package/prompts/agents/ux.md +239 -0
- package/prompts/agents/workflow-designer.md +386 -0
- package/prompts/modes/architect.md +219 -0
- package/prompts/modes/build.md +150 -0
- package/prompts/modes/model.md +211 -0
- package/prompts/modes/plan.md +186 -0
- package/prompts/modes/pm.md +269 -0
- package/prompts/modes/prd.md +238 -0
- package/commands/sdlc-adr.md +0 -265
- package/commands/sdlc-debug.md +0 -376
- package/commands/sdlc-design.md +0 -246
- package/commands/sdlc-dev.md +0 -544
- package/commands/sdlc-info.md +0 -325
- package/commands/sdlc-parallel.md +0 -283
- package/commands/sdlc-recall.md +0 -213
- package/commands/sdlc-remember.md +0 -136
- package/commands/sdlc-research.md +0 -343
- package/commands/sdlc-review.md +0 -265
- package/commands/sdlc-status.md +0 -297
package/dist/cli/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { confirm, select, input, number, checkbox } from '@inquirer/prompts';
|
|
5
|
-
import
|
|
5
|
+
import chalk6 from 'chalk';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { existsSync, readFileSync, mkdirSync, copyFileSync, unlinkSync, readdirSync, renameSync, writeFileSync } from 'fs';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
9
|
import { exec, execSync } from 'child_process';
|
|
10
10
|
import ora5 from 'ora';
|
|
11
|
-
import { mkdir, writeFile,
|
|
11
|
+
import { mkdir, writeFile, copyFile, readdir, rm } from 'fs/promises';
|
|
12
12
|
import { promisify } from 'util';
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import * as semver from 'semver';
|
|
@@ -122,59 +122,65 @@ function createModelChoices(models) {
|
|
|
122
122
|
}
|
|
123
123
|
function getSuggestedModel(role, _subscriptions, availableModels) {
|
|
124
124
|
const suggestions = {
|
|
125
|
-
|
|
125
|
+
marvin: [
|
|
126
|
+
"openai/gpt-5.2-codex",
|
|
126
127
|
"anthropic/claude-sonnet-4-5-thinking",
|
|
127
128
|
"anthropic/claude-opus-4-5-thinking",
|
|
128
129
|
"openai/gpt-5.1-high",
|
|
129
|
-
"google/gemini-
|
|
130
|
+
"google/gemini-3-pro-preview",
|
|
131
|
+
"github-copilot/gpt-5.2-codex",
|
|
130
132
|
"github-copilot/claude-sonnet-4.5",
|
|
131
|
-
"github-copilot/gpt-5.1"
|
|
132
|
-
"github-copilot/gemini-2.5-pro"
|
|
133
|
+
"github-copilot/gpt-5.1"
|
|
133
134
|
],
|
|
134
135
|
oracle: [
|
|
136
|
+
"openai/gpt-5.2",
|
|
135
137
|
"openai/gpt-5.1-high",
|
|
136
138
|
"anthropic/claude-opus-4-5-thinking",
|
|
137
139
|
"anthropic/claude-sonnet-4-5-thinking",
|
|
138
|
-
"google/gemini-
|
|
140
|
+
"google/gemini-3-pro-preview",
|
|
141
|
+
"github-copilot/gpt-5.2",
|
|
139
142
|
"github-copilot/gpt-5.1",
|
|
140
|
-
"github-copilot/claude-opus-4.5"
|
|
141
|
-
"github-copilot/claude-sonnet-4.5"
|
|
143
|
+
"github-copilot/claude-opus-4.5"
|
|
142
144
|
],
|
|
143
145
|
librarian: [
|
|
144
146
|
"anthropic/claude-sonnet-4-5",
|
|
145
147
|
"openai/gpt-4o",
|
|
146
148
|
"google/gemini-2.5-flash",
|
|
149
|
+
"google/gemini-3-flash-preview",
|
|
147
150
|
"github-copilot/claude-haiku-4.5",
|
|
148
151
|
"github-copilot/gpt-5-mini"
|
|
149
152
|
],
|
|
150
153
|
frontend: [
|
|
151
154
|
"anthropic/claude-sonnet-4-5",
|
|
152
|
-
"
|
|
155
|
+
"openai/gpt-5.2-codex",
|
|
156
|
+
"google/gemini-3-pro-preview",
|
|
153
157
|
"openai/gpt-4o",
|
|
154
158
|
"github-copilot/claude-sonnet-4.5",
|
|
155
|
-
"github-copilot/
|
|
159
|
+
"github-copilot/gpt-5.2-codex"
|
|
156
160
|
],
|
|
157
161
|
documentWriter: [
|
|
162
|
+
"google/gemini-3-pro-preview",
|
|
158
163
|
"google/gemini-2.5-pro",
|
|
159
164
|
"anthropic/claude-sonnet-4-5",
|
|
160
165
|
"openai/gpt-4o",
|
|
161
|
-
"github-copilot/gemini-
|
|
166
|
+
"github-copilot/gemini-3-pro",
|
|
162
167
|
"github-copilot/claude-sonnet-4.5"
|
|
163
168
|
],
|
|
164
169
|
multimodalLooker: [
|
|
170
|
+
"google/gemini-3-flash-preview",
|
|
165
171
|
"google/gemini-2.5-flash",
|
|
166
172
|
"openai/gpt-4o",
|
|
167
173
|
"anthropic/claude-sonnet-4-5",
|
|
168
|
-
"github-copilot/gemini-3-flash",
|
|
174
|
+
"github-copilot/gemini-3-flash-preview",
|
|
169
175
|
"github-copilot/gpt-5-mini"
|
|
170
176
|
],
|
|
171
177
|
explore: [
|
|
178
|
+
"google/gemini-3-flash-preview",
|
|
172
179
|
"google/gemini-2.5-flash",
|
|
173
180
|
"anthropic/claude-sonnet-4-5",
|
|
174
181
|
"openai/gpt-4o",
|
|
175
|
-
"github-copilot/
|
|
176
|
-
"github-copilot/
|
|
177
|
-
"github-copilot/gemini-3-flash"
|
|
182
|
+
"github-copilot/gemini-3-flash-preview",
|
|
183
|
+
"github-copilot/claude-haiku-4.5"
|
|
178
184
|
]
|
|
179
185
|
};
|
|
180
186
|
const roleDefaults = suggestions[role] || [];
|
|
@@ -194,9 +200,9 @@ async function gatherModels(subscriptions, defaults, customModels) {
|
|
|
194
200
|
);
|
|
195
201
|
}
|
|
196
202
|
const choices = createModelChoices(availableModels);
|
|
197
|
-
const
|
|
198
|
-
defaults?.
|
|
199
|
-
"
|
|
203
|
+
const marvinDefault = getValidModelOrFallback(
|
|
204
|
+
defaults?.marvin,
|
|
205
|
+
"marvin",
|
|
200
206
|
subscriptions,
|
|
201
207
|
availableModels
|
|
202
208
|
);
|
|
@@ -212,10 +218,10 @@ async function gatherModels(subscriptions, defaults, customModels) {
|
|
|
212
218
|
subscriptions,
|
|
213
219
|
availableModels
|
|
214
220
|
);
|
|
215
|
-
const
|
|
216
|
-
message: "Model for
|
|
221
|
+
const marvin = await select({
|
|
222
|
+
message: "Model for Marvin (main orchestrator - implements stories)?",
|
|
217
223
|
choices,
|
|
218
|
-
default:
|
|
224
|
+
default: marvinDefault
|
|
219
225
|
});
|
|
220
226
|
const oracle = await select({
|
|
221
227
|
message: "Model for Oracle (debugging and complex reasoning)?",
|
|
@@ -246,7 +252,7 @@ async function gatherModels(subscriptions, defaults, customModels) {
|
|
|
246
252
|
availableModels
|
|
247
253
|
);
|
|
248
254
|
return {
|
|
249
|
-
|
|
255
|
+
marvin,
|
|
250
256
|
oracle,
|
|
251
257
|
librarian,
|
|
252
258
|
frontend,
|
|
@@ -267,7 +273,7 @@ function validatePresetModels(presetModels, subscriptions, customModels) {
|
|
|
267
273
|
);
|
|
268
274
|
}
|
|
269
275
|
};
|
|
270
|
-
checkModel(presetModels.
|
|
276
|
+
checkModel(presetModels.marvin, "Marvin");
|
|
271
277
|
checkModel(presetModels.oracle, "Oracle");
|
|
272
278
|
checkModel(presetModels.librarian, "Librarian");
|
|
273
279
|
checkModel(presetModels.frontend, "Frontend");
|
|
@@ -317,7 +323,7 @@ var init_models = __esm({
|
|
|
317
323
|
id: "openai/gpt-5.1",
|
|
318
324
|
name: "GPT-5.1",
|
|
319
325
|
provider: "openai",
|
|
320
|
-
description: "
|
|
326
|
+
description: "GPT-5.1 base model"
|
|
321
327
|
},
|
|
322
328
|
{
|
|
323
329
|
id: "openai/gpt-5.1-high",
|
|
@@ -325,12 +331,24 @@ var init_models = __esm({
|
|
|
325
331
|
provider: "openai",
|
|
326
332
|
description: "GPT-5.1 with high reasoning effort"
|
|
327
333
|
},
|
|
334
|
+
{
|
|
335
|
+
id: "openai/gpt-5.2",
|
|
336
|
+
name: "GPT-5.2",
|
|
337
|
+
provider: "openai",
|
|
338
|
+
description: "Latest GPT model"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "openai/gpt-5.2-codex",
|
|
342
|
+
name: "GPT-5.2 Codex",
|
|
343
|
+
provider: "openai",
|
|
344
|
+
description: "Code-optimized GPT-5.2"
|
|
345
|
+
},
|
|
328
346
|
// Google models
|
|
329
347
|
{
|
|
330
348
|
id: "google/gemini-2.5-pro",
|
|
331
349
|
name: "Gemini 2.5 Pro",
|
|
332
350
|
provider: "google",
|
|
333
|
-
description: "
|
|
351
|
+
description: "Gemini 2.5 Pro model"
|
|
334
352
|
},
|
|
335
353
|
{
|
|
336
354
|
id: "google/gemini-2.5-flash",
|
|
@@ -339,10 +357,16 @@ var init_models = __esm({
|
|
|
339
357
|
description: "Fast Gemini model"
|
|
340
358
|
},
|
|
341
359
|
{
|
|
342
|
-
id: "google/gemini-
|
|
343
|
-
name: "Gemini
|
|
360
|
+
id: "google/gemini-3-flash-preview",
|
|
361
|
+
name: "Gemini 3 Flash (Preview)",
|
|
344
362
|
provider: "google",
|
|
345
|
-
description: "
|
|
363
|
+
description: "Gemini 3 Flash preview - fast and efficient"
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
id: "google/gemini-3-pro-preview",
|
|
367
|
+
name: "Gemini 3 Pro (Preview)",
|
|
368
|
+
provider: "google",
|
|
369
|
+
description: "Latest Gemini 3 Pro preview"
|
|
346
370
|
},
|
|
347
371
|
// GitHub Copilot models (routed through Copilot - smaller context, no thinking)
|
|
348
372
|
// Free tier models
|
|
@@ -401,6 +425,12 @@ var init_models = __esm({
|
|
|
401
425
|
provider: "github-copilot",
|
|
402
426
|
description: "Latest GPT through GitHub Copilot"
|
|
403
427
|
},
|
|
428
|
+
{
|
|
429
|
+
id: "github-copilot/gpt-5.2-codex",
|
|
430
|
+
name: "GPT-5.2 Codex (via Copilot)",
|
|
431
|
+
provider: "github-copilot",
|
|
432
|
+
description: "Code-optimized GPT-5.2 through GitHub Copilot"
|
|
433
|
+
},
|
|
404
434
|
{
|
|
405
435
|
id: "github-copilot/gemini-2.5-pro",
|
|
406
436
|
name: "Gemini 2.5 Pro (via Copilot)",
|
|
@@ -507,11 +537,11 @@ var execAsync = promisify(exec);
|
|
|
507
537
|
function getPackageRoot() {
|
|
508
538
|
const currentFileDir = dirname(fileURLToPath(import.meta.url));
|
|
509
539
|
const bundledRoot = join(currentFileDir, "..", "..");
|
|
510
|
-
if (existsSync(join(bundledRoot, "
|
|
540
|
+
if (existsSync(join(bundledRoot, "prompts"))) {
|
|
511
541
|
return bundledRoot;
|
|
512
542
|
}
|
|
513
543
|
const devRoot = join(currentFileDir, "..", "..", "..");
|
|
514
|
-
if (existsSync(join(devRoot, "
|
|
544
|
+
if (existsSync(join(devRoot, "prompts"))) {
|
|
515
545
|
return devRoot;
|
|
516
546
|
}
|
|
517
547
|
return bundledRoot;
|
|
@@ -616,26 +646,73 @@ var FileManager = class {
|
|
|
616
646
|
}
|
|
617
647
|
/**
|
|
618
648
|
* Copy bridge commands from package to config directory
|
|
649
|
+
*
|
|
650
|
+
* @deprecated Bridge commands have been removed in v1.0.
|
|
651
|
+
* This method now returns an empty array for backwards compatibility.
|
|
652
|
+
* Use mode-based workflow instead.
|
|
619
653
|
*/
|
|
620
654
|
async copyCommands() {
|
|
621
|
-
|
|
622
|
-
|
|
655
|
+
return [];
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Copy Marvin mode agent files to project .opencode/agent directory
|
|
659
|
+
*
|
|
660
|
+
* @param projectDir - The project directory to copy to
|
|
661
|
+
* @param enabledModes - Array of enabled modes to copy
|
|
662
|
+
* @returns Array of copied file names
|
|
663
|
+
*/
|
|
664
|
+
async copyAgentModes(projectDir, enabledModes) {
|
|
665
|
+
const agentDir = join(projectDir, ".opencode", "agent");
|
|
666
|
+
await this.ensureDir(agentDir);
|
|
623
667
|
const packageRoot = getPackageRoot();
|
|
624
|
-
const
|
|
668
|
+
const sourceModesDir = join(packageRoot, "prompts", "modes");
|
|
625
669
|
const copiedFiles = [];
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
670
|
+
const modeFileMap = {
|
|
671
|
+
build: "build.md",
|
|
672
|
+
discover: "plan.md",
|
|
673
|
+
// Discover mode overrides Plan agent
|
|
674
|
+
model: "model.md",
|
|
675
|
+
prd: "prd.md",
|
|
676
|
+
architect: "architect.md",
|
|
677
|
+
pm: "pm.md"
|
|
678
|
+
};
|
|
679
|
+
if (existsSync(sourceModesDir)) {
|
|
680
|
+
for (const mode of enabledModes) {
|
|
681
|
+
const sourceFile = modeFileMap[mode];
|
|
682
|
+
if (!sourceFile) continue;
|
|
683
|
+
const sourcePath = join(sourceModesDir, sourceFile);
|
|
684
|
+
if (existsSync(sourcePath)) {
|
|
685
|
+
const destPath = join(agentDir, sourceFile);
|
|
632
686
|
await copyFile(sourcePath, destPath);
|
|
633
|
-
copiedFiles.push(
|
|
687
|
+
copiedFiles.push(sourceFile);
|
|
634
688
|
}
|
|
635
689
|
}
|
|
636
690
|
}
|
|
637
691
|
return copiedFiles;
|
|
638
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* Remove agent mode files from project .opencode/agent directory
|
|
695
|
+
*
|
|
696
|
+
* @param projectDir - The project directory to remove from
|
|
697
|
+
* @returns Array of removed file names
|
|
698
|
+
*/
|
|
699
|
+
async removeAgentModes(projectDir) {
|
|
700
|
+
const agentDir = join(projectDir, ".opencode", "agent");
|
|
701
|
+
const removedFiles = [];
|
|
702
|
+
if (!existsSync(agentDir)) {
|
|
703
|
+
return removedFiles;
|
|
704
|
+
}
|
|
705
|
+
const modeFiles = ["build.md", "plan.md", "model.md", "prd.md", "architect.md", "pm.md"];
|
|
706
|
+
const files = await readdir(agentDir);
|
|
707
|
+
for (const file of files) {
|
|
708
|
+
if (modeFiles.includes(file)) {
|
|
709
|
+
const filePath = join(agentDir, file);
|
|
710
|
+
await rm(filePath);
|
|
711
|
+
removedFiles.push(file);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return removedFiles;
|
|
715
|
+
}
|
|
639
716
|
/**
|
|
640
717
|
* Remove bridge commands from config directory
|
|
641
718
|
*/
|
|
@@ -727,39 +804,39 @@ var logger = {
|
|
|
727
804
|
* Log an informational message
|
|
728
805
|
*/
|
|
729
806
|
info: (message) => {
|
|
730
|
-
console.log(
|
|
807
|
+
console.log(chalk6.blue("i"), message);
|
|
731
808
|
},
|
|
732
809
|
/**
|
|
733
810
|
* Log a success message
|
|
734
811
|
*/
|
|
735
812
|
success: (message) => {
|
|
736
|
-
console.log(
|
|
813
|
+
console.log(chalk6.green("\u2713"), message);
|
|
737
814
|
},
|
|
738
815
|
/**
|
|
739
816
|
* Log a warning message
|
|
740
817
|
*/
|
|
741
818
|
warn: (message) => {
|
|
742
|
-
console.log(
|
|
819
|
+
console.log(chalk6.yellow("!"), message);
|
|
743
820
|
},
|
|
744
821
|
/**
|
|
745
822
|
* Log an error message
|
|
746
823
|
*/
|
|
747
824
|
error: (message) => {
|
|
748
|
-
console.log(
|
|
825
|
+
console.log(chalk6.red("\u2716"), message);
|
|
749
826
|
},
|
|
750
827
|
/**
|
|
751
828
|
* Log a debug message (only when DEBUG env var is set)
|
|
752
829
|
*/
|
|
753
830
|
debug: (message) => {
|
|
754
831
|
if (process.env.DEBUG) {
|
|
755
|
-
console.log(
|
|
832
|
+
console.log(chalk6.gray("[debug]"), message);
|
|
756
833
|
}
|
|
757
834
|
},
|
|
758
835
|
/**
|
|
759
836
|
* Log a step in a process
|
|
760
837
|
*/
|
|
761
838
|
step: (step, total, message) => {
|
|
762
|
-
console.log(
|
|
839
|
+
console.log(chalk6.cyan(`[${step}/${total}]`), message);
|
|
763
840
|
},
|
|
764
841
|
/**
|
|
765
842
|
* Log a blank line
|
|
@@ -772,7 +849,7 @@ var logger = {
|
|
|
772
849
|
*/
|
|
773
850
|
section: (title) => {
|
|
774
851
|
console.log();
|
|
775
|
-
console.log(
|
|
852
|
+
console.log(chalk6.bold(title));
|
|
776
853
|
console.log();
|
|
777
854
|
},
|
|
778
855
|
/**
|
|
@@ -780,21 +857,21 @@ var logger = {
|
|
|
780
857
|
*/
|
|
781
858
|
keyValue: (key, value, indent = 0) => {
|
|
782
859
|
const padding = " ".repeat(indent);
|
|
783
|
-
console.log(`${padding}${
|
|
860
|
+
console.log(`${padding}${chalk6.gray(`${key}:`)} ${value}`);
|
|
784
861
|
},
|
|
785
862
|
/**
|
|
786
863
|
* Log a list item
|
|
787
864
|
*/
|
|
788
865
|
listItem: (item, indent = 0) => {
|
|
789
866
|
const padding = " ".repeat(indent);
|
|
790
|
-
console.log(`${padding}${
|
|
867
|
+
console.log(`${padding}${chalk6.gray("-")} ${item}`);
|
|
791
868
|
},
|
|
792
869
|
/**
|
|
793
870
|
* Display the Sdlc banner
|
|
794
871
|
*/
|
|
795
872
|
banner: () => {
|
|
796
873
|
console.log(
|
|
797
|
-
|
|
874
|
+
chalk6.cyan(`
|
|
798
875
|
\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
876
|
\u2551 OPENCODE ATHENA \u2551
|
|
800
877
|
\u2551 Strategic Wisdom Meets Practical Execution \u2551
|
|
@@ -810,7 +887,7 @@ var logger = {
|
|
|
810
887
|
successBanner: (message) => {
|
|
811
888
|
const line = "\u2550".repeat(message.length + 4);
|
|
812
889
|
console.log(
|
|
813
|
-
|
|
890
|
+
chalk6.green(`
|
|
814
891
|
\u2554${line}\u2557
|
|
815
892
|
\u2551 ${message} \u2551
|
|
816
893
|
\u255A${line}\u255D
|
|
@@ -938,13 +1015,16 @@ init_esm_shims();
|
|
|
938
1015
|
|
|
939
1016
|
// src/shared/schemas.ts
|
|
940
1017
|
init_esm_shims();
|
|
1018
|
+
var ProviderAuthMethodSchema = z.enum(["subscription", "api", "none"]);
|
|
941
1019
|
var SubscriptionSchema = z.object({
|
|
942
1020
|
claude: z.object({
|
|
943
1021
|
enabled: z.boolean(),
|
|
1022
|
+
authMethod: ProviderAuthMethodSchema.default("subscription"),
|
|
944
1023
|
tier: z.enum(["max5x", "max20x", "pro", "none"])
|
|
945
1024
|
}),
|
|
946
1025
|
openai: z.object({
|
|
947
|
-
enabled: z.boolean()
|
|
1026
|
+
enabled: z.boolean(),
|
|
1027
|
+
authMethod: ProviderAuthMethodSchema.default("subscription")
|
|
948
1028
|
}),
|
|
949
1029
|
google: z.object({
|
|
950
1030
|
enabled: z.boolean(),
|
|
@@ -956,6 +1036,17 @@ var SubscriptionSchema = z.object({
|
|
|
956
1036
|
enabledModels: z.array(z.string()).optional()
|
|
957
1037
|
})
|
|
958
1038
|
});
|
|
1039
|
+
var SdlcModeSchema = z.enum(["discover", "model", "architect", "pm", "build"]);
|
|
1040
|
+
var ModesConfigSchema = z.object({
|
|
1041
|
+
default: SdlcModeSchema.default("build").describe("Default mode on startup"),
|
|
1042
|
+
eventModeling: z.boolean().default(true).describe("true=Model mode, false=PRD mode"),
|
|
1043
|
+
enabled: z.array(SdlcModeSchema).default(["discover", "model", "architect", "pm", "build"]).describe("Array of enabled modes")
|
|
1044
|
+
});
|
|
1045
|
+
var MemoryBackendSchema = z.enum(["memento", "stateless"]);
|
|
1046
|
+
var MemoryConfigSchema = z.object({
|
|
1047
|
+
backend: MemoryBackendSchema.default("stateless").describe("Memory backend type"),
|
|
1048
|
+
checkpointTtl: z.number().int().positive().default(86400).describe("How long to keep checkpoints in seconds")
|
|
1049
|
+
});
|
|
959
1050
|
var GitHubStatusSchema = z.enum(["Backlog", "Ready", "In progress", "In review", "Done"]);
|
|
960
1051
|
var GitHubConfigSchema = z.object({
|
|
961
1052
|
owner: z.string().describe("GitHub repository owner (user or org)"),
|
|
@@ -975,7 +1066,11 @@ var TddConfigSchema = z.object({
|
|
|
975
1066
|
mutationTesting: MutationTestingConfigSchema.default({
|
|
976
1067
|
enabled: false,
|
|
977
1068
|
requiredScore: 80
|
|
978
|
-
})
|
|
1069
|
+
}),
|
|
1070
|
+
// New v1.0.0 fields
|
|
1071
|
+
domainVetoEnabled: z.boolean().default(true).describe("Enable domain agent veto power over type changes"),
|
|
1072
|
+
debateRounds: z.number().int().min(1).max(5).default(2).describe("Max debate rounds before escalation to user"),
|
|
1073
|
+
requireVerification: z.boolean().default(true).describe("Require test output evidence before phase transitions")
|
|
979
1074
|
});
|
|
980
1075
|
var EventModelingConfigSchema = z.object({
|
|
981
1076
|
enabled: z.boolean().default(false).describe("Enable Event Modeling workflow"),
|
|
@@ -1040,7 +1135,7 @@ var RoutingConfigSchema = z.object({
|
|
|
1040
1135
|
gemini: z.array(LLMProviderSchema).optional()
|
|
1041
1136
|
}),
|
|
1042
1137
|
agentOverrides: z.object({
|
|
1043
|
-
|
|
1138
|
+
marvin: AgentRoutingSchema.optional(),
|
|
1044
1139
|
oracle: AgentRoutingSchema.optional(),
|
|
1045
1140
|
librarian: AgentRoutingSchema.optional(),
|
|
1046
1141
|
frontend: AgentRoutingSchema.optional(),
|
|
@@ -1051,7 +1146,9 @@ var RoutingConfigSchema = z.object({
|
|
|
1051
1146
|
autoFallback: z.boolean(),
|
|
1052
1147
|
retryPeriodMs: z.number().min(0),
|
|
1053
1148
|
notifyOnRateLimit: z.boolean()
|
|
1054
|
-
})
|
|
1149
|
+
}),
|
|
1150
|
+
// New v1.0.0 field
|
|
1151
|
+
classifierModel: z.string().default("anthropic/claude-haiku").describe("Model for sdlc_classify_request tool (use cheapest available)")
|
|
1055
1152
|
});
|
|
1056
1153
|
var ThinkingLevelSchema = z.enum(["off", "low", "medium", "high"]);
|
|
1057
1154
|
var AgentSettingsSchema = z.object({
|
|
@@ -1069,15 +1166,75 @@ var CustomModelDefinitionSchema = z.object({
|
|
|
1069
1166
|
supportsTemperature: z.boolean().optional()
|
|
1070
1167
|
}).optional()
|
|
1071
1168
|
});
|
|
1169
|
+
var SubagentModelSchema = z.union([
|
|
1170
|
+
z.string().describe("Full model identifier (e.g., 'anthropic/claude-sonnet-4-20250514')"),
|
|
1171
|
+
z.literal("inherit").describe("Inherit model from marvin")
|
|
1172
|
+
]);
|
|
1173
|
+
z.enum([
|
|
1174
|
+
// TDD agents
|
|
1175
|
+
"red",
|
|
1176
|
+
"green",
|
|
1177
|
+
"domain",
|
|
1178
|
+
"refactor",
|
|
1179
|
+
// Event modeling agents
|
|
1180
|
+
"discovery",
|
|
1181
|
+
"workflow",
|
|
1182
|
+
"gwt",
|
|
1183
|
+
"model-checker",
|
|
1184
|
+
// Architecture agents
|
|
1185
|
+
"architect",
|
|
1186
|
+
"adr",
|
|
1187
|
+
"design-facilitator",
|
|
1188
|
+
// Planning agents
|
|
1189
|
+
"story",
|
|
1190
|
+
"pm",
|
|
1191
|
+
"analyst",
|
|
1192
|
+
// Review agents
|
|
1193
|
+
"reviewer",
|
|
1194
|
+
"ux",
|
|
1195
|
+
"mutation"
|
|
1196
|
+
]);
|
|
1197
|
+
var SubagentModelsSchema = z.object({
|
|
1198
|
+
// TDD agents
|
|
1199
|
+
red: SubagentModelSchema.optional().describe("Model for TDD red phase agent"),
|
|
1200
|
+
green: SubagentModelSchema.optional().describe("Model for TDD green phase agent"),
|
|
1201
|
+
domain: SubagentModelSchema.optional().describe("Model for domain modeling agent"),
|
|
1202
|
+
refactor: SubagentModelSchema.optional().describe("Model for refactoring agent"),
|
|
1203
|
+
// Event modeling agents
|
|
1204
|
+
discovery: SubagentModelSchema.optional().describe(
|
|
1205
|
+
"Model for discovery/domain exploration agent"
|
|
1206
|
+
),
|
|
1207
|
+
workflow: SubagentModelSchema.optional().describe("Model for workflow design agent"),
|
|
1208
|
+
gwt: SubagentModelSchema.optional().describe("Model for GWT specification agent"),
|
|
1209
|
+
"model-checker": SubagentModelSchema.optional().describe(
|
|
1210
|
+
"Model for event model validation agent"
|
|
1211
|
+
),
|
|
1212
|
+
// Architecture agents
|
|
1213
|
+
architect: SubagentModelSchema.optional().describe("Model for architect agent"),
|
|
1214
|
+
adr: SubagentModelSchema.optional().describe("Model for ADR creation agent"),
|
|
1215
|
+
"design-facilitator": SubagentModelSchema.optional().describe(
|
|
1216
|
+
"Model for design facilitation agent"
|
|
1217
|
+
),
|
|
1218
|
+
// Planning agents
|
|
1219
|
+
story: SubagentModelSchema.optional().describe("Model for story/issue planning agent"),
|
|
1220
|
+
pm: SubagentModelSchema.optional().describe("Model for PM agent"),
|
|
1221
|
+
analyst: SubagentModelSchema.optional().describe("Model for business analyst agent"),
|
|
1222
|
+
// Review agents
|
|
1223
|
+
reviewer: SubagentModelSchema.optional().describe("Model for code reviewer agent"),
|
|
1224
|
+
ux: SubagentModelSchema.optional().describe("Model for UX review agent"),
|
|
1225
|
+
mutation: SubagentModelSchema.optional().describe("Model for mutation testing agent")
|
|
1226
|
+
});
|
|
1072
1227
|
var ModelsSchema = z.object({
|
|
1073
|
-
|
|
1228
|
+
marvin: z.string().describe("Model for main orchestrator agent"),
|
|
1074
1229
|
oracle: z.string().describe("Model for debugging/reasoning agent"),
|
|
1075
1230
|
librarian: z.string().describe("Model for research/documentation agent"),
|
|
1076
1231
|
frontend: z.string().optional().describe("Model for UI/UX agent"),
|
|
1077
1232
|
documentWriter: z.string().optional().describe("Model for documentation generation agent"),
|
|
1078
1233
|
multimodalLooker: z.string().optional().describe("Model for image analysis agent"),
|
|
1234
|
+
// New v1.0.0 field for subagent models
|
|
1235
|
+
subagents: SubagentModelsSchema.optional().describe("Per-subagent model overrides"),
|
|
1079
1236
|
settings: z.object({
|
|
1080
|
-
|
|
1237
|
+
marvin: AgentSettingsSchema.optional(),
|
|
1081
1238
|
oracle: AgentSettingsSchema.optional(),
|
|
1082
1239
|
librarian: AgentSettingsSchema.optional(),
|
|
1083
1240
|
frontend: AgentSettingsSchema.optional(),
|
|
@@ -1092,6 +1249,9 @@ var SdlcConfigSchema = z.object({
|
|
|
1092
1249
|
version: z.string(),
|
|
1093
1250
|
subscriptions: SubscriptionSchema,
|
|
1094
1251
|
models: ModelsSchema,
|
|
1252
|
+
// New v1.0.0 configuration
|
|
1253
|
+
modes: ModesConfigSchema.optional(),
|
|
1254
|
+
memory: MemoryConfigSchema.optional(),
|
|
1095
1255
|
// New v0.3.0+ configuration (optional during migration)
|
|
1096
1256
|
github: GitHubConfigSchema.optional(),
|
|
1097
1257
|
tdd: TddConfigSchema.optional(),
|
|
@@ -1278,23 +1438,6 @@ async function doctor(options) {
|
|
|
1278
1438
|
}
|
|
1279
1439
|
}
|
|
1280
1440
|
});
|
|
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
1441
|
if (sdlcConfigValid.valid) {
|
|
1299
1442
|
const config = fileManager.readJsonFile(CONFIG_PATHS.globalSdlcConfig);
|
|
1300
1443
|
const configVersion = config.version || "0.0.0";
|
|
@@ -1374,16 +1517,16 @@ async function doctor(options) {
|
|
|
1374
1517
|
switch (result.status) {
|
|
1375
1518
|
case "pass":
|
|
1376
1519
|
icon = "\u2713";
|
|
1377
|
-
color =
|
|
1520
|
+
color = chalk6.green;
|
|
1378
1521
|
break;
|
|
1379
1522
|
case "warn":
|
|
1380
1523
|
icon = "!";
|
|
1381
|
-
color =
|
|
1524
|
+
color = chalk6.yellow;
|
|
1382
1525
|
hasWarnings = true;
|
|
1383
1526
|
break;
|
|
1384
1527
|
case "fail":
|
|
1385
1528
|
icon = "\u2716";
|
|
1386
|
-
color =
|
|
1529
|
+
color = chalk6.red;
|
|
1387
1530
|
hasFailures = true;
|
|
1388
1531
|
if (result.fix) {
|
|
1389
1532
|
fixableIssues.push(result);
|
|
@@ -1411,7 +1554,7 @@ async function doctor(options) {
|
|
|
1411
1554
|
logger.blank();
|
|
1412
1555
|
logger.info("Run 'opencode-sdlc doctor' again to verify fixes.");
|
|
1413
1556
|
} else if (fixableIssues.length > 0) {
|
|
1414
|
-
logger.info(`Run ${
|
|
1557
|
+
logger.info(`Run ${chalk6.cyan("opencode-sdlc doctor --fix")} to attempt automatic fixes.`);
|
|
1415
1558
|
}
|
|
1416
1559
|
} else if (hasWarnings) {
|
|
1417
1560
|
logger.warn("Some checks have warnings, but Sdlc should work.");
|
|
@@ -1428,32 +1571,32 @@ async function info() {
|
|
|
1428
1571
|
const sdlcConfig = fileManager.readJsonFile(CONFIG_PATHS.globalSdlcConfig);
|
|
1429
1572
|
if (!sdlcConfig) {
|
|
1430
1573
|
logger.warn("OpenCode SDLC is not installed.");
|
|
1431
|
-
logger.info(`Run ${
|
|
1574
|
+
logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to get started.`);
|
|
1432
1575
|
return;
|
|
1433
1576
|
}
|
|
1434
1577
|
logger.section("Version Information");
|
|
1435
1578
|
logger.keyValue("Sdlc Version", sdlcConfig.version || VERSION);
|
|
1436
1579
|
logger.section("Prerequisites");
|
|
1437
1580
|
const prereqs = await checkPrerequisites();
|
|
1438
|
-
const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ?
|
|
1581
|
+
const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ? chalk6.green("\u2713") : chalk6.yellow("!") : chalk6.red("\u2716");
|
|
1439
1582
|
logger.keyValue("Node.js", `${nodeStatus} ${prereqs.node.version || "not found"}`);
|
|
1440
|
-
const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ?
|
|
1583
|
+
const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ? chalk6.green("\u2713") : chalk6.yellow("!") : chalk6.red("\u2716");
|
|
1441
1584
|
logger.keyValue("OpenCode", `${opencodeStatus} ${prereqs.opencode.version || "not found"}`);
|
|
1442
1585
|
logger.section("Configured Providers");
|
|
1443
|
-
const claudeStatus = sdlcConfig.subscriptions.claude.enabled ?
|
|
1586
|
+
const claudeStatus = sdlcConfig.subscriptions.claude.enabled ? chalk6.green("enabled") : chalk6.gray("disabled");
|
|
1444
1587
|
logger.keyValue(
|
|
1445
1588
|
"Claude",
|
|
1446
1589
|
`${claudeStatus}${sdlcConfig.subscriptions.claude.tier !== "none" ? ` (${sdlcConfig.subscriptions.claude.tier})` : ""}`
|
|
1447
1590
|
);
|
|
1448
|
-
const openaiStatus = sdlcConfig.subscriptions.openai.enabled ?
|
|
1591
|
+
const openaiStatus = sdlcConfig.subscriptions.openai.enabled ? chalk6.green("enabled") : chalk6.gray("disabled");
|
|
1449
1592
|
logger.keyValue("OpenAI", openaiStatus);
|
|
1450
|
-
const googleStatus = sdlcConfig.subscriptions.google.enabled ?
|
|
1593
|
+
const googleStatus = sdlcConfig.subscriptions.google.enabled ? chalk6.green("enabled") : chalk6.gray("disabled");
|
|
1451
1594
|
logger.keyValue(
|
|
1452
1595
|
"Google",
|
|
1453
1596
|
`${googleStatus}${sdlcConfig.subscriptions.google.authMethod !== "none" ? ` (${sdlcConfig.subscriptions.google.authMethod})` : ""}`
|
|
1454
1597
|
);
|
|
1455
1598
|
logger.section("Agent Models");
|
|
1456
|
-
logger.keyValue("
|
|
1599
|
+
logger.keyValue("Marvin", sdlcConfig.models.marvin);
|
|
1457
1600
|
logger.keyValue("Oracle", sdlcConfig.models.oracle);
|
|
1458
1601
|
logger.keyValue("Librarian", sdlcConfig.models.librarian);
|
|
1459
1602
|
if (sdlcConfig.models.frontend) {
|
|
@@ -1565,18 +1708,18 @@ async function info() {
|
|
|
1565
1708
|
}
|
|
1566
1709
|
];
|
|
1567
1710
|
for (const feature of featureList) {
|
|
1568
|
-
const status = feature.enabled ?
|
|
1711
|
+
const status = feature.enabled ? chalk6.green("on") : chalk6.gray("off");
|
|
1569
1712
|
logger.keyValue(feature.name, status);
|
|
1570
1713
|
}
|
|
1571
1714
|
logger.section("MCP Servers");
|
|
1572
1715
|
const mcps = sdlcConfig.mcps;
|
|
1573
|
-
logger.keyValue("context7", mcps.context7 ?
|
|
1574
|
-
logger.keyValue("exa", mcps.exa ?
|
|
1575
|
-
logger.keyValue("grep_app", mcps.grepApp ?
|
|
1716
|
+
logger.keyValue("context7", mcps.context7 ? chalk6.green("on") : chalk6.gray("off"));
|
|
1717
|
+
logger.keyValue("exa", mcps.exa ? chalk6.green("on") : chalk6.gray("off"));
|
|
1718
|
+
logger.keyValue("grep_app", mcps.grepApp ? chalk6.green("on") : chalk6.gray("off"));
|
|
1576
1719
|
if ("memento" in mcps) {
|
|
1577
1720
|
logger.keyValue(
|
|
1578
1721
|
"memento",
|
|
1579
|
-
mcps.memento ?
|
|
1722
|
+
mcps.memento ? chalk6.green("on") : chalk6.gray("off")
|
|
1580
1723
|
);
|
|
1581
1724
|
}
|
|
1582
1725
|
logger.section("Installed Plugins");
|
|
@@ -1591,7 +1734,6 @@ async function info() {
|
|
|
1591
1734
|
logger.section("Configuration Paths");
|
|
1592
1735
|
logger.keyValue("Config Dir", CONFIG_PATHS.globalConfigDir);
|
|
1593
1736
|
logger.keyValue("Sdlc Config", CONFIG_PATHS.globalSdlcConfig);
|
|
1594
|
-
logger.keyValue("Commands Dir", CONFIG_PATHS.commandsDir);
|
|
1595
1737
|
console.log();
|
|
1596
1738
|
}
|
|
1597
1739
|
|
|
@@ -1616,7 +1758,7 @@ var MODEL_FAMILY_BASE_TEMPS = {
|
|
|
1616
1758
|
};
|
|
1617
1759
|
var ROLE_TEMP_ADJUSTMENTS = {
|
|
1618
1760
|
oracle: -0.1,
|
|
1619
|
-
|
|
1761
|
+
marvin: 0,
|
|
1620
1762
|
librarian: 0.1,
|
|
1621
1763
|
frontend: 0.2,
|
|
1622
1764
|
documentWriter: 0.1,
|
|
@@ -1625,7 +1767,7 @@ var ROLE_TEMP_ADJUSTMENTS = {
|
|
|
1625
1767
|
};
|
|
1626
1768
|
var ROLE_DEFAULT_THINKING = {
|
|
1627
1769
|
oracle: "high",
|
|
1628
|
-
|
|
1770
|
+
marvin: "medium",
|
|
1629
1771
|
librarian: "low",
|
|
1630
1772
|
frontend: "low",
|
|
1631
1773
|
documentWriter: "low",
|
|
@@ -1788,10 +1930,12 @@ function buildMinimalConfig(answers) {
|
|
|
1788
1930
|
subscriptions: {
|
|
1789
1931
|
claude: {
|
|
1790
1932
|
enabled: subscriptions.hasClaude,
|
|
1933
|
+
authMethod: subscriptions.claudeAuth || "none",
|
|
1791
1934
|
tier: subscriptions.claudeTier || "none"
|
|
1792
1935
|
},
|
|
1793
1936
|
openai: {
|
|
1794
|
-
enabled: subscriptions.hasOpenAI
|
|
1937
|
+
enabled: subscriptions.hasOpenAI,
|
|
1938
|
+
authMethod: subscriptions.openaiAuth || "none"
|
|
1795
1939
|
},
|
|
1796
1940
|
google: {
|
|
1797
1941
|
enabled: subscriptions.hasGoogle,
|
|
@@ -1804,7 +1948,7 @@ function buildMinimalConfig(answers) {
|
|
|
1804
1948
|
}
|
|
1805
1949
|
},
|
|
1806
1950
|
models: {
|
|
1807
|
-
|
|
1951
|
+
marvin: models.marvin,
|
|
1808
1952
|
oracle: models.oracle,
|
|
1809
1953
|
librarian: models.librarian,
|
|
1810
1954
|
frontend: models.frontend,
|
|
@@ -1864,13 +2008,13 @@ function generateOmoConfig(answers) {
|
|
|
1864
2008
|
omoConfig.google_auth = false;
|
|
1865
2009
|
}
|
|
1866
2010
|
const agentConfigs = [
|
|
1867
|
-
{ role: "
|
|
2011
|
+
{ role: "marvin", omoName: "Marvin", modelId: models.marvin },
|
|
1868
2012
|
{ role: "oracle", omoName: "oracle", modelId: models.oracle },
|
|
1869
2013
|
{ role: "librarian", omoName: "librarian", modelId: models.librarian },
|
|
1870
2014
|
{
|
|
1871
2015
|
role: "frontend",
|
|
1872
2016
|
omoName: "frontend-ui-ux-engineer",
|
|
1873
|
-
modelId: models.frontend || models.
|
|
2017
|
+
modelId: models.frontend || models.marvin
|
|
1874
2018
|
},
|
|
1875
2019
|
{
|
|
1876
2020
|
role: "documentWriter",
|
|
@@ -2273,10 +2417,12 @@ function generateSdlcConfig(answers) {
|
|
|
2273
2417
|
subscriptions: {
|
|
2274
2418
|
claude: {
|
|
2275
2419
|
enabled: subscriptions.hasClaude,
|
|
2420
|
+
authMethod: subscriptions.claudeAuth,
|
|
2276
2421
|
tier: subscriptions.claudeTier
|
|
2277
2422
|
},
|
|
2278
2423
|
openai: {
|
|
2279
|
-
enabled: subscriptions.hasOpenAI
|
|
2424
|
+
enabled: subscriptions.hasOpenAI,
|
|
2425
|
+
authMethod: subscriptions.openaiAuth
|
|
2280
2426
|
},
|
|
2281
2427
|
google: {
|
|
2282
2428
|
enabled: subscriptions.hasGoogle,
|
|
@@ -2288,12 +2434,23 @@ function generateSdlcConfig(answers) {
|
|
|
2288
2434
|
}
|
|
2289
2435
|
},
|
|
2290
2436
|
models: {
|
|
2291
|
-
|
|
2437
|
+
marvin: models.marvin,
|
|
2292
2438
|
oracle: models.oracle,
|
|
2293
2439
|
librarian: models.librarian,
|
|
2294
2440
|
frontend: models.frontend,
|
|
2295
2441
|
documentWriter: models.documentWriter,
|
|
2296
|
-
multimodalLooker: models.multimodalLooker
|
|
2442
|
+
multimodalLooker: models.multimodalLooker,
|
|
2443
|
+
// v1.0.0: subagent model configuration
|
|
2444
|
+
subagents: {
|
|
2445
|
+
red: "inherit",
|
|
2446
|
+
green: "inherit",
|
|
2447
|
+
domain: "inherit",
|
|
2448
|
+
refactor: "inherit",
|
|
2449
|
+
architect: "inherit",
|
|
2450
|
+
pm: "inherit",
|
|
2451
|
+
analyst: "inherit",
|
|
2452
|
+
reviewer: "inherit"
|
|
2453
|
+
}
|
|
2297
2454
|
},
|
|
2298
2455
|
// Features and MCPs - use new format if new features selected, otherwise legacy
|
|
2299
2456
|
features: useNewFeatures ? generateNewFeatures(features.enabledFeatures) : featuresToFlags(features.enabledFeatures),
|
|
@@ -2318,7 +2475,9 @@ function generateSdlcConfig(answers) {
|
|
|
2318
2475
|
autoFallback: advanced.autoFallback ?? false,
|
|
2319
2476
|
retryPeriodMs: 3e5,
|
|
2320
2477
|
notifyOnRateLimit: true
|
|
2321
|
-
}
|
|
2478
|
+
},
|
|
2479
|
+
// v1.0.0: classifier model for mode detection
|
|
2480
|
+
classifierModel: "anthropic/claude-haiku"
|
|
2322
2481
|
}
|
|
2323
2482
|
};
|
|
2324
2483
|
if (!useNewFeatures) {
|
|
@@ -2340,6 +2499,22 @@ function generateSdlcConfig(answers) {
|
|
|
2340
2499
|
statuses: answers.github.statuses
|
|
2341
2500
|
};
|
|
2342
2501
|
}
|
|
2502
|
+
config.modes = answers.modes ? {
|
|
2503
|
+
default: answers.modes.default,
|
|
2504
|
+
eventModeling: answers.modes.eventModeling,
|
|
2505
|
+
enabled: answers.modes.enabled
|
|
2506
|
+
} : {
|
|
2507
|
+
default: "build",
|
|
2508
|
+
eventModeling: true,
|
|
2509
|
+
enabled: ["discover", "model", "architect", "pm", "build"]
|
|
2510
|
+
};
|
|
2511
|
+
config.memory = answers.memory ? {
|
|
2512
|
+
backend: answers.memory.backend,
|
|
2513
|
+
checkpointTtl: answers.memory.checkpointTtl
|
|
2514
|
+
} : {
|
|
2515
|
+
backend: "stateless",
|
|
2516
|
+
checkpointTtl: 86400
|
|
2517
|
+
};
|
|
2343
2518
|
config.tdd = answers.tdd ? {
|
|
2344
2519
|
enabled: answers.tdd.enabled,
|
|
2345
2520
|
verbosity: answers.tdd.verbosity,
|
|
@@ -2347,7 +2522,11 @@ function generateSdlcConfig(answers) {
|
|
|
2347
2522
|
mutationTesting: {
|
|
2348
2523
|
enabled: answers.tdd.mutationTesting.enabled,
|
|
2349
2524
|
requiredScore: answers.tdd.mutationTesting.requiredScore
|
|
2350
|
-
}
|
|
2525
|
+
},
|
|
2526
|
+
// v1.0.0 fields
|
|
2527
|
+
domainVetoEnabled: answers.tdd.domainVetoEnabled ?? true,
|
|
2528
|
+
debateRounds: answers.tdd.debateRounds ?? 2,
|
|
2529
|
+
requireVerification: answers.tdd.requireVerification ?? true
|
|
2351
2530
|
} : {
|
|
2352
2531
|
enabled: true,
|
|
2353
2532
|
verbosity: "brief",
|
|
@@ -2355,7 +2534,10 @@ function generateSdlcConfig(answers) {
|
|
|
2355
2534
|
mutationTesting: {
|
|
2356
2535
|
enabled: false,
|
|
2357
2536
|
requiredScore: 80
|
|
2358
|
-
}
|
|
2537
|
+
},
|
|
2538
|
+
domainVetoEnabled: true,
|
|
2539
|
+
debateRounds: 2,
|
|
2540
|
+
requireVerification: true
|
|
2359
2541
|
};
|
|
2360
2542
|
config.eventModeling = answers.eventModeling ? {
|
|
2361
2543
|
enabled: answers.eventModeling.enabled,
|
|
@@ -2431,27 +2613,61 @@ init_esm_shims();
|
|
|
2431
2613
|
init_debug_logger();
|
|
2432
2614
|
async function gatherSubscriptions() {
|
|
2433
2615
|
const hasClaude = await confirm({
|
|
2434
|
-
message: "
|
|
2616
|
+
message: "Will you use Anthropic/Claude models?",
|
|
2435
2617
|
default: false
|
|
2436
2618
|
});
|
|
2437
2619
|
debugLog("subscription.hasClaude", hasClaude);
|
|
2620
|
+
let claudeAuth = "none";
|
|
2438
2621
|
let claudeTier = "none";
|
|
2439
2622
|
if (hasClaude) {
|
|
2440
|
-
|
|
2441
|
-
message: "
|
|
2623
|
+
claudeAuth = await select({
|
|
2624
|
+
message: "How will you authenticate with Anthropic?",
|
|
2442
2625
|
choices: [
|
|
2443
|
-
{
|
|
2444
|
-
|
|
2445
|
-
|
|
2626
|
+
{
|
|
2627
|
+
name: "API Key - Direct API access (pay per use)",
|
|
2628
|
+
value: "api"
|
|
2629
|
+
},
|
|
2630
|
+
{
|
|
2631
|
+
name: "Claude Pro/Max Subscription - OAuth login",
|
|
2632
|
+
value: "subscription"
|
|
2633
|
+
}
|
|
2446
2634
|
]
|
|
2447
2635
|
});
|
|
2448
|
-
debugLog("subscription.
|
|
2636
|
+
debugLog("subscription.claudeAuth", claudeAuth);
|
|
2637
|
+
if (claudeAuth === "subscription") {
|
|
2638
|
+
claudeTier = await select({
|
|
2639
|
+
message: "Which Claude subscription tier?",
|
|
2640
|
+
choices: [
|
|
2641
|
+
{ name: "Max 5x - 5x more usage than Pro", value: "max5x" },
|
|
2642
|
+
{ name: "Max 20x - 20x more usage than Pro", value: "max20x" },
|
|
2643
|
+
{ name: "Pro - Standard Pro subscription", value: "pro" }
|
|
2644
|
+
]
|
|
2645
|
+
});
|
|
2646
|
+
debugLog("subscription.claudeTier", claudeTier);
|
|
2647
|
+
}
|
|
2449
2648
|
}
|
|
2450
2649
|
const hasOpenAI = await confirm({
|
|
2451
|
-
message: "
|
|
2650
|
+
message: "Will you use OpenAI models?",
|
|
2452
2651
|
default: false
|
|
2453
2652
|
});
|
|
2454
2653
|
debugLog("subscription.hasOpenAI", hasOpenAI);
|
|
2654
|
+
let openaiAuth = "none";
|
|
2655
|
+
if (hasOpenAI) {
|
|
2656
|
+
openaiAuth = await select({
|
|
2657
|
+
message: "How will you authenticate with OpenAI?",
|
|
2658
|
+
choices: [
|
|
2659
|
+
{
|
|
2660
|
+
name: "API Key - Direct API access (pay per use)",
|
|
2661
|
+
value: "api"
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
name: "ChatGPT Plus/Pro Subscription - OAuth login",
|
|
2665
|
+
value: "subscription"
|
|
2666
|
+
}
|
|
2667
|
+
]
|
|
2668
|
+
});
|
|
2669
|
+
debugLog("subscription.openaiAuth", openaiAuth);
|
|
2670
|
+
}
|
|
2455
2671
|
const hasGoogle = await confirm({
|
|
2456
2672
|
message: "Will you use Google/Gemini models?",
|
|
2457
2673
|
default: false
|
|
@@ -2499,8 +2715,10 @@ async function gatherSubscriptions() {
|
|
|
2499
2715
|
}
|
|
2500
2716
|
const result = {
|
|
2501
2717
|
hasClaude,
|
|
2718
|
+
claudeAuth,
|
|
2502
2719
|
claudeTier,
|
|
2503
2720
|
hasOpenAI,
|
|
2721
|
+
openaiAuth,
|
|
2504
2722
|
hasGoogle,
|
|
2505
2723
|
googleAuth,
|
|
2506
2724
|
hasGitHubCopilot,
|
|
@@ -2666,7 +2884,10 @@ async function gatherTdd(defaults) {
|
|
|
2666
2884
|
mutationTesting: {
|
|
2667
2885
|
enabled: false,
|
|
2668
2886
|
requiredScore: 80
|
|
2669
|
-
}
|
|
2887
|
+
},
|
|
2888
|
+
domainVetoEnabled: true,
|
|
2889
|
+
debateRounds: 2,
|
|
2890
|
+
requireVerification: true
|
|
2670
2891
|
};
|
|
2671
2892
|
}
|
|
2672
2893
|
const verbosity = await select({
|
|
@@ -2717,6 +2938,24 @@ async function gatherTdd(defaults) {
|
|
|
2717
2938
|
});
|
|
2718
2939
|
requiredScore = scoreChoice;
|
|
2719
2940
|
}
|
|
2941
|
+
const domainVetoEnabled = await confirm({
|
|
2942
|
+
message: "Enable domain agent veto power? (Can block cycle for type violations)",
|
|
2943
|
+
default: defaults?.domainVetoEnabled ?? true
|
|
2944
|
+
});
|
|
2945
|
+
const debateRounds = await select({
|
|
2946
|
+
message: "Max debate rounds before escalation to user:",
|
|
2947
|
+
choices: [
|
|
2948
|
+
{ name: "1 round - Quick decisions", value: 1 },
|
|
2949
|
+
{ name: "2 rounds - Balanced (recommended)", value: 2 },
|
|
2950
|
+
{ name: "3 rounds - Thorough discussion", value: 3 },
|
|
2951
|
+
{ name: "5 rounds - Extended debate", value: 5 }
|
|
2952
|
+
],
|
|
2953
|
+
default: defaults?.debateRounds ?? 2
|
|
2954
|
+
});
|
|
2955
|
+
const requireVerification = await confirm({
|
|
2956
|
+
message: "Require test output evidence before phase transitions?",
|
|
2957
|
+
default: defaults?.requireVerification ?? true
|
|
2958
|
+
});
|
|
2720
2959
|
return {
|
|
2721
2960
|
enabled,
|
|
2722
2961
|
verbosity,
|
|
@@ -2724,7 +2963,10 @@ async function gatherTdd(defaults) {
|
|
|
2724
2963
|
mutationTesting: {
|
|
2725
2964
|
enabled: mutationEnabled,
|
|
2726
2965
|
requiredScore
|
|
2727
|
-
}
|
|
2966
|
+
},
|
|
2967
|
+
domainVetoEnabled,
|
|
2968
|
+
debateRounds,
|
|
2969
|
+
requireVerification
|
|
2728
2970
|
};
|
|
2729
2971
|
}
|
|
2730
2972
|
|
|
@@ -2793,6 +3035,135 @@ async function gatherGitWorkflow(defaults) {
|
|
|
2793
3035
|
};
|
|
2794
3036
|
}
|
|
2795
3037
|
|
|
3038
|
+
// src/cli/questions/modes.ts
|
|
3039
|
+
init_esm_shims();
|
|
3040
|
+
var MODE_OPTIONS = [
|
|
3041
|
+
{
|
|
3042
|
+
value: "build",
|
|
3043
|
+
name: "Build",
|
|
3044
|
+
description: "TDD implementation (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)"
|
|
3045
|
+
},
|
|
3046
|
+
{
|
|
3047
|
+
value: "discover",
|
|
3048
|
+
name: "Discover",
|
|
3049
|
+
description: "Problem space exploration, stakeholder mapping"
|
|
3050
|
+
},
|
|
3051
|
+
{
|
|
3052
|
+
value: "model",
|
|
3053
|
+
name: "Model",
|
|
3054
|
+
description: "Event Modeling (commands, events, views)"
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
value: "architect",
|
|
3058
|
+
name: "Architect",
|
|
3059
|
+
description: "System design, ADRs, technology decisions"
|
|
3060
|
+
},
|
|
3061
|
+
{
|
|
3062
|
+
value: "pm",
|
|
3063
|
+
name: "PM",
|
|
3064
|
+
description: "Project management, git workflow, PRs"
|
|
3065
|
+
}
|
|
3066
|
+
];
|
|
3067
|
+
async function gatherModes(defaults) {
|
|
3068
|
+
console.log(chalk6.gray("\nMarvin operates in different modes depending on your work focus."));
|
|
3069
|
+
console.log(chalk6.gray("Build mode (TDD) is always enabled.\n"));
|
|
3070
|
+
const enabledChoices = MODE_OPTIONS.filter((m) => m.value !== "build").map((m) => ({
|
|
3071
|
+
name: `${m.name} - ${m.description}`,
|
|
3072
|
+
value: m.value,
|
|
3073
|
+
checked: true
|
|
3074
|
+
}));
|
|
3075
|
+
const selectedModes = await checkbox({
|
|
3076
|
+
message: "Which additional modes do you want to enable?",
|
|
3077
|
+
choices: enabledChoices
|
|
3078
|
+
});
|
|
3079
|
+
const enabled = ["build", ...selectedModes];
|
|
3080
|
+
const useEventModeling = await confirm({
|
|
3081
|
+
message: "Use Event Modeling for system design? (No = use PRD-style specifications)",
|
|
3082
|
+
default: true
|
|
3083
|
+
});
|
|
3084
|
+
let finalEnabled = enabled;
|
|
3085
|
+
if (!useEventModeling) {
|
|
3086
|
+
finalEnabled = enabled.filter((m) => m !== "model");
|
|
3087
|
+
}
|
|
3088
|
+
const defaultModeChoices = finalEnabled.map((mode) => {
|
|
3089
|
+
const option = MODE_OPTIONS.find((m) => m.value === mode);
|
|
3090
|
+
return {
|
|
3091
|
+
name: `${option?.name || mode} - ${option?.description || ""}`,
|
|
3092
|
+
value: mode
|
|
3093
|
+
};
|
|
3094
|
+
});
|
|
3095
|
+
const defaultMode = await select({
|
|
3096
|
+
message: "Which mode should be active when you start OpenCode?",
|
|
3097
|
+
choices: defaultModeChoices,
|
|
3098
|
+
default: "build"
|
|
3099
|
+
});
|
|
3100
|
+
return {
|
|
3101
|
+
default: defaultMode,
|
|
3102
|
+
eventModeling: useEventModeling,
|
|
3103
|
+
enabled: finalEnabled
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
// src/cli/questions/memory.ts
|
|
3108
|
+
init_esm_shims();
|
|
3109
|
+
async function gatherMemory(defaults) {
|
|
3110
|
+
console.log(
|
|
3111
|
+
chalk6.gray("\nMemory backend determines how Marvin remembers context across sessions.\n")
|
|
3112
|
+
);
|
|
3113
|
+
const backend = await select({
|
|
3114
|
+
message: "Choose memory backend:",
|
|
3115
|
+
choices: [
|
|
3116
|
+
{
|
|
3117
|
+
name: "Stateless - No persistent memory (simpler, no setup required)",
|
|
3118
|
+
value: "stateless"
|
|
3119
|
+
},
|
|
3120
|
+
{
|
|
3121
|
+
name: "Memento - Persistent memory via Memento MCP (requires setup)",
|
|
3122
|
+
value: "memento"
|
|
3123
|
+
}
|
|
3124
|
+
],
|
|
3125
|
+
default: "stateless"
|
|
3126
|
+
});
|
|
3127
|
+
let checkpointTtl = defaults?.checkpointTtl ?? 86400;
|
|
3128
|
+
if (backend === "memento") {
|
|
3129
|
+
console.log(chalk6.gray("\nMemento stores checkpoints of conversation state."));
|
|
3130
|
+
console.log(chalk6.gray("Set how long to keep checkpoints before they expire.\n"));
|
|
3131
|
+
const ttlChoice = await select({
|
|
3132
|
+
message: "Checkpoint retention period:",
|
|
3133
|
+
choices: [
|
|
3134
|
+
{ name: "1 hour (3600 seconds)", value: 3600 },
|
|
3135
|
+
{ name: "6 hours (21600 seconds)", value: 21600 },
|
|
3136
|
+
{ name: "24 hours (86400 seconds) - Recommended", value: 86400 },
|
|
3137
|
+
{ name: "7 days (604800 seconds)", value: 604800 },
|
|
3138
|
+
{ name: "Custom", value: -1 }
|
|
3139
|
+
],
|
|
3140
|
+
default: 86400
|
|
3141
|
+
});
|
|
3142
|
+
if (ttlChoice === -1) {
|
|
3143
|
+
const customTtl = await input({
|
|
3144
|
+
message: "Enter checkpoint TTL in seconds:",
|
|
3145
|
+
default: String(checkpointTtl),
|
|
3146
|
+
validate: (value) => {
|
|
3147
|
+
const num = Number.parseInt(value, 10);
|
|
3148
|
+
if (Number.isNaN(num) || num < 60) {
|
|
3149
|
+
return "Please enter a number >= 60 seconds";
|
|
3150
|
+
}
|
|
3151
|
+
return true;
|
|
3152
|
+
}
|
|
3153
|
+
});
|
|
3154
|
+
checkpointTtl = Number.parseInt(customTtl, 10);
|
|
3155
|
+
} else {
|
|
3156
|
+
checkpointTtl = ttlChoice;
|
|
3157
|
+
}
|
|
3158
|
+
console.log(chalk6.yellow("\nNote: You'll need to configure Memento MCP separately."));
|
|
3159
|
+
console.log(chalk6.gray("See: https://github.com/gannonh/memento\n"));
|
|
3160
|
+
}
|
|
3161
|
+
return {
|
|
3162
|
+
backend,
|
|
3163
|
+
checkpointTtl
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
|
|
2796
3167
|
// src/cli/utils/config-loader.ts
|
|
2797
3168
|
init_esm_shims();
|
|
2798
3169
|
function loadExistingConfigs() {
|
|
@@ -2840,10 +3211,15 @@ function extractSubscriptions(sdlc) {
|
|
|
2840
3211
|
const openai = subs.openai;
|
|
2841
3212
|
const google = subs.google;
|
|
2842
3213
|
const copilot = subs.githubCopilot;
|
|
3214
|
+
const claudeTier = claude?.tier || "none";
|
|
3215
|
+
const claudeAuth = claude?.authMethod || (claudeTier !== "none" ? "subscription" : "none");
|
|
3216
|
+
const openaiAuth = openai?.authMethod || (openai?.enabled === true ? "subscription" : "none");
|
|
2843
3217
|
return {
|
|
2844
3218
|
hasClaude: claude?.enabled === true,
|
|
2845
|
-
|
|
3219
|
+
claudeAuth,
|
|
3220
|
+
claudeTier,
|
|
2846
3221
|
hasOpenAI: openai?.enabled === true,
|
|
3222
|
+
openaiAuth,
|
|
2847
3223
|
hasGoogle: google?.enabled === true,
|
|
2848
3224
|
googleAuth: google?.authMethod || "none",
|
|
2849
3225
|
hasGitHubCopilot: copilot?.enabled === true,
|
|
@@ -2859,7 +3235,7 @@ function extractModels(sdlc) {
|
|
|
2859
3235
|
const models = sdlc.models;
|
|
2860
3236
|
if (!models) return null;
|
|
2861
3237
|
return {
|
|
2862
|
-
|
|
3238
|
+
marvin: models.marvin || "",
|
|
2863
3239
|
oracle: models.oracle || "",
|
|
2864
3240
|
librarian: models.librarian || "",
|
|
2865
3241
|
frontend: models.frontend,
|
|
@@ -3012,8 +3388,63 @@ var MIGRATIONS = [
|
|
|
3012
3388
|
{
|
|
3013
3389
|
fromVersion: "0.0.1",
|
|
3014
3390
|
toVersion: "0.3.0",
|
|
3015
|
-
description: "Initial SDLC plugin version",
|
|
3391
|
+
description: "Initial SDLC plugin version with Marvin agent and API key auth options",
|
|
3016
3392
|
migrateSdlc: (config) => config
|
|
3393
|
+
},
|
|
3394
|
+
{
|
|
3395
|
+
fromVersion: "0.3.0",
|
|
3396
|
+
toVersion: "1.0.0",
|
|
3397
|
+
description: "Add v1.0 config: modes, memory, TDD updates, classifier model, subagent models",
|
|
3398
|
+
migrateSdlc: (config) => {
|
|
3399
|
+
const migrated = { ...config };
|
|
3400
|
+
if (!migrated.modes) {
|
|
3401
|
+
migrated.modes = {
|
|
3402
|
+
default: "build",
|
|
3403
|
+
eventModeling: true,
|
|
3404
|
+
enabled: ["discover", "model", "architect", "pm", "build"]
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
if (!migrated.memory) {
|
|
3408
|
+
migrated.memory = {
|
|
3409
|
+
backend: "stateless",
|
|
3410
|
+
checkpointTtl: 86400
|
|
3411
|
+
};
|
|
3412
|
+
}
|
|
3413
|
+
if (migrated.tdd) {
|
|
3414
|
+
const tdd = migrated.tdd;
|
|
3415
|
+
if (tdd.domainVetoEnabled === void 0) {
|
|
3416
|
+
tdd.domainVetoEnabled = true;
|
|
3417
|
+
}
|
|
3418
|
+
if (tdd.debateRounds === void 0) {
|
|
3419
|
+
tdd.debateRounds = 2;
|
|
3420
|
+
}
|
|
3421
|
+
if (tdd.requireVerification === void 0) {
|
|
3422
|
+
tdd.requireVerification = true;
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
if (migrated.routing) {
|
|
3426
|
+
const routing = migrated.routing;
|
|
3427
|
+
if (routing.classifierModel === void 0) {
|
|
3428
|
+
routing.classifierModel = "anthropic/claude-haiku";
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
if (migrated.models) {
|
|
3432
|
+
const models = migrated.models;
|
|
3433
|
+
if (!models.subagents) {
|
|
3434
|
+
models.subagents = {
|
|
3435
|
+
red: "inherit",
|
|
3436
|
+
green: "inherit",
|
|
3437
|
+
domain: "inherit",
|
|
3438
|
+
refactor: "inherit",
|
|
3439
|
+
architect: "inherit",
|
|
3440
|
+
pm: "inherit",
|
|
3441
|
+
analyst: "inherit",
|
|
3442
|
+
reviewer: "inherit"
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
return migrated;
|
|
3447
|
+
}
|
|
3017
3448
|
}
|
|
3018
3449
|
];
|
|
3019
3450
|
function migrateLegacyFiles() {
|
|
@@ -3214,7 +3645,7 @@ function presetToDefaults(preset) {
|
|
|
3214
3645
|
const isLegacy = "bmad" in preset && preset.bmad !== void 0;
|
|
3215
3646
|
return {
|
|
3216
3647
|
models: {
|
|
3217
|
-
|
|
3648
|
+
marvin: preset.models.marvin || "",
|
|
3218
3649
|
oracle: preset.models.oracle,
|
|
3219
3650
|
librarian: preset.models.librarian,
|
|
3220
3651
|
frontend: preset.models.frontend,
|
|
@@ -3279,7 +3710,7 @@ function formatPresetSummary(preset, name) {
|
|
|
3279
3710
|
lines.push(`Description: ${preset.description}`);
|
|
3280
3711
|
lines.push("");
|
|
3281
3712
|
lines.push("Models:");
|
|
3282
|
-
lines.push(`
|
|
3713
|
+
lines.push(` Marvin: ${preset.models.marvin}`);
|
|
3283
3714
|
lines.push(` Oracle: ${preset.models.oracle}`);
|
|
3284
3715
|
lines.push(` Librarian: ${preset.models.librarian}`);
|
|
3285
3716
|
if (preset.bmad) {
|
|
@@ -3339,9 +3770,9 @@ function detectInstallMode(options, configs) {
|
|
|
3339
3770
|
async function runUpgradeFlow(configs, existingVersion, options) {
|
|
3340
3771
|
const { sdlc, omo, opencode } = configs;
|
|
3341
3772
|
logger.section("Upgrading Configuration");
|
|
3342
|
-
console.log(
|
|
3773
|
+
console.log(chalk6.cyan(`
|
|
3343
3774
|
Current version: ${existingVersion}`));
|
|
3344
|
-
console.log(
|
|
3775
|
+
console.log(chalk6.cyan(`New version: ${VERSION}
|
|
3345
3776
|
`));
|
|
3346
3777
|
if (!options.yes) {
|
|
3347
3778
|
const proceed = await confirm({
|
|
@@ -3365,22 +3796,22 @@ Current version: ${existingVersion}`));
|
|
|
3365
3796
|
if (fileMigrationResult.stateFileMoved) moved.push("state file");
|
|
3366
3797
|
if (fileMigrationResult.backupsMoved > 0)
|
|
3367
3798
|
moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
|
|
3368
|
-
console.log(
|
|
3799
|
+
console.log(chalk6.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
|
|
3369
3800
|
}
|
|
3370
3801
|
const migrationSpinner = ora5("Applying migrations...").start();
|
|
3371
3802
|
const migrationResult = migrateConfigs(sdlc || {}, omo || {}, existingVersion, opencode || {});
|
|
3372
3803
|
if (migrationResult.migrationsApplied.length > 0) {
|
|
3373
3804
|
migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
|
|
3374
3805
|
for (const migration of migrationResult.migrationsApplied) {
|
|
3375
|
-
console.log(
|
|
3806
|
+
console.log(chalk6.gray(` \u2022 ${migration}`));
|
|
3376
3807
|
}
|
|
3377
3808
|
} else {
|
|
3378
3809
|
migrationSpinner.succeed("No migrations needed");
|
|
3379
3810
|
}
|
|
3380
3811
|
if (migrationResult.hasBreakingChanges && !options.yes) {
|
|
3381
|
-
console.log(
|
|
3812
|
+
console.log(chalk6.yellow("\nBreaking changes detected:"));
|
|
3382
3813
|
for (const warning of migrationResult.breakingChangeWarnings) {
|
|
3383
|
-
console.log(
|
|
3814
|
+
console.log(chalk6.yellow(` \u26A0 ${warning}`));
|
|
3384
3815
|
}
|
|
3385
3816
|
const continueUpgrade = await confirm({
|
|
3386
3817
|
message: "Continue with upgrade despite breaking changes?",
|
|
@@ -3398,20 +3829,25 @@ Current version: ${existingVersion}`));
|
|
|
3398
3829
|
const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
|
|
3399
3830
|
logger.section("Preserved Configuration");
|
|
3400
3831
|
if (existingSubscriptions) {
|
|
3401
|
-
console.log(
|
|
3402
|
-
if (existingSubscriptions.hasClaude)
|
|
3403
|
-
|
|
3404
|
-
|
|
3832
|
+
console.log(chalk6.bold("Subscriptions:"));
|
|
3833
|
+
if (existingSubscriptions.hasClaude) {
|
|
3834
|
+
const claudeInfo = existingSubscriptions.claudeAuth === "api" ? "API Key" : existingSubscriptions.claudeTier;
|
|
3835
|
+
console.log(chalk6.green(` \u2713 Claude (${claudeInfo})`));
|
|
3836
|
+
}
|
|
3837
|
+
if (existingSubscriptions.hasOpenAI) {
|
|
3838
|
+
const openaiInfo = existingSubscriptions.openaiAuth === "api" ? "API Key" : "Subscription";
|
|
3839
|
+
console.log(chalk6.green(` \u2713 OpenAI (${openaiInfo})`));
|
|
3840
|
+
}
|
|
3405
3841
|
if (existingSubscriptions.hasGoogle)
|
|
3406
|
-
console.log(
|
|
3842
|
+
console.log(chalk6.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
|
|
3407
3843
|
if (existingSubscriptions.hasGitHubCopilot)
|
|
3408
|
-
console.log(
|
|
3844
|
+
console.log(chalk6.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
|
|
3409
3845
|
}
|
|
3410
3846
|
if (existingModels) {
|
|
3411
|
-
console.log(
|
|
3412
|
-
console.log(
|
|
3413
|
-
console.log(
|
|
3414
|
-
console.log(
|
|
3847
|
+
console.log(chalk6.bold("\nModel Assignments:"));
|
|
3848
|
+
console.log(chalk6.green(` \u2713 Marvin: ${existingModels.marvin}`));
|
|
3849
|
+
console.log(chalk6.green(` \u2713 Oracle: ${existingModels.oracle}`));
|
|
3850
|
+
console.log(chalk6.green(` \u2713 Librarian: ${existingModels.librarian}`));
|
|
3415
3851
|
}
|
|
3416
3852
|
console.log();
|
|
3417
3853
|
const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
|
|
@@ -3450,7 +3886,7 @@ Current version: ${existingVersion}`));
|
|
|
3450
3886
|
const fullAnswers = {
|
|
3451
3887
|
subscriptions: finalSubscriptions,
|
|
3452
3888
|
models: existingModels || {
|
|
3453
|
-
|
|
3889
|
+
marvin: "",
|
|
3454
3890
|
oracle: "",
|
|
3455
3891
|
librarian: ""
|
|
3456
3892
|
},
|
|
@@ -3486,17 +3922,14 @@ Current version: ${existingVersion}`));
|
|
|
3486
3922
|
await fileManager.installDependencies(packages);
|
|
3487
3923
|
installSpinner.succeed(`Installed ${packages.length} package(s)`);
|
|
3488
3924
|
}
|
|
3489
|
-
const commandsSpinner = ora5("Updating bridge commands...").start();
|
|
3490
|
-
const copiedCommands = await fileManager.copyCommands();
|
|
3491
|
-
commandsSpinner.succeed(`Updated ${copiedCommands.length} bridge commands`);
|
|
3492
3925
|
logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
|
|
3493
3926
|
if (backups.sdlcBackup || backups.omoBackup || backups.opencodeBackup) {
|
|
3494
|
-
console.log(
|
|
3495
|
-
if (backups.sdlcBackup) console.log(
|
|
3496
|
-
if (backups.omoBackup) console.log(
|
|
3497
|
-
if (backups.opencodeBackup) console.log(
|
|
3927
|
+
console.log(chalk6.gray("\nBackups saved:"));
|
|
3928
|
+
if (backups.sdlcBackup) console.log(chalk6.gray(` \u2022 ${backups.sdlcBackup}`));
|
|
3929
|
+
if (backups.omoBackup) console.log(chalk6.gray(` \u2022 ${backups.omoBackup}`));
|
|
3930
|
+
if (backups.opencodeBackup) console.log(chalk6.gray(` \u2022 ${backups.opencodeBackup}`));
|
|
3498
3931
|
}
|
|
3499
|
-
console.log(
|
|
3932
|
+
console.log(chalk6.gray("\nRestart OpenCode to use the upgraded configuration."));
|
|
3500
3933
|
console.log();
|
|
3501
3934
|
}
|
|
3502
3935
|
async function install(options) {
|
|
@@ -3550,6 +3983,7 @@ async function install(options) {
|
|
|
3550
3983
|
}
|
|
3551
3984
|
}
|
|
3552
3985
|
if (!preset && !options.yes) {
|
|
3986
|
+
logger.section("Configuration Profile");
|
|
3553
3987
|
const selectedPreset = await askForPreset();
|
|
3554
3988
|
if (selectedPreset) {
|
|
3555
3989
|
preset = selectedPreset.preset;
|
|
@@ -3557,64 +3991,89 @@ async function install(options) {
|
|
|
3557
3991
|
presetName = selectedPreset.name;
|
|
3558
3992
|
}
|
|
3559
3993
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
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
|
-
}
|
|
3994
|
+
let shouldCustomize = true;
|
|
3995
|
+
if (preset && presetDefaults && presetName && !options.yes) {
|
|
3996
|
+
console.log(chalk6.bold("\nProfile Configuration:\n"));
|
|
3997
|
+
console.log(chalk6.gray(formatPresetSummary(preset, presetName)));
|
|
3586
3998
|
console.log();
|
|
3587
|
-
|
|
3588
|
-
message: "
|
|
3589
|
-
default:
|
|
3999
|
+
shouldCustomize = await confirm({
|
|
4000
|
+
message: "Would you like to customize these settings?",
|
|
4001
|
+
default: false
|
|
3590
4002
|
});
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
if (
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
4003
|
+
} else if (options.yes && preset) {
|
|
4004
|
+
shouldCustomize = false;
|
|
4005
|
+
logger.info("Using preset defaults (--yes flag)");
|
|
4006
|
+
}
|
|
4007
|
+
let subscriptions;
|
|
4008
|
+
if (shouldCustomize || !preset) {
|
|
4009
|
+
logger.section("LLM Subscriptions");
|
|
4010
|
+
subscriptions = await gatherSubscriptions();
|
|
4011
|
+
if (!options.yes) {
|
|
4012
|
+
logger.section("Confirm Provider Selection");
|
|
4013
|
+
console.log(chalk6.bold("You selected:\n"));
|
|
4014
|
+
console.log(
|
|
4015
|
+
` Claude: ${subscriptions.hasClaude ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
|
|
4016
|
+
);
|
|
4017
|
+
if (subscriptions.hasClaude) {
|
|
4018
|
+
console.log(
|
|
4019
|
+
` Auth Method: ${subscriptions.claudeAuth === "api" ? "API Key" : "Subscription"}`
|
|
4020
|
+
);
|
|
4021
|
+
if (subscriptions.claudeAuth === "subscription") {
|
|
4022
|
+
console.log(` Tier: ${subscriptions.claudeTier}`);
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
console.log(
|
|
4026
|
+
` OpenAI: ${subscriptions.hasOpenAI ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
|
|
4027
|
+
);
|
|
4028
|
+
if (subscriptions.hasOpenAI) {
|
|
4029
|
+
console.log(
|
|
4030
|
+
` Auth Method: ${subscriptions.openaiAuth === "api" ? "API Key" : "Subscription"}`
|
|
4031
|
+
);
|
|
4032
|
+
}
|
|
4033
|
+
console.log(
|
|
4034
|
+
` Google: ${subscriptions.hasGoogle ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
|
|
4035
|
+
);
|
|
4036
|
+
if (subscriptions.hasGoogle) {
|
|
4037
|
+
console.log(` Auth Method: ${subscriptions.googleAuth}`);
|
|
4038
|
+
}
|
|
4039
|
+
console.log(
|
|
4040
|
+
` GitHub Copilot: ${subscriptions.hasGitHubCopilot ? chalk6.green("\u2713 Yes") : chalk6.gray("\u2717 No")}`
|
|
4041
|
+
);
|
|
4042
|
+
if (subscriptions.hasGitHubCopilot) {
|
|
4043
|
+
console.log(` Plan: ${subscriptions.copilotPlan}`);
|
|
3603
4044
|
}
|
|
3604
4045
|
console.log();
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
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
|
|
4046
|
+
const confirmed = await confirm({
|
|
4047
|
+
message: "Is this correct?",
|
|
4048
|
+
default: true
|
|
3616
4049
|
});
|
|
4050
|
+
if (!confirmed) {
|
|
4051
|
+
logger.info("Please restart installation with correct provider selections.");
|
|
4052
|
+
process.exit(0);
|
|
4053
|
+
}
|
|
3617
4054
|
}
|
|
4055
|
+
if (preset && presetDefaults) {
|
|
4056
|
+
const modelWarnings = validatePresetModels(presetDefaults.models, subscriptions);
|
|
4057
|
+
if (modelWarnings.length > 0) {
|
|
4058
|
+
console.log(chalk6.yellow("\nPreset model compatibility warnings:"));
|
|
4059
|
+
for (const warning of modelWarnings) {
|
|
4060
|
+
console.log(chalk6.yellow(` - ${warning}`));
|
|
4061
|
+
}
|
|
4062
|
+
console.log();
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
} else {
|
|
4066
|
+
subscriptions = {
|
|
4067
|
+
hasClaude: preset.subscriptions.claude.enabled,
|
|
4068
|
+
claudeAuth: "subscription",
|
|
4069
|
+
claudeTier: preset.subscriptions.claude.tier,
|
|
4070
|
+
hasOpenAI: preset.subscriptions.openai.enabled,
|
|
4071
|
+
openaiAuth: "subscription",
|
|
4072
|
+
hasGoogle: preset.subscriptions.google.enabled,
|
|
4073
|
+
googleAuth: preset.subscriptions.google.authMethod,
|
|
4074
|
+
hasGitHubCopilot: preset.subscriptions.githubCopilot?.enabled ?? false,
|
|
4075
|
+
copilotPlan: preset.subscriptions.githubCopilot?.plan ?? "none"
|
|
4076
|
+
};
|
|
3618
4077
|
}
|
|
3619
4078
|
let models;
|
|
3620
4079
|
let methodology;
|
|
@@ -3624,6 +4083,8 @@ async function install(options) {
|
|
|
3624
4083
|
let tdd;
|
|
3625
4084
|
let eventModeling;
|
|
3626
4085
|
let gitWorkflow;
|
|
4086
|
+
let modes;
|
|
4087
|
+
let memory;
|
|
3627
4088
|
if (!shouldCustomize && presetDefaults) {
|
|
3628
4089
|
logger.section("Applying Preset Configuration");
|
|
3629
4090
|
const availableModels = await Promise.resolve().then(() => (init_models(), models_exports)).then(
|
|
@@ -3636,7 +4097,7 @@ async function install(options) {
|
|
|
3636
4097
|
process.exit(1);
|
|
3637
4098
|
}
|
|
3638
4099
|
models = {
|
|
3639
|
-
|
|
4100
|
+
marvin: getValidModelOrFirst(presetDefaults.models.marvin, availableModels),
|
|
3640
4101
|
oracle: getValidModelOrFirst(presetDefaults.models.oracle, availableModels),
|
|
3641
4102
|
librarian: getValidModelOrFirst(presetDefaults.models.librarian, availableModels),
|
|
3642
4103
|
frontend: getValidModelOrFirst(presetDefaults.models.frontend, availableModels),
|
|
@@ -3651,12 +4112,19 @@ async function install(options) {
|
|
|
3651
4112
|
advanced = presetDefaults.advanced;
|
|
3652
4113
|
logger.section("GitHub Integration");
|
|
3653
4114
|
github = await gatherGitHub();
|
|
4115
|
+
logger.section("Marvin Modes");
|
|
4116
|
+
modes = await gatherModes();
|
|
4117
|
+
logger.section("Memory Configuration");
|
|
4118
|
+
memory = await gatherMemory();
|
|
3654
4119
|
if (preset?.tdd) {
|
|
3655
4120
|
tdd = {
|
|
3656
4121
|
enabled: preset.tdd.enabled,
|
|
3657
4122
|
verbosity: preset.tdd.verbosity,
|
|
3658
4123
|
bypassPatterns: preset.tdd.bypassPatterns,
|
|
3659
|
-
mutationTesting: preset.tdd.mutationTesting
|
|
4124
|
+
mutationTesting: preset.tdd.mutationTesting,
|
|
4125
|
+
domainVetoEnabled: true,
|
|
4126
|
+
debateRounds: 2,
|
|
4127
|
+
requireVerification: true
|
|
3660
4128
|
};
|
|
3661
4129
|
}
|
|
3662
4130
|
if (preset?.eventModeling) {
|
|
@@ -3674,6 +4142,10 @@ async function install(options) {
|
|
|
3674
4142
|
}
|
|
3675
4143
|
logger.success("Preset configuration applied");
|
|
3676
4144
|
} else {
|
|
4145
|
+
logger.section("Marvin Modes");
|
|
4146
|
+
modes = await gatherModes();
|
|
4147
|
+
logger.section("Memory Configuration");
|
|
4148
|
+
memory = await gatherMemory();
|
|
3677
4149
|
logger.section("Model Selection");
|
|
3678
4150
|
models = await gatherModels(subscriptions, presetDefaults?.models);
|
|
3679
4151
|
logger.section("Methodology Preferences");
|
|
@@ -3727,14 +4199,17 @@ async function install(options) {
|
|
|
3727
4199
|
github,
|
|
3728
4200
|
tdd,
|
|
3729
4201
|
eventModeling,
|
|
3730
|
-
gitWorkflow
|
|
4202
|
+
gitWorkflow,
|
|
4203
|
+
// v1.0.0 additions
|
|
4204
|
+
modes,
|
|
4205
|
+
memory
|
|
3731
4206
|
};
|
|
3732
4207
|
const generator = new ConfigGenerator(answers);
|
|
3733
4208
|
const files = await generator.generate();
|
|
3734
|
-
console.log(
|
|
4209
|
+
console.log(chalk6.bold("Files to be created/modified:\n"));
|
|
3735
4210
|
for (const file of files) {
|
|
3736
|
-
const action = file.exists ?
|
|
3737
|
-
console.log(
|
|
4211
|
+
const action = file.exists ? chalk6.yellow("update") : chalk6.green("create");
|
|
4212
|
+
console.log(chalk6.gray(` [${action}] ${file.path}`));
|
|
3738
4213
|
}
|
|
3739
4214
|
console.log();
|
|
3740
4215
|
if (!options.yes) {
|
|
@@ -3757,12 +4232,15 @@ async function install(options) {
|
|
|
3757
4232
|
installSpinner.text = `Installing dependencies: ${packages.join(", ")}...`;
|
|
3758
4233
|
await fileManager.installDependencies(packages);
|
|
3759
4234
|
}
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
4235
|
+
if (modes?.enabled && modes.enabled.length > 0) {
|
|
4236
|
+
installSpinner.text = "Installing Marvin mode agents...";
|
|
4237
|
+
const projectDir = process.cwd();
|
|
4238
|
+
const copiedAgents = await fileManager.copyAgentModes(projectDir, modes.enabled);
|
|
4239
|
+
if (copiedAgents.length > 0) {
|
|
4240
|
+
logger.info(`Installed ${copiedAgents.length} Marvin mode agents to .opencode/agent/`);
|
|
4241
|
+
}
|
|
3765
4242
|
}
|
|
4243
|
+
installSpinner.succeed("Installation complete!");
|
|
3766
4244
|
} catch (error) {
|
|
3767
4245
|
installSpinner.fail("Installation failed");
|
|
3768
4246
|
logger.error(error instanceof Error ? error.message : String(error));
|
|
@@ -3773,16 +4251,16 @@ async function install(options) {
|
|
|
3773
4251
|
async function askForPreset() {
|
|
3774
4252
|
const presets = listPresets();
|
|
3775
4253
|
const choices = [
|
|
3776
|
-
{ name: "No preset - Configure everything manually", value: "none" },
|
|
3777
4254
|
...presets.map((p) => ({
|
|
3778
4255
|
name: `${p.name} - ${p.description}`,
|
|
3779
4256
|
value: p.name
|
|
3780
|
-
}))
|
|
4257
|
+
})),
|
|
4258
|
+
{ name: "No preset - Configure everything manually", value: "none" }
|
|
3781
4259
|
];
|
|
3782
4260
|
const selected = await select({
|
|
3783
|
-
message: "
|
|
4261
|
+
message: "Choose a configuration profile:",
|
|
3784
4262
|
choices,
|
|
3785
|
-
default: "
|
|
4263
|
+
default: "event-modeling"
|
|
3786
4264
|
});
|
|
3787
4265
|
if (selected === "none") {
|
|
3788
4266
|
return null;
|
|
@@ -3804,29 +4282,41 @@ function getValidModelOrFirst(modelId, availableModels) {
|
|
|
3804
4282
|
function printNextSteps(subscriptions) {
|
|
3805
4283
|
const steps = [];
|
|
3806
4284
|
if (subscriptions.hasClaude) {
|
|
3807
|
-
|
|
4285
|
+
if (subscriptions.claudeAuth === "api") {
|
|
4286
|
+
steps.push(`Set ${chalk6.cyan("ANTHROPIC_API_KEY")} environment variable with your API key`);
|
|
4287
|
+
} else {
|
|
4288
|
+
steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select Anthropic -> Claude Pro/Max`);
|
|
4289
|
+
}
|
|
3808
4290
|
}
|
|
3809
4291
|
if (subscriptions.hasOpenAI) {
|
|
3810
|
-
|
|
4292
|
+
if (subscriptions.openaiAuth === "api") {
|
|
4293
|
+
steps.push(`Set ${chalk6.cyan("OPENAI_API_KEY")} environment variable with your API key`);
|
|
4294
|
+
} else {
|
|
4295
|
+
steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select OpenAI -> ChatGPT Plus/Pro`);
|
|
4296
|
+
}
|
|
3811
4297
|
}
|
|
3812
4298
|
if (subscriptions.hasGoogle) {
|
|
3813
|
-
|
|
4299
|
+
if (subscriptions.googleAuth === "api") {
|
|
4300
|
+
steps.push(`Set ${chalk6.cyan("GOOGLE_API_KEY")} environment variable with your API key`);
|
|
4301
|
+
} else {
|
|
4302
|
+
steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select Google -> OAuth with Google`);
|
|
4303
|
+
}
|
|
3814
4304
|
}
|
|
3815
4305
|
if (subscriptions.hasGitHubCopilot) {
|
|
3816
|
-
steps.push(`Run: ${
|
|
4306
|
+
steps.push(`Run: ${chalk6.cyan("opencode auth login")} -> Select GitHub Copilot`);
|
|
3817
4307
|
}
|
|
3818
4308
|
logger.successBanner("OPENCODE ATHENA INSTALLED SUCCESSFULLY!");
|
|
3819
|
-
console.log(
|
|
4309
|
+
console.log(chalk6.bold("Next Steps:\n"));
|
|
3820
4310
|
steps.forEach((step, i) => {
|
|
3821
4311
|
console.log(` ${i + 1}. ${step}`);
|
|
3822
4312
|
});
|
|
3823
|
-
console.log(
|
|
3824
|
-
console.log(
|
|
3825
|
-
console.log(
|
|
3826
|
-
console.log(
|
|
3827
|
-
console.log(
|
|
3828
|
-
|
|
3829
|
-
);
|
|
4313
|
+
console.log(chalk6.bold("\nThen start OpenCode in your project directory.\n"));
|
|
4314
|
+
console.log(chalk6.gray("The SDLC plugin will automatically:"));
|
|
4315
|
+
console.log(chalk6.gray(" \u2022 Enforce TDD cycles (RED \u2192 DOMAIN \u2192 GREEN \u2192 DOMAIN)"));
|
|
4316
|
+
console.log(chalk6.gray(" \u2022 Track work via GitHub Issues"));
|
|
4317
|
+
console.log(chalk6.gray(" \u2022 Apply file ownership rules"));
|
|
4318
|
+
console.log(chalk6.gray("\nUse Tab to switch between Marvin modes (Build, Discover, etc.)"));
|
|
4319
|
+
console.log(chalk6.gray("\nDocumentation: https://github.com/jwilger/opencode-sdlc-plugin"));
|
|
3830
4320
|
console.log();
|
|
3831
4321
|
}
|
|
3832
4322
|
|
|
@@ -3907,7 +4397,7 @@ async function showStatus() {
|
|
|
3907
4397
|
const spinner = ora5("Checking authentication status...").start();
|
|
3908
4398
|
const authStatus = await detectAuthStatus();
|
|
3909
4399
|
spinner.stop();
|
|
3910
|
-
console.log(
|
|
4400
|
+
console.log(chalk6.bold("\nConfigured Providers:\n"));
|
|
3911
4401
|
displayProvider(
|
|
3912
4402
|
"Claude",
|
|
3913
4403
|
subscriptions.hasClaude,
|
|
@@ -3924,32 +4414,32 @@ async function showStatus() {
|
|
|
3924
4414
|
);
|
|
3925
4415
|
const models = configs.sdlc.models;
|
|
3926
4416
|
if (models) {
|
|
3927
|
-
console.log(
|
|
3928
|
-
console.log(` ${
|
|
3929
|
-
console.log(` ${
|
|
3930
|
-
console.log(` ${
|
|
3931
|
-
console.log(` ${
|
|
3932
|
-
console.log(` ${
|
|
3933
|
-
console.log(` ${
|
|
4417
|
+
console.log(chalk6.bold("\nCurrent Model Assignments:\n"));
|
|
4418
|
+
console.log(` ${chalk6.cyan("Marvin:")} ${models.marvin || "not set"}`);
|
|
4419
|
+
console.log(` ${chalk6.cyan("Oracle:")} ${models.oracle || "not set"}`);
|
|
4420
|
+
console.log(` ${chalk6.cyan("Librarian:")} ${models.librarian || "not set"}`);
|
|
4421
|
+
console.log(` ${chalk6.cyan("Frontend:")} ${models.frontend || "not set"}`);
|
|
4422
|
+
console.log(` ${chalk6.cyan("Doc Writer:")} ${models.documentWriter || "not set"}`);
|
|
4423
|
+
console.log(` ${chalk6.cyan("Multimodal:")} ${models.multimodalLooker || "not set"}`);
|
|
3934
4424
|
}
|
|
3935
4425
|
if (!authStatus.anthropic && subscriptions.hasClaude) {
|
|
3936
|
-
console.log(
|
|
4426
|
+
console.log(chalk6.yellow("\n\u26A0 Tip: Run 'opencode auth login' to authenticate Claude"));
|
|
3937
4427
|
}
|
|
3938
4428
|
if (!authStatus.openai && subscriptions.hasOpenAI) {
|
|
3939
|
-
console.log(
|
|
4429
|
+
console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate OpenAI"));
|
|
3940
4430
|
}
|
|
3941
4431
|
if (!authStatus.google && subscriptions.hasGoogle) {
|
|
3942
|
-
console.log(
|
|
4432
|
+
console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate Google"));
|
|
3943
4433
|
}
|
|
3944
4434
|
if (!authStatus.githubCopilot && subscriptions.hasGitHubCopilot) {
|
|
3945
|
-
console.log(
|
|
4435
|
+
console.log(chalk6.yellow("\u26A0 Tip: Run 'opencode auth login' to authenticate GitHub Copilot"));
|
|
3946
4436
|
}
|
|
3947
4437
|
console.log();
|
|
3948
4438
|
}
|
|
3949
4439
|
function displayProvider(name, enabled, tier, authenticated) {
|
|
3950
|
-
const status = enabled ?
|
|
3951
|
-
const auth = enabled ? authenticated ?
|
|
3952
|
-
const tierText = tier && tier !== "none" ?
|
|
4440
|
+
const status = enabled ? chalk6.green("\u2713 Enabled") : chalk6.gray("\u2717 Disabled");
|
|
4441
|
+
const auth = enabled ? authenticated ? chalk6.green("\u2713 Authenticated") : chalk6.yellow("\u26A0 Not authenticated") : chalk6.gray("(disabled)");
|
|
4442
|
+
const tierText = tier && tier !== "none" ? chalk6.gray(` (${tier})`) : "";
|
|
3953
4443
|
console.log(` ${status.padEnd(30)} ${name}${tierText}`);
|
|
3954
4444
|
console.log(` ${" ".repeat(30)} Auth: ${auth}`);
|
|
3955
4445
|
}
|
|
@@ -4011,7 +4501,7 @@ async function addProvider() {
|
|
|
4011
4501
|
});
|
|
4012
4502
|
}
|
|
4013
4503
|
logger.success(`Provider added: ${provider}`);
|
|
4014
|
-
console.log(
|
|
4504
|
+
console.log(chalk6.gray("\nRun 'opencode-sdlc providers refresh' to update model defaults."));
|
|
4015
4505
|
}
|
|
4016
4506
|
async function removeProvider() {
|
|
4017
4507
|
logger.banner();
|
|
@@ -4045,16 +4535,16 @@ async function uninstall(options) {
|
|
|
4045
4535
|
}
|
|
4046
4536
|
logger.section("Uninstalling OpenCode SDLC");
|
|
4047
4537
|
const fileManager = new FileManager();
|
|
4048
|
-
const commandsSpinner = ora5("
|
|
4538
|
+
const commandsSpinner = ora5("Cleaning up legacy bridge commands...").start();
|
|
4049
4539
|
try {
|
|
4050
4540
|
const removedCommands = await fileManager.removeCommands();
|
|
4051
4541
|
if (removedCommands.length > 0) {
|
|
4052
|
-
commandsSpinner.succeed(`Removed ${removedCommands.length}
|
|
4542
|
+
commandsSpinner.succeed(`Removed ${removedCommands.length} legacy command(s)`);
|
|
4053
4543
|
} else {
|
|
4054
|
-
commandsSpinner.info("No bridge commands found");
|
|
4544
|
+
commandsSpinner.info("No legacy bridge commands found");
|
|
4055
4545
|
}
|
|
4056
4546
|
} catch (err) {
|
|
4057
|
-
commandsSpinner.fail("Failed to remove
|
|
4547
|
+
commandsSpinner.fail("Failed to remove legacy commands");
|
|
4058
4548
|
logger.error(err instanceof Error ? err.message : String(err));
|
|
4059
4549
|
}
|
|
4060
4550
|
if (!options.keepConfig) {
|
|
@@ -4105,7 +4595,7 @@ async function uninstall(options) {
|
|
|
4105
4595
|
logger.success("OpenCode SDLC has been uninstalled.");
|
|
4106
4596
|
if (options.keepConfig) {
|
|
4107
4597
|
logger.info("Configuration files were preserved.");
|
|
4108
|
-
logger.info(`Run ${
|
|
4598
|
+
logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to reinstall with existing config.`);
|
|
4109
4599
|
}
|
|
4110
4600
|
console.log();
|
|
4111
4601
|
}
|
|
@@ -4141,7 +4631,7 @@ async function upgrade(options) {
|
|
|
4141
4631
|
const configs = loadExistingConfigs();
|
|
4142
4632
|
if (!configs.sdlc) {
|
|
4143
4633
|
logger.error("No existing Sdlc installation found.");
|
|
4144
|
-
logger.info(`Run ${
|
|
4634
|
+
logger.info(`Run ${chalk6.cyan("opencode-sdlc install")} to install for the first time.`);
|
|
4145
4635
|
process.exit(1);
|
|
4146
4636
|
}
|
|
4147
4637
|
const existingVersion = configs.sdlcVersion || "0.0.1";
|
|
@@ -4176,7 +4666,7 @@ async function upgrade(options) {
|
|
|
4176
4666
|
logger.section("Package Versions");
|
|
4177
4667
|
const updatesAvailable = updates.filter((u) => u.updateAvailable);
|
|
4178
4668
|
for (const pkg of updates) {
|
|
4179
|
-
const status = pkg.updateAvailable ?
|
|
4669
|
+
const status = pkg.updateAvailable ? chalk6.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk6.green(pkg.current);
|
|
4180
4670
|
logger.keyValue(pkg.name, status);
|
|
4181
4671
|
}
|
|
4182
4672
|
console.log();
|
|
@@ -4194,7 +4684,7 @@ async function upgrade(options) {
|
|
|
4194
4684
|
}
|
|
4195
4685
|
if (options.check) {
|
|
4196
4686
|
console.log();
|
|
4197
|
-
logger.info(`Run ${
|
|
4687
|
+
logger.info(`Run ${chalk6.cyan("opencode-sdlc upgrade")} (without --check) to apply upgrades.`);
|
|
4198
4688
|
return;
|
|
4199
4689
|
}
|
|
4200
4690
|
const actionCount = updatesAvailable.length + (existingVersion !== VERSION ? 1 : 0);
|
|
@@ -4213,9 +4703,9 @@ async function upgrade(options) {
|
|
|
4213
4703
|
}
|
|
4214
4704
|
}
|
|
4215
4705
|
logger.section("Upgrading Configuration");
|
|
4216
|
-
console.log(
|
|
4706
|
+
console.log(chalk6.cyan(`
|
|
4217
4707
|
Current version: ${existingVersion}`));
|
|
4218
|
-
console.log(
|
|
4708
|
+
console.log(chalk6.cyan(`New version: ${VERSION}
|
|
4219
4709
|
`));
|
|
4220
4710
|
const backupSpinner = ora5("Creating backup...").start();
|
|
4221
4711
|
const backups = createBackups();
|
|
@@ -4229,7 +4719,7 @@ Current version: ${existingVersion}`));
|
|
|
4229
4719
|
if (fileMigrationResult.stateFileMoved) moved.push("state file");
|
|
4230
4720
|
if (fileMigrationResult.backupsMoved > 0)
|
|
4231
4721
|
moved.push(`${fileMigrationResult.backupsMoved} backup(s)`);
|
|
4232
|
-
console.log(
|
|
4722
|
+
console.log(chalk6.gray(` Migrated ${moved.join(", ")} to new sdlc/ directory`));
|
|
4233
4723
|
}
|
|
4234
4724
|
const migrationSpinner = ora5("Applying migrations...").start();
|
|
4235
4725
|
const migrationResult = migrateConfigs(
|
|
@@ -4241,15 +4731,15 @@ Current version: ${existingVersion}`));
|
|
|
4241
4731
|
if (migrationResult.migrationsApplied.length > 0) {
|
|
4242
4732
|
migrationSpinner.succeed(`Applied ${migrationResult.migrationsApplied.length} migration(s)`);
|
|
4243
4733
|
for (const migration of migrationResult.migrationsApplied) {
|
|
4244
|
-
console.log(
|
|
4734
|
+
console.log(chalk6.gray(` \u2022 ${migration}`));
|
|
4245
4735
|
}
|
|
4246
4736
|
} else {
|
|
4247
4737
|
migrationSpinner.succeed("No migrations needed");
|
|
4248
4738
|
}
|
|
4249
4739
|
if (migrationResult.hasBreakingChanges && !options.yes) {
|
|
4250
|
-
console.log(
|
|
4740
|
+
console.log(chalk6.yellow("\nBreaking changes detected:"));
|
|
4251
4741
|
for (const warning of migrationResult.breakingChangeWarnings) {
|
|
4252
|
-
console.log(
|
|
4742
|
+
console.log(chalk6.yellow(` \u26A0 ${warning}`));
|
|
4253
4743
|
}
|
|
4254
4744
|
const continueUpgrade = await confirm({
|
|
4255
4745
|
message: "Continue with upgrade despite breaking changes?",
|
|
@@ -4267,20 +4757,20 @@ Current version: ${existingVersion}`));
|
|
|
4267
4757
|
const existingAdvanced = extractAdvanced(migrationResult.sdlcConfig);
|
|
4268
4758
|
logger.section("Preserved Configuration");
|
|
4269
4759
|
if (existingSubscriptions) {
|
|
4270
|
-
console.log(
|
|
4760
|
+
console.log(chalk6.bold("Subscriptions:"));
|
|
4271
4761
|
if (existingSubscriptions.hasClaude)
|
|
4272
|
-
console.log(
|
|
4273
|
-
if (existingSubscriptions.hasOpenAI) console.log(
|
|
4762
|
+
console.log(chalk6.green(` \u2713 Claude (${existingSubscriptions.claudeTier})`));
|
|
4763
|
+
if (existingSubscriptions.hasOpenAI) console.log(chalk6.green(" \u2713 OpenAI"));
|
|
4274
4764
|
if (existingSubscriptions.hasGoogle)
|
|
4275
|
-
console.log(
|
|
4765
|
+
console.log(chalk6.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
|
|
4276
4766
|
if (existingSubscriptions.hasGitHubCopilot)
|
|
4277
|
-
console.log(
|
|
4767
|
+
console.log(chalk6.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
|
|
4278
4768
|
}
|
|
4279
4769
|
if (existingModels) {
|
|
4280
|
-
console.log(
|
|
4281
|
-
console.log(
|
|
4282
|
-
console.log(
|
|
4283
|
-
console.log(
|
|
4770
|
+
console.log(chalk6.bold("\nModel Assignments:"));
|
|
4771
|
+
console.log(chalk6.green(` \u2713 Marvin: ${existingModels.marvin}`));
|
|
4772
|
+
console.log(chalk6.green(` \u2713 Oracle: ${existingModels.oracle}`));
|
|
4773
|
+
console.log(chalk6.green(` \u2713 Librarian: ${existingModels.librarian}`));
|
|
4284
4774
|
}
|
|
4285
4775
|
console.log();
|
|
4286
4776
|
const newFeatures = detectNewFeatures(migrationResult.sdlcConfig);
|
|
@@ -4307,7 +4797,7 @@ Current version: ${existingVersion}`));
|
|
|
4307
4797
|
const fullAnswers = {
|
|
4308
4798
|
subscriptions: existingSubscriptions,
|
|
4309
4799
|
models: existingModels || {
|
|
4310
|
-
|
|
4800
|
+
marvin: "",
|
|
4311
4801
|
oracle: "",
|
|
4312
4802
|
librarian: ""
|
|
4313
4803
|
},
|
|
@@ -4390,28 +4880,40 @@ Current version: ${existingVersion}`));
|
|
|
4390
4880
|
}
|
|
4391
4881
|
}
|
|
4392
4882
|
}
|
|
4393
|
-
const commandsSpinner = ora5("Updating bridge commands...").start();
|
|
4394
4883
|
try {
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
}
|
|
4398
|
-
|
|
4884
|
+
await fileManager.removeCommands();
|
|
4885
|
+
} catch {
|
|
4886
|
+
}
|
|
4887
|
+
const modesConfig = migrationResult.sdlcConfig.modes;
|
|
4888
|
+
if (modesConfig?.enabled && modesConfig.enabled.length > 0) {
|
|
4889
|
+
const agentSpinner = ora5("Updating Marvin mode agents...").start();
|
|
4890
|
+
try {
|
|
4891
|
+
const projectDir = process.cwd();
|
|
4892
|
+
const copiedAgents = await fileManager.copyAgentModes(projectDir, modesConfig.enabled);
|
|
4893
|
+
if (copiedAgents.length > 0) {
|
|
4894
|
+
agentSpinner.succeed(`Updated ${copiedAgents.length} Marvin mode agents`);
|
|
4895
|
+
} else {
|
|
4896
|
+
agentSpinner.succeed("Marvin mode agents up to date");
|
|
4897
|
+
}
|
|
4898
|
+
} catch (_err) {
|
|
4899
|
+
agentSpinner.warn("Could not update Marvin mode agents");
|
|
4900
|
+
}
|
|
4399
4901
|
}
|
|
4400
4902
|
logger.successBanner(`UPGRADED TO OPENCODE ATHENA ${VERSION}!`);
|
|
4401
4903
|
if (backups.sdlcBackup || backups.omoBackup || backups.opencodeBackup) {
|
|
4402
|
-
console.log(
|
|
4403
|
-
if (backups.sdlcBackup) console.log(
|
|
4404
|
-
if (backups.omoBackup) console.log(
|
|
4405
|
-
if (backups.opencodeBackup) console.log(
|
|
4904
|
+
console.log(chalk6.gray("\nBackups saved:"));
|
|
4905
|
+
if (backups.sdlcBackup) console.log(chalk6.gray(` \u2022 ${backups.sdlcBackup}`));
|
|
4906
|
+
if (backups.omoBackup) console.log(chalk6.gray(` \u2022 ${backups.omoBackup}`));
|
|
4907
|
+
if (backups.opencodeBackup) console.log(chalk6.gray(` \u2022 ${backups.opencodeBackup}`));
|
|
4406
4908
|
}
|
|
4407
|
-
console.log(
|
|
4909
|
+
console.log(chalk6.gray("\nRestart OpenCode to use the upgraded configuration."));
|
|
4408
4910
|
console.log();
|
|
4409
4911
|
}
|
|
4410
4912
|
|
|
4411
4913
|
// src/cli/index.ts
|
|
4412
4914
|
var program = new Command();
|
|
4413
4915
|
program.name("opencode-sdlc").description(
|
|
4414
|
-
`${
|
|
4916
|
+
`${chalk6.cyan(DISPLAY_NAME)} - ${TAGLINE}
|
|
4415
4917
|
TDD-driven development toolkit with GitHub Issues integration for OpenCode`
|
|
4416
4918
|
).version(VERSION);
|
|
4417
4919
|
program.command("install").description("Install and configure OpenCode SDLC").option(
|
|
@@ -4433,15 +4935,15 @@ program.command("providers [action]").description("Manage LLM provider subscript
|
|
|
4433
4935
|
await providers({ action });
|
|
4434
4936
|
});
|
|
4435
4937
|
function displayPresets() {
|
|
4436
|
-
console.log(
|
|
4938
|
+
console.log(chalk6.bold.cyan("\nAvailable Presets:\n"));
|
|
4437
4939
|
const presets = listPresets();
|
|
4438
4940
|
for (const preset of presets) {
|
|
4439
|
-
console.log(
|
|
4440
|
-
console.log(
|
|
4941
|
+
console.log(chalk6.bold(` ${preset.name}`));
|
|
4942
|
+
console.log(chalk6.gray(` ${preset.description}`));
|
|
4441
4943
|
console.log();
|
|
4442
4944
|
}
|
|
4443
|
-
console.log(
|
|
4444
|
-
console.log(
|
|
4945
|
+
console.log(chalk6.gray("Usage: opencode-sdlc install --preset <name>"));
|
|
4946
|
+
console.log(chalk6.gray(" opencode-sdlc install --preset standard --yes\n"));
|
|
4445
4947
|
}
|
|
4446
4948
|
program.parse();
|
|
4447
4949
|
//# sourceMappingURL=index.js.map
|