gsd-opencode 1.9.1 → 1.10.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.
Files changed (65) hide show
  1. package/agents/gsd-debugger.md +5 -5
  2. package/agents/gsd-set-model.md +287 -0
  3. package/agents/gsd-set-profile.md +239 -0
  4. package/agents/gsd-settings.md +303 -0
  5. package/bin/gsd-install.js +105 -0
  6. package/bin/gsd.js +352 -0
  7. package/bin/install.js +81 -1
  8. package/{command → commands}/gsd/add-phase.md +1 -1
  9. package/{command → commands}/gsd/audit-milestone.md +1 -1
  10. package/{command → commands}/gsd/debug.md +3 -3
  11. package/{command → commands}/gsd/discuss-phase.md +1 -1
  12. package/{command → commands}/gsd/execute-phase.md +1 -1
  13. package/{command → commands}/gsd/list-phase-assumptions.md +1 -1
  14. package/{command → commands}/gsd/map-codebase.md +1 -1
  15. package/{command → commands}/gsd/new-milestone.md +1 -1
  16. package/{command → commands}/gsd/new-project.md +71 -6
  17. package/{command → commands}/gsd/plan-phase.md +2 -2
  18. package/{command → commands}/gsd/research-phase.md +1 -1
  19. package/commands/gsd/set-model.md +77 -0
  20. package/commands/gsd/set-profile.md +46 -0
  21. package/commands/gsd/settings.md +33 -0
  22. package/{command → commands}/gsd/verify-work.md +1 -1
  23. package/get-shit-done/references/model-profiles.md +67 -36
  24. package/get-shit-done/workflows/list-phase-assumptions.md +1 -1
  25. package/get-shit-done/workflows/verify-work.md +5 -5
  26. package/lib/constants.js +193 -0
  27. package/package.json +34 -20
  28. package/src/commands/check.js +329 -0
  29. package/src/commands/config.js +337 -0
  30. package/src/commands/install.js +608 -0
  31. package/src/commands/list.js +256 -0
  32. package/src/commands/repair.js +519 -0
  33. package/src/commands/uninstall.js +732 -0
  34. package/src/commands/update.js +444 -0
  35. package/src/services/backup-manager.js +585 -0
  36. package/src/services/config.js +262 -0
  37. package/src/services/file-ops.js +830 -0
  38. package/src/services/health-checker.js +475 -0
  39. package/src/services/manifest-manager.js +301 -0
  40. package/src/services/migration-service.js +831 -0
  41. package/src/services/repair-service.js +846 -0
  42. package/src/services/scope-manager.js +303 -0
  43. package/src/services/settings.js +553 -0
  44. package/src/services/structure-detector.js +240 -0
  45. package/src/services/update-service.js +863 -0
  46. package/src/utils/hash.js +71 -0
  47. package/src/utils/interactive.js +222 -0
  48. package/src/utils/logger.js +128 -0
  49. package/src/utils/npm-registry.js +255 -0
  50. package/src/utils/path-resolver.js +226 -0
  51. package/command/gsd/set-profile.md +0 -111
  52. package/command/gsd/settings.md +0 -136
  53. /package/{command → commands}/gsd/add-todo.md +0 -0
  54. /package/{command → commands}/gsd/check-todos.md +0 -0
  55. /package/{command → commands}/gsd/complete-milestone.md +0 -0
  56. /package/{command → commands}/gsd/help.md +0 -0
  57. /package/{command → commands}/gsd/insert-phase.md +0 -0
  58. /package/{command → commands}/gsd/pause-work.md +0 -0
  59. /package/{command → commands}/gsd/plan-milestone-gaps.md +0 -0
  60. /package/{command → commands}/gsd/progress.md +0 -0
  61. /package/{command → commands}/gsd/quick.md +0 -0
  62. /package/{command → commands}/gsd/remove-phase.md +0 -0
  63. /package/{command → commands}/gsd/resume-work.md +0 -0
  64. /package/{command → commands}/gsd/update.md +0 -0
  65. /package/{command → commands}/gsd/whats-new.md +0 -0
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Check command for GSD-OpenCode CLI.
3
+ *
4
+ * This module provides the check functionality to verify installation health,
5
+ * showing detailed pass/fail results for file existence, version matching,
6
+ * and file integrity checks.
7
+ *
8
+ * Implements requirements:
9
+ * - CLI-03: User can run gsd-opencode check to verify installation health
10
+ * - CHECK-01: Check verifies all required files exist
11
+ * - CHECK-02: Check verifies installed version matches expected version
12
+ * - CHECK-03: Check detects corrupted or modified files
13
+ * - CHECK-04: Check provides clear pass/fail output for each verification
14
+ * - CHECK-05: Check returns appropriate exit codes (0 for healthy, non-zero for issues)
15
+ * - ERROR-03: All commands support --verbose flag for detailed debugging output
16
+ * - ERROR-06: CLI shows consistent branding and formatted output using colors
17
+ *
18
+ * @module check
19
+ */
20
+
21
+ import { ScopeManager } from '../services/scope-manager.js';
22
+ import { HealthChecker } from '../services/health-checker.js';
23
+ import { logger, setVerbose } from '../utils/logger.js';
24
+ import { ERROR_CODES } from '../../lib/constants.js';
25
+ import fs from 'fs/promises';
26
+ import path from 'path';
27
+ import { fileURLToPath } from 'url';
28
+
29
+ /**
30
+ * Gets the package version from package.json.
31
+ *
32
+ * @returns {Promise<string>} The package version
33
+ * @private
34
+ */
35
+ async function getPackageVersion() {
36
+ try {
37
+ const __filename = fileURLToPath(import.meta.url);
38
+ const __dirname = path.dirname(__filename);
39
+ const packageRoot = path.resolve(__dirname, '../..');
40
+ const packageJsonPath = path.join(packageRoot, 'package.json');
41
+
42
+ const content = await fs.readFile(packageJsonPath, 'utf-8');
43
+ const pkg = JSON.parse(content);
44
+ return pkg.version || '1.0.0';
45
+ } catch (error) {
46
+ logger.warning('Could not read package version, using 1.0.0');
47
+ return '1.0.0';
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Displays health check results with pass/fail indicators.
53
+ *
54
+ * @param {Object} results - Health check results from HealthChecker
55
+ * @param {string} scopeLabel - Label for this scope
56
+ * @private
57
+ */
58
+ function displayCheckResults(results, scopeLabel) {
59
+ logger.heading(`${scopeLabel} Installation Health`);
60
+ logger.dim('================================');
61
+
62
+ if (!results) {
63
+ logger.info('Not installed');
64
+ logger.dim('');
65
+ return;
66
+ }
67
+
68
+ const { categories } = results;
69
+
70
+ // Files check section
71
+ logger.dim('');
72
+ logger.info('Required Files');
73
+ if (categories.files && categories.files.checks) {
74
+ for (const check of categories.files.checks) {
75
+ const status = check.passed ? 'OK' : (check.error || 'Missing');
76
+ if (check.passed) {
77
+ logger.success(`${check.name}: ${status}`);
78
+ } else {
79
+ logger.error(`${check.name}: ${status}`);
80
+ }
81
+ }
82
+ }
83
+
84
+ // Version check section
85
+ if (categories.version) {
86
+ logger.dim('');
87
+ logger.info('Version Verification');
88
+ const versionCheck = categories.version.checks[0];
89
+ if (versionCheck.passed) {
90
+ logger.success(`Version: ${versionCheck.installed} - OK`);
91
+ } else {
92
+ const errorMsg = versionCheck.error
93
+ ? `Version check failed - ${versionCheck.error}`
94
+ : `Version mismatch (installed: ${versionCheck.installed || 'none'}, expected: ${versionCheck.expected})`;
95
+ logger.error(errorMsg);
96
+ }
97
+ }
98
+
99
+ // Integrity check section
100
+ logger.dim('');
101
+ logger.info('File Integrity');
102
+ if (categories.integrity && categories.integrity.checks) {
103
+ for (const check of categories.integrity.checks) {
104
+ const relativePath = check.relative || path.basename(check.file);
105
+ const message = check.passed
106
+ ? `${relativePath} - OK`
107
+ : `${relativePath} - ${check.error || 'Corrupted or missing'}`;
108
+
109
+ if (check.passed) {
110
+ logger.success(message);
111
+ } else {
112
+ logger.error(message);
113
+ }
114
+ }
115
+ }
116
+
117
+ // Structure check section (NEW)
118
+ if (categories.structure) {
119
+ logger.dim('');
120
+ logger.info('Directory Structure');
121
+ const structure = categories.structure;
122
+ const statusText = structure.label;
123
+
124
+ if (structure.type === 'dual') {
125
+ logger.error(`${statusText} - Action required`);
126
+ logger.dim(' Both old (command/gsd/) and new (commands/gsd/) structures detected.');
127
+ logger.dim(' This can happen if an update was interrupted.');
128
+ logger.dim(" Run 'gsd-opencode update' to complete migration");
129
+ } else if (structure.needsMigration) {
130
+ logger.warning(`${statusText} - Migration recommended`);
131
+ logger.dim(" Run 'gsd-opencode update' to migrate to new structure");
132
+ } else if (structure.type === 'new') {
133
+ logger.success(`${statusText} - OK`);
134
+ } else if (structure.type === 'none') {
135
+ logger.info(`${statusText}`);
136
+ } else {
137
+ logger.info(statusText);
138
+ }
139
+ }
140
+
141
+ // Overall status
142
+ logger.dim('');
143
+ if (results.passed) {
144
+ logger.success('All checks passed - Installation is healthy');
145
+ } else {
146
+ logger.error('Some checks failed - Issues detected');
147
+ }
148
+ logger.dim('');
149
+ }
150
+
151
+ /**
152
+ * Checks health for a single scope.
153
+ *
154
+ * @param {string} scope - 'global' or 'local'
155
+ * @param {Object} options - Options
156
+ * @param {boolean} options.verbose - Enable verbose output
157
+ * @returns {Promise<Object>} Health check results
158
+ * @private
159
+ */
160
+ async function checkScope(scope, options = {}) {
161
+ const scopeManager = new ScopeManager({ scope });
162
+ const scopeLabel = scope.charAt(0).toUpperCase() + scope.slice(1);
163
+
164
+ logger.debug(`Checking ${scope} installation...`);
165
+
166
+ const isInstalled = await scopeManager.isInstalled();
167
+ if (!isInstalled) {
168
+ logger.debug(`No ${scope} installation found`);
169
+ return {
170
+ installed: false,
171
+ scope,
172
+ scopeLabel,
173
+ results: null,
174
+ passed: false
175
+ };
176
+ }
177
+
178
+ logger.debug(`${scope} installation detected, running health checks...`);
179
+
180
+ const healthChecker = new HealthChecker(scopeManager);
181
+ const expectedVersion = await getPackageVersion();
182
+
183
+ try {
184
+ const results = await healthChecker.checkAll({
185
+ expectedVersion,
186
+ verbose: options.verbose
187
+ });
188
+
189
+ return {
190
+ installed: true,
191
+ scope,
192
+ scopeLabel,
193
+ results,
194
+ passed: results.passed
195
+ };
196
+ } catch (error) {
197
+ logger.debug(`Error during health check: ${error.message}`);
198
+ return {
199
+ installed: true,
200
+ scope,
201
+ scopeLabel,
202
+ results: null,
203
+ passed: false,
204
+ error: error.message
205
+ };
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Handles errors with helpful messages.
211
+ *
212
+ * @param {Error} error - The error to handle
213
+ * @param {boolean} verbose - Whether verbose mode is enabled
214
+ * @returns {number} Exit code for the error
215
+ * @private
216
+ */
217
+ function handleError(error, verbose) {
218
+ if (verbose) {
219
+ logger.debug(`Error details: ${error.stack || error.message}`);
220
+ logger.debug(`Error code: ${error.code}`);
221
+ }
222
+
223
+ if (error.code === 'EACCES') {
224
+ logger.error('Permission denied: Cannot access installation directory');
225
+ logger.dim('');
226
+ logger.dim('Suggestion: Check directory permissions or run with appropriate privileges.');
227
+ return ERROR_CODES.PERMISSION_ERROR;
228
+ }
229
+
230
+ logger.error(`Failed to check installation health: ${error.message}`);
231
+
232
+ if (!verbose) {
233
+ logger.dim('');
234
+ logger.dim('Suggestion: Run with --verbose for detailed error information');
235
+ }
236
+
237
+ return ERROR_CODES.GENERAL_ERROR;
238
+ }
239
+
240
+ /**
241
+ * Main check command function.
242
+ *
243
+ * @param {Object} options - Command options
244
+ * @param {boolean} [options.global] - Check global installation only
245
+ * @param {boolean} [options.local] - Check local installation only
246
+ * @param {boolean} [options.verbose] - Enable verbose output
247
+ * @returns {Promise<number>} Exit code
248
+ */
249
+ export async function checkCommand(options = {}) {
250
+ const verbose = options.verbose || false;
251
+ setVerbose(verbose);
252
+
253
+ logger.debug('Starting check command');
254
+ logger.debug(`Options: global=${options.global}, local=${options.local}, verbose=${verbose}`);
255
+
256
+ try {
257
+ logger.heading('GSD-OpenCode Installation Health');
258
+ logger.dim('================================');
259
+
260
+ const scopesToCheck = [];
261
+ if (options.global) {
262
+ scopesToCheck.push('global');
263
+ } else if (options.local) {
264
+ scopesToCheck.push('local');
265
+ } else {
266
+ scopesToCheck.push('global', 'local');
267
+ }
268
+
269
+ let anyInstalled = false;
270
+ let allPassed = true;
271
+
272
+ for (const scope of scopesToCheck) {
273
+ try {
274
+ const result = await checkScope(scope, options);
275
+
276
+ if (result.installed) {
277
+ anyInstalled = true;
278
+ if (!result.passed) {
279
+ allPassed = false;
280
+ }
281
+ }
282
+
283
+ if (scopesToCheck.length > 1) {
284
+ logger.dim('');
285
+ }
286
+
287
+ displayCheckResults(result.results, result.scopeLabel);
288
+
289
+ if (verbose && result.error) {
290
+ logger.debug(`Error checking ${scope}: ${result.error}`);
291
+ }
292
+ } catch (error) {
293
+ logger.error(`Failed to check ${scope} installation: ${error.message}`);
294
+ allPassed = false;
295
+
296
+ if (verbose) {
297
+ logger.debug(error.stack);
298
+ }
299
+ }
300
+ }
301
+
302
+ if (!anyInstalled) {
303
+ logger.dim('');
304
+ logger.dim('Not installed anywhere');
305
+ logger.dim('');
306
+ logger.info("Run 'gsd-opencode install' to install");
307
+ logger.dim('');
308
+ return ERROR_CODES.SUCCESS;
309
+ }
310
+
311
+ const exitCode = allPassed ? ERROR_CODES.SUCCESS : ERROR_CODES.GENERAL_ERROR;
312
+
313
+ if (verbose) {
314
+ logger.debug(`Check complete. Exit code: ${exitCode}`);
315
+ }
316
+
317
+ return exitCode;
318
+
319
+ } catch (error) {
320
+ if (error.name === 'AbortPromptError' || error.message?.includes('cancel')) {
321
+ logger.info('Command cancelled by user');
322
+ return ERROR_CODES.INTERRUPTED;
323
+ }
324
+
325
+ return handleError(error, verbose);
326
+ }
327
+ }
328
+
329
+ export default checkCommand;
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Config command for GSD-OpenCode CLI.
3
+ *
4
+ * This module provides configuration management with subcommands for getting,
5
+ * setting, resetting, and listing configuration values. Supports value auto-parsing
6
+ * (booleans, numbers, JSON strings) and multiple output formats (human-readable and JSON).
7
+ *
8
+ * Implements requirements:
9
+ * - CLI-07: User can run gsd-opencode config to manage settings
10
+ * - CONFIG-01: Config get <key> returns value (or error if not set)
11
+ * - CONFIG-02: Config set <key> <value> parses booleans, numbers, JSON
12
+ * - CONFIG-03: Config reset <key> restores default
13
+ * - CONFIG-04: Config reset --all removes entire config
14
+ * - CONFIG-05: Config list shows all settings
15
+ * - CONFIG-06: Config list --json outputs valid JSON
16
+ * - ERROR-03: All commands support --verbose flag for detailed debugging output
17
+ *
18
+ * @module commands/config
19
+ * @description Configuration management command for GSD-OpenCode
20
+ */
21
+
22
+ import { SettingsManager } from '../services/settings.js';
23
+ import { logger, setVerbose } from '../utils/logger.js';
24
+ import { ERROR_CODES } from '../../lib/constants.js';
25
+
26
+ /**
27
+ * Flattens a nested object using dot-notation keys.
28
+ *
29
+ * Recursively traverses an object and creates flat key-value pairs
30
+ * where nested keys are joined with dots (e.g., 'ui.colors').
31
+ *
32
+ * @param {Object} obj - Object to flatten
33
+ * @param {string} [prefix=''] - Key prefix for recursion
34
+ * @param {Object} [result={}] - Result accumulator
35
+ * @returns {Object} Flattened object with dot-notation keys
36
+ * @private
37
+ */
38
+ function flattenObject(obj, prefix = '', result = {}) {
39
+ for (const key in obj) {
40
+ if (obj.hasOwnProperty(key)) {
41
+ const newKey = prefix ? `${prefix}.${key}` : key;
42
+ const value = obj[key];
43
+
44
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
45
+ flattenObject(value, newKey, result);
46
+ } else {
47
+ result[newKey] = value;
48
+ }
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+
54
+ /**
55
+ * Formats a value for display.
56
+ *
57
+ * @param {*} value - Value to format
58
+ * @returns {string} Formatted string representation
59
+ * @private
60
+ */
61
+ function formatValue(value) {
62
+ if (typeof value === 'boolean') {
63
+ return value ? 'true' : 'false';
64
+ }
65
+ if (typeof value === 'string') {
66
+ return value;
67
+ }
68
+ return JSON.stringify(value);
69
+ }
70
+
71
+ /**
72
+ * Parses a configuration value from string input.
73
+ *
74
+ * @param {string} value - String value to parse
75
+ * @returns {*} Parsed value (boolean, number, object, array, or string)
76
+ * @private
77
+ */
78
+ function parseValue(value) {
79
+ if (value === 'true') {
80
+ return true;
81
+ }
82
+ if (value === 'false') {
83
+ return false;
84
+ }
85
+
86
+ if (value !== '' && !isNaN(value) && !isNaN(parseFloat(value))) {
87
+ if (value.includes('.')) {
88
+ return parseFloat(value);
89
+ }
90
+ return parseInt(value, 10);
91
+ }
92
+
93
+ if ((value.startsWith('{') && value.endsWith('}')) ||
94
+ (value.startsWith('[') && value.endsWith(']'))) {
95
+ try {
96
+ return JSON.parse(value);
97
+ } catch {
98
+ return value;
99
+ }
100
+ }
101
+
102
+ return value;
103
+ }
104
+
105
+ /**
106
+ * Gets a configuration value by key.
107
+ *
108
+ * @param {string} key - Configuration key using dot-notation
109
+ * @param {Object} options - Command options
110
+ * @param {boolean} [options.verbose] - Enable verbose output
111
+ * @returns {Promise<number>} Exit code
112
+ */
113
+ export async function configGetCommand(key, options = {}) {
114
+ setVerbose(options.verbose);
115
+
116
+ logger.debug('Starting config get command');
117
+ logger.debug(`Key: ${key}, verbose: ${options.verbose}`);
118
+
119
+ try {
120
+ if (!key || typeof key !== 'string') {
121
+ logger.error('Configuration key is required');
122
+ logger.dim('Usage: gsd-opencode config get <key>');
123
+ return ERROR_CODES.GENERAL_ERROR;
124
+ }
125
+
126
+ const settings = new SettingsManager();
127
+ const value = await settings.get(key);
128
+
129
+ if (value === undefined) {
130
+ logger.error(`Configuration key not found: ${key}`);
131
+ return ERROR_CODES.GENERAL_ERROR;
132
+ }
133
+
134
+ if (typeof value === 'object' && value !== null) {
135
+ console.log(JSON.stringify(value));
136
+ } else {
137
+ console.log(String(value));
138
+ }
139
+
140
+ return ERROR_CODES.SUCCESS;
141
+
142
+ } catch (error) {
143
+ if (error.name === 'AbortPromptError' || error.message?.includes('cancel')) {
144
+ logger.info('\nCommand cancelled by user');
145
+ return ERROR_CODES.INTERRUPTED;
146
+ }
147
+
148
+ logger.error(`Failed to get configuration: ${error.message}`);
149
+
150
+ if (options.verbose) {
151
+ logger.debug(error.stack);
152
+ }
153
+
154
+ return ERROR_CODES.GENERAL_ERROR;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Sets a configuration value by key.
160
+ *
161
+ * @param {string} key - Configuration key using dot-notation
162
+ * @param {string} value - Value to set (will be auto-parsed)
163
+ * @param {Object} options - Command options
164
+ * @param {boolean} [options.verbose] - Enable verbose output
165
+ * @returns {Promise<number>} Exit code
166
+ */
167
+ export async function configSetCommand(key, value, options = {}) {
168
+ setVerbose(options.verbose);
169
+
170
+ logger.debug('Starting config set command');
171
+ logger.debug(`Key: ${key}, value: ${value}, verbose: ${options.verbose}`);
172
+
173
+ try {
174
+ if (!key || typeof key !== 'string') {
175
+ logger.error('Configuration key is required');
176
+ logger.dim('Usage: gsd-opencode config set <key> <value>');
177
+ return ERROR_CODES.GENERAL_ERROR;
178
+ }
179
+
180
+ if (value === undefined || value === null) {
181
+ logger.error('Configuration value is required');
182
+ logger.dim('Usage: gsd-opencode config set <key> <value>');
183
+ return ERROR_CODES.GENERAL_ERROR;
184
+ }
185
+
186
+ const parsedValue = parseValue(String(value));
187
+ logger.debug(`Parsed value: ${JSON.stringify(parsedValue)} (type: ${typeof parsedValue})`);
188
+
189
+ const settings = new SettingsManager();
190
+ await settings.set(key, parsedValue);
191
+
192
+ logger.success(`Set ${key} = ${JSON.stringify(parsedValue)}`);
193
+
194
+ return ERROR_CODES.SUCCESS;
195
+
196
+ } catch (error) {
197
+ if (error.name === 'AbortPromptError' || error.message?.includes('cancel')) {
198
+ logger.info('\nCommand cancelled by user');
199
+ return ERROR_CODES.INTERRUPTED;
200
+ }
201
+
202
+ logger.error(`Failed to set configuration: ${error.message}`);
203
+
204
+ if (options.verbose) {
205
+ logger.debug(error.stack);
206
+ }
207
+
208
+ return ERROR_CODES.GENERAL_ERROR;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Resets configuration to defaults.
214
+ *
215
+ * @param {string} [key] - Specific key to reset, or omit to use --all
216
+ * @param {Object} options - Command options
217
+ * @param {boolean} [options.all] - Reset all settings to defaults
218
+ * @param {boolean} [options.verbose] - Enable verbose output
219
+ * @returns {Promise<number>} Exit code
220
+ */
221
+ export async function configResetCommand(key, options = {}) {
222
+ setVerbose(options.verbose);
223
+
224
+ logger.debug('Starting config reset command');
225
+ logger.debug(`Key: ${key}, all: ${options.all}, verbose: ${options.verbose}`);
226
+
227
+ try {
228
+ const settings = new SettingsManager();
229
+
230
+ if (options.all) {
231
+ await settings.reset();
232
+ logger.success('All configuration reset to defaults');
233
+ return ERROR_CODES.SUCCESS;
234
+ }
235
+
236
+ if (key && typeof key === 'string') {
237
+ const currentValue = await settings.get(key);
238
+ if (currentValue === undefined) {
239
+ logger.error(`Configuration key not found: ${key}`);
240
+ return ERROR_CODES.GENERAL_ERROR;
241
+ }
242
+
243
+ await settings.reset(key);
244
+ logger.success(`Reset ${key} to default`);
245
+ return ERROR_CODES.SUCCESS;
246
+ }
247
+
248
+ logger.error('Specify a configuration key or use --all to reset all settings');
249
+ logger.dim('Usage: gsd-opencode config reset <key>');
250
+ logger.dim(' or: gsd-opencode config reset --all');
251
+ return ERROR_CODES.GENERAL_ERROR;
252
+
253
+ } catch (error) {
254
+ if (error.name === 'AbortPromptError' || error.message?.includes('cancel')) {
255
+ logger.info('\nCommand cancelled by user');
256
+ return ERROR_CODES.INTERRUPTED;
257
+ }
258
+
259
+ logger.error(`Failed to reset configuration: ${error.message}`);
260
+
261
+ if (options.verbose) {
262
+ logger.debug(error.stack);
263
+ }
264
+
265
+ return ERROR_CODES.GENERAL_ERROR;
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Lists all configuration settings.
271
+ *
272
+ * @param {Object} options - Command options
273
+ * @param {boolean} [options.json] - Output as JSON
274
+ * @param {boolean} [options.verbose] - Enable verbose output
275
+ * @returns {Promise<number>} Exit code
276
+ */
277
+ export async function configListCommand(options = {}) {
278
+ setVerbose(options.verbose);
279
+
280
+ logger.debug('Starting config list command');
281
+ logger.debug(`json: ${options.json}, verbose: ${options.verbose}`);
282
+
283
+ try {
284
+ const settings = new SettingsManager();
285
+ const config = await settings.list();
286
+
287
+ if (options.json) {
288
+ console.log(JSON.stringify(config, null, 2));
289
+ } else {
290
+ logger.heading('Configuration');
291
+ logger.dim(`Source: ${settings.getConfigPath()}`);
292
+ logger.dim('');
293
+
294
+ const flatConfig = flattenObject(config);
295
+
296
+ if (Object.keys(flatConfig).length === 0) {
297
+ logger.dim('No configuration settings');
298
+ } else {
299
+ const keys = Object.keys(flatConfig);
300
+ const maxKeyLength = Math.max(...keys.map(k => k.length));
301
+
302
+ for (const key of keys.sort()) {
303
+ const value = flatConfig[key];
304
+ const paddedKey = key.padEnd(maxKeyLength);
305
+ const formattedValue = formatValue(value);
306
+ logger.dim(` ${paddedKey} ${formattedValue}`);
307
+ }
308
+ }
309
+ }
310
+
311
+ return ERROR_CODES.SUCCESS;
312
+
313
+ } catch (error) {
314
+ if (error.name === 'AbortPromptError' || error.message?.includes('cancel')) {
315
+ logger.info('\nCommand cancelled by user');
316
+ return ERROR_CODES.INTERRUPTED;
317
+ }
318
+
319
+ logger.error(`Failed to list configuration: ${error.message}`);
320
+
321
+ if (options.verbose) {
322
+ logger.debug(error.stack);
323
+ }
324
+
325
+ return ERROR_CODES.GENERAL_ERROR;
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Default export for the config commands.
331
+ */
332
+ export default {
333
+ configGetCommand,
334
+ configSetCommand,
335
+ configResetCommand,
336
+ configListCommand
337
+ };