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