gsdd-cli 0.18.4 → 0.19.0
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/LICENSE +21 -21
- package/README.md +625 -607
- package/agents/DISTILLATION.md +421 -421
- package/agents/README.md +62 -62
- package/agents/approach-explorer.md +361 -361
- package/agents/debugger.md +82 -82
- package/agents/executor.md +394 -394
- package/agents/integration-checker.md +318 -318
- package/agents/mapper.md +103 -103
- package/agents/planner.md +313 -313
- package/agents/researcher.md +84 -84
- package/agents/roadmapper.md +296 -296
- package/agents/synthesizer.md +236 -236
- package/agents/verifier.md +337 -337
- package/bin/adapters/agents.mjs +34 -34
- package/bin/adapters/claude.mjs +191 -191
- package/bin/adapters/codex.mjs +85 -85
- package/bin/adapters/index.mjs +20 -20
- package/bin/adapters/opencode.mjs +278 -278
- package/bin/gsdd.mjs +123 -116
- package/bin/lib/cli-utils.mjs +28 -28
- package/bin/lib/evidence-contract.mjs +112 -112
- package/bin/lib/file-ops.mjs +209 -161
- package/bin/lib/health-truth.mjs +181 -178
- package/bin/lib/health.mjs +265 -235
- package/bin/lib/init-flow.mjs +252 -236
- package/bin/lib/init-prompts.mjs +248 -247
- package/bin/lib/init-runtime.mjs +225 -212
- package/bin/lib/init.mjs +17 -17
- package/bin/lib/lifecycle-preflight.mjs +360 -333
- package/bin/lib/lifecycle-state.mjs +351 -267
- package/bin/lib/manifest.mjs +116 -114
- package/bin/lib/models.mjs +411 -411
- package/bin/lib/phase.mjs +397 -378
- package/bin/lib/plan-constants.mjs +30 -30
- package/bin/lib/provenance.mjs +109 -106
- package/bin/lib/rendering.mjs +178 -130
- package/bin/lib/runtime-freshness.mjs +221 -219
- package/bin/lib/templates.mjs +225 -224
- package/bin/lib/workspace-root.mjs +132 -0
- package/distilled/DESIGN.md +2347 -2327
- package/distilled/EVIDENCE-INDEX.md +397 -394
- package/distilled/README.md +196 -193
- package/distilled/SKILL.md +86 -85
- package/distilled/templates/agents.block.md +21 -21
- package/distilled/templates/agents.md +6 -6
- package/distilled/templates/approach.md +232 -232
- package/distilled/templates/auth-matrix.md +78 -78
- package/distilled/templates/brownfield-change/CHANGE.md +99 -0
- package/distilled/templates/brownfield-change/HANDOFF.md +38 -0
- package/distilled/templates/brownfield-change/VERIFICATION.md +56 -0
- package/distilled/templates/codebase/architecture.md +110 -110
- package/distilled/templates/codebase/concerns.md +95 -95
- package/distilled/templates/codebase/conventions.md +193 -193
- package/distilled/templates/codebase/stack.md +96 -96
- package/distilled/templates/delegates/approach-explorer.md +25 -25
- package/distilled/templates/delegates/mapper-arch.md +26 -26
- package/distilled/templates/delegates/mapper-concerns.md +27 -27
- package/distilled/templates/delegates/mapper-quality.md +28 -28
- package/distilled/templates/delegates/mapper-tech.md +25 -25
- package/distilled/templates/delegates/plan-checker.md +68 -68
- package/distilled/templates/delegates/researcher-architecture.md +30 -30
- package/distilled/templates/delegates/researcher-features.md +30 -30
- package/distilled/templates/delegates/researcher-pitfalls.md +30 -30
- package/distilled/templates/delegates/researcher-stack.md +30 -30
- package/distilled/templates/delegates/researcher-synthesizer.md +31 -31
- package/distilled/templates/research/architecture.md +57 -57
- package/distilled/templates/research/features.md +23 -23
- package/distilled/templates/research/pitfalls.md +46 -46
- package/distilled/templates/research/stack.md +45 -45
- package/distilled/templates/research/summary.md +67 -67
- package/distilled/templates/roadmap.md +74 -62
- package/distilled/templates/spec.md +110 -110
- package/distilled/workflows/audit-milestone.md +275 -271
- package/distilled/workflows/complete-milestone.md +336 -332
- package/distilled/workflows/execute.md +454 -449
- package/distilled/workflows/map-codebase.md +253 -253
- package/distilled/workflows/new-milestone.md +242 -238
- package/distilled/workflows/new-project.md +398 -398
- package/distilled/workflows/pause.md +160 -156
- package/distilled/workflows/plan-milestone-gaps.md +183 -183
- package/distilled/workflows/plan.md +451 -447
- package/distilled/workflows/progress.md +227 -223
- package/distilled/workflows/quick.md +351 -347
- package/distilled/workflows/resume.md +220 -212
- package/distilled/workflows/verify-work.md +260 -260
- package/distilled/workflows/verify.md +431 -429
- package/docs/BROWNFIELD-PROOF.md +95 -95
- package/docs/RUNTIME-SUPPORT.md +93 -75
- package/docs/USER-GUIDE.md +440 -399
- package/docs/VERIFICATION-DISCIPLINE.md +59 -59
- package/docs/claude/context-monitor.md +98 -98
- package/docs/proof/consumer-node-cli/README.md +37 -37
- package/docs/proof/consumer-node-cli/ROADMAP.md +14 -14
- package/docs/proof/consumer-node-cli/SPEC.md +17 -17
- package/docs/proof/consumer-node-cli/brief.md +9 -9
- package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-PLAN.md +34 -34
- package/docs/proof/consumer-node-cli/phases/01-foundation/01-01-SUMMARY.md +10 -10
- package/docs/proof/consumer-node-cli/phases/01-foundation/01-VERIFICATION.md +30 -30
- package/package.json +62 -61
package/bin/lib/models.mjs
CHANGED
|
@@ -1,411 +1,411 @@
|
|
|
1
|
-
// models.mjs — Model profile management, config CRUD, and validation constants
|
|
2
|
-
//
|
|
3
|
-
// IMPORTANT: No module-scope process.cwd() — ESM caching means sub-modules
|
|
4
|
-
// evaluate once, so CWD must be computed inside function bodies.
|
|
5
|
-
|
|
6
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
7
|
-
import { join } from 'path';
|
|
8
|
-
import { CLAUDE_MODEL_PROFILES } from '../adapters/claude.mjs';
|
|
9
|
-
import { detectOpenCodeConfiguredModel } from '../adapters/opencode.mjs';
|
|
10
|
-
import { parseFlagValue, output } from './cli-utils.mjs';
|
|
11
|
-
|
|
12
|
-
export const DEFAULT_GIT_PROTOCOL = {
|
|
13
|
-
branch: 'Follow the existing repo or team branching convention. Use a feature branch for significant changes when no convention exists.',
|
|
14
|
-
commit: 'Group changes logically and follow the existing repo conventions. Do not mention phase, plan, or task IDs unless explicitly requested.',
|
|
15
|
-
pr: 'Follow the existing repo or team review workflow. Do not assume PR creation, timing, or naming unless explicitly requested.',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const RIGOR_PROFILES = {
|
|
19
|
-
quick: { researchDepth: 'fast', workflow: { research: false, discuss: false, planCheck: false, verifier: true } },
|
|
20
|
-
balanced: { researchDepth: 'balanced', workflow: { research: true, discuss: false, planCheck: true, verifier: true } },
|
|
21
|
-
thorough: { researchDepth: 'deep', workflow: { research: true, discuss: true, planCheck: true, verifier: true } },
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const COST_PROFILES = {
|
|
25
|
-
budget: { modelProfile: 'budget', parallelization: false },
|
|
26
|
-
balanced: { modelProfile: 'balanced', parallelization: true },
|
|
27
|
-
quality: { modelProfile: 'quality', parallelization: true },
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export function resolveRigor(id) { return RIGOR_PROFILES[id] ?? RIGOR_PROFILES.balanced; }
|
|
31
|
-
export function resolveCost(id) { return COST_PROFILES[id] ?? COST_PROFILES.balanced; }
|
|
32
|
-
|
|
33
|
-
export const VALID_MODEL_PROFILES = ['quality', 'balanced', 'budget'];
|
|
34
|
-
export const PORTABLE_AGENT_IDS = ['plan-checker', 'approach-explorer'];
|
|
35
|
-
export const MODEL_RUNTIME_IDS = ['claude', 'opencode', 'codex'];
|
|
36
|
-
export const MODEL_ID_PATTERN = /^[a-zA-Z0-9._\/:@-]+$/;
|
|
37
|
-
|
|
38
|
-
export function normalizeModelProfile(value) {
|
|
39
|
-
return VALID_MODEL_PROFILES.includes(value) ? value : 'balanced';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function buildDefaultConfig({ autoAdvance = false } = {}) {
|
|
43
|
-
const rigor = resolveRigor('balanced');
|
|
44
|
-
const cost = resolveCost('balanced');
|
|
45
|
-
const config = {
|
|
46
|
-
...rigor,
|
|
47
|
-
...cost,
|
|
48
|
-
commitDocs: true,
|
|
49
|
-
gitProtocol: { ...DEFAULT_GIT_PROTOCOL },
|
|
50
|
-
initVersion: 'v1.1',
|
|
51
|
-
};
|
|
52
|
-
if (autoAdvance) config.autoAdvance = true;
|
|
53
|
-
return config;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function isProjectInitialized(cwd = process.cwd()) {
|
|
57
|
-
return existsSync(join(cwd, '.planning', 'config.json'));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function loadProjectModelConfig(cwd = process.cwd()) {
|
|
61
|
-
const configPath = join(cwd, '.planning', 'config.json');
|
|
62
|
-
if (!existsSync(configPath)) return buildDefaultConfig();
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
return {
|
|
66
|
-
...buildDefaultConfig(),
|
|
67
|
-
...JSON.parse(readFileSync(configPath, 'utf-8')),
|
|
68
|
-
};
|
|
69
|
-
} catch (e) {
|
|
70
|
-
console.error(`WARNING: .planning/config.json is malformed (${e.message}). Using defaults.`);
|
|
71
|
-
return buildDefaultConfig();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function loadConfigForMutation(cwd = process.cwd()) {
|
|
76
|
-
const configPath = join(cwd, '.planning', 'config.json');
|
|
77
|
-
let raw;
|
|
78
|
-
try {
|
|
79
|
-
raw = readFileSync(configPath, 'utf-8');
|
|
80
|
-
} catch (e) {
|
|
81
|
-
return { ok: false, error: `could not read config file (${e.message})` };
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
return { ok: true, config: { ...buildDefaultConfig(), ...JSON.parse(raw) } };
|
|
85
|
-
} catch (e) {
|
|
86
|
-
return { ok: false, error: `malformed JSON (${e.message})` };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function ensureProjectConfig(cwd = process.cwd()) {
|
|
91
|
-
mkdirSync(join(cwd, '.planning'), { recursive: true });
|
|
92
|
-
const config = loadProjectModelConfig(cwd);
|
|
93
|
-
writeProjectConfig(config, cwd);
|
|
94
|
-
return config;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function writeProjectConfig(config, cwd = process.cwd()) {
|
|
98
|
-
const configPath = join(cwd, '.planning', 'config.json');
|
|
99
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function getPortableAgentProfile(config, agentId) {
|
|
103
|
-
const override = config.agentModelProfiles?.[agentId];
|
|
104
|
-
if (VALID_MODEL_PROFILES.includes(override)) return override;
|
|
105
|
-
return normalizeModelProfile(config.modelProfile);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function getRuntimeModelOverride(config, runtime, agentId) {
|
|
109
|
-
const override = config.runtimeModelOverrides?.[runtime]?.[agentId];
|
|
110
|
-
return typeof override === 'string' && override.trim() ? override.trim() : null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function resolveRuntimeAgentModel({ cwd = process.cwd(), runtime, agentId, profileMap = null }) {
|
|
114
|
-
const config = loadProjectModelConfig(cwd);
|
|
115
|
-
const runtimeOverride = getRuntimeModelOverride(config, runtime, agentId);
|
|
116
|
-
if (runtimeOverride) return runtimeOverride;
|
|
117
|
-
|
|
118
|
-
if (!profileMap) return null;
|
|
119
|
-
const profile = getPortableAgentProfile(config, agentId);
|
|
120
|
-
return profileMap[profile] ?? profileMap.balanced ?? null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function getRuntimeAgentModelState({ config, runtime, agentId, profileMap = null }) {
|
|
124
|
-
const runtimeOverride = getRuntimeModelOverride(config, runtime, agentId);
|
|
125
|
-
if (runtimeOverride) {
|
|
126
|
-
return {
|
|
127
|
-
mode: 'override',
|
|
128
|
-
model: runtimeOverride,
|
|
129
|
-
source: 'runtimeOverride',
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (!profileMap) {
|
|
134
|
-
return {
|
|
135
|
-
mode: 'inherit',
|
|
136
|
-
model: null,
|
|
137
|
-
runtimeDetectedModel: null,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const agentOverride = config.agentModelProfiles?.[agentId];
|
|
142
|
-
const profile = getPortableAgentProfile(config, agentId);
|
|
143
|
-
return {
|
|
144
|
-
mode: 'mapped',
|
|
145
|
-
model: profileMap[profile] ?? profileMap.balanced ?? null,
|
|
146
|
-
source: VALID_MODEL_PROFILES.includes(agentOverride) ? 'agentModelProfile' : 'modelProfile',
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function cmdModels(...modelArgs) {
|
|
151
|
-
const subcommand = modelArgs[0] || 'show';
|
|
152
|
-
|
|
153
|
-
switch (subcommand) {
|
|
154
|
-
case 'show':
|
|
155
|
-
return cmdModelsShow();
|
|
156
|
-
case 'profile':
|
|
157
|
-
return cmdModelsProfile(modelArgs[1]);
|
|
158
|
-
case 'agent-profile':
|
|
159
|
-
return cmdModelsAgentProfile(modelArgs.slice(1));
|
|
160
|
-
case 'clear-agent-profile':
|
|
161
|
-
return cmdModelsClearAgentProfile(modelArgs.slice(1));
|
|
162
|
-
case 'set':
|
|
163
|
-
return cmdModelsSetRuntimeOverride(modelArgs.slice(1));
|
|
164
|
-
case 'clear':
|
|
165
|
-
return cmdModelsClearRuntimeOverride(modelArgs.slice(1));
|
|
166
|
-
default:
|
|
167
|
-
console.error('Usage: gsdd models [show|profile|agent-profile|clear-agent-profile|set|clear]');
|
|
168
|
-
process.exitCode = 1;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function cmdModelsShow() {
|
|
173
|
-
const cwd = process.cwd();
|
|
174
|
-
const config = loadProjectModelConfig(cwd);
|
|
175
|
-
const ocCheckerOverride = getRuntimeModelOverride(config, 'opencode', 'plan-checker');
|
|
176
|
-
const ocExplorerOverride = getRuntimeModelOverride(config, 'opencode', 'approach-explorer');
|
|
177
|
-
const ocDetected = detectOpenCodeConfiguredModel(cwd);
|
|
178
|
-
const codexCheckerOverride = getRuntimeModelOverride(config, 'codex', 'plan-checker');
|
|
179
|
-
const codexExplorerOverride = getRuntimeModelOverride(config, 'codex', 'approach-explorer');
|
|
180
|
-
output({
|
|
181
|
-
modelProfile: normalizeModelProfile(config.modelProfile),
|
|
182
|
-
agentModelProfiles: config.agentModelProfiles || {},
|
|
183
|
-
runtimeModelOverrides: config.runtimeModelOverrides || {},
|
|
184
|
-
effective: {
|
|
185
|
-
claude: {
|
|
186
|
-
'plan-checker': getRuntimeAgentModelState({
|
|
187
|
-
config,
|
|
188
|
-
runtime: 'claude',
|
|
189
|
-
agentId: 'plan-checker',
|
|
190
|
-
profileMap: CLAUDE_MODEL_PROFILES,
|
|
191
|
-
}),
|
|
192
|
-
'approach-explorer': getRuntimeAgentModelState({
|
|
193
|
-
config,
|
|
194
|
-
runtime: 'claude',
|
|
195
|
-
agentId: 'approach-explorer',
|
|
196
|
-
profileMap: CLAUDE_MODEL_PROFILES,
|
|
197
|
-
}),
|
|
198
|
-
},
|
|
199
|
-
opencode: {
|
|
200
|
-
'plan-checker': {
|
|
201
|
-
mode: ocCheckerOverride ? 'override' : 'inherit',
|
|
202
|
-
model: ocCheckerOverride,
|
|
203
|
-
runtimeDetectedModel: ocDetected,
|
|
204
|
-
},
|
|
205
|
-
'approach-explorer': {
|
|
206
|
-
mode: ocExplorerOverride ? 'override' : 'inherit',
|
|
207
|
-
model: ocExplorerOverride,
|
|
208
|
-
runtimeDetectedModel: ocDetected,
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
codex: {
|
|
212
|
-
'plan-checker': {
|
|
213
|
-
mode: codexCheckerOverride ? 'override' : 'inherit',
|
|
214
|
-
model: codexCheckerOverride,
|
|
215
|
-
},
|
|
216
|
-
'approach-explorer': {
|
|
217
|
-
mode: codexExplorerOverride ? 'override' : 'inherit',
|
|
218
|
-
model: codexExplorerOverride,
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
detectedRuntimeModels: {
|
|
223
|
-
opencode: ocDetected,
|
|
224
|
-
},
|
|
225
|
-
hints: (!ocCheckerOverride || !ocExplorerOverride) ? {
|
|
226
|
-
opencode: 'OpenCode currently inherits its runtime model unless you set an explicit override. Use gsdd models set --runtime opencode --agent <agent-id> --model <provider/model-id> to inject an explicit agent model.',
|
|
227
|
-
} : undefined,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function cmdModelsProfile(profile) {
|
|
232
|
-
if (!VALID_MODEL_PROFILES.includes(profile)) {
|
|
233
|
-
console.error(`ERROR: Invalid profile "${profile}". Valid profiles: ${VALID_MODEL_PROFILES.join(', ')}`);
|
|
234
|
-
process.exitCode = 1;
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (!isProjectInitialized()) {
|
|
239
|
-
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
240
|
-
process.exitCode = 1;
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const result = loadConfigForMutation();
|
|
245
|
-
if (!result.ok) {
|
|
246
|
-
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
247
|
-
process.exitCode = 1;
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
result.config.modelProfile = profile;
|
|
252
|
-
writeProjectConfig(result.config);
|
|
253
|
-
console.log(` - set modelProfile to ${profile}`);
|
|
254
|
-
console.log(' Run gsdd update to regenerate adapter files.');
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function cmdModelsAgentProfile(args) {
|
|
258
|
-
const agent = parseFlagValue(args, '--agent').value;
|
|
259
|
-
const profile = parseFlagValue(args, '--profile').value;
|
|
260
|
-
|
|
261
|
-
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
262
|
-
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
263
|
-
process.exitCode = 1;
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (!VALID_MODEL_PROFILES.includes(profile)) {
|
|
267
|
-
console.error(`ERROR: Invalid profile "${profile}". Valid profiles: ${VALID_MODEL_PROFILES.join(', ')}`);
|
|
268
|
-
process.exitCode = 1;
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (!isProjectInitialized()) {
|
|
273
|
-
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
274
|
-
process.exitCode = 1;
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const result = loadConfigForMutation();
|
|
279
|
-
if (!result.ok) {
|
|
280
|
-
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
281
|
-
process.exitCode = 1;
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
result.config.agentModelProfiles = result.config.agentModelProfiles || {};
|
|
286
|
-
result.config.agentModelProfiles[agent] = profile;
|
|
287
|
-
writeProjectConfig(result.config);
|
|
288
|
-
console.log(` - set ${agent} semantic profile to ${profile}`);
|
|
289
|
-
console.log(' Run gsdd update to regenerate adapter files.');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function cmdModelsClearAgentProfile(args) {
|
|
293
|
-
const agent = parseFlagValue(args, '--agent').value;
|
|
294
|
-
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
295
|
-
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
296
|
-
process.exitCode = 1;
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (!isProjectInitialized()) {
|
|
301
|
-
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
302
|
-
process.exitCode = 1;
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const result = loadConfigForMutation();
|
|
307
|
-
if (!result.ok) {
|
|
308
|
-
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
309
|
-
process.exitCode = 1;
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (result.config.agentModelProfiles) {
|
|
314
|
-
delete result.config.agentModelProfiles[agent];
|
|
315
|
-
if (Object.keys(result.config.agentModelProfiles).length === 0) {
|
|
316
|
-
delete result.config.agentModelProfiles;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
writeProjectConfig(result.config);
|
|
320
|
-
console.log(` - cleared semantic profile override for ${agent}`);
|
|
321
|
-
console.log(' Run gsdd update to regenerate adapter files.');
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function cmdModelsSetRuntimeOverride(args) {
|
|
325
|
-
const runtime = parseFlagValue(args, '--runtime').value;
|
|
326
|
-
const agent = parseFlagValue(args, '--agent').value;
|
|
327
|
-
const model = parseFlagValue(args, '--model').value;
|
|
328
|
-
|
|
329
|
-
if (!MODEL_RUNTIME_IDS.includes(runtime)) {
|
|
330
|
-
console.error(`ERROR: Invalid runtime "${runtime}". Valid runtimes: ${MODEL_RUNTIME_IDS.join(', ')}`);
|
|
331
|
-
process.exitCode = 1;
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
335
|
-
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
336
|
-
process.exitCode = 1;
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
if (!model) {
|
|
340
|
-
console.error('ERROR: --model requires a value.');
|
|
341
|
-
process.exitCode = 1;
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
if (!MODEL_ID_PATTERN.test(model.trim())) {
|
|
345
|
-
console.error('ERROR: Model ID contains invalid characters. Only alphanumeric, dots, hyphens, underscores, forward slashes, colons, and @ are allowed.');
|
|
346
|
-
process.exitCode = 1;
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (!isProjectInitialized()) {
|
|
351
|
-
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
352
|
-
process.exitCode = 1;
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const result = loadConfigForMutation();
|
|
357
|
-
if (!result.ok) {
|
|
358
|
-
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
359
|
-
process.exitCode = 1;
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
result.config.runtimeModelOverrides = result.config.runtimeModelOverrides || {};
|
|
364
|
-
result.config.runtimeModelOverrides[runtime] = result.config.runtimeModelOverrides[runtime] || {};
|
|
365
|
-
result.config.runtimeModelOverrides[runtime][agent] = model.trim();
|
|
366
|
-
writeProjectConfig(result.config);
|
|
367
|
-
console.log(` - set ${runtime} runtime override for ${agent}`);
|
|
368
|
-
console.log(' Run gsdd update to regenerate adapter files.');
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function cmdModelsClearRuntimeOverride(args) {
|
|
372
|
-
const runtime = parseFlagValue(args, '--runtime').value;
|
|
373
|
-
const agent = parseFlagValue(args, '--agent').value;
|
|
374
|
-
|
|
375
|
-
if (!MODEL_RUNTIME_IDS.includes(runtime)) {
|
|
376
|
-
console.error(`ERROR: Invalid runtime "${runtime}". Valid runtimes: ${MODEL_RUNTIME_IDS.join(', ')}`);
|
|
377
|
-
process.exitCode = 1;
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
381
|
-
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
382
|
-
process.exitCode = 1;
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (!isProjectInitialized()) {
|
|
387
|
-
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
388
|
-
process.exitCode = 1;
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const result = loadConfigForMutation();
|
|
393
|
-
if (!result.ok) {
|
|
394
|
-
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
395
|
-
process.exitCode = 1;
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (result.config.runtimeModelOverrides?.[runtime]) {
|
|
400
|
-
delete result.config.runtimeModelOverrides[runtime][agent];
|
|
401
|
-
if (Object.keys(result.config.runtimeModelOverrides[runtime]).length === 0) {
|
|
402
|
-
delete result.config.runtimeModelOverrides[runtime];
|
|
403
|
-
}
|
|
404
|
-
if (Object.keys(result.config.runtimeModelOverrides).length === 0) {
|
|
405
|
-
delete result.config.runtimeModelOverrides;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
writeProjectConfig(result.config);
|
|
409
|
-
console.log(` - cleared ${runtime} runtime override for ${agent}`);
|
|
410
|
-
console.log(' Run gsdd update to regenerate adapter files.');
|
|
411
|
-
}
|
|
1
|
+
// models.mjs — Model profile management, config CRUD, and validation constants
|
|
2
|
+
//
|
|
3
|
+
// IMPORTANT: No module-scope process.cwd() — ESM caching means sub-modules
|
|
4
|
+
// evaluate once, so CWD must be computed inside function bodies.
|
|
5
|
+
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { CLAUDE_MODEL_PROFILES } from '../adapters/claude.mjs';
|
|
9
|
+
import { detectOpenCodeConfiguredModel } from '../adapters/opencode.mjs';
|
|
10
|
+
import { parseFlagValue, output } from './cli-utils.mjs';
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_GIT_PROTOCOL = {
|
|
13
|
+
branch: 'Follow the existing repo or team branching convention. Use a feature branch for significant changes when no convention exists.',
|
|
14
|
+
commit: 'Group changes logically and follow the existing repo conventions. Do not mention phase, plan, or task IDs unless explicitly requested.',
|
|
15
|
+
pr: 'Follow the existing repo or team review workflow. Do not assume PR creation, timing, or naming unless explicitly requested.',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const RIGOR_PROFILES = {
|
|
19
|
+
quick: { researchDepth: 'fast', workflow: { research: false, discuss: false, planCheck: false, verifier: true } },
|
|
20
|
+
balanced: { researchDepth: 'balanced', workflow: { research: true, discuss: false, planCheck: true, verifier: true } },
|
|
21
|
+
thorough: { researchDepth: 'deep', workflow: { research: true, discuss: true, planCheck: true, verifier: true } },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const COST_PROFILES = {
|
|
25
|
+
budget: { modelProfile: 'budget', parallelization: false },
|
|
26
|
+
balanced: { modelProfile: 'balanced', parallelization: true },
|
|
27
|
+
quality: { modelProfile: 'quality', parallelization: true },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function resolveRigor(id) { return RIGOR_PROFILES[id] ?? RIGOR_PROFILES.balanced; }
|
|
31
|
+
export function resolveCost(id) { return COST_PROFILES[id] ?? COST_PROFILES.balanced; }
|
|
32
|
+
|
|
33
|
+
export const VALID_MODEL_PROFILES = ['quality', 'balanced', 'budget'];
|
|
34
|
+
export const PORTABLE_AGENT_IDS = ['plan-checker', 'approach-explorer'];
|
|
35
|
+
export const MODEL_RUNTIME_IDS = ['claude', 'opencode', 'codex'];
|
|
36
|
+
export const MODEL_ID_PATTERN = /^[a-zA-Z0-9._\/:@-]+$/;
|
|
37
|
+
|
|
38
|
+
export function normalizeModelProfile(value) {
|
|
39
|
+
return VALID_MODEL_PROFILES.includes(value) ? value : 'balanced';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function buildDefaultConfig({ autoAdvance = false } = {}) {
|
|
43
|
+
const rigor = resolveRigor('balanced');
|
|
44
|
+
const cost = resolveCost('balanced');
|
|
45
|
+
const config = {
|
|
46
|
+
...rigor,
|
|
47
|
+
...cost,
|
|
48
|
+
commitDocs: true,
|
|
49
|
+
gitProtocol: { ...DEFAULT_GIT_PROTOCOL },
|
|
50
|
+
initVersion: 'v1.1',
|
|
51
|
+
};
|
|
52
|
+
if (autoAdvance) config.autoAdvance = true;
|
|
53
|
+
return config;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isProjectInitialized(cwd = process.cwd()) {
|
|
57
|
+
return existsSync(join(cwd, '.planning', 'config.json'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function loadProjectModelConfig(cwd = process.cwd()) {
|
|
61
|
+
const configPath = join(cwd, '.planning', 'config.json');
|
|
62
|
+
if (!existsSync(configPath)) return buildDefaultConfig();
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
return {
|
|
66
|
+
...buildDefaultConfig(),
|
|
67
|
+
...JSON.parse(readFileSync(configPath, 'utf-8')),
|
|
68
|
+
};
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error(`WARNING: .planning/config.json is malformed (${e.message}). Using defaults.`);
|
|
71
|
+
return buildDefaultConfig();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function loadConfigForMutation(cwd = process.cwd()) {
|
|
76
|
+
const configPath = join(cwd, '.planning', 'config.json');
|
|
77
|
+
let raw;
|
|
78
|
+
try {
|
|
79
|
+
raw = readFileSync(configPath, 'utf-8');
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return { ok: false, error: `could not read config file (${e.message})` };
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
return { ok: true, config: { ...buildDefaultConfig(), ...JSON.parse(raw) } };
|
|
85
|
+
} catch (e) {
|
|
86
|
+
return { ok: false, error: `malformed JSON (${e.message})` };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function ensureProjectConfig(cwd = process.cwd()) {
|
|
91
|
+
mkdirSync(join(cwd, '.planning'), { recursive: true });
|
|
92
|
+
const config = loadProjectModelConfig(cwd);
|
|
93
|
+
writeProjectConfig(config, cwd);
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function writeProjectConfig(config, cwd = process.cwd()) {
|
|
98
|
+
const configPath = join(cwd, '.planning', 'config.json');
|
|
99
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getPortableAgentProfile(config, agentId) {
|
|
103
|
+
const override = config.agentModelProfiles?.[agentId];
|
|
104
|
+
if (VALID_MODEL_PROFILES.includes(override)) return override;
|
|
105
|
+
return normalizeModelProfile(config.modelProfile);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function getRuntimeModelOverride(config, runtime, agentId) {
|
|
109
|
+
const override = config.runtimeModelOverrides?.[runtime]?.[agentId];
|
|
110
|
+
return typeof override === 'string' && override.trim() ? override.trim() : null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function resolveRuntimeAgentModel({ cwd = process.cwd(), runtime, agentId, profileMap = null }) {
|
|
114
|
+
const config = loadProjectModelConfig(cwd);
|
|
115
|
+
const runtimeOverride = getRuntimeModelOverride(config, runtime, agentId);
|
|
116
|
+
if (runtimeOverride) return runtimeOverride;
|
|
117
|
+
|
|
118
|
+
if (!profileMap) return null;
|
|
119
|
+
const profile = getPortableAgentProfile(config, agentId);
|
|
120
|
+
return profileMap[profile] ?? profileMap.balanced ?? null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function getRuntimeAgentModelState({ config, runtime, agentId, profileMap = null }) {
|
|
124
|
+
const runtimeOverride = getRuntimeModelOverride(config, runtime, agentId);
|
|
125
|
+
if (runtimeOverride) {
|
|
126
|
+
return {
|
|
127
|
+
mode: 'override',
|
|
128
|
+
model: runtimeOverride,
|
|
129
|
+
source: 'runtimeOverride',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!profileMap) {
|
|
134
|
+
return {
|
|
135
|
+
mode: 'inherit',
|
|
136
|
+
model: null,
|
|
137
|
+
runtimeDetectedModel: null,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const agentOverride = config.agentModelProfiles?.[agentId];
|
|
142
|
+
const profile = getPortableAgentProfile(config, agentId);
|
|
143
|
+
return {
|
|
144
|
+
mode: 'mapped',
|
|
145
|
+
model: profileMap[profile] ?? profileMap.balanced ?? null,
|
|
146
|
+
source: VALID_MODEL_PROFILES.includes(agentOverride) ? 'agentModelProfile' : 'modelProfile',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function cmdModels(...modelArgs) {
|
|
151
|
+
const subcommand = modelArgs[0] || 'show';
|
|
152
|
+
|
|
153
|
+
switch (subcommand) {
|
|
154
|
+
case 'show':
|
|
155
|
+
return cmdModelsShow();
|
|
156
|
+
case 'profile':
|
|
157
|
+
return cmdModelsProfile(modelArgs[1]);
|
|
158
|
+
case 'agent-profile':
|
|
159
|
+
return cmdModelsAgentProfile(modelArgs.slice(1));
|
|
160
|
+
case 'clear-agent-profile':
|
|
161
|
+
return cmdModelsClearAgentProfile(modelArgs.slice(1));
|
|
162
|
+
case 'set':
|
|
163
|
+
return cmdModelsSetRuntimeOverride(modelArgs.slice(1));
|
|
164
|
+
case 'clear':
|
|
165
|
+
return cmdModelsClearRuntimeOverride(modelArgs.slice(1));
|
|
166
|
+
default:
|
|
167
|
+
console.error('Usage: gsdd models [show|profile|agent-profile|clear-agent-profile|set|clear]');
|
|
168
|
+
process.exitCode = 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function cmdModelsShow() {
|
|
173
|
+
const cwd = process.cwd();
|
|
174
|
+
const config = loadProjectModelConfig(cwd);
|
|
175
|
+
const ocCheckerOverride = getRuntimeModelOverride(config, 'opencode', 'plan-checker');
|
|
176
|
+
const ocExplorerOverride = getRuntimeModelOverride(config, 'opencode', 'approach-explorer');
|
|
177
|
+
const ocDetected = detectOpenCodeConfiguredModel(cwd);
|
|
178
|
+
const codexCheckerOverride = getRuntimeModelOverride(config, 'codex', 'plan-checker');
|
|
179
|
+
const codexExplorerOverride = getRuntimeModelOverride(config, 'codex', 'approach-explorer');
|
|
180
|
+
output({
|
|
181
|
+
modelProfile: normalizeModelProfile(config.modelProfile),
|
|
182
|
+
agentModelProfiles: config.agentModelProfiles || {},
|
|
183
|
+
runtimeModelOverrides: config.runtimeModelOverrides || {},
|
|
184
|
+
effective: {
|
|
185
|
+
claude: {
|
|
186
|
+
'plan-checker': getRuntimeAgentModelState({
|
|
187
|
+
config,
|
|
188
|
+
runtime: 'claude',
|
|
189
|
+
agentId: 'plan-checker',
|
|
190
|
+
profileMap: CLAUDE_MODEL_PROFILES,
|
|
191
|
+
}),
|
|
192
|
+
'approach-explorer': getRuntimeAgentModelState({
|
|
193
|
+
config,
|
|
194
|
+
runtime: 'claude',
|
|
195
|
+
agentId: 'approach-explorer',
|
|
196
|
+
profileMap: CLAUDE_MODEL_PROFILES,
|
|
197
|
+
}),
|
|
198
|
+
},
|
|
199
|
+
opencode: {
|
|
200
|
+
'plan-checker': {
|
|
201
|
+
mode: ocCheckerOverride ? 'override' : 'inherit',
|
|
202
|
+
model: ocCheckerOverride,
|
|
203
|
+
runtimeDetectedModel: ocDetected,
|
|
204
|
+
},
|
|
205
|
+
'approach-explorer': {
|
|
206
|
+
mode: ocExplorerOverride ? 'override' : 'inherit',
|
|
207
|
+
model: ocExplorerOverride,
|
|
208
|
+
runtimeDetectedModel: ocDetected,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
codex: {
|
|
212
|
+
'plan-checker': {
|
|
213
|
+
mode: codexCheckerOverride ? 'override' : 'inherit',
|
|
214
|
+
model: codexCheckerOverride,
|
|
215
|
+
},
|
|
216
|
+
'approach-explorer': {
|
|
217
|
+
mode: codexExplorerOverride ? 'override' : 'inherit',
|
|
218
|
+
model: codexExplorerOverride,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
detectedRuntimeModels: {
|
|
223
|
+
opencode: ocDetected,
|
|
224
|
+
},
|
|
225
|
+
hints: (!ocCheckerOverride || !ocExplorerOverride) ? {
|
|
226
|
+
opencode: 'OpenCode currently inherits its runtime model unless you set an explicit override. Use gsdd models set --runtime opencode --agent <agent-id> --model <provider/model-id> to inject an explicit agent model.',
|
|
227
|
+
} : undefined,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function cmdModelsProfile(profile) {
|
|
232
|
+
if (!VALID_MODEL_PROFILES.includes(profile)) {
|
|
233
|
+
console.error(`ERROR: Invalid profile "${profile}". Valid profiles: ${VALID_MODEL_PROFILES.join(', ')}`);
|
|
234
|
+
process.exitCode = 1;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!isProjectInitialized()) {
|
|
239
|
+
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
240
|
+
process.exitCode = 1;
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const result = loadConfigForMutation();
|
|
245
|
+
if (!result.ok) {
|
|
246
|
+
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
result.config.modelProfile = profile;
|
|
252
|
+
writeProjectConfig(result.config);
|
|
253
|
+
console.log(` - set modelProfile to ${profile}`);
|
|
254
|
+
console.log(' Run gsdd update to regenerate adapter files.');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function cmdModelsAgentProfile(args) {
|
|
258
|
+
const agent = parseFlagValue(args, '--agent').value;
|
|
259
|
+
const profile = parseFlagValue(args, '--profile').value;
|
|
260
|
+
|
|
261
|
+
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
262
|
+
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
263
|
+
process.exitCode = 1;
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (!VALID_MODEL_PROFILES.includes(profile)) {
|
|
267
|
+
console.error(`ERROR: Invalid profile "${profile}". Valid profiles: ${VALID_MODEL_PROFILES.join(', ')}`);
|
|
268
|
+
process.exitCode = 1;
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (!isProjectInitialized()) {
|
|
273
|
+
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const result = loadConfigForMutation();
|
|
279
|
+
if (!result.ok) {
|
|
280
|
+
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
281
|
+
process.exitCode = 1;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
result.config.agentModelProfiles = result.config.agentModelProfiles || {};
|
|
286
|
+
result.config.agentModelProfiles[agent] = profile;
|
|
287
|
+
writeProjectConfig(result.config);
|
|
288
|
+
console.log(` - set ${agent} semantic profile to ${profile}`);
|
|
289
|
+
console.log(' Run gsdd update to regenerate adapter files.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function cmdModelsClearAgentProfile(args) {
|
|
293
|
+
const agent = parseFlagValue(args, '--agent').value;
|
|
294
|
+
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
295
|
+
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
296
|
+
process.exitCode = 1;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!isProjectInitialized()) {
|
|
301
|
+
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
302
|
+
process.exitCode = 1;
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const result = loadConfigForMutation();
|
|
307
|
+
if (!result.ok) {
|
|
308
|
+
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
309
|
+
process.exitCode = 1;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (result.config.agentModelProfiles) {
|
|
314
|
+
delete result.config.agentModelProfiles[agent];
|
|
315
|
+
if (Object.keys(result.config.agentModelProfiles).length === 0) {
|
|
316
|
+
delete result.config.agentModelProfiles;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
writeProjectConfig(result.config);
|
|
320
|
+
console.log(` - cleared semantic profile override for ${agent}`);
|
|
321
|
+
console.log(' Run gsdd update to regenerate adapter files.');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function cmdModelsSetRuntimeOverride(args) {
|
|
325
|
+
const runtime = parseFlagValue(args, '--runtime').value;
|
|
326
|
+
const agent = parseFlagValue(args, '--agent').value;
|
|
327
|
+
const model = parseFlagValue(args, '--model').value;
|
|
328
|
+
|
|
329
|
+
if (!MODEL_RUNTIME_IDS.includes(runtime)) {
|
|
330
|
+
console.error(`ERROR: Invalid runtime "${runtime}". Valid runtimes: ${MODEL_RUNTIME_IDS.join(', ')}`);
|
|
331
|
+
process.exitCode = 1;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
335
|
+
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
336
|
+
process.exitCode = 1;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!model) {
|
|
340
|
+
console.error('ERROR: --model requires a value.');
|
|
341
|
+
process.exitCode = 1;
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (!MODEL_ID_PATTERN.test(model.trim())) {
|
|
345
|
+
console.error('ERROR: Model ID contains invalid characters. Only alphanumeric, dots, hyphens, underscores, forward slashes, colons, and @ are allowed.');
|
|
346
|
+
process.exitCode = 1;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (!isProjectInitialized()) {
|
|
351
|
+
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
352
|
+
process.exitCode = 1;
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const result = loadConfigForMutation();
|
|
357
|
+
if (!result.ok) {
|
|
358
|
+
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
359
|
+
process.exitCode = 1;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
result.config.runtimeModelOverrides = result.config.runtimeModelOverrides || {};
|
|
364
|
+
result.config.runtimeModelOverrides[runtime] = result.config.runtimeModelOverrides[runtime] || {};
|
|
365
|
+
result.config.runtimeModelOverrides[runtime][agent] = model.trim();
|
|
366
|
+
writeProjectConfig(result.config);
|
|
367
|
+
console.log(` - set ${runtime} runtime override for ${agent}`);
|
|
368
|
+
console.log(' Run gsdd update to regenerate adapter files.');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function cmdModelsClearRuntimeOverride(args) {
|
|
372
|
+
const runtime = parseFlagValue(args, '--runtime').value;
|
|
373
|
+
const agent = parseFlagValue(args, '--agent').value;
|
|
374
|
+
|
|
375
|
+
if (!MODEL_RUNTIME_IDS.includes(runtime)) {
|
|
376
|
+
console.error(`ERROR: Invalid runtime "${runtime}". Valid runtimes: ${MODEL_RUNTIME_IDS.join(', ')}`);
|
|
377
|
+
process.exitCode = 1;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (!PORTABLE_AGENT_IDS.includes(agent)) {
|
|
381
|
+
console.error(`ERROR: Invalid agent "${agent}". Valid agents: ${PORTABLE_AGENT_IDS.join(', ')}`);
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (!isProjectInitialized()) {
|
|
387
|
+
console.error('ERROR: Project not initialized. Run gsdd init first.');
|
|
388
|
+
process.exitCode = 1;
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const result = loadConfigForMutation();
|
|
393
|
+
if (!result.ok) {
|
|
394
|
+
console.error(`ERROR: .planning/config.json is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
395
|
+
process.exitCode = 1;
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (result.config.runtimeModelOverrides?.[runtime]) {
|
|
400
|
+
delete result.config.runtimeModelOverrides[runtime][agent];
|
|
401
|
+
if (Object.keys(result.config.runtimeModelOverrides[runtime]).length === 0) {
|
|
402
|
+
delete result.config.runtimeModelOverrides[runtime];
|
|
403
|
+
}
|
|
404
|
+
if (Object.keys(result.config.runtimeModelOverrides).length === 0) {
|
|
405
|
+
delete result.config.runtimeModelOverrides;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
writeProjectConfig(result.config);
|
|
409
|
+
console.log(` - cleared ${runtime} runtime override for ${agent}`);
|
|
410
|
+
console.log(' Run gsdd update to regenerate adapter files.');
|
|
411
|
+
}
|