opencode-athena 0.0.1

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.
@@ -0,0 +1,2185 @@
1
+ #!/usr/bin/env node
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { confirm, select, checkbox } from '@inquirer/prompts';
5
+ import chalk3 from 'chalk';
6
+ import { Command } from 'commander';
7
+ import { homedir } from 'os';
8
+ import ora3 from 'ora';
9
+ import { exec } from 'child_process';
10
+ import { existsSync, readFileSync } from 'fs';
11
+ import { mkdir, writeFile, readdir, copyFile, rm } from 'fs/promises';
12
+ import { promisify } from 'util';
13
+ import 'yaml';
14
+ import { z } from 'zod';
15
+
16
+ var __defProp = Object.defineProperty;
17
+ var __getOwnPropNames = Object.getOwnPropertyNames;
18
+ var __esm = (fn, res) => function __init() {
19
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
25
+ var init_esm_shims = __esm({
26
+ "node_modules/tsup/assets/esm_shims.js"() {
27
+ }
28
+ });
29
+
30
+ // src/cli/questions/models.ts
31
+ var models_exports = {};
32
+ __export(models_exports, {
33
+ gatherModels: () => gatherModels,
34
+ getAvailableModels: () => getAvailableModels,
35
+ getModelList: () => getModelList,
36
+ validatePresetModels: () => validatePresetModels
37
+ });
38
+ function getAvailableModels(subscriptions) {
39
+ return AVAILABLE_MODELS.filter((model) => {
40
+ if (model.provider === "anthropic" && !subscriptions.hasClaude) return false;
41
+ if (model.provider === "openai" && !subscriptions.hasOpenAI) return false;
42
+ if (model.provider === "google" && !subscriptions.hasGoogle) return false;
43
+ return true;
44
+ });
45
+ }
46
+ function isModelAvailable(modelId, availableModels) {
47
+ return availableModels.some((m) => m.id === modelId);
48
+ }
49
+ function getValidModelOrFallback(presetModel, role, subscriptions, availableModels) {
50
+ if (presetModel && isModelAvailable(presetModel, availableModels)) {
51
+ return presetModel;
52
+ }
53
+ return getSuggestedModel(role, subscriptions, availableModels);
54
+ }
55
+ function createModelChoices(models) {
56
+ return models.map((model) => ({
57
+ name: `${model.name} - ${model.description}`,
58
+ value: model.id
59
+ }));
60
+ }
61
+ function getSuggestedModel(role, _subscriptions, availableModels) {
62
+ const suggestions = {
63
+ sisyphus: [
64
+ "anthropic/claude-opus-4-5-thinking",
65
+ "anthropic/claude-sonnet-4-5-thinking",
66
+ "openai/gpt-5.1-high",
67
+ "google/gemini-2.5-pro"
68
+ ],
69
+ oracle: [
70
+ "openai/gpt-5.1-high",
71
+ "anthropic/claude-opus-4-5-thinking",
72
+ "anthropic/claude-sonnet-4-5-thinking",
73
+ "google/gemini-2.5-pro"
74
+ ],
75
+ librarian: ["anthropic/claude-sonnet-4-5", "openai/gpt-4o", "google/gemini-2.5-flash"],
76
+ frontend: ["anthropic/claude-sonnet-4-5", "google/gemini-2.5-pro", "openai/gpt-4o"],
77
+ documentWriter: ["google/gemini-2.5-pro", "anthropic/claude-sonnet-4-5", "openai/gpt-4o"],
78
+ multimodalLooker: ["google/gemini-2.5-flash", "openai/gpt-4o", "anthropic/claude-sonnet-4-5"]
79
+ };
80
+ const roleDefaults = suggestions[role] || [];
81
+ const availableIds = availableModels.map((m) => m.id);
82
+ for (const modelId of roleDefaults) {
83
+ if (availableIds.includes(modelId)) {
84
+ return modelId;
85
+ }
86
+ }
87
+ return availableModels[0]?.id;
88
+ }
89
+ async function gatherModels(subscriptions, defaults) {
90
+ const availableModels = getAvailableModels(subscriptions);
91
+ if (availableModels.length === 0) {
92
+ throw new Error(
93
+ "No models available. Please enable at least one provider (Claude, OpenAI, or Google)."
94
+ );
95
+ }
96
+ const choices = createModelChoices(availableModels);
97
+ const sisyphusDefault = getValidModelOrFallback(
98
+ defaults?.sisyphus,
99
+ "sisyphus",
100
+ subscriptions,
101
+ availableModels
102
+ );
103
+ const oracleDefault = getValidModelOrFallback(
104
+ defaults?.oracle,
105
+ "oracle",
106
+ subscriptions,
107
+ availableModels
108
+ );
109
+ const librarianDefault = getValidModelOrFallback(
110
+ defaults?.librarian,
111
+ "librarian",
112
+ subscriptions,
113
+ availableModels
114
+ );
115
+ const sisyphus = await select({
116
+ message: "Model for Sisyphus (main orchestrator - implements stories)?",
117
+ choices,
118
+ default: sisyphusDefault
119
+ });
120
+ const oracle = await select({
121
+ message: "Model for Oracle (debugging and complex reasoning)?",
122
+ choices,
123
+ default: oracleDefault
124
+ });
125
+ const librarian = await select({
126
+ message: "Model for Librarian (research and documentation lookup)?",
127
+ choices,
128
+ default: librarianDefault
129
+ });
130
+ const frontend = getValidModelOrFallback(
131
+ defaults?.frontend,
132
+ "frontend",
133
+ subscriptions,
134
+ availableModels
135
+ );
136
+ const documentWriter = getValidModelOrFallback(
137
+ defaults?.documentWriter,
138
+ "documentWriter",
139
+ subscriptions,
140
+ availableModels
141
+ );
142
+ const multimodalLooker = getValidModelOrFallback(
143
+ defaults?.multimodalLooker,
144
+ "multimodalLooker",
145
+ subscriptions,
146
+ availableModels
147
+ );
148
+ return {
149
+ sisyphus,
150
+ oracle,
151
+ librarian,
152
+ frontend,
153
+ documentWriter,
154
+ multimodalLooker
155
+ };
156
+ }
157
+ function getModelList(subscriptions) {
158
+ return getAvailableModels(subscriptions);
159
+ }
160
+ function validatePresetModels(presetModels, subscriptions) {
161
+ const warnings = [];
162
+ const availableModels = getAvailableModels(subscriptions);
163
+ const checkModel = (model, role) => {
164
+ if (model && !isModelAvailable(model, availableModels)) {
165
+ warnings.push(
166
+ `Preset model for ${role} (${model}) is not available with your subscriptions. A fallback will be used.`
167
+ );
168
+ }
169
+ };
170
+ checkModel(presetModels.sisyphus, "Sisyphus");
171
+ checkModel(presetModels.oracle, "Oracle");
172
+ checkModel(presetModels.librarian, "Librarian");
173
+ checkModel(presetModels.frontend, "Frontend");
174
+ checkModel(presetModels.documentWriter, "Document Writer");
175
+ checkModel(presetModels.multimodalLooker, "Multimodal Looker");
176
+ return warnings;
177
+ }
178
+ var AVAILABLE_MODELS;
179
+ var init_models = __esm({
180
+ "src/cli/questions/models.ts"() {
181
+ init_esm_shims();
182
+ AVAILABLE_MODELS = [
183
+ // Anthropic models
184
+ {
185
+ id: "anthropic/claude-sonnet-4-5",
186
+ name: "Claude Sonnet 4.5",
187
+ provider: "anthropic",
188
+ description: "Latest Sonnet - balanced performance and speed"
189
+ },
190
+ {
191
+ id: "anthropic/claude-opus-4-5",
192
+ name: "Claude Opus 4.5",
193
+ provider: "anthropic",
194
+ description: "Most capable Claude model"
195
+ },
196
+ {
197
+ id: "anthropic/claude-sonnet-4-5-thinking",
198
+ name: "Claude Sonnet 4.5 (Thinking)",
199
+ provider: "anthropic",
200
+ description: "Sonnet with extended thinking enabled"
201
+ },
202
+ {
203
+ id: "anthropic/claude-opus-4-5-thinking",
204
+ name: "Claude Opus 4.5 (Thinking)",
205
+ provider: "anthropic",
206
+ description: "Opus with extended thinking enabled"
207
+ },
208
+ // OpenAI models
209
+ {
210
+ id: "openai/gpt-4o",
211
+ name: "GPT-4o",
212
+ provider: "openai",
213
+ description: "Fast multimodal model"
214
+ },
215
+ {
216
+ id: "openai/gpt-5.1",
217
+ name: "GPT-5.1",
218
+ provider: "openai",
219
+ description: "Latest GPT model"
220
+ },
221
+ {
222
+ id: "openai/gpt-5.1-high",
223
+ name: "GPT-5.1 High",
224
+ provider: "openai",
225
+ description: "GPT-5.1 with high reasoning effort"
226
+ },
227
+ // Google models
228
+ {
229
+ id: "google/gemini-2.5-pro",
230
+ name: "Gemini 2.5 Pro",
231
+ provider: "google",
232
+ description: "Latest Gemini Pro model"
233
+ },
234
+ {
235
+ id: "google/gemini-2.5-flash",
236
+ name: "Gemini 2.5 Flash",
237
+ provider: "google",
238
+ description: "Fast Gemini model"
239
+ },
240
+ {
241
+ id: "google/gemini-2.0-flash",
242
+ name: "Gemini 2.0 Flash",
243
+ provider: "google",
244
+ description: "Previous generation fast model"
245
+ }
246
+ ];
247
+ }
248
+ });
249
+
250
+ // src/cli/index.ts
251
+ init_esm_shims();
252
+
253
+ // src/shared/constants.ts
254
+ init_esm_shims();
255
+ var VERSION = "0.0.1";
256
+ var DISPLAY_NAME = "OpenCode Athena";
257
+ var TAGLINE = "Strategic wisdom meets practical execution";
258
+ var CONFIG_PATHS = {
259
+ /** Global OpenCode config directory */
260
+ globalConfigDir: join(homedir(), ".config", "opencode"),
261
+ /** Global Athena config file */
262
+ globalAthenaConfig: join(homedir(), ".config", "opencode", "athena.json"),
263
+ /** Global OpenCode config file */
264
+ globalOpencodeConfig: join(homedir(), ".config", "opencode", "opencode.json"),
265
+ /** Global oh-my-opencode config file */
266
+ globalOmoConfig: join(homedir(), ".config", "opencode", "oh-my-opencode.json"),
267
+ /** Commands directory */
268
+ commandsDir: join(homedir(), ".config", "opencode", "command"),
269
+ /** Plugin directory */
270
+ pluginDir: join(homedir(), ".config", "opencode", "plugin"),
271
+ /** Athena state file (for story tracking) */
272
+ stateFile: join(homedir(), ".config", "opencode", "athena-state.json")
273
+ };
274
+ var DEFAULTS = {
275
+ /** Maximum parallel stories */
276
+ parallelStoryLimit: 3};
277
+ var MIN_VERSIONS = {
278
+ node: "20.0.0",
279
+ opencode: "1.0.132"
280
+ };
281
+
282
+ // src/cli/commands/doctor.ts
283
+ init_esm_shims();
284
+
285
+ // src/cli/utils/file-manager.ts
286
+ init_esm_shims();
287
+ var execAsync = promisify(exec);
288
+ function getPackageRoot() {
289
+ const currentFileDir = dirname(fileURLToPath(import.meta.url));
290
+ const bundledRoot = join(currentFileDir, "..", "..");
291
+ if (existsSync(join(bundledRoot, "commands"))) {
292
+ return bundledRoot;
293
+ }
294
+ const devRoot = join(currentFileDir, "..", "..", "..");
295
+ if (existsSync(join(devRoot, "commands"))) {
296
+ return devRoot;
297
+ }
298
+ return bundledRoot;
299
+ }
300
+ var FileManager = class {
301
+ configDir;
302
+ constructor(configDir) {
303
+ this.configDir = configDir || CONFIG_PATHS.globalConfigDir;
304
+ }
305
+ /**
306
+ * Get the configuration directory path
307
+ */
308
+ getConfigDir() {
309
+ return this.configDir;
310
+ }
311
+ /**
312
+ * Ensure a directory exists
313
+ */
314
+ async ensureDir(dir) {
315
+ if (!existsSync(dir)) {
316
+ await mkdir(dir, { recursive: true });
317
+ }
318
+ }
319
+ /**
320
+ * Write multiple files atomically
321
+ */
322
+ async writeFiles(files) {
323
+ for (const file of files) {
324
+ const dir = dirname(file.path);
325
+ await this.ensureDir(dir);
326
+ await writeFile(file.path, file.content, "utf-8");
327
+ }
328
+ }
329
+ /**
330
+ * Read a JSON configuration file
331
+ */
332
+ readJsonFile(path2) {
333
+ if (!existsSync(path2)) {
334
+ return null;
335
+ }
336
+ try {
337
+ const content = readFileSync(path2, "utf-8");
338
+ return JSON.parse(content);
339
+ } catch {
340
+ return null;
341
+ }
342
+ }
343
+ /**
344
+ * Write a JSON configuration file
345
+ */
346
+ async writeJsonFile(path2, data) {
347
+ const dir = dirname(path2);
348
+ await this.ensureDir(dir);
349
+ await writeFile(path2, JSON.stringify(data, null, 2), "utf-8");
350
+ }
351
+ /**
352
+ * Check if a file exists
353
+ */
354
+ exists(path2) {
355
+ return existsSync(path2);
356
+ }
357
+ /**
358
+ * Install npm dependencies in the config directory
359
+ */
360
+ async installDependencies(packages) {
361
+ if (packages.length === 0) return;
362
+ await this.ensureDir(this.configDir);
363
+ const packageJsonPath = join(this.configDir, "package.json");
364
+ if (!existsSync(packageJsonPath)) {
365
+ await writeFile(
366
+ packageJsonPath,
367
+ JSON.stringify(
368
+ {
369
+ name: "opencode-config",
370
+ private: true,
371
+ type: "module"
372
+ },
373
+ null,
374
+ 2
375
+ )
376
+ );
377
+ }
378
+ const packageList = packages.join(" ");
379
+ await execAsync(`npm install ${packageList}`, {
380
+ cwd: this.configDir,
381
+ timeout: 12e4
382
+ });
383
+ }
384
+ /**
385
+ * Uninstall npm dependencies from the config directory
386
+ */
387
+ async uninstallDependencies(packages) {
388
+ if (packages.length === 0) return;
389
+ const packageList = packages.join(" ");
390
+ try {
391
+ await execAsync(`npm uninstall ${packageList}`, {
392
+ cwd: this.configDir,
393
+ timeout: 6e4
394
+ });
395
+ } catch {
396
+ }
397
+ }
398
+ /**
399
+ * Copy bridge commands from package to config directory
400
+ */
401
+ async copyCommands() {
402
+ const commandsDir = CONFIG_PATHS.commandsDir;
403
+ await this.ensureDir(commandsDir);
404
+ const packageRoot = getPackageRoot();
405
+ const sourceCommandsDir = join(packageRoot, "commands");
406
+ const copiedFiles = [];
407
+ if (existsSync(sourceCommandsDir)) {
408
+ const files = await readdir(sourceCommandsDir);
409
+ for (const file of files) {
410
+ if (file.endsWith(".md")) {
411
+ const sourcePath = join(sourceCommandsDir, file);
412
+ const destPath = join(commandsDir, file);
413
+ await copyFile(sourcePath, destPath);
414
+ copiedFiles.push(file);
415
+ }
416
+ }
417
+ }
418
+ return copiedFiles;
419
+ }
420
+ /**
421
+ * Remove bridge commands from config directory
422
+ */
423
+ async removeCommands() {
424
+ const commandsDir = CONFIG_PATHS.commandsDir;
425
+ const removedFiles = [];
426
+ if (!existsSync(commandsDir)) {
427
+ return removedFiles;
428
+ }
429
+ const files = await readdir(commandsDir);
430
+ for (const file of files) {
431
+ if (file.startsWith("athena-") && file.endsWith(".md")) {
432
+ const filePath = join(commandsDir, file);
433
+ await rm(filePath);
434
+ removedFiles.push(file);
435
+ }
436
+ }
437
+ return removedFiles;
438
+ }
439
+ /**
440
+ * Remove Athena configuration files
441
+ */
442
+ async removeConfigFiles() {
443
+ const removedFiles = [];
444
+ const filesToRemove = [CONFIG_PATHS.globalAthenaConfig, CONFIG_PATHS.stateFile];
445
+ for (const file of filesToRemove) {
446
+ if (existsSync(file)) {
447
+ await rm(file);
448
+ removedFiles.push(file);
449
+ }
450
+ }
451
+ return removedFiles;
452
+ }
453
+ /**
454
+ * Remove Athena from opencode.json plugin list
455
+ */
456
+ async removeFromOpencodeConfig() {
457
+ const opencodeConfig = this.readJsonFile(CONFIG_PATHS.globalOpencodeConfig);
458
+ if (!opencodeConfig || !opencodeConfig.plugin) {
459
+ return false;
460
+ }
461
+ const athenaPlugins = [
462
+ "opencode-athena",
463
+ "oh-my-opencode",
464
+ "opencode-antigravity-auth",
465
+ "opencode-openai-codex-auth"
466
+ ];
467
+ const originalLength = opencodeConfig.plugin.length;
468
+ opencodeConfig.plugin = opencodeConfig.plugin.filter(
469
+ (p) => !athenaPlugins.some((ap) => p.includes(ap))
470
+ );
471
+ if (opencodeConfig.plugin.length !== originalLength) {
472
+ await this.writeJsonFile(CONFIG_PATHS.globalOpencodeConfig, opencodeConfig);
473
+ return true;
474
+ }
475
+ return false;
476
+ }
477
+ /**
478
+ * Backup a file before modifying
479
+ */
480
+ async backupFile(path2) {
481
+ if (!existsSync(path2)) {
482
+ return null;
483
+ }
484
+ const backupPath = `${path2}.backup`;
485
+ await copyFile(path2, backupPath);
486
+ return backupPath;
487
+ }
488
+ /**
489
+ * Restore a file from backup
490
+ */
491
+ async restoreFromBackup(backupPath) {
492
+ const originalPath = backupPath.replace(/\.backup$/, "");
493
+ if (existsSync(backupPath)) {
494
+ await copyFile(backupPath, originalPath);
495
+ await rm(backupPath);
496
+ }
497
+ }
498
+ };
499
+
500
+ // src/cli/utils/logger.ts
501
+ init_esm_shims();
502
+ var logger = {
503
+ /**
504
+ * Log an informational message
505
+ */
506
+ info: (message) => {
507
+ console.log(chalk3.blue("i"), message);
508
+ },
509
+ /**
510
+ * Log a success message
511
+ */
512
+ success: (message) => {
513
+ console.log(chalk3.green("\u2713"), message);
514
+ },
515
+ /**
516
+ * Log a warning message
517
+ */
518
+ warn: (message) => {
519
+ console.log(chalk3.yellow("!"), message);
520
+ },
521
+ /**
522
+ * Log an error message
523
+ */
524
+ error: (message) => {
525
+ console.log(chalk3.red("\u2716"), message);
526
+ },
527
+ /**
528
+ * Log a debug message (only when DEBUG env var is set)
529
+ */
530
+ debug: (message) => {
531
+ if (process.env.DEBUG) {
532
+ console.log(chalk3.gray("[debug]"), message);
533
+ }
534
+ },
535
+ /**
536
+ * Log a step in a process
537
+ */
538
+ step: (step, total, message) => {
539
+ console.log(chalk3.cyan(`[${step}/${total}]`), message);
540
+ },
541
+ /**
542
+ * Log a blank line
543
+ */
544
+ blank: () => {
545
+ console.log();
546
+ },
547
+ /**
548
+ * Log a section header
549
+ */
550
+ section: (title) => {
551
+ console.log();
552
+ console.log(chalk3.bold(title));
553
+ console.log();
554
+ },
555
+ /**
556
+ * Log a key-value pair
557
+ */
558
+ keyValue: (key, value, indent = 0) => {
559
+ const padding = " ".repeat(indent);
560
+ console.log(`${padding}${chalk3.gray(`${key}:`)} ${value}`);
561
+ },
562
+ /**
563
+ * Log a list item
564
+ */
565
+ listItem: (item, indent = 0) => {
566
+ const padding = " ".repeat(indent);
567
+ console.log(`${padding}${chalk3.gray("-")} ${item}`);
568
+ },
569
+ /**
570
+ * Display the Athena banner
571
+ */
572
+ banner: () => {
573
+ console.log(
574
+ chalk3.cyan(`
575
+ \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
576
+ \u2551 OPENCODE ATHENA \u2551
577
+ \u2551 Strategic Wisdom Meets Practical Execution \u2551
578
+ \u2560\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\u2563
579
+ \u2551 Unifying oh-my-opencode + BMAD METHOD for OpenCode \u2551
580
+ \u255A\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\u255D
581
+ `)
582
+ );
583
+ },
584
+ /**
585
+ * Display a success banner
586
+ */
587
+ successBanner: (message) => {
588
+ const line = "\u2550".repeat(message.length + 4);
589
+ console.log(
590
+ chalk3.green(`
591
+ \u2554${line}\u2557
592
+ \u2551 ${message} \u2551
593
+ \u255A${line}\u255D
594
+ `)
595
+ );
596
+ }
597
+ };
598
+
599
+ // src/cli/utils/prerequisites.ts
600
+ init_esm_shims();
601
+ var execAsync2 = promisify(exec);
602
+ function parseVersion(version) {
603
+ const match = version.match(/^v?(\d+)\.(\d+)\.(\d+)/);
604
+ if (!match) return null;
605
+ return {
606
+ major: Number.parseInt(match[1], 10),
607
+ minor: Number.parseInt(match[2], 10),
608
+ patch: Number.parseInt(match[3], 10)
609
+ };
610
+ }
611
+ function compareVersions(a, b) {
612
+ const parsedA = parseVersion(a);
613
+ const parsedB = parseVersion(b);
614
+ if (!parsedA || !parsedB) return 0;
615
+ if (parsedA.major !== parsedB.major) {
616
+ return parsedA.major < parsedB.major ? -1 : 1;
617
+ }
618
+ if (parsedA.minor !== parsedB.minor) {
619
+ return parsedA.minor < parsedB.minor ? -1 : 1;
620
+ }
621
+ if (parsedA.patch !== parsedB.patch) {
622
+ return parsedA.patch < parsedB.patch ? -1 : 1;
623
+ }
624
+ return 0;
625
+ }
626
+ async function checkNode() {
627
+ try {
628
+ const { stdout } = await execAsync2("node --version");
629
+ const version = stdout.trim().replace(/^v/, "");
630
+ const compatible = compareVersions(version, MIN_VERSIONS.node) >= 0;
631
+ return { installed: true, version, compatible };
632
+ } catch {
633
+ return { installed: false, compatible: false };
634
+ }
635
+ }
636
+ async function checkOpenCode() {
637
+ try {
638
+ const { stdout } = await execAsync2("opencode --version");
639
+ const version = stdout.trim();
640
+ const compatible = compareVersions(version, MIN_VERSIONS.opencode) >= 0;
641
+ return { installed: true, version, compatible };
642
+ } catch {
643
+ return { installed: false, compatible: false };
644
+ }
645
+ }
646
+ async function checkAthena() {
647
+ if (!existsSync(CONFIG_PATHS.globalAthenaConfig)) {
648
+ return { installed: false };
649
+ }
650
+ try {
651
+ const content = readFileSync(CONFIG_PATHS.globalAthenaConfig, "utf-8");
652
+ const config = JSON.parse(content);
653
+ return {
654
+ installed: true,
655
+ version: config.version
656
+ };
657
+ } catch {
658
+ return { installed: true };
659
+ }
660
+ }
661
+ async function checkPrerequisites() {
662
+ const [node, opencode, athena] = await Promise.all([checkNode(), checkOpenCode(), checkAthena()]);
663
+ return { node, opencode, athena };
664
+ }
665
+ async function checkOhMyOpenCode() {
666
+ try {
667
+ const { stdout } = await execAsync2("npm list oh-my-opencode --json", {
668
+ cwd: CONFIG_PATHS.globalConfigDir
669
+ });
670
+ const data = JSON.parse(stdout);
671
+ const version = data.dependencies?.["oh-my-opencode"]?.version;
672
+ return { installed: !!version, version };
673
+ } catch {
674
+ return { installed: false };
675
+ }
676
+ }
677
+ async function getInstalledPlugins() {
678
+ try {
679
+ const { stdout } = await execAsync2("npm list --depth=0 --json", {
680
+ cwd: CONFIG_PATHS.globalConfigDir
681
+ });
682
+ const data = JSON.parse(stdout);
683
+ const deps = data.dependencies || {};
684
+ const result = {};
685
+ for (const [name, info2] of Object.entries(deps)) {
686
+ result[name] = info2.version || "unknown";
687
+ }
688
+ return result;
689
+ } catch {
690
+ return {};
691
+ }
692
+ }
693
+ function validateJsonFile(path2) {
694
+ if (!existsSync(path2)) {
695
+ return { valid: false, error: "File does not exist" };
696
+ }
697
+ try {
698
+ const content = readFileSync(path2, "utf-8");
699
+ JSON.parse(content);
700
+ return { valid: true };
701
+ } catch (err) {
702
+ return {
703
+ valid: false,
704
+ error: err instanceof Error ? err.message : "Invalid JSON"
705
+ };
706
+ }
707
+ }
708
+
709
+ // src/cli/utils/validators.ts
710
+ init_esm_shims();
711
+
712
+ // src/shared/schemas.ts
713
+ init_esm_shims();
714
+ var SubscriptionSchema = z.object({
715
+ claude: z.object({
716
+ enabled: z.boolean(),
717
+ tier: z.enum(["max5x", "max20x", "pro", "none"])
718
+ }),
719
+ openai: z.object({
720
+ enabled: z.boolean()
721
+ }),
722
+ google: z.object({
723
+ enabled: z.boolean(),
724
+ authMethod: z.enum(["antigravity", "personal", "api", "none"])
725
+ })
726
+ });
727
+ var BmadConfigSchema = z.object({
728
+ defaultTrack: z.enum(["quick-flow", "bmad-method", "enterprise"]),
729
+ autoStatusUpdate: z.boolean(),
730
+ parallelStoryLimit: z.number().int().min(0).max(10)
731
+ });
732
+ var FeaturesSchema = z.object({
733
+ bmadBridge: z.boolean(),
734
+ autoStatus: z.boolean(),
735
+ parallelExecution: z.boolean(),
736
+ notifications: z.boolean(),
737
+ contextMonitor: z.boolean(),
738
+ commentChecker: z.boolean(),
739
+ lspTools: z.boolean()
740
+ });
741
+ var McpsSchema = z.object({
742
+ context7: z.boolean(),
743
+ exa: z.boolean(),
744
+ grepApp: z.boolean()
745
+ });
746
+ var ModelsSchema = z.object({
747
+ sisyphus: z.string().describe("Model for main orchestrator agent"),
748
+ oracle: z.string().describe("Model for debugging/reasoning agent"),
749
+ librarian: z.string().describe("Model for research/documentation agent"),
750
+ frontend: z.string().optional().describe("Model for UI/UX agent"),
751
+ documentWriter: z.string().optional().describe("Model for documentation generation agent"),
752
+ multimodalLooker: z.string().optional().describe("Model for image analysis agent")
753
+ });
754
+ var AthenaConfigSchema = z.object({
755
+ $schema: z.string().optional(),
756
+ version: z.string(),
757
+ subscriptions: SubscriptionSchema,
758
+ models: ModelsSchema,
759
+ bmad: BmadConfigSchema,
760
+ features: FeaturesSchema,
761
+ mcps: McpsSchema
762
+ });
763
+ z.object({
764
+ storyId: z.string().optional().describe(
765
+ "Specific story ID (e.g., '2.3'). If omitted, loads the next pending story from sprint-status.yaml."
766
+ )
767
+ });
768
+ z.object({
769
+ storyId: z.string().describe("The story ID (e.g., '2.3')"),
770
+ status: z.enum(["in_progress", "completed", "blocked", "needs_review"]).describe("The new status for the story"),
771
+ notes: z.string().optional().describe("Notes about the status change (required for 'blocked' status)"),
772
+ completionSummary: z.string().optional().describe("Summary of what was implemented (required for 'completed' status)")
773
+ });
774
+ z.object({
775
+ includeArchitecture: z.boolean().optional().default(true),
776
+ includePrd: z.boolean().optional().default(false),
777
+ includeSprintStatus: z.boolean().optional().default(true)
778
+ });
779
+ z.object({
780
+ storyIds: z.array(z.string()).describe("Array of story IDs to implement in parallel"),
781
+ maxConcurrent: z.number().int().min(1).max(5).optional().default(3)
782
+ });
783
+ z.object({
784
+ action: z.enum(["get", "set", "reset"]).describe("Configuration action to perform"),
785
+ key: z.string().optional().describe("Configuration key (dot notation, e.g., 'bmad.autoStatusUpdate')"),
786
+ value: z.unknown().optional().describe("Value to set (for 'set' action)")
787
+ });
788
+ var StoryStatusEnum = z.enum([
789
+ "pending",
790
+ "in_progress",
791
+ "completed",
792
+ "blocked",
793
+ "needs_review"
794
+ ]);
795
+ z.enum([
796
+ "pending",
797
+ "in_progress",
798
+ "completed",
799
+ "blocked",
800
+ "needs_review",
801
+ "loading"
802
+ ]);
803
+ z.object({
804
+ sprint_number: z.number().int().optional(),
805
+ current_epic: z.string().optional(),
806
+ current_story: z.string().nullable().optional(),
807
+ completed_stories: z.array(z.string()).default([]),
808
+ pending_stories: z.array(z.string()).default([]),
809
+ in_progress_stories: z.array(z.string()).default([]),
810
+ blocked_stories: z.array(z.string()).default([]),
811
+ stories_needing_review: z.array(z.string()).optional(),
812
+ story_updates: z.record(
813
+ z.object({
814
+ status: StoryStatusEnum,
815
+ updated_at: z.string(),
816
+ notes: z.string().optional(),
817
+ completion_summary: z.string().optional()
818
+ })
819
+ ).optional(),
820
+ last_modified: z.string().optional()
821
+ });
822
+
823
+ // src/cli/utils/validators.ts
824
+ function validateAthenaConfig(config) {
825
+ const result = { valid: true, errors: [], warnings: [] };
826
+ const parseResult = AthenaConfigSchema.safeParse(config);
827
+ if (!parseResult.success) {
828
+ result.valid = false;
829
+ for (const issue of parseResult.error.issues) {
830
+ result.errors.push(`${issue.path.join(".")}: ${issue.message}`);
831
+ }
832
+ }
833
+ return result;
834
+ }
835
+ function validateJsonConfig(path2) {
836
+ const result = { valid: true, errors: [], warnings: [] };
837
+ if (!existsSync(path2)) {
838
+ result.valid = false;
839
+ result.errors.push("File does not exist");
840
+ return result;
841
+ }
842
+ try {
843
+ const content = readFileSync(path2, "utf-8");
844
+ JSON.parse(content);
845
+ } catch (err) {
846
+ result.valid = false;
847
+ result.errors.push(err instanceof Error ? err.message : "Invalid JSON");
848
+ }
849
+ return result;
850
+ }
851
+
852
+ // src/cli/commands/doctor.ts
853
+ async function doctor(options) {
854
+ logger.banner();
855
+ logger.section("Running Diagnostics");
856
+ const results = [];
857
+ const fileManager = new FileManager();
858
+ const prereqs = await checkPrerequisites();
859
+ results.push({
860
+ name: "Node.js",
861
+ status: prereqs.node.installed ? prereqs.node.compatible ? "pass" : "warn" : "fail",
862
+ message: prereqs.node.installed ? prereqs.node.compatible ? `Version ${prereqs.node.version} is compatible` : `Version ${prereqs.node.version} detected, 20+ recommended` : "Not installed"
863
+ });
864
+ results.push({
865
+ name: "OpenCode",
866
+ status: prereqs.opencode.installed ? prereqs.opencode.compatible ? "pass" : "warn" : "fail",
867
+ message: prereqs.opencode.installed ? prereqs.opencode.compatible ? `Version ${prereqs.opencode.version} is compatible` : `Version ${prereqs.opencode.version} detected, 1.0.132+ recommended` : "Not installed"
868
+ });
869
+ const athenaConfigValid = validateJsonFile(CONFIG_PATHS.globalAthenaConfig);
870
+ if (athenaConfigValid.valid) {
871
+ const config = fileManager.readJsonFile(CONFIG_PATHS.globalAthenaConfig);
872
+ const schemaValidation = validateAthenaConfig(config);
873
+ results.push({
874
+ name: "Athena Config",
875
+ status: schemaValidation.valid ? "pass" : "warn",
876
+ message: schemaValidation.valid ? "Valid configuration" : `Schema issues: ${schemaValidation.errors.join(", ")}`
877
+ });
878
+ } else {
879
+ results.push({
880
+ name: "Athena Config",
881
+ status: "fail",
882
+ message: athenaConfigValid.error || "Not found",
883
+ fix: async () => {
884
+ logger.info("Run 'opencode-athena install' to create configuration");
885
+ }
886
+ });
887
+ }
888
+ const opencodeConfigValid = validateJsonConfig(CONFIG_PATHS.globalOpencodeConfig);
889
+ results.push({
890
+ name: "OpenCode Config",
891
+ status: opencodeConfigValid.valid ? "pass" : "fail",
892
+ message: opencodeConfigValid.valid ? "Valid JSON" : opencodeConfigValid.errors[0] || "Invalid"
893
+ });
894
+ const omoConfigValid = validateJsonConfig(CONFIG_PATHS.globalOmoConfig);
895
+ results.push({
896
+ name: "oh-my-opencode Config",
897
+ status: omoConfigValid.valid ? "pass" : "warn",
898
+ message: omoConfigValid.valid ? "Valid JSON" : omoConfigValid.errors[0] || "Not found"
899
+ });
900
+ const omoInstalled = await checkOhMyOpenCode();
901
+ results.push({
902
+ name: "oh-my-opencode Plugin",
903
+ status: omoInstalled.installed ? "pass" : "fail",
904
+ message: omoInstalled.installed ? `Version ${omoInstalled.version}` : "Not installed",
905
+ fix: async () => {
906
+ const spinner = ora3("Installing oh-my-opencode...").start();
907
+ try {
908
+ await fileManager.installDependencies(["oh-my-opencode"]);
909
+ spinner.succeed("oh-my-opencode installed");
910
+ } catch (err) {
911
+ spinner.fail("Failed to install oh-my-opencode");
912
+ throw err;
913
+ }
914
+ }
915
+ });
916
+ const commandsDirExists = fileManager.exists(CONFIG_PATHS.commandsDir);
917
+ results.push({
918
+ name: "Commands Directory",
919
+ status: commandsDirExists ? "pass" : "warn",
920
+ message: commandsDirExists ? "Exists" : "Not found",
921
+ fix: async () => {
922
+ const spinner = ora3("Creating commands directory...").start();
923
+ try {
924
+ await fileManager.ensureDir(CONFIG_PATHS.commandsDir);
925
+ await fileManager.copyCommands();
926
+ spinner.succeed("Commands directory created and populated");
927
+ } catch (err) {
928
+ spinner.fail("Failed to create commands directory");
929
+ throw err;
930
+ }
931
+ }
932
+ });
933
+ logger.section("Diagnostic Results");
934
+ let hasFailures = false;
935
+ let hasWarnings = false;
936
+ const fixableIssues = [];
937
+ for (const result of results) {
938
+ let icon;
939
+ let color;
940
+ switch (result.status) {
941
+ case "pass":
942
+ icon = "\u2713";
943
+ color = chalk3.green;
944
+ break;
945
+ case "warn":
946
+ icon = "!";
947
+ color = chalk3.yellow;
948
+ hasWarnings = true;
949
+ break;
950
+ case "fail":
951
+ icon = "\u2716";
952
+ color = chalk3.red;
953
+ hasFailures = true;
954
+ if (result.fix) {
955
+ fixableIssues.push(result);
956
+ }
957
+ break;
958
+ }
959
+ console.log(` ${color(icon)} ${result.name}: ${result.message}`);
960
+ }
961
+ console.log();
962
+ if (hasFailures) {
963
+ logger.error("Some checks failed.");
964
+ if (fixableIssues.length > 0 && options.fix) {
965
+ logger.section("Applying Fixes");
966
+ for (const issue of fixableIssues) {
967
+ if (issue.fix) {
968
+ try {
969
+ await issue.fix();
970
+ } catch (err) {
971
+ logger.error(
972
+ `Failed to fix ${issue.name}: ${err instanceof Error ? err.message : String(err)}`
973
+ );
974
+ }
975
+ }
976
+ }
977
+ logger.blank();
978
+ logger.info("Run 'opencode-athena doctor' again to verify fixes.");
979
+ } else if (fixableIssues.length > 0) {
980
+ logger.info(`Run ${chalk3.cyan("opencode-athena doctor --fix")} to attempt automatic fixes.`);
981
+ }
982
+ } else if (hasWarnings) {
983
+ logger.warn("Some checks have warnings, but Athena should work.");
984
+ } else {
985
+ logger.success("All checks passed! OpenCode Athena is healthy.");
986
+ }
987
+ }
988
+
989
+ // src/cli/commands/info.ts
990
+ init_esm_shims();
991
+ async function info() {
992
+ logger.banner();
993
+ const fileManager = new FileManager();
994
+ const athenaConfig = fileManager.readJsonFile(CONFIG_PATHS.globalAthenaConfig);
995
+ if (!athenaConfig) {
996
+ logger.warn("OpenCode Athena is not installed.");
997
+ logger.info(`Run ${chalk3.cyan("opencode-athena install")} to get started.`);
998
+ return;
999
+ }
1000
+ logger.section("Version Information");
1001
+ logger.keyValue("Athena Version", athenaConfig.version || VERSION);
1002
+ logger.section("Prerequisites");
1003
+ const prereqs = await checkPrerequisites();
1004
+ const nodeStatus = prereqs.node.installed ? prereqs.node.compatible ? chalk3.green("\u2713") : chalk3.yellow("!") : chalk3.red("\u2716");
1005
+ logger.keyValue("Node.js", `${nodeStatus} ${prereqs.node.version || "not found"}`);
1006
+ const opencodeStatus = prereqs.opencode.installed ? prereqs.opencode.compatible ? chalk3.green("\u2713") : chalk3.yellow("!") : chalk3.red("\u2716");
1007
+ logger.keyValue("OpenCode", `${opencodeStatus} ${prereqs.opencode.version || "not found"}`);
1008
+ logger.section("Configured Providers");
1009
+ const claudeStatus = athenaConfig.subscriptions.claude.enabled ? chalk3.green("enabled") : chalk3.gray("disabled");
1010
+ logger.keyValue(
1011
+ "Claude",
1012
+ `${claudeStatus}${athenaConfig.subscriptions.claude.tier !== "none" ? ` (${athenaConfig.subscriptions.claude.tier})` : ""}`
1013
+ );
1014
+ const openaiStatus = athenaConfig.subscriptions.openai.enabled ? chalk3.green("enabled") : chalk3.gray("disabled");
1015
+ logger.keyValue("OpenAI", openaiStatus);
1016
+ const googleStatus = athenaConfig.subscriptions.google.enabled ? chalk3.green("enabled") : chalk3.gray("disabled");
1017
+ logger.keyValue(
1018
+ "Google",
1019
+ `${googleStatus}${athenaConfig.subscriptions.google.authMethod !== "none" ? ` (${athenaConfig.subscriptions.google.authMethod})` : ""}`
1020
+ );
1021
+ logger.section("Agent Models");
1022
+ logger.keyValue("Sisyphus", athenaConfig.models.sisyphus);
1023
+ logger.keyValue("Oracle", athenaConfig.models.oracle);
1024
+ logger.keyValue("Librarian", athenaConfig.models.librarian);
1025
+ if (athenaConfig.models.frontend) {
1026
+ logger.keyValue("Frontend", athenaConfig.models.frontend);
1027
+ }
1028
+ if (athenaConfig.models.documentWriter) {
1029
+ logger.keyValue("Doc Writer", athenaConfig.models.documentWriter);
1030
+ }
1031
+ if (athenaConfig.models.multimodalLooker) {
1032
+ logger.keyValue("Multimodal", athenaConfig.models.multimodalLooker);
1033
+ }
1034
+ logger.section("BMAD Settings");
1035
+ logger.keyValue("Default Track", athenaConfig.bmad.defaultTrack);
1036
+ logger.keyValue("Auto Status Update", athenaConfig.bmad.autoStatusUpdate ? "yes" : "no");
1037
+ logger.keyValue("Parallel Story Limit", athenaConfig.bmad.parallelStoryLimit.toString());
1038
+ logger.section("Features");
1039
+ const features = athenaConfig.features;
1040
+ const featureList = [
1041
+ { name: "BMAD Bridge", enabled: features.bmadBridge },
1042
+ { name: "Auto Status", enabled: features.autoStatus },
1043
+ { name: "Parallel Exec", enabled: features.parallelExecution },
1044
+ { name: "Notifications", enabled: features.notifications },
1045
+ { name: "Context Monitor", enabled: features.contextMonitor },
1046
+ { name: "Comment Checker", enabled: features.commentChecker },
1047
+ { name: "LSP Tools", enabled: features.lspTools }
1048
+ ];
1049
+ for (const feature of featureList) {
1050
+ const status = feature.enabled ? chalk3.green("on") : chalk3.gray("off");
1051
+ logger.keyValue(feature.name, status);
1052
+ }
1053
+ logger.section("MCP Servers");
1054
+ const mcps = athenaConfig.mcps;
1055
+ logger.keyValue("context7", mcps.context7 ? chalk3.green("on") : chalk3.gray("off"));
1056
+ logger.keyValue("exa", mcps.exa ? chalk3.green("on") : chalk3.gray("off"));
1057
+ logger.keyValue("grep_app", mcps.grepApp ? chalk3.green("on") : chalk3.gray("off"));
1058
+ logger.section("Installed Plugins");
1059
+ const plugins = await getInstalledPlugins();
1060
+ if (Object.keys(plugins).length === 0) {
1061
+ logger.info("No plugins installed in OpenCode config directory");
1062
+ } else {
1063
+ for (const [name, version] of Object.entries(plugins)) {
1064
+ logger.keyValue(name, version);
1065
+ }
1066
+ }
1067
+ logger.section("Configuration Paths");
1068
+ logger.keyValue("Config Dir", CONFIG_PATHS.globalConfigDir);
1069
+ logger.keyValue("Athena Config", CONFIG_PATHS.globalAthenaConfig);
1070
+ logger.keyValue("Commands Dir", CONFIG_PATHS.commandsDir);
1071
+ console.log();
1072
+ }
1073
+
1074
+ // src/cli/commands/install.ts
1075
+ init_esm_shims();
1076
+
1077
+ // src/cli/generators/config-generator.ts
1078
+ init_esm_shims();
1079
+
1080
+ // src/cli/generators/athena-config.ts
1081
+ init_esm_shims();
1082
+
1083
+ // src/cli/questions/features.ts
1084
+ init_esm_shims();
1085
+ var AVAILABLE_FEATURES = [
1086
+ {
1087
+ name: "BMAD Bridge Commands (/athena-dev, /athena-review, etc.)",
1088
+ value: "bmad-bridge"
1089
+ },
1090
+ {
1091
+ name: "Auto Sprint Status Updates - Update sprint-status.yaml automatically",
1092
+ value: "auto-status"
1093
+ },
1094
+ {
1095
+ name: "Parallel Story Execution - Work on multiple stories simultaneously",
1096
+ value: "parallel"
1097
+ },
1098
+ {
1099
+ name: "Session Notifications - Desktop notifications for completions",
1100
+ value: "notifications"
1101
+ },
1102
+ {
1103
+ name: "Context Window Monitoring - Track context usage",
1104
+ value: "context-monitor"
1105
+ },
1106
+ {
1107
+ name: "Comment Checker - Ensure code is not over-commented",
1108
+ value: "comment-checker"
1109
+ },
1110
+ {
1111
+ name: "LSP Refactoring Tools - Enable lsp_rename, lsp_find_references, etc.",
1112
+ value: "lsp-tools"
1113
+ }
1114
+ ];
1115
+ var ALL_FEATURE_VALUES = AVAILABLE_FEATURES.map((f) => f.value);
1116
+ var AVAILABLE_MCPS = [
1117
+ {
1118
+ name: "context7 - Documentation lookup and context retrieval",
1119
+ value: "context7"
1120
+ },
1121
+ {
1122
+ name: "websearch_exa - Web search capabilities",
1123
+ value: "exa"
1124
+ },
1125
+ {
1126
+ name: "grep_app - GitHub code search",
1127
+ value: "grep_app"
1128
+ }
1129
+ ];
1130
+ var ALL_MCP_VALUES = AVAILABLE_MCPS.map((m) => m.value);
1131
+ function createFeatureChoices(defaults) {
1132
+ const enabledSet = new Set(defaults ?? ALL_FEATURE_VALUES);
1133
+ return AVAILABLE_FEATURES.map((feature) => ({
1134
+ ...feature,
1135
+ checked: enabledSet.has(feature.value)
1136
+ }));
1137
+ }
1138
+ function createMcpChoices(defaults) {
1139
+ const enabledSet = new Set(defaults ?? ALL_MCP_VALUES);
1140
+ return AVAILABLE_MCPS.map((mcp) => ({
1141
+ ...mcp,
1142
+ checked: enabledSet.has(mcp.value)
1143
+ }));
1144
+ }
1145
+ async function gatherFeatures(defaults) {
1146
+ const enabledFeatures = await checkbox({
1147
+ message: "Select features to enable:",
1148
+ choices: createFeatureChoices(defaults?.enabledFeatures)
1149
+ });
1150
+ const mcps = await checkbox({
1151
+ message: "Select MCP servers to enable:",
1152
+ choices: createMcpChoices(defaults?.mcps)
1153
+ });
1154
+ return {
1155
+ enabledFeatures,
1156
+ mcps
1157
+ };
1158
+ }
1159
+ function featuresToFlags(enabledFeatures) {
1160
+ return {
1161
+ bmadBridge: enabledFeatures.includes("bmad-bridge"),
1162
+ autoStatus: enabledFeatures.includes("auto-status"),
1163
+ parallelExecution: enabledFeatures.includes("parallel"),
1164
+ notifications: enabledFeatures.includes("notifications"),
1165
+ contextMonitor: enabledFeatures.includes("context-monitor"),
1166
+ commentChecker: enabledFeatures.includes("comment-checker"),
1167
+ lspTools: enabledFeatures.includes("lsp-tools")
1168
+ };
1169
+ }
1170
+ function mcpsToFlags(mcps) {
1171
+ return {
1172
+ context7: mcps.includes("context7"),
1173
+ exa: mcps.includes("exa"),
1174
+ grepApp: mcps.includes("grep_app")
1175
+ };
1176
+ }
1177
+
1178
+ // src/cli/generators/athena-config.ts
1179
+ function generateAthenaConfig(answers) {
1180
+ const { subscriptions, models, methodology, features, advanced } = answers;
1181
+ return {
1182
+ $schema: "https://raw.githubusercontent.com/ZebulonRouseFrantzich/opencode-athena/main/config/schemas/athena.schema.json",
1183
+ version: VERSION,
1184
+ subscriptions: {
1185
+ claude: {
1186
+ enabled: subscriptions.hasClaude,
1187
+ tier: subscriptions.claudeTier
1188
+ },
1189
+ openai: {
1190
+ enabled: subscriptions.hasOpenAI
1191
+ },
1192
+ google: {
1193
+ enabled: subscriptions.hasGoogle,
1194
+ authMethod: subscriptions.googleAuth
1195
+ }
1196
+ },
1197
+ models: {
1198
+ sisyphus: models.sisyphus,
1199
+ oracle: models.oracle,
1200
+ librarian: models.librarian,
1201
+ frontend: models.frontend,
1202
+ documentWriter: models.documentWriter,
1203
+ multimodalLooker: models.multimodalLooker
1204
+ },
1205
+ bmad: {
1206
+ defaultTrack: methodology.defaultTrack,
1207
+ autoStatusUpdate: methodology.autoStatusUpdate,
1208
+ parallelStoryLimit: advanced.parallelStoryLimit ?? 3
1209
+ },
1210
+ features: featuresToFlags(features.enabledFeatures),
1211
+ mcps: mcpsToFlags(features.mcps)
1212
+ };
1213
+ }
1214
+
1215
+ // src/cli/generators/omo-config.ts
1216
+ init_esm_shims();
1217
+ function generateOmoConfig(answers) {
1218
+ const { subscriptions, models, features, advanced } = answers;
1219
+ const config = {
1220
+ $schema: "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json"
1221
+ };
1222
+ if (subscriptions.hasGoogle && subscriptions.googleAuth === "antigravity") {
1223
+ config.google_auth = false;
1224
+ }
1225
+ config.agents = {
1226
+ // Main orchestrator
1227
+ Sisyphus: {
1228
+ model: models.sisyphus
1229
+ },
1230
+ // Debugging/reasoning agent
1231
+ oracle: {
1232
+ model: models.oracle
1233
+ },
1234
+ // Research agent
1235
+ librarian: {
1236
+ model: models.librarian
1237
+ },
1238
+ // UI/UX agent
1239
+ "frontend-ui-ux-engineer": {
1240
+ model: models.frontend || models.sisyphus
1241
+ },
1242
+ // Documentation agent
1243
+ "document-writer": {
1244
+ model: models.documentWriter || models.librarian
1245
+ },
1246
+ // Image analysis agent
1247
+ "multimodal-looker": {
1248
+ model: models.multimodalLooker || models.librarian
1249
+ }
1250
+ };
1251
+ const disabledHooks = [];
1252
+ if (!features.enabledFeatures.includes("context-monitor")) {
1253
+ disabledHooks.push("context-window-monitor");
1254
+ }
1255
+ if (!features.enabledFeatures.includes("comment-checker")) {
1256
+ disabledHooks.push("comment-checker");
1257
+ }
1258
+ if (!features.enabledFeatures.includes("notifications")) {
1259
+ disabledHooks.push("session-notification", "background-notification");
1260
+ }
1261
+ if (disabledHooks.length > 0) {
1262
+ config.disabled_hooks = disabledHooks;
1263
+ }
1264
+ const allMcps = ["context7", "websearch_exa", "grep_app"];
1265
+ const enabledMcpIds = features.mcps.map((mcp) => {
1266
+ if (mcp === "exa") return "websearch_exa";
1267
+ return mcp;
1268
+ });
1269
+ const disabledMcps = allMcps.filter((mcp) => !enabledMcpIds.includes(mcp));
1270
+ if (disabledMcps.length > 0) {
1271
+ config.disabled_mcps = disabledMcps;
1272
+ }
1273
+ if (advanced.experimental && advanced.experimental.length > 0) {
1274
+ config.experimental = {};
1275
+ if (advanced.experimental.includes("aggressive-truncation")) {
1276
+ config.experimental.aggressive_truncation = true;
1277
+ }
1278
+ if (advanced.experimental.includes("auto-resume")) {
1279
+ config.experimental.auto_resume = true;
1280
+ }
1281
+ }
1282
+ return config;
1283
+ }
1284
+
1285
+ // src/cli/generators/opencode-config.ts
1286
+ init_esm_shims();
1287
+ async function generateOpencodeConfig(answers, configDir) {
1288
+ const configPath = join(configDir, "opencode.json");
1289
+ let existingConfig = {};
1290
+ if (existsSync(configPath)) {
1291
+ try {
1292
+ const content = readFileSync(configPath, "utf-8");
1293
+ existingConfig = JSON.parse(content);
1294
+ } catch {
1295
+ }
1296
+ }
1297
+ const plugins = ["opencode-athena/plugin", "oh-my-opencode"];
1298
+ if (answers.subscriptions.hasGoogle && answers.subscriptions.googleAuth === "antigravity") {
1299
+ plugins.push("opencode-antigravity-auth");
1300
+ }
1301
+ if (answers.subscriptions.hasOpenAI) {
1302
+ plugins.push("opencode-openai-codex-auth");
1303
+ }
1304
+ const existingPlugins = existingConfig.plugin || [];
1305
+ const mergedPlugins = [.../* @__PURE__ */ new Set([...existingPlugins, ...plugins])];
1306
+ const config = {
1307
+ ...existingConfig,
1308
+ $schema: existingConfig.$schema || "https://opencode.ai/config.json",
1309
+ plugin: mergedPlugins
1310
+ };
1311
+ if (answers.subscriptions.hasClaude) {
1312
+ const existingProvider = existingConfig.provider || {};
1313
+ const existingAnthropic = existingProvider.anthropic || {};
1314
+ const existingAnthropicModels = existingAnthropic.models || {};
1315
+ config.provider = {
1316
+ ...existingProvider,
1317
+ anthropic: {
1318
+ ...existingAnthropic,
1319
+ models: {
1320
+ ...existingAnthropicModels,
1321
+ // Add thinking model variants
1322
+ "claude-opus-4-5-thinking": {
1323
+ id: "claude-opus-4-5",
1324
+ options: {
1325
+ thinking: {
1326
+ type: "enabled",
1327
+ budgetTokens: 32e3
1328
+ }
1329
+ }
1330
+ },
1331
+ "claude-sonnet-4-5-thinking": {
1332
+ id: "claude-sonnet-4-5",
1333
+ options: {
1334
+ thinking: {
1335
+ type: "enabled",
1336
+ budgetTokens: 1e4
1337
+ }
1338
+ }
1339
+ }
1340
+ }
1341
+ }
1342
+ };
1343
+ }
1344
+ if (answers.subscriptions.hasOpenAI) {
1345
+ const existingProvider = config.provider || {};
1346
+ const existingOpenAI = existingProvider.openai || {};
1347
+ const existingOpenAIModels = existingOpenAI.models || {};
1348
+ config.provider = {
1349
+ ...existingProvider,
1350
+ openai: {
1351
+ ...existingOpenAI,
1352
+ models: {
1353
+ ...existingOpenAIModels,
1354
+ // Add reasoning effort configurations
1355
+ "gpt-5.1-high": {
1356
+ id: "gpt-5.1",
1357
+ options: {
1358
+ reasoningEffort: "high",
1359
+ reasoningSummary: "auto"
1360
+ }
1361
+ }
1362
+ }
1363
+ }
1364
+ };
1365
+ }
1366
+ return config;
1367
+ }
1368
+ function getRequiredPlugins(answers) {
1369
+ const plugins = ["opencode-athena", "oh-my-opencode"];
1370
+ if (answers.subscriptions.hasGoogle && answers.subscriptions.googleAuth === "antigravity") {
1371
+ plugins.push("opencode-antigravity-auth");
1372
+ }
1373
+ if (answers.subscriptions.hasOpenAI) {
1374
+ plugins.push("opencode-openai-codex-auth");
1375
+ }
1376
+ return plugins;
1377
+ }
1378
+
1379
+ // src/cli/generators/config-generator.ts
1380
+ var ConfigGenerator = class {
1381
+ answers;
1382
+ configDir;
1383
+ constructor(answers) {
1384
+ this.answers = answers;
1385
+ this.configDir = answers.installLocation === "local" ? join(process.cwd(), ".opencode") : CONFIG_PATHS.globalConfigDir;
1386
+ }
1387
+ /**
1388
+ * Get the configuration directory path
1389
+ */
1390
+ getConfigDir() {
1391
+ return this.configDir;
1392
+ }
1393
+ /**
1394
+ * Generate all configuration files
1395
+ */
1396
+ async generate() {
1397
+ const files = [];
1398
+ const opencodeConfig = await generateOpencodeConfig(this.answers, this.configDir);
1399
+ files.push({
1400
+ path: join(this.configDir, "opencode.json"),
1401
+ content: JSON.stringify(opencodeConfig, null, 2),
1402
+ exists: existsSync(join(this.configDir, "opencode.json"))
1403
+ });
1404
+ const omoConfig = generateOmoConfig(this.answers);
1405
+ files.push({
1406
+ path: join(this.configDir, "oh-my-opencode.json"),
1407
+ content: JSON.stringify(omoConfig, null, 2),
1408
+ exists: existsSync(join(this.configDir, "oh-my-opencode.json"))
1409
+ });
1410
+ const athenaConfig = generateAthenaConfig(this.answers);
1411
+ files.push({
1412
+ path: join(this.configDir, "athena.json"),
1413
+ content: JSON.stringify(athenaConfig, null, 2),
1414
+ exists: existsSync(join(this.configDir, "athena.json"))
1415
+ });
1416
+ return files;
1417
+ }
1418
+ /**
1419
+ * Get the list of npm packages to install
1420
+ */
1421
+ getRequiredPackages() {
1422
+ return getRequiredPlugins(this.answers);
1423
+ }
1424
+ };
1425
+
1426
+ // src/cli/questions/index.ts
1427
+ init_esm_shims();
1428
+
1429
+ // src/cli/questions/subscriptions.ts
1430
+ init_esm_shims();
1431
+ async function gatherSubscriptions() {
1432
+ const hasClaude = await confirm({
1433
+ message: "Do you have a Claude Pro/Max subscription?",
1434
+ default: true
1435
+ });
1436
+ let claudeTier = "none";
1437
+ if (hasClaude) {
1438
+ claudeTier = await select({
1439
+ message: "Which Claude tier?",
1440
+ choices: [
1441
+ { name: "Max 5x - 5x more usage than Pro", value: "max5x" },
1442
+ { name: "Max 20x - 20x more usage than Pro", value: "max20x" },
1443
+ { name: "Pro - Standard Pro subscription", value: "pro" }
1444
+ ]
1445
+ });
1446
+ }
1447
+ const hasOpenAI = await confirm({
1448
+ message: "Do you have a ChatGPT Plus/Pro subscription?",
1449
+ default: true
1450
+ });
1451
+ const hasGoogle = await confirm({
1452
+ message: "Will you use Google/Gemini models?",
1453
+ default: true
1454
+ });
1455
+ let googleAuth = "none";
1456
+ if (hasGoogle) {
1457
+ googleAuth = await select({
1458
+ message: "Google authentication method?",
1459
+ choices: [
1460
+ {
1461
+ name: "Google Workspace (Antigravity OAuth) - Recommended for Workspace users",
1462
+ value: "antigravity"
1463
+ },
1464
+ {
1465
+ name: "Personal Google Account",
1466
+ value: "personal"
1467
+ },
1468
+ {
1469
+ name: "API Key - Direct API access",
1470
+ value: "api"
1471
+ }
1472
+ ]
1473
+ });
1474
+ }
1475
+ return {
1476
+ hasClaude,
1477
+ claudeTier,
1478
+ hasOpenAI,
1479
+ hasGoogle,
1480
+ googleAuth
1481
+ };
1482
+ }
1483
+
1484
+ // src/cli/questions/index.ts
1485
+ init_models();
1486
+
1487
+ // src/cli/questions/methodology.ts
1488
+ init_esm_shims();
1489
+ async function gatherMethodology(defaults) {
1490
+ const defaultTrack = await select({
1491
+ message: "Default BMAD track for new projects?",
1492
+ choices: [
1493
+ {
1494
+ name: "Quick Flow - Fast implementation for small features and bug fixes",
1495
+ value: "quick-flow"
1496
+ },
1497
+ {
1498
+ name: "BMad Method - Full planning for products and platforms (recommended)",
1499
+ value: "bmad-method"
1500
+ },
1501
+ {
1502
+ name: "Enterprise - Extended planning with compliance and scale considerations",
1503
+ value: "enterprise"
1504
+ }
1505
+ ],
1506
+ default: defaults?.defaultTrack ?? "bmad-method"
1507
+ });
1508
+ const autoStatusUpdate = await confirm({
1509
+ message: "Automatically update sprint-status.yaml when stories complete?",
1510
+ default: defaults?.autoStatusUpdate ?? true
1511
+ });
1512
+ return {
1513
+ defaultTrack,
1514
+ autoStatusUpdate
1515
+ };
1516
+ }
1517
+
1518
+ // src/cli/questions/advanced.ts
1519
+ init_esm_shims();
1520
+ async function gatherAdvanced(defaults) {
1521
+ const parallelStoryLimit = await select({
1522
+ message: "Maximum parallel stories?",
1523
+ choices: [
1524
+ { name: "1 (sequential - one story at a time)", value: 1 },
1525
+ { name: "2", value: 2 },
1526
+ { name: "3 (recommended)", value: 3 },
1527
+ { name: "5", value: 5 },
1528
+ { name: "Unlimited (0)", value: 0 }
1529
+ ],
1530
+ default: defaults?.parallelStoryLimit ?? 3
1531
+ });
1532
+ const experimentalDefaults = new Set(defaults?.experimental ?? []);
1533
+ const experimental = await checkbox({
1534
+ message: "Enable experimental features?",
1535
+ choices: [
1536
+ {
1537
+ name: "Aggressive Truncation - More aggressive context management",
1538
+ value: "aggressive-truncation",
1539
+ checked: experimentalDefaults.has("aggressive-truncation")
1540
+ },
1541
+ {
1542
+ name: "Auto Resume - Automatically resume interrupted sessions",
1543
+ value: "auto-resume",
1544
+ checked: experimentalDefaults.has("auto-resume")
1545
+ }
1546
+ ]
1547
+ });
1548
+ return {
1549
+ parallelStoryLimit,
1550
+ experimental
1551
+ };
1552
+ }
1553
+
1554
+ // src/cli/utils/preset-loader.ts
1555
+ init_esm_shims();
1556
+ var PRESET_NAMES = ["minimal", "standard", "enterprise", "solo-quick"];
1557
+ function getPresetsDir() {
1558
+ const currentFileDir = dirname(fileURLToPath(import.meta.url));
1559
+ const bundledRoot = join(currentFileDir, "..", "..");
1560
+ const bundledPresetsDir = join(bundledRoot, "config", "presets");
1561
+ if (existsSync(bundledPresetsDir)) {
1562
+ return bundledPresetsDir;
1563
+ }
1564
+ const devRoot = join(currentFileDir, "..", "..", "..");
1565
+ const devPresetsDir = join(devRoot, "config", "presets");
1566
+ if (existsSync(devPresetsDir)) {
1567
+ return devPresetsDir;
1568
+ }
1569
+ const nodeModulesPath = currentFileDir.split("node_modules")[0];
1570
+ if (nodeModulesPath !== currentFileDir) {
1571
+ const packagePresetsDir = join(
1572
+ nodeModulesPath,
1573
+ "node_modules",
1574
+ "opencode-athena",
1575
+ "config",
1576
+ "presets"
1577
+ );
1578
+ if (existsSync(packagePresetsDir)) {
1579
+ return packagePresetsDir;
1580
+ }
1581
+ }
1582
+ throw new Error(
1583
+ `Could not find presets directory. Searched:
1584
+ - ${bundledPresetsDir}
1585
+ - ${devPresetsDir}`
1586
+ );
1587
+ }
1588
+ function isValidPresetName(name) {
1589
+ return PRESET_NAMES.includes(name);
1590
+ }
1591
+ function loadPreset(name) {
1592
+ if (!isValidPresetName(name)) {
1593
+ const validNames = PRESET_NAMES.join(", ");
1594
+ throw new Error(`Invalid preset name: "${name}". Valid presets are: ${validNames}`);
1595
+ }
1596
+ const presetsDir = getPresetsDir();
1597
+ const presetPath = join(presetsDir, `${name}.json`);
1598
+ if (!existsSync(presetPath)) {
1599
+ throw new Error(
1600
+ `Preset file not found: ${presetPath}
1601
+ This may indicate a corrupted installation. Try reinstalling opencode-athena.`
1602
+ );
1603
+ }
1604
+ try {
1605
+ const content = readFileSync(presetPath, "utf-8");
1606
+ const preset = JSON.parse(content);
1607
+ if (!preset.version || !preset.models || !preset.bmad || !preset.features) {
1608
+ throw new Error(`Preset "${name}" is missing required fields`);
1609
+ }
1610
+ return preset;
1611
+ } catch (error) {
1612
+ if (error instanceof SyntaxError) {
1613
+ throw new Error(`Invalid JSON in preset file: ${presetPath}
1614
+ Error: ${error.message}`);
1615
+ }
1616
+ throw error;
1617
+ }
1618
+ }
1619
+ function listPresets() {
1620
+ const presetsDir = getPresetsDir();
1621
+ const summaries = [];
1622
+ for (const name of PRESET_NAMES) {
1623
+ const presetPath = join(presetsDir, `${name}.json`);
1624
+ if (existsSync(presetPath)) {
1625
+ try {
1626
+ const content = readFileSync(presetPath, "utf-8");
1627
+ const preset = JSON.parse(content);
1628
+ summaries.push({
1629
+ name,
1630
+ description: preset.description || `${name} preset`,
1631
+ path: presetPath
1632
+ });
1633
+ } catch {
1634
+ summaries.push({
1635
+ name,
1636
+ description: `${name} preset (unable to read)`,
1637
+ path: presetPath
1638
+ });
1639
+ }
1640
+ }
1641
+ }
1642
+ return summaries;
1643
+ }
1644
+ function presetToDefaults(preset) {
1645
+ return {
1646
+ models: {
1647
+ sisyphus: preset.models.sisyphus,
1648
+ oracle: preset.models.oracle,
1649
+ librarian: preset.models.librarian,
1650
+ frontend: preset.models.frontend,
1651
+ documentWriter: preset.models.documentWriter,
1652
+ multimodalLooker: preset.models.multimodalLooker
1653
+ },
1654
+ methodology: {
1655
+ defaultTrack: preset.bmad.defaultTrack,
1656
+ autoStatusUpdate: preset.bmad.autoStatusUpdate
1657
+ },
1658
+ features: {
1659
+ enabledFeatures: flagsToFeatures(preset.features),
1660
+ mcps: flagsToMcps(preset.mcps)
1661
+ },
1662
+ advanced: {
1663
+ parallelStoryLimit: preset.bmad.parallelStoryLimit,
1664
+ experimental: []
1665
+ }
1666
+ };
1667
+ }
1668
+ function flagsToFeatures(flags) {
1669
+ const features = [];
1670
+ if (flags.bmadBridge) features.push("bmad-bridge");
1671
+ if (flags.autoStatus) features.push("auto-status");
1672
+ if (flags.parallelExecution) features.push("parallel");
1673
+ if (flags.notifications) features.push("notifications");
1674
+ if (flags.contextMonitor) features.push("context-monitor");
1675
+ if (flags.commentChecker) features.push("comment-checker");
1676
+ if (flags.lspTools) features.push("lsp-tools");
1677
+ return features;
1678
+ }
1679
+ function flagsToMcps(mcps) {
1680
+ const result = [];
1681
+ if (mcps.context7) result.push("context7");
1682
+ if (mcps.exa) result.push("exa");
1683
+ if (mcps.grepApp) result.push("grep_app");
1684
+ return result;
1685
+ }
1686
+ function formatPresetSummary(preset, name) {
1687
+ const lines = [];
1688
+ lines.push(`Preset: ${name}`);
1689
+ lines.push(`Description: ${preset.description}`);
1690
+ lines.push("");
1691
+ lines.push("Models:");
1692
+ lines.push(` Sisyphus: ${preset.models.sisyphus}`);
1693
+ lines.push(` Oracle: ${preset.models.oracle}`);
1694
+ lines.push(` Librarian: ${preset.models.librarian}`);
1695
+ lines.push("");
1696
+ lines.push("BMAD Settings:");
1697
+ lines.push(` Default Track: ${preset.bmad.defaultTrack}`);
1698
+ lines.push(` Auto Status Update: ${preset.bmad.autoStatusUpdate ? "Yes" : "No"}`);
1699
+ lines.push(` Parallel Story Limit: ${preset.bmad.parallelStoryLimit}`);
1700
+ lines.push("");
1701
+ lines.push("Features:");
1702
+ const enabledFeatures = flagsToFeatures(preset.features);
1703
+ if (enabledFeatures.length > 0) {
1704
+ lines.push(` Enabled: ${enabledFeatures.join(", ")}`);
1705
+ } else {
1706
+ lines.push(" Enabled: none");
1707
+ }
1708
+ lines.push("");
1709
+ lines.push("MCP Servers:");
1710
+ const enabledMcps = flagsToMcps(preset.mcps);
1711
+ if (enabledMcps.length > 0) {
1712
+ lines.push(` Enabled: ${enabledMcps.join(", ")}`);
1713
+ } else {
1714
+ lines.push(" Enabled: none");
1715
+ }
1716
+ return lines.join("\n");
1717
+ }
1718
+
1719
+ // src/cli/commands/install.ts
1720
+ async function install(options) {
1721
+ logger.banner();
1722
+ const spinner = ora3("Checking prerequisites...").start();
1723
+ const prereqs = await checkPrerequisites();
1724
+ if (!prereqs.node.installed) {
1725
+ spinner.fail("Node.js not found");
1726
+ logger.error("Please install Node.js 20+ first: https://nodejs.org/");
1727
+ process.exit(1);
1728
+ }
1729
+ if (!prereqs.node.compatible) {
1730
+ spinner.warn(`Node.js ${prereqs.node.version} detected. Recommended: 20+`);
1731
+ }
1732
+ if (!prereqs.opencode.installed) {
1733
+ spinner.fail("OpenCode not found");
1734
+ logger.error("Please install OpenCode first: https://opencode.ai/docs");
1735
+ process.exit(1);
1736
+ }
1737
+ if (!prereqs.opencode.compatible) {
1738
+ spinner.warn(`OpenCode ${prereqs.opencode.version} detected. Recommended: 1.0.132+`);
1739
+ } else {
1740
+ spinner.succeed(`OpenCode ${prereqs.opencode.version || ""} detected`);
1741
+ }
1742
+ if (prereqs.athena.installed && !options.yes) {
1743
+ const overwrite = await confirm({
1744
+ message: `OpenCode Athena ${prereqs.athena.version || ""} is already installed. Overwrite configuration?`,
1745
+ default: false
1746
+ });
1747
+ if (!overwrite) {
1748
+ logger.info("Installation cancelled.");
1749
+ process.exit(0);
1750
+ }
1751
+ }
1752
+ let preset = null;
1753
+ let presetDefaults = null;
1754
+ let presetName = null;
1755
+ if (options.preset && options.preset !== "none") {
1756
+ if (!isValidPresetName(options.preset)) {
1757
+ logger.error(`Invalid preset: "${options.preset}"`);
1758
+ logger.info(`Valid presets: ${PRESET_NAMES.join(", ")}`);
1759
+ process.exit(1);
1760
+ }
1761
+ try {
1762
+ preset = loadPreset(options.preset);
1763
+ presetDefaults = presetToDefaults(preset);
1764
+ presetName = options.preset;
1765
+ logger.success(`Loaded preset: ${options.preset}`);
1766
+ } catch (error) {
1767
+ logger.error(error instanceof Error ? error.message : String(error));
1768
+ process.exit(1);
1769
+ }
1770
+ }
1771
+ if (!preset && !options.yes) {
1772
+ const selectedPreset = await askForPreset();
1773
+ if (selectedPreset) {
1774
+ preset = selectedPreset.preset;
1775
+ presetDefaults = presetToDefaults(preset);
1776
+ presetName = selectedPreset.name;
1777
+ }
1778
+ }
1779
+ logger.section("LLM Subscriptions");
1780
+ const subscriptions = await gatherSubscriptions();
1781
+ let shouldCustomize = true;
1782
+ if (preset && presetDefaults && presetName) {
1783
+ const modelWarnings = validatePresetModels(presetDefaults.models, subscriptions);
1784
+ if (modelWarnings.length > 0) {
1785
+ console.log(chalk3.yellow("\nPreset model compatibility warnings:"));
1786
+ for (const warning of modelWarnings) {
1787
+ console.log(chalk3.yellow(` - ${warning}`));
1788
+ }
1789
+ console.log();
1790
+ }
1791
+ console.log(chalk3.bold("\nPreset Configuration:\n"));
1792
+ console.log(chalk3.gray(formatPresetSummary(preset, presetName)));
1793
+ console.log();
1794
+ if (options.yes) {
1795
+ shouldCustomize = false;
1796
+ logger.info("Using preset defaults (--yes flag)");
1797
+ } else {
1798
+ shouldCustomize = await confirm({
1799
+ message: "Would you like to customize these settings?",
1800
+ default: false
1801
+ });
1802
+ }
1803
+ }
1804
+ let models;
1805
+ let methodology;
1806
+ let features;
1807
+ let advanced;
1808
+ if (!shouldCustomize && presetDefaults) {
1809
+ logger.section("Applying Preset Configuration");
1810
+ const availableModels = await Promise.resolve().then(() => (init_models(), models_exports)).then(
1811
+ (m) => m.getAvailableModels(subscriptions)
1812
+ );
1813
+ if (availableModels.length === 0) {
1814
+ logger.error(
1815
+ "No models available. Please enable at least one provider (Claude, OpenAI, or Google)."
1816
+ );
1817
+ process.exit(1);
1818
+ }
1819
+ models = {
1820
+ sisyphus: getValidModelOrFirst(presetDefaults.models.sisyphus, availableModels),
1821
+ oracle: getValidModelOrFirst(presetDefaults.models.oracle, availableModels),
1822
+ librarian: getValidModelOrFirst(presetDefaults.models.librarian, availableModels),
1823
+ frontend: getValidModelOrFirst(presetDefaults.models.frontend, availableModels),
1824
+ documentWriter: getValidModelOrFirst(presetDefaults.models.documentWriter, availableModels),
1825
+ multimodalLooker: getValidModelOrFirst(
1826
+ presetDefaults.models.multimodalLooker,
1827
+ availableModels
1828
+ )
1829
+ };
1830
+ methodology = presetDefaults.methodology;
1831
+ features = presetDefaults.features;
1832
+ advanced = presetDefaults.advanced;
1833
+ logger.success("Preset configuration applied");
1834
+ } else {
1835
+ logger.section("Model Selection");
1836
+ models = await gatherModels(subscriptions, presetDefaults?.models);
1837
+ logger.section("Methodology Preferences");
1838
+ methodology = await gatherMethodology(presetDefaults?.methodology);
1839
+ logger.section("Feature Selection");
1840
+ features = await gatherFeatures(presetDefaults?.features);
1841
+ if (options.advanced) {
1842
+ logger.section("Advanced Configuration");
1843
+ advanced = await gatherAdvanced(presetDefaults?.advanced);
1844
+ } else {
1845
+ advanced = presetDefaults?.advanced ?? {
1846
+ parallelStoryLimit: DEFAULTS.parallelStoryLimit,
1847
+ experimental: []
1848
+ };
1849
+ }
1850
+ }
1851
+ logger.section("Generating Configuration");
1852
+ const answers = {
1853
+ subscriptions,
1854
+ models,
1855
+ methodology,
1856
+ features,
1857
+ advanced,
1858
+ installLocation: options.local ? "local" : "global"
1859
+ };
1860
+ const generator = new ConfigGenerator(answers);
1861
+ const files = await generator.generate();
1862
+ console.log(chalk3.bold("Files to be created/modified:\n"));
1863
+ for (const file of files) {
1864
+ const action = file.exists ? chalk3.yellow("update") : chalk3.green("create");
1865
+ console.log(chalk3.gray(` [${action}] ${file.path}`));
1866
+ }
1867
+ console.log();
1868
+ if (!options.yes) {
1869
+ const proceed = await confirm({
1870
+ message: "Proceed with installation?",
1871
+ default: true
1872
+ });
1873
+ if (!proceed) {
1874
+ logger.info("Installation cancelled.");
1875
+ process.exit(0);
1876
+ }
1877
+ }
1878
+ const installSpinner = ora3("Installing OpenCode Athena...").start();
1879
+ try {
1880
+ const fileManager = new FileManager(generator.getConfigDir());
1881
+ await fileManager.writeFiles(files);
1882
+ installSpinner.text = "Configuration files written...";
1883
+ const packages = generator.getRequiredPackages();
1884
+ if (packages.length > 0) {
1885
+ installSpinner.text = `Installing dependencies: ${packages.join(", ")}...`;
1886
+ await fileManager.installDependencies(packages);
1887
+ }
1888
+ installSpinner.text = "Installing commands...";
1889
+ const copiedCommands = await fileManager.copyCommands();
1890
+ installSpinner.succeed("Installation complete!");
1891
+ if (copiedCommands.length > 0) {
1892
+ logger.info(`Installed ${copiedCommands.length} bridge commands`);
1893
+ }
1894
+ } catch (error) {
1895
+ installSpinner.fail("Installation failed");
1896
+ logger.error(error instanceof Error ? error.message : String(error));
1897
+ process.exit(1);
1898
+ }
1899
+ printNextSteps(subscriptions);
1900
+ }
1901
+ async function askForPreset() {
1902
+ const presets = listPresets();
1903
+ const choices = [
1904
+ { name: "No preset - Configure everything manually", value: "none" },
1905
+ ...presets.map((p) => ({
1906
+ name: `${p.name} - ${p.description}`,
1907
+ value: p.name
1908
+ }))
1909
+ ];
1910
+ const selected = await select({
1911
+ message: "Start from a preset?",
1912
+ choices,
1913
+ default: "standard"
1914
+ });
1915
+ if (selected === "none") {
1916
+ return null;
1917
+ }
1918
+ try {
1919
+ const preset = loadPreset(selected);
1920
+ return { preset, name: selected };
1921
+ } catch (error) {
1922
+ logger.warn(`Failed to load preset: ${error instanceof Error ? error.message : String(error)}`);
1923
+ return null;
1924
+ }
1925
+ }
1926
+ function getValidModelOrFirst(modelId, availableModels) {
1927
+ if (modelId && availableModels.some((m) => m.id === modelId)) {
1928
+ return modelId;
1929
+ }
1930
+ return availableModels[0]?.id ?? "";
1931
+ }
1932
+ function printNextSteps(subscriptions) {
1933
+ const steps = [];
1934
+ if (subscriptions.hasClaude) {
1935
+ steps.push(`Run: ${chalk3.cyan("opencode auth login")} -> Select Anthropic -> Claude Pro/Max`);
1936
+ }
1937
+ if (subscriptions.hasOpenAI) {
1938
+ steps.push(`Run: ${chalk3.cyan("opencode auth login")} -> Select OpenAI -> ChatGPT Plus/Pro`);
1939
+ }
1940
+ if (subscriptions.hasGoogle) {
1941
+ steps.push(`Run: ${chalk3.cyan("opencode auth login")} -> Select Google -> OAuth with Google`);
1942
+ }
1943
+ logger.successBanner("OPENCODE ATHENA INSTALLED SUCCESSFULLY!");
1944
+ console.log(chalk3.bold("Next Steps:\n"));
1945
+ steps.forEach((step, i) => {
1946
+ console.log(` ${i + 1}. ${step}`);
1947
+ });
1948
+ console.log(chalk3.bold("\nThen start OpenCode and try:\n"));
1949
+ console.log(` ${chalk3.cyan("/athena-dev")} Implement a BMAD story with Sisyphus`);
1950
+ console.log(` ${chalk3.cyan("/athena-status")} Check sprint status`);
1951
+ console.log(` ${chalk3.cyan("/athena-info")} View toolkit configuration`);
1952
+ console.log(chalk3.bold("\nFor BMAD project setup:\n"));
1953
+ console.log(` ${chalk3.cyan("npx bmad-method@alpha install")} Install BMAD in your project`);
1954
+ console.log(
1955
+ chalk3.gray("\nDocumentation: https://github.com/ZebulonRouseFrantzich/opencode-athena")
1956
+ );
1957
+ console.log();
1958
+ }
1959
+
1960
+ // src/cli/commands/uninstall.ts
1961
+ init_esm_shims();
1962
+ async function uninstall(options) {
1963
+ logger.banner();
1964
+ logger.warn("This will remove OpenCode Athena from your system.");
1965
+ console.log();
1966
+ const proceed = await confirm({
1967
+ message: "Are you sure you want to uninstall OpenCode Athena?",
1968
+ default: false
1969
+ });
1970
+ if (!proceed) {
1971
+ logger.info("Uninstall cancelled.");
1972
+ return;
1973
+ }
1974
+ logger.section("Uninstalling OpenCode Athena");
1975
+ const fileManager = new FileManager();
1976
+ const commandsSpinner = ora3("Removing bridge commands...").start();
1977
+ try {
1978
+ const removedCommands = await fileManager.removeCommands();
1979
+ if (removedCommands.length > 0) {
1980
+ commandsSpinner.succeed(`Removed ${removedCommands.length} bridge command(s)`);
1981
+ } else {
1982
+ commandsSpinner.info("No bridge commands found");
1983
+ }
1984
+ } catch (err) {
1985
+ commandsSpinner.fail("Failed to remove bridge commands");
1986
+ logger.error(err instanceof Error ? err.message : String(err));
1987
+ }
1988
+ if (!options.keepConfig) {
1989
+ const configSpinner = ora3("Removing configuration files...").start();
1990
+ try {
1991
+ const removedFiles = await fileManager.removeConfigFiles();
1992
+ if (removedFiles.length > 0) {
1993
+ configSpinner.succeed(`Removed ${removedFiles.length} configuration file(s)`);
1994
+ } else {
1995
+ configSpinner.info("No Athena configuration files found");
1996
+ }
1997
+ } catch (err) {
1998
+ configSpinner.fail("Failed to remove configuration files");
1999
+ logger.error(err instanceof Error ? err.message : String(err));
2000
+ }
2001
+ } else {
2002
+ logger.info("Keeping configuration files (--keep-config)");
2003
+ }
2004
+ const opencodeSpinner = ora3("Updating opencode.json...").start();
2005
+ try {
2006
+ const updated = await fileManager.removeFromOpencodeConfig();
2007
+ if (updated) {
2008
+ opencodeSpinner.succeed("Removed Athena plugins from opencode.json");
2009
+ } else {
2010
+ opencodeSpinner.info("No Athena plugins found in opencode.json");
2011
+ }
2012
+ } catch (err) {
2013
+ opencodeSpinner.fail("Failed to update opencode.json");
2014
+ logger.error(err instanceof Error ? err.message : String(err));
2015
+ }
2016
+ if (!options.keepDeps) {
2017
+ const depsSpinner = ora3("Removing npm dependencies...").start();
2018
+ try {
2019
+ const packagesToRemove = [
2020
+ "oh-my-opencode",
2021
+ "opencode-antigravity-auth",
2022
+ "opencode-openai-codex-auth"
2023
+ ];
2024
+ await fileManager.uninstallDependencies(packagesToRemove);
2025
+ depsSpinner.succeed("Removed npm dependencies");
2026
+ } catch (_err) {
2027
+ depsSpinner.warn("Some dependencies could not be removed");
2028
+ }
2029
+ } else {
2030
+ logger.info("Keeping npm dependencies (--keep-deps)");
2031
+ }
2032
+ console.log();
2033
+ logger.success("OpenCode Athena has been uninstalled.");
2034
+ if (options.keepConfig) {
2035
+ logger.info("Configuration files were preserved.");
2036
+ logger.info(`Run ${chalk3.cyan("opencode-athena install")} to reinstall with existing config.`);
2037
+ }
2038
+ console.log();
2039
+ }
2040
+
2041
+ // src/cli/commands/update.ts
2042
+ init_esm_shims();
2043
+ var execAsync3 = promisify(exec);
2044
+ async function getLatestVersion(packageName) {
2045
+ try {
2046
+ const { stdout } = await execAsync3(`npm view ${packageName} version`);
2047
+ return stdout.trim();
2048
+ } catch {
2049
+ return null;
2050
+ }
2051
+ }
2052
+ async function checkPackageUpdate(name, currentVersion) {
2053
+ const latest = await getLatestVersion(name);
2054
+ return {
2055
+ name,
2056
+ current: currentVersion,
2057
+ latest: latest || currentVersion,
2058
+ updateAvailable: latest !== null && latest !== currentVersion
2059
+ };
2060
+ }
2061
+ async function update(options) {
2062
+ logger.banner();
2063
+ logger.section("Checking for Updates");
2064
+ const spinner = ora3("Checking package versions...").start();
2065
+ const installedPlugins = await getInstalledPlugins();
2066
+ const packagesToCheck = [
2067
+ "oh-my-opencode",
2068
+ "opencode-antigravity-auth",
2069
+ "opencode-openai-codex-auth"
2070
+ ];
2071
+ const updates = [];
2072
+ const athenaLatest = await getLatestVersion("opencode-athena");
2073
+ if (athenaLatest) {
2074
+ updates.push({
2075
+ name: "opencode-athena",
2076
+ current: VERSION,
2077
+ latest: athenaLatest,
2078
+ updateAvailable: athenaLatest !== VERSION
2079
+ });
2080
+ }
2081
+ for (const pkgName of packagesToCheck) {
2082
+ const currentVersion = installedPlugins[pkgName];
2083
+ if (currentVersion) {
2084
+ const updateInfo = await checkPackageUpdate(pkgName, currentVersion);
2085
+ updates.push(updateInfo);
2086
+ }
2087
+ }
2088
+ spinner.stop();
2089
+ logger.section("Package Versions");
2090
+ const updatesAvailable = updates.filter((u) => u.updateAvailable);
2091
+ for (const pkg of updates) {
2092
+ const status = pkg.updateAvailable ? chalk3.yellow(`${pkg.current} -> ${pkg.latest}`) : chalk3.green(pkg.current);
2093
+ logger.keyValue(pkg.name, status);
2094
+ }
2095
+ console.log();
2096
+ if (updatesAvailable.length === 0) {
2097
+ logger.success("All packages are up to date!");
2098
+ return;
2099
+ }
2100
+ logger.info(`${updatesAvailable.length} update(s) available`);
2101
+ if (options.check) {
2102
+ console.log();
2103
+ logger.info(`Run ${chalk3.cyan("opencode-athena update")} (without --check) to apply updates.`);
2104
+ return;
2105
+ }
2106
+ const proceed = await confirm({
2107
+ message: `Update ${updatesAvailable.length} package(s)?`,
2108
+ default: true
2109
+ });
2110
+ if (!proceed) {
2111
+ logger.info("Update cancelled.");
2112
+ return;
2113
+ }
2114
+ logger.section("Applying Updates");
2115
+ const fileManager = new FileManager();
2116
+ const athenaUpdate = updatesAvailable.find((u) => u.name === "opencode-athena");
2117
+ if (athenaUpdate) {
2118
+ const athenaSpinner = ora3("Updating opencode-athena...").start();
2119
+ try {
2120
+ await execAsync3("npm install -g opencode-athena@latest");
2121
+ athenaSpinner.succeed(`opencode-athena updated to ${athenaUpdate.latest}`);
2122
+ } catch (err) {
2123
+ athenaSpinner.fail("Failed to update opencode-athena");
2124
+ logger.error(err instanceof Error ? err.message : String(err));
2125
+ }
2126
+ }
2127
+ const pluginUpdates = updatesAvailable.filter((u) => u.name !== "opencode-athena");
2128
+ if (pluginUpdates.length > 0) {
2129
+ const pluginSpinner = ora3("Updating plugins...").start();
2130
+ try {
2131
+ const packages = pluginUpdates.map((u) => `${u.name}@latest`);
2132
+ await fileManager.installDependencies(packages);
2133
+ pluginSpinner.succeed(`Updated ${pluginUpdates.length} plugin(s)`);
2134
+ } catch (err) {
2135
+ pluginSpinner.fail("Failed to update plugins");
2136
+ logger.error(err instanceof Error ? err.message : String(err));
2137
+ }
2138
+ }
2139
+ const commandsSpinner = ora3("Updating bridge commands...").start();
2140
+ try {
2141
+ await fileManager.copyCommands();
2142
+ commandsSpinner.succeed("Bridge commands updated");
2143
+ } catch (_err) {
2144
+ commandsSpinner.warn("Could not update bridge commands");
2145
+ }
2146
+ console.log();
2147
+ logger.success("Update complete!");
2148
+ logger.info("Restart OpenCode to use the updated version.");
2149
+ }
2150
+
2151
+ // src/cli/index.ts
2152
+ var program = new Command();
2153
+ program.name("opencode-athena").description(
2154
+ `${chalk3.cyan(DISPLAY_NAME)} - ${TAGLINE}
2155
+ Unified oh-my-opencode + BMAD METHOD toolkit for OpenCode`
2156
+ ).version(VERSION);
2157
+ program.command("install").description("Install and configure OpenCode Athena").option(
2158
+ "-p, --preset <preset>",
2159
+ "Use a preset configuration (minimal, standard, enterprise, solo-quick)",
2160
+ "standard"
2161
+ ).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).action(async (options) => {
2162
+ if (options.listPresets) {
2163
+ displayPresets();
2164
+ return;
2165
+ }
2166
+ await install(options);
2167
+ });
2168
+ program.command("update").description("Update OpenCode Athena to latest version").option("--check", "Check for updates without installing", false).action(update);
2169
+ program.command("doctor").description("Diagnose and fix common issues").option("--fix", "Automatically fix issues", false).action(doctor);
2170
+ program.command("uninstall").description("Remove OpenCode Athena").option("--keep-config", "Keep configuration files", false).option("--keep-deps", "Keep npm dependencies", false).action(uninstall);
2171
+ program.command("info").description("Show current configuration and status").action(info);
2172
+ function displayPresets() {
2173
+ console.log(chalk3.bold.cyan("\nAvailable Presets:\n"));
2174
+ const presets = listPresets();
2175
+ for (const preset of presets) {
2176
+ console.log(chalk3.bold(` ${preset.name}`));
2177
+ console.log(chalk3.gray(` ${preset.description}`));
2178
+ console.log();
2179
+ }
2180
+ console.log(chalk3.gray("Usage: opencode-athena install --preset <name>"));
2181
+ console.log(chalk3.gray(" opencode-athena install --preset standard --yes\n"));
2182
+ }
2183
+ program.parse();
2184
+ //# sourceMappingURL=index.js.map
2185
+ //# sourceMappingURL=index.js.map