gsd-opencode 1.9.2 → 1.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/gsd-debugger.md +5 -5
- package/agents/gsd-settings.md +476 -30
- package/bin/gsd-install.js +105 -0
- package/bin/gsd.js +352 -0
- package/{command → commands}/gsd/add-phase.md +1 -1
- package/{command → commands}/gsd/audit-milestone.md +1 -1
- package/{command → commands}/gsd/debug.md +3 -3
- package/{command → commands}/gsd/discuss-phase.md +1 -1
- package/{command → commands}/gsd/execute-phase.md +1 -1
- package/{command → commands}/gsd/list-phase-assumptions.md +1 -1
- package/{command → commands}/gsd/map-codebase.md +1 -1
- package/{command → commands}/gsd/new-milestone.md +1 -1
- package/{command → commands}/gsd/new-project.md +3 -3
- package/{command → commands}/gsd/plan-phase.md +2 -2
- package/{command → commands}/gsd/research-phase.md +1 -1
- package/{command → commands}/gsd/verify-work.md +1 -1
- package/get-shit-done/workflows/list-phase-assumptions.md +1 -1
- package/get-shit-done/workflows/verify-work.md +5 -5
- package/lib/constants.js +199 -0
- package/package.json +34 -20
- package/src/commands/check.js +329 -0
- package/src/commands/config.js +337 -0
- package/src/commands/install.js +608 -0
- package/src/commands/list.js +256 -0
- package/src/commands/repair.js +519 -0
- package/src/commands/uninstall.js +732 -0
- package/src/commands/update.js +444 -0
- package/src/services/backup-manager.js +585 -0
- package/src/services/config.js +262 -0
- package/src/services/file-ops.js +855 -0
- package/src/services/health-checker.js +475 -0
- package/src/services/manifest-manager.js +301 -0
- package/src/services/migration-service.js +831 -0
- package/src/services/repair-service.js +846 -0
- package/src/services/scope-manager.js +303 -0
- package/src/services/settings.js +553 -0
- package/src/services/structure-detector.js +240 -0
- package/src/services/update-service.js +863 -0
- package/src/utils/hash.js +71 -0
- package/src/utils/interactive.js +222 -0
- package/src/utils/logger.js +128 -0
- package/src/utils/npm-registry.js +255 -0
- package/src/utils/path-resolver.js +226 -0
- /package/{command → commands}/gsd/add-todo.md +0 -0
- /package/{command → commands}/gsd/check-todos.md +0 -0
- /package/{command → commands}/gsd/complete-milestone.md +0 -0
- /package/{command → commands}/gsd/help.md +0 -0
- /package/{command → commands}/gsd/insert-phase.md +0 -0
- /package/{command → commands}/gsd/pause-work.md +0 -0
- /package/{command → commands}/gsd/plan-milestone-gaps.md +0 -0
- /package/{command → commands}/gsd/progress.md +0 -0
- /package/{command → commands}/gsd/quick.md +0 -0
- /package/{command → commands}/gsd/remove-phase.md +0 -0
- /package/{command → commands}/gsd/resume-work.md +0 -0
- /package/{command → commands}/gsd/set-model.md +0 -0
- /package/{command → commands}/gsd/set-profile.md +0 -0
- /package/{command → commands}/gsd/settings.md +0 -0
- /package/{command → commands}/gsd/update.md +0 -0
- /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
|
+
};
|