gsd-opencode 1.9.2 → 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 (58) hide show
  1. package/agents/gsd-debugger.md +5 -5
  2. package/bin/gsd-install.js +105 -0
  3. package/bin/gsd.js +352 -0
  4. package/{command → commands}/gsd/add-phase.md +1 -1
  5. package/{command → commands}/gsd/audit-milestone.md +1 -1
  6. package/{command → commands}/gsd/debug.md +3 -3
  7. package/{command → commands}/gsd/discuss-phase.md +1 -1
  8. package/{command → commands}/gsd/execute-phase.md +1 -1
  9. package/{command → commands}/gsd/list-phase-assumptions.md +1 -1
  10. package/{command → commands}/gsd/map-codebase.md +1 -1
  11. package/{command → commands}/gsd/new-milestone.md +1 -1
  12. package/{command → commands}/gsd/new-project.md +3 -3
  13. package/{command → commands}/gsd/plan-phase.md +2 -2
  14. package/{command → commands}/gsd/research-phase.md +1 -1
  15. package/{command → commands}/gsd/verify-work.md +1 -1
  16. package/get-shit-done/workflows/list-phase-assumptions.md +1 -1
  17. package/get-shit-done/workflows/verify-work.md +5 -5
  18. package/lib/constants.js +193 -0
  19. package/package.json +34 -20
  20. package/src/commands/check.js +329 -0
  21. package/src/commands/config.js +337 -0
  22. package/src/commands/install.js +608 -0
  23. package/src/commands/list.js +256 -0
  24. package/src/commands/repair.js +519 -0
  25. package/src/commands/uninstall.js +732 -0
  26. package/src/commands/update.js +444 -0
  27. package/src/services/backup-manager.js +585 -0
  28. package/src/services/config.js +262 -0
  29. package/src/services/file-ops.js +830 -0
  30. package/src/services/health-checker.js +475 -0
  31. package/src/services/manifest-manager.js +301 -0
  32. package/src/services/migration-service.js +831 -0
  33. package/src/services/repair-service.js +846 -0
  34. package/src/services/scope-manager.js +303 -0
  35. package/src/services/settings.js +553 -0
  36. package/src/services/structure-detector.js +240 -0
  37. package/src/services/update-service.js +863 -0
  38. package/src/utils/hash.js +71 -0
  39. package/src/utils/interactive.js +222 -0
  40. package/src/utils/logger.js +128 -0
  41. package/src/utils/npm-registry.js +255 -0
  42. package/src/utils/path-resolver.js +226 -0
  43. /package/{command → commands}/gsd/add-todo.md +0 -0
  44. /package/{command → commands}/gsd/check-todos.md +0 -0
  45. /package/{command → commands}/gsd/complete-milestone.md +0 -0
  46. /package/{command → commands}/gsd/help.md +0 -0
  47. /package/{command → commands}/gsd/insert-phase.md +0 -0
  48. /package/{command → commands}/gsd/pause-work.md +0 -0
  49. /package/{command → commands}/gsd/plan-milestone-gaps.md +0 -0
  50. /package/{command → commands}/gsd/progress.md +0 -0
  51. /package/{command → commands}/gsd/quick.md +0 -0
  52. /package/{command → commands}/gsd/remove-phase.md +0 -0
  53. /package/{command → commands}/gsd/resume-work.md +0 -0
  54. /package/{command → commands}/gsd/set-model.md +0 -0
  55. /package/{command → commands}/gsd/set-profile.md +0 -0
  56. /package/{command → commands}/gsd/settings.md +0 -0
  57. /package/{command → commands}/gsd/update.md +0 -0
  58. /package/{command → commands}/gsd/whats-new.md +0 -0
@@ -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
+ };