gsd-opencode 1.20.2 → 1.20.4
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/commands/gsd/gsd-check-profile.md +30 -0
- package/get-shit-done/bin/gsd-oc-commands/allow-read-config.cjs +235 -0
- package/get-shit-done/bin/gsd-oc-commands/check-oc-config-json.cjs +169 -0
- package/get-shit-done/bin/gsd-oc-commands/check-opencode-json.cjs +86 -0
- package/get-shit-done/bin/gsd-oc-commands/get-profile.cjs +117 -0
- package/get-shit-done/bin/gsd-oc-commands/set-profile.cjs +357 -0
- package/get-shit-done/bin/gsd-oc-commands/update-opencode-json.cjs +199 -0
- package/get-shit-done/bin/gsd-oc-commands/validate-models.cjs +75 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-config.cjs +205 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-core.cjs +113 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-models.cjs +133 -0
- package/get-shit-done/bin/gsd-oc-lib/oc-profile-config.cjs +409 -0
- package/get-shit-done/bin/gsd-oc-tools.cjs +136 -0
- package/get-shit-done/bin/lib/oc-config.cjs +200 -0
- package/get-shit-done/bin/lib/oc-core.cjs +114 -0
- package/get-shit-done/bin/lib/oc-models.cjs +133 -0
- package/get-shit-done/bin/test/allow-read-config.test.cjs +262 -0
- package/get-shit-done/bin/test/fixtures/oc-config-invalid.json +14 -0
- package/get-shit-done/bin/test/fixtures/oc-config-valid.json +22 -0
- package/get-shit-done/bin/test/get-profile.test.cjs +447 -0
- package/get-shit-done/bin/test/oc-profile-config.test.cjs +377 -0
- package/get-shit-done/bin/test/pivot-profile.test.cjs +276 -0
- package/get-shit-done/bin/test/set-profile.test.cjs +301 -0
- package/get-shit-done/workflows/oc-check-profile.md +181 -0
- package/get-shit-done/workflows/oc-set-profile.md +98 -234
- package/get-shit-done/workflows/settings.md +4 -3
- package/package.json +2 -2
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* oc-profile-config.cjs — Profile configuration operations for oc_config.json
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for loading, validating, and applying profiles from .planning/oc_config.json.
|
|
5
|
+
* Uses separate oc_config.json file (NOT config.json from Phase 15).
|
|
6
|
+
* Follows validate-then-modify pattern with atomic transactions.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { output, error: outputError, createBackup } = require('./oc-core.cjs');
|
|
12
|
+
const { getModelCatalog } = require('./oc-models.cjs');
|
|
13
|
+
const { applyProfileToOpencode } = require('./oc-config.cjs');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Error codes for oc_config.json operations
|
|
17
|
+
*/
|
|
18
|
+
const ERROR_CODES = {
|
|
19
|
+
CONFIG_NOT_FOUND: 'CONFIG_NOT_FOUND',
|
|
20
|
+
INVALID_JSON: 'INVALID_JSON',
|
|
21
|
+
PROFILE_NOT_FOUND: 'PROFILE_NOT_FOUND',
|
|
22
|
+
INVALID_MODELS: 'INVALID_MODELS',
|
|
23
|
+
INCOMPLETE_PROFILE: 'INCOMPLETE_PROFILE',
|
|
24
|
+
WRITE_FAILED: 'WRITE_FAILED',
|
|
25
|
+
APPLY_FAILED: 'APPLY_FAILED',
|
|
26
|
+
ROLLBACK_FAILED: 'ROLLBACK_FAILED'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Load oc_config.json from .planning directory
|
|
31
|
+
*
|
|
32
|
+
* @param {string} cwd - Current working directory
|
|
33
|
+
* @returns {Object} {success: true, config, configPath} or {success: false, error: {code, message}}
|
|
34
|
+
*/
|
|
35
|
+
function loadOcProfileConfig(cwd) {
|
|
36
|
+
try {
|
|
37
|
+
const configPath = path.join(cwd, '.planning', 'oc_config.json');
|
|
38
|
+
|
|
39
|
+
if (!fs.existsSync(configPath)) {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
error: {
|
|
43
|
+
code: ERROR_CODES.CONFIG_NOT_FOUND,
|
|
44
|
+
message: `.planning/oc_config.json not found at ${configPath}`
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
50
|
+
const config = JSON.parse(content);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
config,
|
|
55
|
+
configPath
|
|
56
|
+
};
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (err instanceof SyntaxError) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: {
|
|
62
|
+
code: ERROR_CODES.INVALID_JSON,
|
|
63
|
+
message: `Invalid JSON in oc_config.json: ${err.message}`
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: {
|
|
70
|
+
code: ERROR_CODES.CONFIG_NOT_FOUND,
|
|
71
|
+
message: `Failed to read oc_config.json: ${err.message}`
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate a profile definition against model whitelist and completeness requirements
|
|
79
|
+
*
|
|
80
|
+
* @param {Object} config - oc_config.json config object
|
|
81
|
+
* @param {string} profileName - Name of profile to validate
|
|
82
|
+
* @param {string[]} validModels - Array of valid model IDs (from getModelCatalog)
|
|
83
|
+
* @returns {Object} {valid: boolean, errors: [{code, message, field}]}
|
|
84
|
+
*/
|
|
85
|
+
function validateProfile(config, profileName, validModels) {
|
|
86
|
+
const errors = [];
|
|
87
|
+
|
|
88
|
+
// Check if profile exists in presets
|
|
89
|
+
const presets = config.profiles?.presets;
|
|
90
|
+
if (!presets || !presets[profileName]) {
|
|
91
|
+
errors.push({
|
|
92
|
+
code: ERROR_CODES.PROFILE_NOT_FOUND,
|
|
93
|
+
message: `Profile "${profileName}" not found in profiles.presets`,
|
|
94
|
+
field: 'profiles.presets'
|
|
95
|
+
});
|
|
96
|
+
return { valid: false, errors };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const profile = presets[profileName];
|
|
100
|
+
|
|
101
|
+
// Check for complete profile definition (all three keys required)
|
|
102
|
+
const requiredKeys = ['planning', 'execution', 'verification'];
|
|
103
|
+
const missingKeys = requiredKeys.filter(key => !profile[key]);
|
|
104
|
+
|
|
105
|
+
if (missingKeys.length > 0) {
|
|
106
|
+
errors.push({
|
|
107
|
+
code: ERROR_CODES.INCOMPLETE_PROFILE,
|
|
108
|
+
message: `Profile "${profileName}" is missing required keys: ${missingKeys.join(', ')}`,
|
|
109
|
+
field: 'profiles.presets.' + profileName,
|
|
110
|
+
missingKeys
|
|
111
|
+
});
|
|
112
|
+
// Return early - can't validate models if profile is incomplete
|
|
113
|
+
return { valid: false, errors };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Validate all models against whitelist
|
|
117
|
+
const invalidModels = [];
|
|
118
|
+
for (const key of requiredKeys) {
|
|
119
|
+
const modelId = profile[key];
|
|
120
|
+
if (!validModels.includes(modelId)) {
|
|
121
|
+
invalidModels.push({
|
|
122
|
+
key,
|
|
123
|
+
model: modelId,
|
|
124
|
+
reason: 'Model ID not found in opencode models catalog'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (invalidModels.length > 0) {
|
|
130
|
+
errors.push({
|
|
131
|
+
code: ERROR_CODES.INVALID_MODELS,
|
|
132
|
+
message: `Profile "${profileName}" contains ${invalidModels.length} invalid model ID(s)`,
|
|
133
|
+
field: 'profiles.presets.' + profileName,
|
|
134
|
+
invalidModels
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
valid: errors.length === 0,
|
|
140
|
+
errors
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Apply profile with full validation, backup, and atomic transaction
|
|
146
|
+
*
|
|
147
|
+
* @param {string} cwd - Current working directory
|
|
148
|
+
* @param {string} profileName - Name of profile to apply
|
|
149
|
+
* @param {Object} options - Options object
|
|
150
|
+
* @param {boolean} options.dryRun - If true, preview changes without modifications
|
|
151
|
+
* @param {boolean} options.verbose - If true, output progress to console.error
|
|
152
|
+
* @param {Object} options.inlineProfile - Optional inline profile definition to create/update
|
|
153
|
+
* @returns {Object} {success: true, data: {profile, models, backup, updated}} or {success: false, error}
|
|
154
|
+
*/
|
|
155
|
+
function applyProfileWithValidation(cwd, profileName, options = {}) {
|
|
156
|
+
const { dryRun = false, verbose = false, inlineProfile = null } = options;
|
|
157
|
+
const log = verbose ? (...args) => console.error('[oc-profile-config]', ...args) : () => {};
|
|
158
|
+
|
|
159
|
+
// Step 1: Load oc_config.json
|
|
160
|
+
const loadResult = loadOcProfileConfig(cwd);
|
|
161
|
+
if (!loadResult.success) {
|
|
162
|
+
return { success: false, error: loadResult.error };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const { config, configPath } = loadResult;
|
|
166
|
+
let targetProfileName = profileName;
|
|
167
|
+
let profileToUpdate;
|
|
168
|
+
|
|
169
|
+
// Step 2: Handle inline profile definition (Mode 3)
|
|
170
|
+
if (inlineProfile) {
|
|
171
|
+
log('Processing inline profile definition');
|
|
172
|
+
|
|
173
|
+
// Check if profile already exists
|
|
174
|
+
const presets = config.profiles?.presets || {};
|
|
175
|
+
if (presets[profileName] && !dryRun) {
|
|
176
|
+
return {
|
|
177
|
+
success: false,
|
|
178
|
+
error: {
|
|
179
|
+
code: 'PROFILE_EXISTS',
|
|
180
|
+
message: `Profile "${profileName}" already exists. Use a different name or remove --inline flag.`
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Validate inline profile has all required keys
|
|
186
|
+
const requiredKeys = ['planning', 'execution', 'verification'];
|
|
187
|
+
const missingKeys = requiredKeys.filter(key => !inlineProfile[key]);
|
|
188
|
+
|
|
189
|
+
if (missingKeys.length > 0) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: {
|
|
193
|
+
code: ERROR_CODES.INCOMPLETE_PROFILE,
|
|
194
|
+
message: `Inline profile is missing required keys: ${missingKeys.join(', ')}`,
|
|
195
|
+
missingKeys
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
profileToUpdate = inlineProfile;
|
|
201
|
+
} else {
|
|
202
|
+
// Step 2: Use existing profile from config
|
|
203
|
+
const presets = config.profiles?.presets;
|
|
204
|
+
if (!presets || !presets[profileName]) {
|
|
205
|
+
const availableProfiles = presets ? Object.keys(presets).join(', ') : 'none';
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
error: {
|
|
209
|
+
code: ERROR_CODES.PROFILE_NOT_FOUND,
|
|
210
|
+
message: `Profile "${profileName}" not found in profiles.presets. Available profiles: ${availableProfiles}`
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
profileToUpdate = presets[profileName];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Step 3: Get model catalog for validation
|
|
218
|
+
const catalogResult = getModelCatalog();
|
|
219
|
+
if (!catalogResult.success) {
|
|
220
|
+
return { success: false, error: catalogResult.error };
|
|
221
|
+
}
|
|
222
|
+
const validModels = catalogResult.models;
|
|
223
|
+
|
|
224
|
+
// Step 4: Validate profile models
|
|
225
|
+
const validation = validateProfile(
|
|
226
|
+
{ profiles: { presets: { [targetProfileName]: profileToUpdate } } },
|
|
227
|
+
targetProfileName,
|
|
228
|
+
validModels
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
if (!validation.valid) {
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
error: {
|
|
235
|
+
code: validation.errors[0].code,
|
|
236
|
+
message: validation.errors[0].message,
|
|
237
|
+
details: validation.errors
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
log('Profile validation passed');
|
|
243
|
+
|
|
244
|
+
// Step 5: Dry-run mode - return preview without modifications
|
|
245
|
+
if (dryRun) {
|
|
246
|
+
const opencodePath = path.join(cwd, 'opencode.json');
|
|
247
|
+
return {
|
|
248
|
+
success: true,
|
|
249
|
+
dryRun: true,
|
|
250
|
+
preview: {
|
|
251
|
+
profile: targetProfileName,
|
|
252
|
+
models: {
|
|
253
|
+
planning: profileToUpdate.planning,
|
|
254
|
+
execution: profileToUpdate.execution,
|
|
255
|
+
verification: profileToUpdate.verification
|
|
256
|
+
},
|
|
257
|
+
changes: {
|
|
258
|
+
oc_config: {
|
|
259
|
+
path: configPath,
|
|
260
|
+
updates: {
|
|
261
|
+
current_oc_profile: targetProfileName,
|
|
262
|
+
...(inlineProfile ? { 'profiles.presets': { [targetProfileName]: profileToUpdate } } : {})
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
opencode: {
|
|
266
|
+
path: opencodePath,
|
|
267
|
+
action: fs.existsSync(opencodePath) ? 'update' : 'create',
|
|
268
|
+
agentsToUpdate: getAgentsForProfile(profileToUpdate)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Step 6: Create backup of oc_config.json
|
|
276
|
+
log('Creating backup of oc_config.json');
|
|
277
|
+
const backupPath = createBackup(configPath, path.join(cwd, '.planning', 'backups'));
|
|
278
|
+
if (!backupPath) {
|
|
279
|
+
return {
|
|
280
|
+
success: false,
|
|
281
|
+
error: {
|
|
282
|
+
code: 'BACKUP_FAILED',
|
|
283
|
+
message: 'Failed to create backup of oc_config.json'
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Step 7: Update oc_config.json (atomic transaction start)
|
|
289
|
+
try {
|
|
290
|
+
// Update current_oc_profile
|
|
291
|
+
config.current_oc_profile = targetProfileName;
|
|
292
|
+
|
|
293
|
+
// Add inline profile if provided
|
|
294
|
+
if (inlineProfile) {
|
|
295
|
+
if (!config.profiles) config.profiles = {};
|
|
296
|
+
if (!config.profiles.presets) config.profiles.presets = {};
|
|
297
|
+
config.profiles.presets[targetProfileName] = inlineProfile;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// write updated oc_config.json
|
|
301
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
302
|
+
log('Updated oc_config.json');
|
|
303
|
+
} catch (err) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
error: {
|
|
307
|
+
code: ERROR_CODES.WRITE_FAILED,
|
|
308
|
+
message: `Failed to write oc_config.json: ${err.message}`
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Step 8: Apply to opencode.json
|
|
314
|
+
const opencodePath = path.join(cwd, 'opencode.json');
|
|
315
|
+
const applyResult = applyProfileToOpencode(opencodePath, configPath, targetProfileName);
|
|
316
|
+
|
|
317
|
+
if (!applyResult.success) {
|
|
318
|
+
// Step 9: Rollback oc_config.json on failure
|
|
319
|
+
log('Applying to opencode.json failed, rolling back');
|
|
320
|
+
try {
|
|
321
|
+
const backupContent = fs.readFileSync(backupPath, 'utf8');
|
|
322
|
+
fs.writeFileSync(configPath, backupContent, 'utf8');
|
|
323
|
+
return {
|
|
324
|
+
success: false,
|
|
325
|
+
error: {
|
|
326
|
+
code: ERROR_CODES.APPLY_FAILED,
|
|
327
|
+
message: applyResult.error.message,
|
|
328
|
+
rolledBack: true,
|
|
329
|
+
backupPath
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
} catch (rollbackErr) {
|
|
333
|
+
return {
|
|
334
|
+
success: false,
|
|
335
|
+
error: {
|
|
336
|
+
code: ERROR_CODES.ROLLBACK_FAILED,
|
|
337
|
+
message: `Failed to apply profile AND failed to rollback: ${rollbackErr.message}`,
|
|
338
|
+
originalError: applyResult.error,
|
|
339
|
+
backupPath
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
log('Successfully applied profile');
|
|
346
|
+
|
|
347
|
+
// Step 10: Return success with details
|
|
348
|
+
return {
|
|
349
|
+
success: true,
|
|
350
|
+
data: {
|
|
351
|
+
profile: targetProfileName,
|
|
352
|
+
models: {
|
|
353
|
+
planning: profileToUpdate.planning,
|
|
354
|
+
execution: profileToUpdate.execution,
|
|
355
|
+
verification: profileToUpdate.verification
|
|
356
|
+
},
|
|
357
|
+
backup: backupPath,
|
|
358
|
+
updated: applyResult.updated,
|
|
359
|
+
configPath
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get list of agent names that should be updated for a profile
|
|
366
|
+
* Helper function for dry-run preview
|
|
367
|
+
*
|
|
368
|
+
* @param {Object} profile - Profile object with planning/execution/verification
|
|
369
|
+
* @returns {Array} Array of {agent, model} objects
|
|
370
|
+
*/
|
|
371
|
+
function getAgentsForProfile(profile) {
|
|
372
|
+
const PROFILE_AGENT_MAPPING = {
|
|
373
|
+
planning: [
|
|
374
|
+
'gsd-planner',
|
|
375
|
+
'gsd-plan-checker',
|
|
376
|
+
'gsd-phase-researcher',
|
|
377
|
+
'gsd-roadmapper',
|
|
378
|
+
'gsd-project-researcher',
|
|
379
|
+
'gsd-research-synthesizer',
|
|
380
|
+
'gsd-codebase-mapper'
|
|
381
|
+
],
|
|
382
|
+
execution: [
|
|
383
|
+
'gsd-executor',
|
|
384
|
+
'gsd-debugger'
|
|
385
|
+
],
|
|
386
|
+
verification: [
|
|
387
|
+
'gsd-verifier',
|
|
388
|
+
'gsd-integration-checker'
|
|
389
|
+
]
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const agents = [];
|
|
393
|
+
for (const [category, agentNames] of Object.entries(PROFILE_AGENT_MAPPING)) {
|
|
394
|
+
if (profile[category]) {
|
|
395
|
+
for (const agentName of agentNames) {
|
|
396
|
+
agents.push({ agent: agentName, model: profile[category] });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return agents;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
module.exports = {
|
|
404
|
+
loadOcProfileConfig,
|
|
405
|
+
validateProfile,
|
|
406
|
+
applyProfileWithValidation,
|
|
407
|
+
getAgentsForProfile,
|
|
408
|
+
ERROR_CODES
|
|
409
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* gsd-oc-tools.cjs — Main CLI entry point for OpenCode tools
|
|
5
|
+
*
|
|
6
|
+
* Provides command routing for validation utilities and profile management.
|
|
7
|
+
* Follows gsd-tools.cjs architecture pattern.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node gsd-oc-tools.cjs <command> [args] [--raw] [--verbose]
|
|
10
|
+
*
|
|
11
|
+
* Available Commands:
|
|
12
|
+
* check-opencode-json Validate model IDs in opencode.json
|
|
13
|
+
* check-config-json Validate profile configuration in .planning/oc_config.json (migrated from config.json)
|
|
14
|
+
* check-oc-config-json Validate profile configuration in .planning/oc_config.json
|
|
15
|
+
* update-opencode-json Update opencode.json agent models from oc_config profile
|
|
16
|
+
* validate-models Validate model IDs against opencode catalog
|
|
17
|
+
* set-profile Switch profile with interactive model selection
|
|
18
|
+
* get-profile Get current profile or specific profile from oc_config.json
|
|
19
|
+
* allow-read-config Add external_directory permission to read GSD config folder
|
|
20
|
+
* help Show this help message
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const { output, error } = require('./gsd-oc-lib/oc-core.cjs');
|
|
25
|
+
|
|
26
|
+
// Parse command line arguments
|
|
27
|
+
const args = process.argv.slice(2);
|
|
28
|
+
const command = args[0];
|
|
29
|
+
const flags = args.slice(1);
|
|
30
|
+
|
|
31
|
+
const verbose = flags.includes('--verbose');
|
|
32
|
+
const raw = flags.includes('--raw');
|
|
33
|
+
|
|
34
|
+
// Current working directory
|
|
35
|
+
const cwd = process.cwd();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Show help message
|
|
39
|
+
*/
|
|
40
|
+
function showHelp() {
|
|
41
|
+
const helpText = `
|
|
42
|
+
gsd-oc-tools — OpenCode validation utilities
|
|
43
|
+
|
|
44
|
+
Usage: node gsd-oc-tools.cjs <command> [options]
|
|
45
|
+
|
|
46
|
+
Available Commands:
|
|
47
|
+
check-opencode-json Validate model IDs in opencode.json against opencode models catalog
|
|
48
|
+
check-config-json Validate profile configuration in .planning/oc_config.json (migrated from config.json)
|
|
49
|
+
check-oc-config-json Validate profile configuration in .planning/oc_config.json
|
|
50
|
+
update-opencode-json Update opencode.json agent models from oc_config profile (creates backup)
|
|
51
|
+
validate-models Validate one or more model IDs against opencode catalog
|
|
52
|
+
set-profile Switch profile with interactive model selection wizard
|
|
53
|
+
get-profile Get current profile or specific profile from oc_config.json
|
|
54
|
+
allow-read-config Add external_directory permission to read ~/.config/opencode/get-shit-done/**
|
|
55
|
+
help Show this help message
|
|
56
|
+
|
|
57
|
+
Options:
|
|
58
|
+
--verbose Enable verbose output (stderr)
|
|
59
|
+
--raw Output raw value instead of JSON envelope
|
|
60
|
+
--dry-run Preview changes without applying (update-opencode-json, allow-read-config)
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
node gsd-oc-tools.cjs check-opencode-json
|
|
64
|
+
node gsd-oc-tools.cjs check-config-json
|
|
65
|
+
node gsd-oc-tools.cjs update-opencode-json --dry-run
|
|
66
|
+
node gsd-oc-tools.cjs validate-models opencode/glm-4.7
|
|
67
|
+
node gsd-oc-tools.cjs set-profile genius
|
|
68
|
+
node gsd-oc-tools.cjs get-profile
|
|
69
|
+
node gsd-oc-tools.cjs get-profile genius
|
|
70
|
+
node gsd-oc-tools.cjs get-profile --raw
|
|
71
|
+
node gsd-oc-tools.cjs allow-read-config
|
|
72
|
+
node gsd-oc-tools.cjs allow-read-config --dry-run
|
|
73
|
+
`.trim();
|
|
74
|
+
|
|
75
|
+
console.log(helpText);
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Command routing
|
|
80
|
+
if (!command || command === 'help') {
|
|
81
|
+
showHelp();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
switch (command) {
|
|
85
|
+
case 'check-opencode-json': {
|
|
86
|
+
const checkOpencodeJson = require('./gsd-oc-commands/check-opencode-json.cjs');
|
|
87
|
+
checkOpencodeJson(cwd, flags);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case 'check-config-json': {
|
|
92
|
+
// Updated implementation: validates .planning/oc_config.json (migrated from old config.json format)
|
|
93
|
+
const checkOcConfigJson = require('./gsd-oc-commands/check-oc-config-json.cjs');
|
|
94
|
+
checkOcConfigJson(cwd, flags);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case 'check-oc-config-json': {
|
|
99
|
+
const checkOcConfigJson = require('./gsd-oc-commands/check-oc-config-json.cjs');
|
|
100
|
+
checkOcConfigJson(cwd, flags);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case 'update-opencode-json': {
|
|
105
|
+
const updateOpencodeJson = require('./gsd-oc-commands/update-opencode-json.cjs');
|
|
106
|
+
updateOpencodeJson(cwd, flags);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
case 'validate-models': {
|
|
111
|
+
const validateModels = require('./gsd-oc-commands/validate-models.cjs');
|
|
112
|
+
validateModels(cwd, flags);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'set-profile': {
|
|
117
|
+
const setProfile = require('./gsd-oc-commands/set-profile.cjs');
|
|
118
|
+
setProfile(cwd, flags);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case 'get-profile': {
|
|
123
|
+
const getProfile = require('./gsd-oc-commands/get-profile.cjs');
|
|
124
|
+
getProfile(cwd, flags);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case 'allow-read-config': {
|
|
129
|
+
const allowReadConfig = require('./gsd-oc-commands/allow-read-config.cjs');
|
|
130
|
+
allowReadConfig(cwd, flags);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
default:
|
|
135
|
+
error(`Unknown command: ${command}\nRun 'node gsd-oc-tools.cjs help' for available commands.`);
|
|
136
|
+
}
|