gsd-opencode 1.20.1 → 1.20.3

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 (31) hide show
  1. package/commands/gsd/gsd-check-profile.md +30 -0
  2. package/commands/gsd/gsd-research-phase.md +2 -2
  3. package/get-shit-done/bin/gsd-oc-commands/check-oc-config-json.cjs +169 -0
  4. package/get-shit-done/bin/gsd-oc-commands/check-opencode-json.cjs +86 -0
  5. package/get-shit-done/bin/gsd-oc-commands/get-profile.cjs +117 -0
  6. package/get-shit-done/bin/gsd-oc-commands/set-profile.cjs +357 -0
  7. package/get-shit-done/bin/gsd-oc-commands/update-opencode-json.cjs +199 -0
  8. package/get-shit-done/bin/gsd-oc-commands/validate-models.cjs +75 -0
  9. package/get-shit-done/bin/gsd-oc-lib/oc-config.cjs +205 -0
  10. package/get-shit-done/bin/gsd-oc-lib/oc-core.cjs +113 -0
  11. package/get-shit-done/bin/gsd-oc-lib/oc-models.cjs +133 -0
  12. package/get-shit-done/bin/gsd-oc-lib/oc-profile-config.cjs +409 -0
  13. package/get-shit-done/bin/gsd-oc-tools.cjs +130 -0
  14. package/get-shit-done/bin/lib/oc-config.cjs +200 -0
  15. package/get-shit-done/bin/lib/oc-core.cjs +114 -0
  16. package/get-shit-done/bin/lib/oc-models.cjs +133 -0
  17. package/get-shit-done/bin/test/fixtures/oc-config-invalid.json +14 -0
  18. package/get-shit-done/bin/test/fixtures/oc-config-valid.json +22 -0
  19. package/get-shit-done/bin/test/get-profile.test.cjs +447 -0
  20. package/get-shit-done/bin/test/oc-profile-config.test.cjs +377 -0
  21. package/get-shit-done/bin/test/pivot-profile.test.cjs +276 -0
  22. package/get-shit-done/bin/test/set-profile.test.cjs +301 -0
  23. package/get-shit-done/workflows/diagnose-issues.md +1 -1
  24. package/get-shit-done/workflows/discuss-phase.md +1 -1
  25. package/get-shit-done/workflows/new-project.md +4 -4
  26. package/get-shit-done/workflows/oc-check-profile.md +181 -0
  27. package/get-shit-done/workflows/oc-set-profile.md +83 -243
  28. package/get-shit-done/workflows/plan-phase.md +4 -4
  29. package/get-shit-done/workflows/quick.md +1 -1
  30. package/get-shit-done/workflows/settings.md +4 -3
  31. package/package.json +2 -2
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: gsd-check-profile
3
+ description: Validate gsd-opencode profile configuration
4
+ allowed-tools:
5
+ - read
6
+ - bash
7
+ ---
8
+ <objective>
9
+ Validate gsd-opencode profile configuration across both `opencode.json` and `.planning/oc_config.json`, then report results.
10
+
11
+ Routes to the oc-check-profile workflow which handles:
12
+ - Validating all agent model IDs exist in the opencode models catalog
13
+ - Validating gsd-opencode profile structure and current profile exists
14
+ - Reporting results with severity classification (OK/WARNING/ERROR)
15
+ - Recommending /gsd-set-profile when issues are found
16
+ </objective>
17
+
18
+ <execution_context>
19
+ @~/.config/opencode/get-shit-done/workflows/oc-check-profile.md
20
+ </execution_context>
21
+
22
+ <process>
23
+ **Follow the oc-check-profile workflow** from `@~/.config/opencode/get-shit-done/workflows/oc-check-profile.md`.
24
+
25
+ The workflow handles all logic including:
26
+ 1. Running both validations (check-opencode-json and check-config-json)
27
+ 2. Classifying results by severity (OK/WARNING/ERROR)
28
+ 3. Reporting results with structured diagnostic output
29
+ 4. Recommending /gsd-set-profile when errors are found
30
+ </process>
@@ -136,7 +136,7 @@ write to: .planning/phases/${PHASE}-{slug}/${PHASE}-RESEARCH.md
136
136
  ```
137
137
  task(
138
138
  prompt="First, read ~/.config/opencode/agents/gsd-phase-researcher.md for your role and instructions.\n\n" + filled_prompt,
139
- subagent_type="task",
139
+ subagent_type="general",
140
140
  model="{researcher_model}",
141
141
  description="Research Phase {phase}"
142
142
  )
@@ -172,7 +172,7 @@ Continue research for Phase {phase_number}: {phase_name}
172
172
  ```
173
173
  task(
174
174
  prompt="First, read ~/.config/opencode/agents/gsd-phase-researcher.md for your role and instructions.\n\n" + continuation_prompt,
175
- subagent_type="task",
175
+ subagent_type="general",
176
176
  model="{researcher_model}",
177
177
  description="Continue research Phase {phase}"
178
178
  )
@@ -0,0 +1,169 @@
1
+ /**
2
+ * check-oc-config-json.cjs — Validate profile configuration in .planning/oc_config.json
3
+ *
4
+ * Command module that validates .planning/oc_config.json profile configuration.
5
+ * Validates:
6
+ * - current_oc_profile field exists and refers to a profile in profiles.presets
7
+ * - profiles.presets.{current_oc_profile} contains required keys: planning, execution, verification
8
+ * - All model IDs exist in opencode models catalog
9
+ * Outputs JSON envelope format with validation results.
10
+ *
11
+ * Usage: node check-oc-config-json.cjs [cwd]
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { output, error } = require('../gsd-oc-lib/oc-core.cjs');
17
+ const { getModelCatalog } = require('../gsd-oc-lib/oc-models.cjs');
18
+
19
+ /**
20
+ * Main command function
21
+ *
22
+ * @param {string} cwd - Current working directory
23
+ * @param {string[]} args - Command line arguments
24
+ */
25
+ function checkOcConfigJson(cwd, args) {
26
+ const verbose = args.includes('--verbose');
27
+ const configPath = path.join(cwd, '.planning', 'oc_config.json');
28
+
29
+ // Check if oc_config.json exists
30
+ if (!fs.existsSync(configPath)) {
31
+ error('.planning/oc_config.json not found', 'CONFIG_NOT_FOUND');
32
+ }
33
+
34
+ // read and parse config
35
+ let config;
36
+ try {
37
+ const content = fs.readFileSync(configPath, 'utf8');
38
+ config = JSON.parse(content);
39
+ } catch (err) {
40
+ if (err instanceof SyntaxError) {
41
+ error('.planning/oc_config.json is not valid JSON', 'INVALID_JSON');
42
+ }
43
+ error(`Failed to read config: ${err.message}`, 'READ_FAILED');
44
+ }
45
+
46
+ const issues = [];
47
+
48
+ // Extract profile information
49
+ const currentOcProfile = config.current_oc_profile;
50
+ const presets = config.profiles?.presets;
51
+
52
+ // Validate current_oc_profile field (required)
53
+ if (currentOcProfile === undefined) {
54
+ issues.push({
55
+ field: 'current_oc_profile',
56
+ value: '(missing)',
57
+ reason: 'current_oc_profile field is required'
58
+ });
59
+ } else if (presets && typeof presets === 'object' && !presets[currentOcProfile]) {
60
+ const availableProfiles = presets ? Object.keys(presets).join(', ') : 'none';
61
+ issues.push({
62
+ field: 'current_oc_profile',
63
+ value: currentOcProfile,
64
+ reason: `Profile "${currentOcProfile}" not found in profiles.presets. Available: ${availableProfiles}`
65
+ });
66
+ }
67
+
68
+ // Validate profiles.presets section exists
69
+ if (!presets || typeof presets !== 'object') {
70
+ issues.push({
71
+ field: 'profiles.presets',
72
+ value: '(missing or invalid)',
73
+ reason: 'profiles.presets section is required'
74
+ });
75
+ const result = {
76
+ success: false,
77
+ data: {
78
+ passed: false,
79
+ current_oc_profile: currentOcProfile || null,
80
+ profile_data: null,
81
+ issues
82
+ },
83
+ error: {
84
+ code: 'INVALID_PROFILE',
85
+ message: `${issues.length} invalid profile configuration(s) found`
86
+ }
87
+ };
88
+ output(result);
89
+ process.exit(1);
90
+ }
91
+
92
+ // Validate profile structure if current profile exists
93
+ if (currentOcProfile && presets[currentOcProfile]) {
94
+ const profile = presets[currentOcProfile];
95
+
96
+ // Validate that profile has required keys: planning, execution, verification
97
+ const requiredKeys = ['planning', 'execution', 'verification'];
98
+ for (const key of requiredKeys) {
99
+ if (profile[key] === undefined) {
100
+ issues.push({
101
+ field: `profiles.presets.${currentOcProfile}.${key}`,
102
+ value: '(missing)',
103
+ reason: `${key} model is required for ${currentOcProfile} profile`
104
+ });
105
+ } else if (typeof profile[key] !== 'string') {
106
+ issues.push({
107
+ field: `profiles.presets.${currentOcProfile}.${key}`,
108
+ value: profile[key],
109
+ reason: `${key} must be a string model ID`
110
+ });
111
+ }
112
+ }
113
+
114
+ // Validate model IDs against catalog
115
+ if (verbose) {
116
+ console.error('[verbose] Fetching model catalog...');
117
+ }
118
+
119
+ const catalogResult = getModelCatalog();
120
+ if (!catalogResult.success) {
121
+ error(catalogResult.error.message, catalogResult.error.code);
122
+ }
123
+
124
+ const validModels = catalogResult.models;
125
+
126
+ if (verbose) {
127
+ console.error(`[verbose] Found ${validModels.length} models in catalog`);
128
+ console.error('[verbose] Validating profile model IDs...');
129
+ }
130
+
131
+ for (const key of requiredKeys) {
132
+ if (profile[key] && typeof profile[key] === 'string') {
133
+ if (!validModels.includes(profile[key])) {
134
+ issues.push({
135
+ field: `profiles.presets.${currentOcProfile}.${key}`,
136
+ value: profile[key],
137
+ reason: `Model ID not found in opencode models catalog`
138
+ });
139
+ } else if (verbose) {
140
+ console.error(`[verbose] ✓ profiles.presets.${currentOcProfile}.${key}: ${profile[key]} (valid)`);
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ const passed = issues.length === 0;
147
+
148
+ const result = {
149
+ success: passed,
150
+ data: {
151
+ passed,
152
+ current_oc_profile: currentOcProfile || null,
153
+ profile_data: currentOcProfile && presets ? presets[currentOcProfile] : null,
154
+ issues
155
+ }
156
+ };
157
+
158
+ if (!passed) {
159
+ result.error = {
160
+ code: 'INVALID_PROFILE',
161
+ message: `${issues.length} invalid profile configuration(s) found`
162
+ };
163
+ }
164
+
165
+ output(result);
166
+ process.exit(passed ? 0 : 1);
167
+ }
168
+
169
+ module.exports = checkOcConfigJson;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * check-opencode-json.cjs — Validate model IDs in opencode.json
3
+ *
4
+ * Command module that validates opencode.json model IDs against the opencode models catalog.
5
+ * Outputs JSON envelope format with validation results.
6
+ *
7
+ * Usage: node check-opencode-json.cjs [cwd] [--verbose]
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { output, error } = require('../gsd-oc-lib/oc-core.cjs');
13
+ const { getModelCatalog, validateModelIds } = require('../gsd-oc-lib/oc-models.cjs');
14
+
15
+ /**
16
+ * Main command function
17
+ *
18
+ * @param {string} cwd - Current working directory
19
+ * @param {string[]} args - Command line arguments
20
+ */
21
+ function checkOpencodeJson(cwd, args) {
22
+ const verbose = args.includes('--verbose');
23
+ const opencodePath = path.join(cwd, 'opencode.json');
24
+
25
+ // Check if opencode.json exists
26
+ if (!fs.existsSync(opencodePath)) {
27
+ error('opencode.json not found in current directory', 'CONFIG_NOT_FOUND');
28
+ }
29
+
30
+ if (verbose) {
31
+ console.error(`[verbose] Validating: ${opencodePath}`);
32
+ }
33
+
34
+ // Fetch model catalog
35
+ if (verbose) {
36
+ console.error('[verbose] Fetching model catalog from opencode models...');
37
+ }
38
+
39
+ const catalogResult = getModelCatalog();
40
+ if (!catalogResult.success) {
41
+ error(catalogResult.error.message, catalogResult.error.code);
42
+ }
43
+
44
+ if (verbose) {
45
+ console.error(`[verbose] Found ${catalogResult.models.length} models in catalog`);
46
+ }
47
+
48
+ // Validate model IDs
49
+ if (verbose) {
50
+ console.error('[verbose] Validating model IDs...');
51
+ }
52
+
53
+ try {
54
+ const validationResult = validateModelIds(opencodePath, catalogResult.models);
55
+
56
+ const result = {
57
+ success: true,
58
+ data: validationResult
59
+ };
60
+
61
+ // Exit code based on validation result
62
+ if (validationResult.valid) {
63
+ output(result);
64
+ process.exit(0);
65
+ } else {
66
+ // Add error details for invalid models
67
+ result.error = {
68
+ code: 'INVALID_MODEL_ID',
69
+ message: `${validationResult.invalidCount} invalid model ID(s) found`
70
+ };
71
+ output(result);
72
+ process.exit(1);
73
+ }
74
+ } catch (err) {
75
+ if (err.message === 'CONFIG_NOT_FOUND') {
76
+ error('opencode.json not found', 'CONFIG_NOT_FOUND');
77
+ } else if (err.message === 'INVALID_JSON') {
78
+ error('opencode.json is not valid JSON', 'INVALID_JSON');
79
+ } else {
80
+ error(err.message, 'VALIDATION_FAILED');
81
+ }
82
+ }
83
+ }
84
+
85
+ // Export for use by main router
86
+ module.exports = checkOpencodeJson;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * get-profile.cjs — Retrieve profile definitions from oc_config.json
3
+ *
4
+ * Command module that exports getProfile(cwd, args) function with two operation modes:
5
+ * 1. No parameters: Returns current profile definition
6
+ * 2. Profile name parameter: Returns specified profile definition
7
+ *
8
+ * Output format: JSON envelope {success: true, data: {profileName: {planning, execution, verification}}}
9
+ * Flags: --raw (output raw JSON without envelope), --verbose (output diagnostics to stderr)
10
+ *
11
+ * Usage: node get-profile.cjs [profile-name] [--raw] [--verbose]
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { output, error } = require('../gsd-oc-lib/oc-core.cjs');
17
+ const { loadOcProfileConfig } = require('../gsd-oc-lib/oc-profile-config.cjs');
18
+
19
+ /**
20
+ * Main command function
21
+ *
22
+ * @param {string} cwd - Current working directory
23
+ * @param {string[]} args - Command line arguments
24
+ */
25
+ function getProfile(cwd, args) {
26
+ const verbose = args.includes('--verbose');
27
+ const raw = args.includes('--raw');
28
+ const log = verbose ? (...args) => console.error('[get-profile]', ...args) : () => {};
29
+
30
+ // Filter out flags to get profile name argument
31
+ const profileArgs = args.filter(arg => !arg.startsWith('--'));
32
+
33
+ // Check for too many arguments
34
+ if (profileArgs.length > 1) {
35
+ error('Too many arguments. Usage: get-profile [profile-name]', 'INVALID_ARGS');
36
+ }
37
+
38
+ const profileName = profileArgs.length > 0 ? profileArgs[0] : null;
39
+
40
+ log('Loading oc_config.json');
41
+
42
+ // Load oc_config.json
43
+ const loadResult = loadOcProfileConfig(cwd);
44
+ if (!loadResult.success) {
45
+ error(loadResult.error.message, loadResult.error.code);
46
+ }
47
+
48
+ const { config, configPath } = loadResult;
49
+
50
+ log(`Config loaded from ${configPath}`);
51
+
52
+ // ========== MODE 1: No parameters (get current profile) ==========
53
+ if (!profileName) {
54
+ log('Mode 1: Getting current profile');
55
+
56
+ // Check current_oc_profile is set
57
+ if (!config.current_oc_profile) {
58
+ error(
59
+ 'current_oc_profile not set in oc_config.json. Run set-profile first.',
60
+ 'MISSING_CURRENT_PROFILE'
61
+ );
62
+ }
63
+
64
+ const currentProfileName = config.current_oc_profile;
65
+ log(`Current profile: ${currentProfileName}`);
66
+
67
+ // Check profile exists in profiles.presets
68
+ const presets = config.profiles?.presets;
69
+ if (!presets || !presets[currentProfileName]) {
70
+ const availableProfiles = presets ? Object.keys(presets).join(', ') : 'none';
71
+ error(
72
+ `Current profile "${currentProfileName}" not found in profiles.presets. Available profiles: ${availableProfiles}`,
73
+ 'PROFILE_NOT_FOUND'
74
+ );
75
+ }
76
+
77
+ const profile = presets[currentProfileName];
78
+ const result = { [currentProfileName]: profile };
79
+
80
+ log(`Returning profile definition for "${currentProfileName}"`);
81
+
82
+ if (raw) {
83
+ output(result, true, JSON.stringify(result, null, 2));
84
+ } else {
85
+ output({ success: true, data: result });
86
+ }
87
+ process.exit(0);
88
+ }
89
+
90
+ // ========== MODE 2: Profile name parameter (get specific profile) ==========
91
+ log(`Mode 2: Getting profile "${profileName}"`);
92
+
93
+ // Check profile exists in profiles.presets
94
+ // Note: Does NOT require current_oc_profile to be set
95
+ const presets = config.profiles?.presets;
96
+ if (!presets || !presets[profileName]) {
97
+ const availableProfiles = presets ? Object.keys(presets).join(', ') : 'none';
98
+ error(
99
+ `Profile "${profileName}" not found in profiles.presets. Available profiles: ${availableProfiles}`,
100
+ 'PROFILE_NOT_FOUND'
101
+ );
102
+ }
103
+
104
+ const profile = presets[profileName];
105
+ const result = { [profileName]: profile };
106
+
107
+ log(`Returning profile definition for "${profileName}"`);
108
+
109
+ if (raw) {
110
+ output(result, true, JSON.stringify(result, null, 2));
111
+ } else {
112
+ output({ success: true, data: result });
113
+ }
114
+ process.exit(0);
115
+ }
116
+
117
+ module.exports = getProfile;