gsdd-cli 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -13
- package/bin/adapters/claude.mjs +1 -1
- package/bin/adapters/opencode.mjs +1 -1
- package/bin/gsdd.mjs +44 -34
- package/bin/lib/init-flow.mjs +220 -0
- package/bin/lib/init-prompts.mjs +308 -0
- package/bin/lib/init-runtime.mjs +224 -0
- package/bin/lib/init.mjs +20 -383
- package/distilled/DESIGN.md +286 -0
- package/distilled/README.md +6 -2
- package/distilled/templates/agents.block.md +4 -2
- package/distilled/templates/delegates/plan-checker.md +5 -1
- package/distilled/workflows/audit-milestone.md +2 -0
- package/distilled/workflows/map-codebase.md +10 -5
- package/distilled/workflows/new-project.md +26 -21
- package/distilled/workflows/pause.md +2 -0
- package/distilled/workflows/plan.md +3 -2
- package/distilled/workflows/quick.md +163 -4
- package/package.json +3 -3
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { DEFAULT_GIT_PROTOCOL, normalizeModelProfile } from './models.mjs';
|
|
3
|
+
import { buildRuntimeChoices, INIT_VERSION, resolveWizardAdapterTargets } from './init-runtime.mjs';
|
|
4
|
+
|
|
5
|
+
const ANSI = {
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
dim: '\x1b[2m',
|
|
8
|
+
bold: '\x1b[1m',
|
|
9
|
+
cyan: '\x1b[36m',
|
|
10
|
+
green: '\x1b[32m',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function createInitPromptApi({ input = process.stdin, output = process.stdout } = {}) {
|
|
14
|
+
return {
|
|
15
|
+
async runInitWizard({ cwd, adapters }) {
|
|
16
|
+
output.write(`${ANSI.bold}${ANSI.cyan}GSDD Install Wizard${ANSI.reset}\n`);
|
|
17
|
+
output.write(`${ANSI.dim}Portable skills are always installed. Select the runtimes you want GSDD to feel native in.${ANSI.reset}\n\n`);
|
|
18
|
+
|
|
19
|
+
const runtimeChoices = buildRuntimeChoices(adapters);
|
|
20
|
+
const selectedRuntimes = await promptMultiSelect({
|
|
21
|
+
input,
|
|
22
|
+
output,
|
|
23
|
+
title: 'Step 1 of 3 - Select runtimes',
|
|
24
|
+
hint: 'Space toggles, Enter confirms.',
|
|
25
|
+
choices: runtimeChoices,
|
|
26
|
+
});
|
|
27
|
+
const installGovernance = await promptConfirm({
|
|
28
|
+
input,
|
|
29
|
+
output,
|
|
30
|
+
title: 'Step 2 of 3 - Repo-wide AGENTS.md governance',
|
|
31
|
+
prompt: 'Install repo-wide AGENTS.md rules?',
|
|
32
|
+
defaultValue: false,
|
|
33
|
+
details: [
|
|
34
|
+
'Why care: consistent behavioral discipline across sessions and mixed-runtime teams.',
|
|
35
|
+
'Why skip it: it writes to the repo root, and your selected runtimes already discover .agents/skills/ natively.',
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
const config = await promptForConfig(cwd, { input, output });
|
|
39
|
+
return {
|
|
40
|
+
selectedRuntimes,
|
|
41
|
+
adapterTargets: resolveWizardAdapterTargets(selectedRuntimes, installGovernance),
|
|
42
|
+
config,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
promptForConfig(cwd) {
|
|
46
|
+
return promptForConfig(cwd, { input, output });
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function promptForConfig(cwd, { input = process.stdin, output = process.stdout } = {}) {
|
|
52
|
+
output.write(`\n${ANSI.bold}Step 3 of 3 - Configure planning defaults${ANSI.reset}\n`);
|
|
53
|
+
|
|
54
|
+
const researchDepth = await promptSingleSelect({
|
|
55
|
+
input,
|
|
56
|
+
output,
|
|
57
|
+
title: 'Research depth',
|
|
58
|
+
choices: [
|
|
59
|
+
{ value: 'balanced', label: 'balanced', description: 'SOTA research per phase (recommended)' },
|
|
60
|
+
{ value: 'fast', label: 'fast', description: 'Skip deeper domain research and plan from current context' },
|
|
61
|
+
{ value: 'deep', label: 'deep', description: 'Exhaustive research sweeps and parallel researchers' },
|
|
62
|
+
],
|
|
63
|
+
defaultIndex: 0,
|
|
64
|
+
});
|
|
65
|
+
const parallelization = await promptSingleSelect({
|
|
66
|
+
input,
|
|
67
|
+
output,
|
|
68
|
+
title: 'Parallelization',
|
|
69
|
+
choices: yesNoChoices('Run independent agents in parallel?', true),
|
|
70
|
+
defaultIndex: 0,
|
|
71
|
+
});
|
|
72
|
+
const commitDocs = await promptSingleSelect({
|
|
73
|
+
input,
|
|
74
|
+
output,
|
|
75
|
+
title: 'Planning docs in git',
|
|
76
|
+
choices: yesNoChoices('Track .planning/ in git?', true),
|
|
77
|
+
defaultIndex: 0,
|
|
78
|
+
});
|
|
79
|
+
const modelProfile = await promptSingleSelect({
|
|
80
|
+
input,
|
|
81
|
+
output,
|
|
82
|
+
title: 'Model profile',
|
|
83
|
+
choices: [
|
|
84
|
+
{ value: 'balanced', label: 'balanced', description: 'Capable default for most agents (recommended)' },
|
|
85
|
+
{ value: 'quality', label: 'quality', description: 'Most capable model for research and roadmap work' },
|
|
86
|
+
{ value: 'budget', label: 'budget', description: 'Cheapest and fastest model profile' },
|
|
87
|
+
],
|
|
88
|
+
defaultIndex: 0,
|
|
89
|
+
});
|
|
90
|
+
const workflowResearch = await promptSingleSelect({
|
|
91
|
+
input,
|
|
92
|
+
output,
|
|
93
|
+
title: 'Workflow research',
|
|
94
|
+
choices: yesNoChoices('Research before planning each phase?', true),
|
|
95
|
+
defaultIndex: 0,
|
|
96
|
+
});
|
|
97
|
+
const workflowDiscuss = await promptSingleSelect({
|
|
98
|
+
input,
|
|
99
|
+
output,
|
|
100
|
+
title: 'Approach discussion',
|
|
101
|
+
choices: yesNoChoices('Explore approaches with the user before planning?', false),
|
|
102
|
+
defaultIndex: 0,
|
|
103
|
+
});
|
|
104
|
+
const workflowPlanCheck = await promptSingleSelect({
|
|
105
|
+
input,
|
|
106
|
+
output,
|
|
107
|
+
title: 'Plan checking',
|
|
108
|
+
choices: yesNoChoices('Run the fresh-context plan checker before execution?', true),
|
|
109
|
+
defaultIndex: 0,
|
|
110
|
+
});
|
|
111
|
+
const workflowVerifier = await promptSingleSelect({
|
|
112
|
+
input,
|
|
113
|
+
output,
|
|
114
|
+
title: 'Phase verification',
|
|
115
|
+
choices: yesNoChoices('Run phase verification after execution?', true),
|
|
116
|
+
defaultIndex: 0,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const gitProtocol = await promptGitProtocol(cwd, { input, output });
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
researchDepth,
|
|
123
|
+
parallelization,
|
|
124
|
+
commitDocs,
|
|
125
|
+
modelProfile: normalizeModelProfile(modelProfile),
|
|
126
|
+
workflow: {
|
|
127
|
+
research: workflowResearch,
|
|
128
|
+
discuss: workflowDiscuss,
|
|
129
|
+
planCheck: workflowPlanCheck,
|
|
130
|
+
verifier: workflowVerifier,
|
|
131
|
+
},
|
|
132
|
+
gitProtocol,
|
|
133
|
+
initVersion: INIT_VERSION,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function promptGitProtocol(cwd, { input = process.stdin, output = process.stdout } = {}) {
|
|
138
|
+
if (typeof input.resume === 'function') input.resume();
|
|
139
|
+
output.write(`\n${ANSI.bold}Version control guidance${ANSI.reset}\n`);
|
|
140
|
+
output.write(`${ANSI.dim}This is advisory only. Repo or user conventions still win.${ANSI.reset}\n`);
|
|
141
|
+
const rl = readline.createInterface({ input, output });
|
|
142
|
+
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
let branch = await askQuestion(' Branching guidance (Enter for default): ');
|
|
146
|
+
branch = branch.trim() || DEFAULT_GIT_PROTOCOL.branch;
|
|
147
|
+
|
|
148
|
+
let commit = await askQuestion(' Commit guidance (Enter for default): ');
|
|
149
|
+
commit = commit.trim() || DEFAULT_GIT_PROTOCOL.commit;
|
|
150
|
+
|
|
151
|
+
let pr = await askQuestion(' PR guidance (Enter for default): ');
|
|
152
|
+
pr = pr.trim() || DEFAULT_GIT_PROTOCOL.pr;
|
|
153
|
+
|
|
154
|
+
return { branch, commit, pr };
|
|
155
|
+
} finally {
|
|
156
|
+
rl.close();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function yesNoChoices(prompt, defaultYes) {
|
|
161
|
+
return [
|
|
162
|
+
{ value: true, label: 'yes', description: `${prompt} Yes.` },
|
|
163
|
+
{ value: false, label: 'no', description: `${prompt} No.` },
|
|
164
|
+
].sort((a, b) => {
|
|
165
|
+
if (defaultYes) return a.value === true ? -1 : 1;
|
|
166
|
+
return a.value === false ? -1 : 1;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function promptConfirm({ input, output, title, prompt, defaultValue, details = [] }) {
|
|
171
|
+
if (typeof input.resume === 'function') input.resume();
|
|
172
|
+
output.write(`\n${ANSI.bold}${title}${ANSI.reset}\n`);
|
|
173
|
+
for (const detail of details) {
|
|
174
|
+
output.write(` ${ANSI.dim}${detail}${ANSI.reset}\n`);
|
|
175
|
+
}
|
|
176
|
+
const rl = readline.createInterface({ input, output });
|
|
177
|
+
const suffix = defaultValue ? '[Y/n]' : '[y/N]';
|
|
178
|
+
try {
|
|
179
|
+
const answer = await new Promise((resolve) => rl.question(` ${prompt} ${suffix}: `, resolve));
|
|
180
|
+
const normalized = answer.trim().toLowerCase();
|
|
181
|
+
if (!normalized) return defaultValue;
|
|
182
|
+
return normalized === 'y' || normalized === 'yes';
|
|
183
|
+
} finally {
|
|
184
|
+
rl.close();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function promptSingleSelect({ input, output, title, choices, defaultIndex = 0 }) {
|
|
189
|
+
const selected = await promptChoiceList({
|
|
190
|
+
input,
|
|
191
|
+
output,
|
|
192
|
+
title,
|
|
193
|
+
choices: choices.map((choice, index) => ({
|
|
194
|
+
...choice,
|
|
195
|
+
selected: index === defaultIndex,
|
|
196
|
+
detected: false,
|
|
197
|
+
})),
|
|
198
|
+
multi: false,
|
|
199
|
+
});
|
|
200
|
+
return selected[0];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export async function promptMultiSelect({ input, output, title, hint, choices }) {
|
|
204
|
+
return promptChoiceList({ input, output, title, hint, choices, multi: true });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function promptChoiceList({ input, output, title, hint = 'Use arrows to move. Enter confirms.', choices, multi }) {
|
|
208
|
+
if (typeof input.resume === 'function') input.resume();
|
|
209
|
+
readline.emitKeypressEvents(input);
|
|
210
|
+
const previousRawMode = typeof input.isRaw === 'boolean' ? input.isRaw : false;
|
|
211
|
+
if (typeof input.setRawMode === 'function') input.setRawMode(true);
|
|
212
|
+
|
|
213
|
+
let cursor = Math.max(0, choices.findIndex((choice) => choice.selected));
|
|
214
|
+
let renderedLines = 0;
|
|
215
|
+
|
|
216
|
+
const selectCursor = () => {
|
|
217
|
+
if (multi) return;
|
|
218
|
+
for (const choice of choices) choice.selected = false;
|
|
219
|
+
choices[cursor].selected = true;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const measureLines = (text) => {
|
|
223
|
+
const columns = Math.max(20, Number(output.columns) || 80);
|
|
224
|
+
const visible = stripAnsi(text);
|
|
225
|
+
return visible.split('\n').reduce((total, line) => {
|
|
226
|
+
const width = Math.max(1, line.length);
|
|
227
|
+
return total + Math.ceil(width / columns);
|
|
228
|
+
}, 0);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const render = () => {
|
|
232
|
+
if (renderedLines > 0) {
|
|
233
|
+
readline.moveCursor(output, 0, -renderedLines);
|
|
234
|
+
}
|
|
235
|
+
readline.cursorTo(output, 0);
|
|
236
|
+
readline.clearScreenDown(output);
|
|
237
|
+
const lines = [`${ANSI.bold}${title}${ANSI.reset}`];
|
|
238
|
+
if (hint) lines.push(`${ANSI.dim}${hint}${ANSI.reset}`);
|
|
239
|
+
lines.push('');
|
|
240
|
+
for (let i = 0; i < choices.length; i++) {
|
|
241
|
+
const choice = choices[i];
|
|
242
|
+
const pointer = i === cursor ? `${ANSI.green}>${ANSI.reset}` : ' ';
|
|
243
|
+
const mark = choice.selected ? `${ANSI.green}[x]${ANSI.reset}` : '[ ]';
|
|
244
|
+
const detected = choice.detected ? ` ${ANSI.dim}(detected)${ANSI.reset}` : '';
|
|
245
|
+
lines.push(`${pointer} ${mark} ${choice.label}${detected}`);
|
|
246
|
+
lines.push(` ${ANSI.dim}${choice.description}${ANSI.reset}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
renderedLines = 0;
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
output.write(`${line}\n`);
|
|
252
|
+
renderedLines += measureLines(line);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
render();
|
|
257
|
+
|
|
258
|
+
const values = await new Promise((resolve, reject) => {
|
|
259
|
+
const cleanup = () => {
|
|
260
|
+
input.off('keypress', onKeypress);
|
|
261
|
+
if (typeof input.setRawMode === 'function') input.setRawMode(previousRawMode);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const onKeypress = (_, key = {}) => {
|
|
265
|
+
if (key.ctrl && key.name === 'c') {
|
|
266
|
+
cleanup();
|
|
267
|
+
reject(new Error('Prompt cancelled by user'));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (key.name === 'up') {
|
|
271
|
+
cursor = cursor === 0 ? choices.length - 1 : cursor - 1;
|
|
272
|
+
selectCursor();
|
|
273
|
+
render();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (key.name === 'down') {
|
|
277
|
+
cursor = cursor === choices.length - 1 ? 0 : cursor + 1;
|
|
278
|
+
selectCursor();
|
|
279
|
+
render();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (key.name === 'space') {
|
|
283
|
+
if (multi) {
|
|
284
|
+
choices[cursor].selected = !choices[cursor].selected;
|
|
285
|
+
} else {
|
|
286
|
+
for (const choice of choices) choice.selected = false;
|
|
287
|
+
choices[cursor].selected = true;
|
|
288
|
+
}
|
|
289
|
+
render();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (key.name === 'return') {
|
|
293
|
+
selectCursor();
|
|
294
|
+
cleanup();
|
|
295
|
+
resolve(choices.filter((choice) => choice.selected).map((choice) => choice.value ?? choice.id));
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
input.on('keypress', onKeypress);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
output.write('\n');
|
|
303
|
+
return values;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function stripAnsi(text) {
|
|
307
|
+
return String(text).replace(/\x1B\[[0-9;]*[A-Za-z]/g, '');
|
|
308
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
const RUNTIME_OPTIONS = [
|
|
2
|
+
{
|
|
3
|
+
id: 'claude',
|
|
4
|
+
label: 'Claude Code',
|
|
5
|
+
description: 'Native skills, commands, and agents',
|
|
6
|
+
kind: 'native',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: 'opencode',
|
|
10
|
+
label: 'OpenCode',
|
|
11
|
+
description: 'Native slash commands and agents',
|
|
12
|
+
kind: 'native',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'codex',
|
|
16
|
+
label: 'Codex CLI',
|
|
17
|
+
description: 'Portable skills plus native checker agents',
|
|
18
|
+
kind: 'native',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'cursor',
|
|
22
|
+
label: 'Cursor',
|
|
23
|
+
description: 'Skills-native slash commands from .agents/skills/',
|
|
24
|
+
kind: 'skills_native',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'copilot',
|
|
28
|
+
label: 'GitHub Copilot',
|
|
29
|
+
description: 'Skills-native slash commands from .agents/skills/',
|
|
30
|
+
kind: 'skills_native',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'gemini',
|
|
34
|
+
label: 'Gemini CLI',
|
|
35
|
+
description: 'Skills-native slash commands from .agents/skills/',
|
|
36
|
+
kind: 'skills_native',
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export const INIT_VERSION = 'v1.1';
|
|
41
|
+
|
|
42
|
+
export function normalizeRequestedTools(requestedTools) {
|
|
43
|
+
const selectedRuntimes = [];
|
|
44
|
+
const adapterTargets = [];
|
|
45
|
+
const addRuntime = (runtime) => {
|
|
46
|
+
if (!selectedRuntimes.includes(runtime)) selectedRuntimes.push(runtime);
|
|
47
|
+
};
|
|
48
|
+
const addAdapter = (adapter) => {
|
|
49
|
+
if (!adapterTargets.includes(adapter)) adapterTargets.push(adapter);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
for (const tool of requestedTools) {
|
|
53
|
+
if (tool === 'claude' || tool === 'opencode' || tool === 'codex') {
|
|
54
|
+
addRuntime(tool);
|
|
55
|
+
addAdapter(tool);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (tool === 'cursor' || tool === 'copilot' || tool === 'gemini') {
|
|
59
|
+
addRuntime(tool);
|
|
60
|
+
addAdapter(tool);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (tool === 'agents') {
|
|
64
|
+
addAdapter('agents');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { selectedRuntimes, adapterTargets };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function detectPlatforms(adapters) {
|
|
72
|
+
return Object.values(adapters)
|
|
73
|
+
.filter((adapter, index, arr) => arr.findIndex((other) => other.id === adapter.id) === index)
|
|
74
|
+
.filter((adapter) => adapter.detect())
|
|
75
|
+
.map((adapter) => adapter.name);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function buildRuntimeChoices(adapters) {
|
|
79
|
+
const detected = new Set(detectPlatforms(adapters));
|
|
80
|
+
return RUNTIME_OPTIONS.map((option) => ({
|
|
81
|
+
...option,
|
|
82
|
+
detected: detected.has(option.id),
|
|
83
|
+
selected: detected.has(option.id),
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function resolveAdapters(adapters, platformNames) {
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
const resolved = [];
|
|
90
|
+
|
|
91
|
+
for (const platformName of platformNames) {
|
|
92
|
+
const adapter = adapters[platformName];
|
|
93
|
+
if (!adapter || seen.has(adapter.id)) continue;
|
|
94
|
+
seen.add(adapter.id);
|
|
95
|
+
resolved.push(adapter);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return resolved;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getAdaptersToUpdate(adapters, platformNames) {
|
|
102
|
+
const requested = new Set(platformNames);
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
const installed = [];
|
|
105
|
+
|
|
106
|
+
for (const [platformName, adapter] of Object.entries(adapters)) {
|
|
107
|
+
if (seen.has(adapter.id)) continue;
|
|
108
|
+
if (!requested.has(platformName) && !adapter.isInstalled()) continue;
|
|
109
|
+
seen.add(adapter.id);
|
|
110
|
+
installed.push(adapter);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return installed;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function resolveInteractiveInitSession({ ctx, promptApi, parsedTools, isAuto }) {
|
|
117
|
+
if (parsedTools.length > 0) {
|
|
118
|
+
return {
|
|
119
|
+
...normalizeRequestedTools(parsedTools),
|
|
120
|
+
config: null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (isAuto) {
|
|
125
|
+
return { selectedRuntimes: [], adapterTargets: [], config: null };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!process.stdin.isTTY) {
|
|
129
|
+
return {
|
|
130
|
+
selectedRuntimes: detectPlatforms(ctx.adapters),
|
|
131
|
+
adapterTargets: detectPlatforms(ctx.adapters),
|
|
132
|
+
config: null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return promptApi.runInitWizard({ cwd: ctx.cwd, adapters: ctx.adapters });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function resolveWizardAdapterTargets(selectedRuntimes, installGovernance) {
|
|
140
|
+
const adapterTargets = [];
|
|
141
|
+
for (const runtime of selectedRuntimes) {
|
|
142
|
+
if (runtime === 'claude' || runtime === 'opencode' || runtime === 'codex') {
|
|
143
|
+
adapterTargets.push(runtime);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (installGovernance) adapterTargets.push('agents');
|
|
147
|
+
return adapterTargets;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function getPostInitRoutingLines(selectedRuntimes) {
|
|
151
|
+
const lines = [];
|
|
152
|
+
if (selectedRuntimes.includes('claude')) lines.push(' Claude Code: /gsdd-new-project');
|
|
153
|
+
if (selectedRuntimes.includes('opencode')) lines.push(' OpenCode: /gsdd-new-project');
|
|
154
|
+
if (selectedRuntimes.includes('codex')) lines.push(' Codex CLI: $gsdd-new-project');
|
|
155
|
+
if (selectedRuntimes.includes('cursor')) lines.push(' Cursor: /gsdd-new-project');
|
|
156
|
+
if (selectedRuntimes.includes('copilot')) lines.push(' Copilot: /gsdd-new-project');
|
|
157
|
+
if (selectedRuntimes.includes('gemini')) lines.push(' Gemini CLI: /gsdd-new-project');
|
|
158
|
+
lines.push(' Any tool: open .agents/skills/gsdd-new-project/SKILL.md');
|
|
159
|
+
return lines;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function getHelpText() {
|
|
163
|
+
return `
|
|
164
|
+
gsdd - GSD Distilled CLI
|
|
165
|
+
Spec-Driven Development for AI coding agents.
|
|
166
|
+
|
|
167
|
+
Usage: gsdd <command> [args]
|
|
168
|
+
|
|
169
|
+
Commands:
|
|
170
|
+
init [--tools <platform>] [--auto] [--brief <file>]
|
|
171
|
+
Launch guided install wizard in TTYs, or use --tools for manual/headless setup
|
|
172
|
+
--auto: non-interactive mode with smart defaults (requires --tools)
|
|
173
|
+
--brief <file>: copy project brief to .planning/PROJECT_BRIEF.md
|
|
174
|
+
update [--tools <platform>] [--templates] [--dry]
|
|
175
|
+
Regenerate adapters from latest framework sources
|
|
176
|
+
--templates: also refresh .planning/templates/ and roles
|
|
177
|
+
--dry: preview changes without writing files
|
|
178
|
+
health [--json] Check workspace integrity (healthy/degraded/broken)
|
|
179
|
+
models [subcommand] Inspect or update model profile / runtime overrides
|
|
180
|
+
find-phase [N] Show phase info as JSON (for agent consumption)
|
|
181
|
+
verify <N> Run artifact checks for phase N
|
|
182
|
+
scaffold phase <N> [name] Create a new phase plan file
|
|
183
|
+
|
|
184
|
+
Platforms (for --tools):
|
|
185
|
+
claude Generate Claude Code skills (.claude/skills/gsdd-*), commands (.claude/commands/gsdd-*.md), and native agents (.claude/agents/gsdd-*.md)
|
|
186
|
+
opencode Generate OpenCode local slash commands (.opencode/commands/gsdd-*.md) + native agents (.opencode/agents/gsdd-*.md)
|
|
187
|
+
codex Generate Codex CLI native plan-checker agent (.codex/agents/gsdd-plan-checker.toml)
|
|
188
|
+
agents Generate/Update root AGENTS.md (bounded GSDD block)
|
|
189
|
+
cursor Generate root AGENTS.md governance block; workflows are already discovered natively from .agents/skills/ (legacy alias kept for backward compatibility)
|
|
190
|
+
copilot Generate root AGENTS.md governance block; workflows are already discovered natively from .agents/skills/ (legacy alias kept for backward compatibility)
|
|
191
|
+
gemini Generate root AGENTS.md governance block; workflows are already discovered natively from .agents/skills/ (legacy alias kept for backward compatibility)
|
|
192
|
+
all Generate all adapters (Claude, OpenCode, Codex, AGENTS-based surfaces)
|
|
193
|
+
|
|
194
|
+
Notes:
|
|
195
|
+
- init always generates open-standard skills at .agents/skills/gsdd-* (portable workflow entrypoints)
|
|
196
|
+
- running plain \`gsdd init\` in a terminal opens the guided runtime-selection wizard
|
|
197
|
+
- the wizard lets you pick runtimes first, then separately decide whether repo-wide AGENTS.md governance is worth installing
|
|
198
|
+
- --tools remains the advanced/manual path and preserves legacy runtime aliases for backward compatibility
|
|
199
|
+
- --tools codex generates .codex/agents/gsdd-plan-checker.toml (portable skill is the entry surface)
|
|
200
|
+
- root AGENTS.md is only written on init when explicitly requested via --tools agents, --tools all, or the wizard governance opt-in
|
|
201
|
+
|
|
202
|
+
Examples:
|
|
203
|
+
npx gsdd-cli init
|
|
204
|
+
npx gsdd-cli init --tools claude
|
|
205
|
+
npx gsdd-cli init --tools cursor
|
|
206
|
+
npx gsdd-cli init --auto --tools claude --brief project-idea.md
|
|
207
|
+
npx gsdd-cli init --auto --tools all
|
|
208
|
+
npx gsdd-cli models show
|
|
209
|
+
npx gsdd-cli models profile quality
|
|
210
|
+
npx gsdd-cli models agent-profile --agent plan-checker --profile quality
|
|
211
|
+
npx gsdd-cli models set --runtime opencode --agent plan-checker --model anthropic/claude-opus-4-6
|
|
212
|
+
npx gsdd-cli models clear --runtime opencode --agent plan-checker
|
|
213
|
+
npx gsdd-cli init --tools agents
|
|
214
|
+
npx gsdd-cli init --tools all
|
|
215
|
+
npx gsdd-cli update
|
|
216
|
+
npx gsdd-cli find-phase
|
|
217
|
+
npx gsdd-cli verify 1
|
|
218
|
+
npx gsdd-cli scaffold phase 4 Payments
|
|
219
|
+
|
|
220
|
+
Workflows (run via skills/adapters generated by init, not direct CLI):
|
|
221
|
+
map-codebase Map or refresh codebase (.agents/skills/gsdd-map-codebase/)
|
|
222
|
+
audit-milestone Audit a completed milestone (.agents/skills/gsdd-audit-milestone/)
|
|
223
|
+
`;
|
|
224
|
+
}
|