sdd-toolkit 1.5.0 → 1.6.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/src/index.js CHANGED
@@ -1,284 +1,335 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const fsp = require('fs/promises');
5
- const path = require('path');
6
- const { intro, outro, multiselect, spinner, note, select, text } = require('@clack/prompts');
7
- const pc = require('picocolors');
8
-
9
- // Internal Modules
10
- const { loadAgents } = require('./lib/agents');
11
- const { STACK_PROFILES } = require('./lib/profiles');
12
- const { setLocale, t, getLocale } = require('./lib/i18n');
13
- const {
14
- toGeminiTOML,
15
- toRooConfig,
16
- toKiloMarkdown,
17
- toCopilotInstructions,
18
- toCursorMDC,
19
- toWindsurfRules,
20
- toPlainSystemPrompt,
21
- toTraeRules
22
- } = require('./lib/transformers');
23
- const { generateWorkflowGuide } = require('./lib/docs');
24
-
25
- async function main() {
26
- console.clear();
27
-
28
- const args = process.argv.slice(2);
29
- const isUpgrade = args.includes('upgrade') || args.includes('--upgrade');
30
-
31
- // 0. Language Selection (Always first unless forced upgrade silent mode - but upgrade has interaction)
32
- // We show this before Upgrade title to ensure upgrade messages are localized too if possible
33
- // But typically flags like --lang would be better. For now, interactive.
34
-
35
- if (!isUpgrade) {
36
- intro(pc.bgMagenta(pc.white(' UNIVERSAL SPEC CLI ')));
37
-
38
- const lang = await select({
39
- message: 'Select Language / Selecione o Idioma / Seleccione el Idioma',
40
- options: [
41
- { value: 'en', label: 'English' },
42
- { value: 'pt_br', label: 'Português (Brasil)' },
43
- { value: 'es', label: 'Español' }
44
- ]
45
- });
46
-
47
- if (typeof lang === 'symbol') {
48
- outro(pc.yellow('Operation cancelled.'));
49
- process.exit(0);
50
- }
51
-
52
- setLocale(lang);
53
- } else {
54
- // Default EN for upgrade for now
55
- setLocale('en');
56
- }
57
-
58
- if (isUpgrade) {
59
- intro(pc.bgBlue(pc.white(t('INTRO.UPGRADE_TITLE'))));
60
-
61
- // Detecção de Ferramentas Existentes
62
- const tools = [];
63
- if (fs.existsSync(path.join(process.cwd(), '.gemini'))) tools.push('gemini');
64
- if (fs.existsSync(path.join(process.cwd(), '.roo'))) tools.push('roo');
65
- if (fs.existsSync(path.join(process.cwd(), '.cline'))) tools.push('cline');
66
- if (fs.existsSync(path.join(process.cwd(), '.cursor'))) tools.push('cursor');
67
- if (fs.existsSync(path.join(process.cwd(), '.windsurfrules'))) tools.push('windsurf');
68
- if (fs.existsSync(path.join(process.cwd(), '.trae'))) tools.push('trae');
69
- if (fs.existsSync(path.join(process.cwd(), '.kilo'))) tools.push('kilo');
70
- if (fs.existsSync(path.join(process.cwd(), '.github'))) tools.push('copilot'); // Assume copilot se .github existir
71
- if (fs.existsSync(path.join(process.cwd(), '.opencode'))) tools.push('opencode');
72
- if (fs.existsSync(path.join(process.cwd(), 'prompts'))) tools.push('web');
73
-
74
- if (tools.length === 0) {
75
- note(t('UPGRADE.NO_CONFIG'), t('UPGRADE.NO_CONFIG_TITLE'));
76
- } else {
77
- note(t('UPGRADE.DETECTED_TOOLS', tools.join(', ')), t('UPGRADE.DETECTED_TITLE'));
78
- await processAgentsInstallation(tools, { locale: getLocale() });
79
- outro(pc.green(t('UPGRADE.SUCCESS')));
80
- process.exit(0);
81
- }
82
- }
83
-
84
- // 1. Automatic Scaffold
85
- const s = spinner();
86
- s.start(t('SCAFFOLD.LOADING'));
87
-
88
- try {
89
- const stats = generateWorkflowGuide(process.cwd());
90
- if (stats.created > 0) {
91
- s.stop(`${t('SCAFFOLD.SUCCESS')} (${stats.created} new, ${stats.verified} verified)`);
92
- } else {
93
- s.stop(t('SCAFFOLD.ALREADY_EXISTS'));
94
- }
95
- } catch (e) {
96
- s.stop(pc.red(t('SCAFFOLD.ERROR')));
97
- }
98
-
99
- // 2. Feature 5: Stack Selection (Profile)
100
- const stackOptions = Object.entries(STACK_PROFILES).map(([key, profile]) => ({
101
- value: key,
102
- label: profile.label
103
- }));
104
-
105
- const stackProfile = await select({
106
- message: t('SETUP.STACK_SELECT'),
107
- options: stackOptions,
108
- initialValue: 'generic'
109
- });
110
-
111
- if (typeof stackProfile === 'symbol') {
112
- outro(pc.yellow(t('GENERAL.CANCELLED')));
113
- process.exit(0);
114
- }
115
-
116
- // 3. Feature 3: Global Rules (Optional)
117
- const globalRules = await text({
118
- message: t('SETUP.GLOBAL_RULES'),
119
- placeholder: t('SETUP.GLOBAL_RULES_HINT'),
120
- required: false
121
- });
122
-
123
- if (typeof globalRules === 'symbol') {
124
- outro(pc.yellow(t('GENERAL.CANCELLED')));
125
- process.exit(0);
126
- }
127
-
128
- // 4. Tool Selection (Multiple choice)
129
- const tools = await multiselect({
130
- message: t('SETUP.TOOL_SELECT'),
131
- options: [
132
- { value: 'gemini', label: t('TOOLS.GEMINI'), hint: '.gemini/commands/dev' },
133
- { value: 'roo', label: t('TOOLS.ROO'), hint: '.roo/ & custom_modes.json' },
134
- { value: 'cline', label: t('TOOLS.CLINE'), hint: '.cline/ & custom_modes.json' },
135
- { value: 'cursor', label: t('TOOLS.CURSOR'), hint: '.cursor/rules/*.mdc' },
136
- { value: 'windsurf', label: t('TOOLS.WINDSURF'), hint: '.windsurfrules' },
137
- { value: 'trae', label: t('TOOLS.TRAE'), hint: '.trae/instructions.md' },
138
- { value: 'kilo', label: t('TOOLS.KILO'), hint: '.kilo/prompts/*.md' },
139
- { value: 'copilot', label: t('TOOLS.COPILOT'), hint: '.github/copilot-instructions.md' },
140
- { value: 'web', label: t('TOOLS.WEB'), hint: 'prompts/*.txt' },
141
- { value: 'opencode', label: t('TOOLS.OPENCODE'), hint: '.opencode/*.md' },
142
- ],
143
- required: true,
144
- hint: t('SETUP.TOOL_HINT')
145
- });
146
-
147
- if (typeof tools === 'symbol') {
148
- outro(pc.yellow(t('GENERAL.CANCELLED')));
149
- process.exit(0);
150
- }
151
-
152
- if (!tools || tools.length === 0) {
153
- outro(t('SETUP.NO_TOOLS'));
154
- process.exit(0);
155
- }
156
-
157
- // Pass locale to installation process
158
- await processAgentsInstallation(tools, { stackProfile, globalRules, locale: getLocale() });
159
-
160
- outro(pc.green(t('SETUP.SUCCESS')));
161
- }
162
-
163
- async function processAgentsInstallation(tools, options) {
164
- const s = spinner();
165
- s.start(t('INSTALL.LOADING'));
166
-
167
- try {
168
- const validAgents = await loadAgents(options);
169
-
170
- if (validAgents.length === 0) {
171
- s.stop(t('INSTALL.NO_AGENTS'));
172
- return;
173
- }
174
-
175
- s.message(t('INSTALL.INSTALLING', tools.join(', ')));
176
-
177
- // Iterate over each selected tool
178
- for (const tool of tools) {
179
-
180
- // Tool-Specific Installation
181
- if (tool === 'gemini') {
182
- const targetDir = path.join(process.cwd(), '.gemini', 'commands', 'dev');
183
- await fsp.mkdir(targetDir, { recursive: true });
184
-
185
- await Promise.all(validAgents.map(agent => {
186
- const toml = toGeminiTOML(agent, options);
187
- const fileName = `${agent.originalName}.toml`;
188
- return fsp.writeFile(path.join(targetDir, fileName), toml);
189
- }));
190
- }
191
- else if (tool === 'roo' || tool === 'cline') {
192
- const configDir = tool === 'roo' ? '.roo' : '.cline';
193
- const targetDir = path.join(process.cwd(), configDir);
194
- await fsp.mkdir(targetDir, { recursive: true });
195
-
196
- await Promise.all(validAgents.map(agent => {
197
- const md = toKiloMarkdown(agent, options);
198
- return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
199
- }));
200
-
201
- const modes = validAgents.map(agent => toRooConfig(agent, agent.slug, options));
202
- const jsonContent = JSON.stringify({ customModes: modes }, null, 2);
203
- const fileName = `${tool}_custom_modes.json`;
204
- await fsp.writeFile(path.join(process.cwd(), fileName), jsonContent);
205
- }
206
- else if (tool === 'kilo') {
207
- const targetDir = path.join(process.cwd(), '.kilo', 'prompts');
208
- await fsp.mkdir(targetDir, { recursive: true });
209
-
210
- await Promise.all(validAgents.map(agent => {
211
- const md = toKiloMarkdown(agent, options);
212
- return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
213
- }));
214
- }
215
- else if (tool === 'copilot') {
216
- const githubDir = path.join(process.cwd(), '.github');
217
- const agentsDir = path.join(githubDir, 'agents');
218
- await fsp.mkdir(agentsDir, { recursive: true });
219
-
220
- await Promise.all(validAgents.map(agent => {
221
- const md = toCopilotInstructions(agent, options);
222
- return fsp.writeFile(path.join(agentsDir, `${agent.slug}.md`), md);
223
- }));
224
-
225
- const mainAgent = validAgents.find(a => a.slug.includes('coder')) || validAgents[0];
226
- const mainInstructions = toCopilotInstructions(mainAgent, options);
227
- await fsp.writeFile(path.join(githubDir, 'copilot-instructions.md'), mainInstructions);
228
- }
229
- else if (tool === 'cursor') {
230
- const rulesDir = path.join(process.cwd(), '.cursor', 'rules');
231
- await fsp.mkdir(rulesDir, { recursive: true });
232
-
233
- await Promise.all(validAgents.map(agent => {
234
- const mdc = toCursorMDC(agent, options);
235
- return fsp.writeFile(path.join(rulesDir, `${agent.slug}.mdc`), mdc);
236
- }));
237
- }
238
- else if (tool === 'windsurf') {
239
- const mainAgent = validAgents.find(a => a.slug.includes('coder')) || validAgents[0];
240
- const rules = toWindsurfRules(mainAgent, options);
241
- await fsp.writeFile(path.join(process.cwd(), '.windsurfrules'), rules);
242
- }
243
- else if (tool === 'trae') {
244
- const traeDir = path.join(process.cwd(), '.trae');
245
- await fsp.mkdir(traeDir, { recursive: true });
246
-
247
- const mainAgent = validAgents.find(a => a.slug.includes('coder')) || validAgents[0];
248
- const rules = toTraeRules(mainAgent, options);
249
- await fsp.writeFile(path.join(traeDir, 'instructions.md'), rules);
250
- }
251
- else if (tool === 'web') {
252
- const targetDir = path.join(process.cwd(), 'prompts');
253
- await fsp.mkdir(targetDir, { recursive: true });
254
-
255
- await Promise.all(validAgents.map(agent => {
256
- const txt = toPlainSystemPrompt(agent, options);
257
- return fsp.writeFile(path.join(targetDir, `${agent.slug}.txt`), txt);
258
- }));
259
- }
260
- else if (tool === 'opencode') {
261
- const targetDir = path.join(process.cwd(), '.opencode');
262
- await fsp.mkdir(targetDir, { recursive: true });
263
-
264
- await Promise.all(validAgents.map(agent => {
265
- const md = toKiloMarkdown(agent, options);
266
- return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
267
- }));
268
- }
269
- }
270
-
271
- s.stop(t('INSTALL.FINISHED'));
272
-
273
- // Consolidated feedback
274
- if (tools.includes('roo') || tools.includes('cline')) {
275
- note(t('INSTALL.ROO_WARNING'), t('INSTALL.ROO_WARNING_TITLE'));
276
- }
277
-
278
- } catch (e) {
279
- s.stop(pc.red(`${t('INSTALL.FAILED')}: ${e.message}`));
280
- process.exit(1);
281
- }
282
- }
283
-
284
- main().catch(console.error);
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const fsp = require('fs/promises');
5
+ const path = require('path');
6
+ const { intro, outro, multiselect, spinner, note, select, text } = require('@clack/prompts');
7
+ const pc = require('picocolors');
8
+
9
+ // Internal Modules
10
+ const { loadAgents } = require('./lib/agents');
11
+ const { STACK_PROFILES } = require('./lib/profiles');
12
+ const { setLocale, t, getLocale } = require('./lib/i18n');
13
+ const {
14
+ toGeminiTOML,
15
+ toRooConfig,
16
+ toKiloMarkdown,
17
+ toCopilotInstructions,
18
+ toCursorMDC,
19
+ toWindsurfRules,
20
+ toClaudeCommand,
21
+ toPlainSystemPrompt,
22
+ toTraeRules,
23
+ toOpenCodeAgent
24
+ } = require('./lib/transformers');
25
+ const { generateWorkflowGuide } = require('./lib/docs');
26
+ const { view } = require('./commands/view');
27
+
28
+ async function main() {
29
+ console.clear();
30
+
31
+ const args = process.argv.slice(2);
32
+
33
+ if (args[0] === 'view') {
34
+ await view();
35
+ process.exit(0);
36
+ }
37
+
38
+ const isUpgrade = args.includes('upgrade') || args.includes('--upgrade');
39
+
40
+ // 0. Language Selection
41
+ if (!isUpgrade) {
42
+ intro(pc.bgMagenta(pc.white(' UNIVERSAL SPEC CLI ')));
43
+
44
+ const lang = await select({
45
+ message: 'Select Language / Selecione o Idioma / Seleccione el Idioma',
46
+ options: [
47
+ { value: 'en', label: 'English' },
48
+ { value: 'pt_br', label: 'Português (Brasil)' },
49
+ { value: 'es', label: 'Español' }
50
+ ]
51
+ });
52
+
53
+ if (typeof lang === 'symbol') {
54
+ outro(pc.yellow('Operation cancelled.'));
55
+ process.exit(0);
56
+ }
57
+
58
+ setLocale(lang);
59
+ } else {
60
+ setLocale('en');
61
+ }
62
+
63
+ if (isUpgrade) {
64
+ intro(pc.bgBlue(pc.white(t('INTRO.UPGRADE_TITLE'))));
65
+
66
+ const tools = [];
67
+ if (fs.existsSync(path.join(process.cwd(), '.gemini'))) tools.push('gemini');
68
+ if (fs.existsSync(path.join(process.cwd(), '.roo'))) tools.push('roo');
69
+ if (fs.existsSync(path.join(process.cwd(), '.cline'))) tools.push('cline');
70
+ if (fs.existsSync(path.join(process.cwd(), '.cursor'))) tools.push('cursor');
71
+ if (fs.existsSync(path.join(process.cwd(), '.windsurf'))) tools.push('windsurf');
72
+ if (fs.existsSync(path.join(process.cwd(), '.claude'))) tools.push('claude');
73
+ if (fs.existsSync(path.join(process.cwd(), '.trae'))) tools.push('trae');
74
+ if (fs.existsSync(path.join(process.cwd(), '.kilo'))) tools.push('kilo');
75
+ if (fs.existsSync(path.join(process.cwd(), '.github'))) tools.push('copilot');
76
+ if (fs.existsSync(path.join(process.cwd(), '.opencode'))) tools.push('opencode');
77
+ if (fs.existsSync(path.join(process.cwd(), 'prompts'))) tools.push('web');
78
+
79
+ if (tools.length === 0) {
80
+ note(t('UPGRADE.NO_CONFIG'), t('UPGRADE.NO_CONFIG_TITLE'));
81
+ } else {
82
+ note(t('UPGRADE.DETECTED_TOOLS', tools.join(', ')), t('UPGRADE.DETECTED_TITLE'));
83
+ await processAgentsInstallation(tools, { locale: getLocale() });
84
+ outro(pc.green(t('UPGRADE.SUCCESS')));
85
+ process.exit(0);
86
+ }
87
+ }
88
+
89
+ // 1. Automatic Scaffold
90
+ const s = spinner();
91
+ s.start(t('SCAFFOLD.LOADING'));
92
+
93
+ try {
94
+ const stats = generateWorkflowGuide(process.cwd());
95
+ if (stats.created > 0) {
96
+ s.stop(`${t('SCAFFOLD.SUCCESS')} (${stats.created} new, ${stats.verified} verified)`);
97
+ } else {
98
+ s.stop(t('SCAFFOLD.ALREADY_EXISTS'));
99
+ }
100
+ } catch (e) {
101
+ s.stop(pc.red(t('SCAFFOLD.ERROR')));
102
+ }
103
+
104
+ // 2. Feature 5: Stack Selection
105
+ const stackOptions = Object.entries(STACK_PROFILES).map(([key, profile]) => ({
106
+ value: key,
107
+ label: profile.label
108
+ }));
109
+
110
+ const stackProfile = await select({
111
+ message: t('SETUP.STACK_SELECT'),
112
+ options: stackOptions,
113
+ initialValue: 'generic'
114
+ });
115
+
116
+ if (typeof stackProfile === 'symbol') {
117
+ outro(pc.yellow(t('GENERAL.CANCELLED')));
118
+ process.exit(0);
119
+ }
120
+
121
+ // 3. Feature 3: Global Rules
122
+ const globalRules = await text({
123
+ message: t('SETUP.GLOBAL_RULES'),
124
+ placeholder: t('SETUP.GLOBAL_RULES_HINT'),
125
+ required: false
126
+ });
127
+
128
+ if (typeof globalRules === 'symbol') {
129
+ outro(pc.yellow(t('GENERAL.CANCELLED')));
130
+ process.exit(0);
131
+ }
132
+
133
+ // 4. Tool Selection
134
+ const tools = await multiselect({
135
+ message: t('SETUP.TOOL_SELECT'),
136
+ options: [
137
+ { value: 'gemini', label: t('TOOLS.GEMINI'), hint: '.gemini/commands/dev' },
138
+ { value: 'roo', label: t('TOOLS.ROO'), hint: '.roo/ & custom_modes.json' },
139
+ { value: 'cline', label: t('TOOLS.CLINE'), hint: '.cline/ & custom_modes.json' },
140
+ { value: 'cursor', label: t('TOOLS.CURSOR'), hint: '.cursor/rules/*.mdc' },
141
+ { value: 'windsurf', label: t('TOOLS.WINDSURF'), hint: '.windsurf/workflows/*.md' },
142
+ { value: 'claude', label: 'Claude Code', hint: '.claude/commands/openspec/*.md' },
143
+ { value: 'trae', label: t('TOOLS.TRAE'), hint: '.trae/instructions.md' },
144
+ { value: 'kilo', label: t('TOOLS.KILO'), hint: '.kilo/prompts/*.md' },
145
+ { value: 'copilot', label: t('TOOLS.COPILOT'), hint: '.github/copilot-instructions.md' },
146
+ { value: 'web', label: t('TOOLS.WEB'), hint: 'prompts/*.txt' },
147
+ { value: 'opencode', label: t('TOOLS.OPENCODE'), hint: '.opencode/*.md' }
148
+ ],
149
+ required: true,
150
+ hint: t('SETUP.TOOL_HINT')
151
+ });
152
+
153
+ if (typeof tools === 'symbol') {
154
+ outro(pc.yellow(t('GENERAL.CANCELLED')));
155
+ process.exit(0);
156
+ }
157
+
158
+ if (!tools || tools.length === 0) {
159
+ outro(t('SETUP.NO_TOOLS'));
160
+ process.exit(0);
161
+ }
162
+
163
+ await processAgentsInstallation(tools, { stackProfile, globalRules, locale: getLocale() });
164
+
165
+ outro(pc.green(t('SETUP.SUCCESS')));
166
+ }
167
+
168
+ async function processAgentsInstallation(tools, options) {
169
+ const s = spinner();
170
+ s.start(t('INSTALL.LOADING'));
171
+
172
+ try {
173
+ const validAgents = await loadAgents(options);
174
+
175
+ if (validAgents.length === 0) {
176
+ s.stop(t('INSTALL.NO_AGENTS'));
177
+ return;
178
+ }
179
+
180
+ s.message(t('INSTALL.INSTALLING', tools.join(', ')));
181
+
182
+ const toolHandlers = {
183
+ gemini: async (validAgents, options) => {
184
+ const targetDir = path.join(process.cwd(), '.gemini', 'commands', 'dev');
185
+ await fsp.mkdir(targetDir, { recursive: true });
186
+
187
+ await Promise.all(
188
+ validAgents.map((agent) => {
189
+ const toml = toGeminiTOML(agent, options);
190
+ const fileName = `${agent.originalName}.toml`;
191
+ return fsp.writeFile(path.join(targetDir, fileName), toml);
192
+ })
193
+ );
194
+ },
195
+ roo: async (validAgents, options) => {
196
+ const targetDir = path.join(process.cwd(), '.roo');
197
+ await fsp.mkdir(targetDir, { recursive: true });
198
+
199
+ await Promise.all(
200
+ validAgents.map((agent) => {
201
+ const md = toKiloMarkdown(agent, options);
202
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
203
+ })
204
+ );
205
+
206
+ const modes = validAgents.map((agent) => toRooConfig(agent, agent.slug, options));
207
+ const jsonContent = JSON.stringify({ customModes: modes }, null, 2);
208
+ await fsp.writeFile(path.join(process.cwd(), 'roo_custom_modes.json'), jsonContent);
209
+ },
210
+ cline: async (validAgents, options) => {
211
+ const targetDir = path.join(process.cwd(), '.cline');
212
+ await fsp.mkdir(targetDir, { recursive: true });
213
+
214
+ await Promise.all(
215
+ validAgents.map((agent) => {
216
+ const md = toKiloMarkdown(agent, options);
217
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
218
+ })
219
+ );
220
+
221
+ const modes = validAgents.map((agent) => toRooConfig(agent, agent.slug, options));
222
+ const jsonContent = JSON.stringify({ customModes: modes }, null, 2);
223
+ await fsp.writeFile(path.join(process.cwd(), 'cline_custom_modes.json'), jsonContent);
224
+ },
225
+ windsurf: async (validAgents, options) => {
226
+ const targetDir = path.join(process.cwd(), '.windsurf', 'workflows');
227
+ await fsp.mkdir(targetDir, { recursive: true });
228
+
229
+ await Promise.all(
230
+ validAgents.map((agent) => {
231
+ const md = toWindsurfRules(agent, options);
232
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
233
+ })
234
+ );
235
+ },
236
+ claude: async (validAgents, options) => {
237
+ const targetDir = path.join(process.cwd(), '.claude', 'commands', 'openspec');
238
+ await fsp.mkdir(targetDir, { recursive: true });
239
+
240
+ await Promise.all(
241
+ validAgents.map((agent) => {
242
+ const md = toClaudeCommand(agent, options);
243
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
244
+ })
245
+ );
246
+ },
247
+ cursor: async (validAgents, options) => {
248
+ const rulesDir = path.join(process.cwd(), '.cursor', 'rules');
249
+ await fsp.mkdir(rulesDir, { recursive: true });
250
+
251
+ await Promise.all(
252
+ validAgents.map((agent) => {
253
+ const mdc = toCursorMDC(agent, options);
254
+ return fsp.writeFile(path.join(rulesDir, `${agent.slug}.mdc`), mdc);
255
+ })
256
+ );
257
+ },
258
+ kilo: async (validAgents, options) => {
259
+ const targetDir = path.join(process.cwd(), '.kilo', 'prompts');
260
+ await fsp.mkdir(targetDir, { recursive: true });
261
+
262
+ await Promise.all(
263
+ validAgents.map((agent) => {
264
+ const md = toKiloMarkdown(agent, options);
265
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
266
+ })
267
+ );
268
+ },
269
+ copilot: async (validAgents, options) => {
270
+ const githubDir = path.join(process.cwd(), '.github');
271
+ const agentsDir = path.join(githubDir, 'agents');
272
+ await fsp.mkdir(agentsDir, { recursive: true });
273
+
274
+ await Promise.all(
275
+ validAgents.map((agent) => {
276
+ const md = toCopilotInstructions(agent, options);
277
+ return fsp.writeFile(path.join(agentsDir, `${agent.slug}.md`), md);
278
+ })
279
+ );
280
+
281
+ const mainAgent = validAgents.find((a) => a.slug.includes('coder')) || validAgents[0];
282
+ const mainInstructions = toCopilotInstructions(mainAgent, options);
283
+ await fsp.writeFile(path.join(githubDir, 'copilot-instructions.md'), mainInstructions);
284
+ },
285
+ trae: async (validAgents, options) => {
286
+ const traeDir = path.join(process.cwd(), '.trae');
287
+ await fsp.mkdir(traeDir, { recursive: true });
288
+
289
+ const mainAgent = validAgents.find((a) => a.slug.includes('coder')) || validAgents[0];
290
+ const rules = toTraeRules(mainAgent, options);
291
+ await fsp.writeFile(path.join(traeDir, 'instructions.md'), rules);
292
+ },
293
+ web: async (validAgents, options) => {
294
+ const targetDir = path.join(process.cwd(), 'prompts');
295
+ await fsp.mkdir(targetDir, { recursive: true });
296
+
297
+ await Promise.all(
298
+ validAgents.map((agent) => {
299
+ const txt = toPlainSystemPrompt(agent, options);
300
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.txt`), txt);
301
+ })
302
+ );
303
+ },
304
+ opencode: async (validAgents, options) => {
305
+ const targetDir = path.join(process.cwd(), '.opencode', 'agent');
306
+ await fsp.mkdir(targetDir, { recursive: true });
307
+
308
+ await Promise.all(
309
+ validAgents.map((agent) => {
310
+ const md = toOpenCodeAgent(agent, options);
311
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
312
+ })
313
+ );
314
+ }
315
+ };
316
+
317
+ for (const tool of tools) {
318
+ const handler = toolHandlers[tool];
319
+ if (handler) {
320
+ await handler(validAgents, options);
321
+ }
322
+ }
323
+
324
+ s.stop(t('INSTALL.FINISHED'));
325
+
326
+ if (tools.includes('roo') || tools.includes('cline')) {
327
+ note(t('INSTALL.ROO_WARNING'), t('INSTALL.ROO_WARNING_TITLE'));
328
+ }
329
+ } catch (e) {
330
+ s.stop(pc.red(`${t('INSTALL.FAILED')}: ${e.message}`));
331
+ process.exit(1);
332
+ }
333
+ }
334
+
335
+ main().catch(console.error);