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.
- package/README.md +90 -17
- package/config/presets/event-modeling.json +19 -8
- package/config/presets/minimal.json +29 -16
- package/config/presets/standard.json +19 -8
- package/config/schemas/athena.schema.json +4 -4
- package/config/schemas/sdlc.schema.json +101 -5
- package/dist/cli/index.js +1431 -1336
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +428 -66
- package/dist/index.js +6262 -2440
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.js +5793 -2010
- package/dist/plugin/index.js.map +1 -1
- package/package.json +2 -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/config/presets/copilot-only.json +0 -69
- package/config/presets/enterprise.json +0 -79
- package/config/presets/solo-quick.json +0 -70
- 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
|
|
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,
|
|
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, "
|
|
72
|
+
if (existsSync(join(bundledRoot, "prompts"))) {
|
|
511
73
|
return bundledRoot;
|
|
512
74
|
}
|
|
513
75
|
const devRoot = join(currentFileDir, "..", "..", "..");
|
|
514
|
-
if (existsSync(join(devRoot, "
|
|
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(
|
|
552
|
-
if (!existsSync(
|
|
113
|
+
readJsonFile(path) {
|
|
114
|
+
if (!existsSync(path)) {
|
|
553
115
|
return null;
|
|
554
116
|
}
|
|
555
117
|
try {
|
|
556
|
-
const content = readFileSync(
|
|
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(
|
|
566
|
-
const dir = dirname(
|
|
127
|
+
async writeJsonFile(path, data) {
|
|
128
|
+
const dir = dirname(path);
|
|
567
129
|
await this.ensureDir(dir);
|
|
568
|
-
await writeFile(
|
|
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(
|
|
574
|
-
return existsSync(
|
|
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
|
-
|
|
622
|
-
|
|
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
|
|
200
|
+
const sourceModesDir = join(packageRoot, "prompts", "modes");
|
|
625
201
|
const copiedFiles = [];
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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(
|
|
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(
|
|
704
|
-
if (!existsSync(
|
|
312
|
+
async backupFile(path) {
|
|
313
|
+
if (!existsSync(path)) {
|
|
705
314
|
return null;
|
|
706
315
|
}
|
|
707
|
-
const backupPath = `${
|
|
708
|
-
await copyFile(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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}${
|
|
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}${
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
921
|
-
if (!existsSync(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
821
|
+
function validateJsonConfig(path) {
|
|
1142
822
|
const result = { valid: true, errors: [], warnings: [] };
|
|
1143
|
-
if (!existsSync(
|
|
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(
|
|
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 =
|
|
1040
|
+
color = chalk5.green;
|
|
1378
1041
|
break;
|
|
1379
1042
|
case "warn":
|
|
1380
1043
|
icon = "!";
|
|
1381
|
-
color =
|
|
1044
|
+
color = chalk5.yellow;
|
|
1382
1045
|
hasWarnings = true;
|
|
1383
1046
|
break;
|
|
1384
1047
|
case "fail":
|
|
1385
1048
|
icon = "\u2716";
|
|
1386
|
-
color =
|
|
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 ${
|
|
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 ${
|
|
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 ?
|
|
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 ?
|
|
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 ?
|
|
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 ?
|
|
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 ?
|
|
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("
|
|
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 ?
|
|
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 ?
|
|
1574
|
-
logger.keyValue("exa", mcps.exa ?
|
|
1575
|
-
logger.keyValue("grep_app", mcps.grepApp ?
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
})
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
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
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
//
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
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/
|
|
2544
|
-
|
|
2545
|
-
async function
|
|
2546
|
-
const
|
|
2547
|
-
message: "
|
|
2548
|
-
|
|
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
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
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
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
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
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
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
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
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: "
|
|
2602
|
-
|
|
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: "
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
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
|
|
2627
|
-
message: "
|
|
2628
|
-
default:
|
|
2751
|
+
const description = await input({
|
|
2752
|
+
message: "Repository description (optional):",
|
|
2753
|
+
default: ""
|
|
2629
2754
|
});
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
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
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
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 (!
|
|
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
|
|
2673
|
-
message: "
|
|
2803
|
+
const projectAction = await select({
|
|
2804
|
+
message: "Project board setup:",
|
|
2674
2805
|
choices: [
|
|
2675
2806
|
{
|
|
2676
|
-
|
|
2677
|
-
|
|
2807
|
+
value: "existing",
|
|
2808
|
+
name: "Link existing project",
|
|
2809
|
+
description: "Enter project number from URL"
|
|
2678
2810
|
},
|
|
2679
2811
|
{
|
|
2680
|
-
|
|
2681
|
-
|
|
2812
|
+
value: "create-blank",
|
|
2813
|
+
name: "Create new blank project",
|
|
2814
|
+
description: "Create a new project with default columns"
|
|
2682
2815
|
},
|
|
2683
2816
|
{
|
|
2684
|
-
|
|
2685
|
-
|
|
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
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
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
|
|
2703
|
-
message: "
|
|
2704
|
-
|
|
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
|
-
|
|
2707
|
-
|
|
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
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
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
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
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 (!
|
|
2740
|
-
return {
|
|
2741
|
-
enabled: false,
|
|
2742
|
-
outputPath: defaults?.outputPath ?? DEFAULT_OUTPUT_PATH
|
|
2743
|
-
};
|
|
2869
|
+
if (!createdProject) {
|
|
2870
|
+
return { action: "skip" };
|
|
2744
2871
|
}
|
|
2745
|
-
const
|
|
2746
|
-
message: "
|
|
2747
|
-
|
|
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
|
-
|
|
2760
|
-
|
|
2877
|
+
action: "create-blank",
|
|
2878
|
+
projectNumber: projectNum
|
|
2761
2879
|
};
|
|
2762
2880
|
}
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
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: "
|
|
2772
|
-
|
|
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
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
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
|
|
2782
|
-
message: "
|
|
2783
|
-
|
|
2934
|
+
const repo = await input({
|
|
2935
|
+
message: "GitHub repository name:",
|
|
2936
|
+
validate: (v) => v.trim() ? true : "Repository name is required"
|
|
2784
2937
|
});
|
|
2785
|
-
const
|
|
2786
|
-
message: "
|
|
2787
|
-
default:
|
|
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
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
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
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
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
|
-
|
|
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(`
|
|
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(
|
|
3553
|
+
console.log(chalk5.cyan(`
|
|
3343
3554
|
Current version: ${existingVersion}`));
|
|
3344
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
3592
|
+
console.log(chalk5.yellow("\nBreaking changes detected:"));
|
|
3382
3593
|
for (const warning of migrationResult.breakingChangeWarnings) {
|
|
3383
|
-
console.log(
|
|
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(
|
|
3402
|
-
if (existingSubscriptions.hasClaude)
|
|
3403
|
-
|
|
3404
|
-
|
|
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(
|
|
3622
|
+
console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
|
|
3407
3623
|
if (existingSubscriptions.hasGitHubCopilot)
|
|
3408
|
-
console.log(
|
|
3624
|
+
console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
|
|
3409
3625
|
}
|
|
3410
3626
|
if (existingModels) {
|
|
3411
|
-
console.log(
|
|
3412
|
-
console.log(
|
|
3413
|
-
console.log(
|
|
3414
|
-
console.log(
|
|
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
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
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
|
-
|
|
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(
|
|
3495
|
-
if (backups.sdlcBackup) console.log(
|
|
3496
|
-
if (backups.omoBackup) console.log(
|
|
3497
|
-
if (backups.opencodeBackup) console.log(
|
|
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(
|
|
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
|
-
|
|
3534
|
-
let
|
|
3535
|
-
let
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
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
|
-
|
|
3561
|
-
|
|
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
|
-
|
|
3597
|
-
|
|
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(
|
|
3847
|
+
console.log(chalk5.bold("Files to be created/modified:\n"));
|
|
3735
3848
|
for (const file of files) {
|
|
3736
|
-
const action = file.exists ?
|
|
3737
|
-
console.log(
|
|
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
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
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(
|
|
3887
|
+
printNextSteps();
|
|
3772
3888
|
}
|
|
3773
|
-
async function
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
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: "
|
|
3899
|
+
message: "Select a development profile:",
|
|
3784
3900
|
choices,
|
|
3785
|
-
default: "
|
|
3901
|
+
default: "event-modeling"
|
|
3786
3902
|
});
|
|
3787
|
-
|
|
3788
|
-
|
|
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
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
3928
|
-
console.log(` ${
|
|
3929
|
-
console.log(` ${
|
|
3930
|
-
console.log(` ${
|
|
3931
|
-
console.log(` ${
|
|
3932
|
-
console.log(` ${
|
|
3933
|
-
console.log(` ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ?
|
|
3951
|
-
const auth = enabled ? authenticated ?
|
|
3952
|
-
const tierText = tier && tier !== "none" ?
|
|
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(
|
|
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("
|
|
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}
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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 ?
|
|
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 ${
|
|
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(
|
|
4303
|
+
console.log(chalk5.cyan(`
|
|
4217
4304
|
Current version: ${existingVersion}`));
|
|
4218
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
4337
|
+
console.log(chalk5.yellow("\nBreaking changes detected:"));
|
|
4251
4338
|
for (const warning of migrationResult.breakingChangeWarnings) {
|
|
4252
|
-
console.log(
|
|
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(
|
|
4357
|
+
console.log(chalk5.bold("Subscriptions:"));
|
|
4271
4358
|
if (existingSubscriptions.hasClaude)
|
|
4272
|
-
console.log(
|
|
4273
|
-
if (existingSubscriptions.hasOpenAI) console.log(
|
|
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(
|
|
4362
|
+
console.log(chalk5.green(` \u2713 Google (${existingSubscriptions.googleAuth})`));
|
|
4276
4363
|
if (existingSubscriptions.hasGitHubCopilot)
|
|
4277
|
-
console.log(
|
|
4364
|
+
console.log(chalk5.green(` \u2713 GitHub Copilot (${existingSubscriptions.copilotPlan})`));
|
|
4278
4365
|
}
|
|
4279
4366
|
if (existingModels) {
|
|
4280
|
-
console.log(
|
|
4281
|
-
console.log(
|
|
4282
|
-
console.log(
|
|
4283
|
-
console.log(
|
|
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
|
-
|
|
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
|
-
|
|
4396
|
-
|
|
4397
|
-
}
|
|
4398
|
-
|
|
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(
|
|
4403
|
-
if (backups.sdlcBackup) console.log(
|
|
4404
|
-
if (backups.omoBackup) console.log(
|
|
4405
|
-
if (backups.opencodeBackup) console.log(
|
|
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(
|
|
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
|
-
`${
|
|
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
|
-
|
|
4419
|
-
|
|
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
|
|
4436
|
-
console.log(
|
|
4437
|
-
const
|
|
4438
|
-
for (const
|
|
4439
|
-
console.log(
|
|
4440
|
-
console.log(
|
|
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(
|
|
4444
|
-
console.log(
|
|
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
|