sdd-toolkit 1.5.0 → 1.8.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,366 @@
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');
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(), '.kilocode'))) tools.push('kilo');
75
+ if (fs.existsSync(path.join(process.cwd(), '.github'))) tools.push('copilot');
76
+ if (fs.existsSync(path.join(process.cwd(), '.roo'))) tools.push('roo');
77
+ if (fs.existsSync(path.join(process.cwd(), '.opencode'))) tools.push('opencode');
78
+ if (fs.existsSync(path.join(process.cwd(), 'prompts'))) tools.push('web');
79
+
80
+ if (tools.length === 0) {
81
+ note(t('UPGRADE.NO_CONFIG'), t('UPGRADE.NO_CONFIG_TITLE'));
82
+ } else {
83
+ note(t('UPGRADE.DETECTED_TOOLS', tools.join(', ')), t('UPGRADE.DETECTED_TITLE'));
84
+ await processAgentsInstallation(tools, { locale: getLocale() });
85
+ outro(pc.green(t('UPGRADE.SUCCESS')));
86
+ process.exit(0);
87
+ }
88
+ }
89
+
90
+ // 1. Automatic Scaffold
91
+ const s = spinner();
92
+ s.start(t('SCAFFOLD.LOADING'));
93
+
94
+ try {
95
+ const stats = generateWorkflowGuide(process.cwd());
96
+ if (stats.created > 0) {
97
+ s.stop(`${t('SCAFFOLD.SUCCESS')} (${stats.created} new, ${stats.verified} verified)`);
98
+ } else {
99
+ s.stop(t('SCAFFOLD.ALREADY_EXISTS'));
100
+ }
101
+ } catch (e) {
102
+ s.stop(pc.red(t('SCAFFOLD.ERROR')));
103
+ }
104
+
105
+ // 2. Feature 5: Stack Selection
106
+ const stackOptions = Object.entries(STACK_PROFILES).map(([key, profile]) => ({
107
+ value: key,
108
+ label: profile.label
109
+ }));
110
+
111
+ const stackProfile = await select({
112
+ message: t('SETUP.STACK_SELECT'),
113
+ options: stackOptions,
114
+ initialValue: 'generic'
115
+ });
116
+
117
+ if (typeof stackProfile === 'symbol') {
118
+ outro(pc.yellow(t('GENERAL.CANCELLED')));
119
+ process.exit(0);
120
+ }
121
+
122
+ // 3. Feature 3: Global Rules
123
+ const globalRules = await text({
124
+ message: t('SETUP.GLOBAL_RULES'),
125
+ placeholder: t('SETUP.GLOBAL_RULES_HINT'),
126
+ required: false
127
+ });
128
+
129
+ if (typeof globalRules === 'symbol') {
130
+ outro(pc.yellow(t('GENERAL.CANCELLED')));
131
+ process.exit(0);
132
+ }
133
+
134
+ // 4. Tool Selection
135
+ const tools = await multiselect({
136
+ message: t('SETUP.TOOL_SELECT'),
137
+ options: [
138
+ { value: 'gemini', label: t('TOOLS.GEMINI'), hint: '.gemini/commands/dev' },
139
+ { value: 'roo', label: t('TOOLS.ROO'), hint: '.roo/commands/*.md' },
140
+ { value: 'cline', label: t('TOOLS.CLINE'), hint: '.cline/ & custom_modes.json' },
141
+ { value: 'cursor', label: t('TOOLS.CURSOR'), hint: '.cursor/commands/*.mdc' },
142
+ { value: 'windsurf', label: t('TOOLS.WINDSURF'), hint: '.windsurf/workflows/*.md' },
143
+ { value: 'claude', label: 'Claude Code', hint: '.claude/commands/agents/*.md' },
144
+ { value: 'trae', label: t('TOOLS.TRAE'), hint: '.trae/instructions.md' },
145
+ { value: 'kilo', label: t('TOOLS.KILO'), hint: '.kilocode/workflows/*.md' },
146
+ { value: 'copilot', label: t('TOOLS.COPILOT'), hint: '.github/prompts/*.md' },
147
+ { value: 'web', label: t('TOOLS.WEB'), hint: 'prompts/*.txt' },
148
+ { value: 'opencode', label: t('TOOLS.OPENCODE'), hint: '.opencode/commands/*.md' }
149
+ ],
150
+ required: true,
151
+ hint: t('SETUP.TOOL_HINT')
152
+ });
153
+
154
+ if (typeof tools === 'symbol') {
155
+ outro(pc.yellow(t('GENERAL.CANCELLED')));
156
+ process.exit(0);
157
+ }
158
+
159
+ if (!tools || tools.length === 0) {
160
+ outro(t('SETUP.NO_TOOLS'));
161
+ process.exit(0);
162
+ }
163
+
164
+ await processAgentsInstallation(tools, { stackProfile, globalRules, locale: getLocale() });
165
+
166
+ outro(pc.green(t('SETUP.SUCCESS')));
167
+ }
168
+
169
+ async function processAgentsInstallation(tools, options) {
170
+ const s = spinner();
171
+ s.start(t('INSTALL.LOADING'));
172
+
173
+ try {
174
+ const validAgents = await loadAgents(options);
175
+
176
+ if (validAgents.length === 0) {
177
+ s.stop(t('INSTALL.NO_AGENTS'));
178
+ return;
179
+ }
180
+
181
+ s.message(t('INSTALL.INSTALLING', tools.join(', ')));
182
+
183
+ const toolHandlers = {
184
+ gemini: async (validAgents, options) => {
185
+ const targetDir = path.join(process.cwd(), '.gemini', 'commands', 'dev');
186
+ await fsp.mkdir(targetDir, { recursive: true });
187
+
188
+ await Promise.all(
189
+ validAgents.map((agent) => {
190
+ const toml = toGeminiTOML(agent, options);
191
+ const fileName = `${agent.originalName}.toml`;
192
+ return fsp.writeFile(path.join(targetDir, fileName), toml);
193
+ })
194
+ );
195
+ },
196
+ roo: async (validAgents, options) => {
197
+ const targetDir = path.join(process.cwd(), '.roo', 'commands');
183
198
  await fsp.mkdir(targetDir, { recursive: true });
184
199
 
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);
200
+ await Promise.all(
201
+ validAgents.map((agent) => {
202
+ const md = toOpenCodeAgent(agent, options);
203
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
204
+ })
205
+ );
206
+ },
207
+ cline: async (validAgents, options) => {
208
+ const targetDir = path.join(process.cwd(), '.cline');
209
+ await fsp.mkdir(targetDir, { recursive: true });
210
+
211
+ await Promise.all(
212
+ validAgents.map((agent) => {
213
+ const md = toKiloMarkdown(agent, options);
214
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
215
+ })
216
+ );
217
+
218
+ const modes = validAgents.map((agent) => toRooConfig(agent, agent.slug, options));
219
+ const jsonContent = JSON.stringify({ customModes: modes }, null, 2);
220
+ await fsp.writeFile(path.join(process.cwd(), 'cline_custom_modes.json'), jsonContent);
221
+ },
222
+ windsurf: async (validAgents, options) => {
223
+ const targetDir = path.join(process.cwd(), '.windsurf', 'workflows');
224
+ await fsp.mkdir(targetDir, { recursive: true });
225
+
226
+ await Promise.all(
227
+ validAgents.map((agent) => {
228
+ const md = toWindsurfRules(agent, options);
229
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
230
+ })
231
+ );
232
+ },
233
+ claude: async (validAgents, options) => {
234
+ const targetDir = path.join(process.cwd(), '.claude', 'commands', 'agents');
194
235
  await fsp.mkdir(targetDir, { recursive: true });
195
236
 
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');
237
+ await Promise.all(
238
+ validAgents.map((agent) => {
239
+ const md = toClaudeCommand(agent, options);
240
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
241
+ })
242
+ );
243
+ },
244
+ cursor: async (validAgents, options) => {
245
+ const commandsDir = path.join(process.cwd(), '.cursor', 'commands');
246
+ await fsp.mkdir(commandsDir, { recursive: true });
247
+
248
+ await Promise.all(
249
+ validAgents.map((agent) => {
250
+ const mdc = toCursorMDC(agent, options);
251
+ return fsp.writeFile(path.join(commandsDir, `${agent.slug}.mdc`), mdc);
252
+ })
253
+ );
254
+ },
255
+ kilo: async (validAgents, options) => {
256
+ const targetDir = path.join(process.cwd(), '.kilocode', 'workflows');
208
257
  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') {
258
+
259
+ await Promise.all(
260
+ validAgents.map((agent) => {
261
+ const md = toKiloMarkdown(agent, options);
262
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
263
+ })
264
+ );
265
+ },
266
+ copilot: async (validAgents, options) => {
216
267
  const githubDir = path.join(process.cwd(), '.github');
217
- const agentsDir = path.join(githubDir, 'agents');
218
- await fsp.mkdir(agentsDir, { recursive: true });
268
+ const promptsDir = path.join(githubDir, 'prompts');
269
+ await fsp.mkdir(promptsDir, { recursive: true });
219
270
 
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
- }));
271
+ await Promise.all(
272
+ validAgents.map((agent) => {
273
+ const md = toCopilotInstructions(agent, options);
274
+ return fsp.writeFile(path.join(promptsDir, `${agent.slug}.md`), md);
275
+ })
276
+ );
224
277
 
225
- const mainAgent = validAgents.find(a => a.slug.includes('coder')) || validAgents[0];
278
+ const mainAgent = validAgents.find((a) => a.slug.includes('coder')) || validAgents[0];
226
279
  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');
280
+ await fsp.writeFile(path.join(githubDir, 'prompts.md'), mainInstructions);
281
+ },
282
+ trae: async (validAgents, options) => {
283
+ const traeDir = path.join(process.cwd(), '.trae');
284
+ await fsp.mkdir(traeDir, { recursive: true });
285
+
286
+ const mainAgent = validAgents.find((a) => a.slug.includes('coder')) || validAgents[0];
287
+ const rules = toTraeRules(mainAgent, options);
288
+ await fsp.writeFile(path.join(traeDir, 'instructions.md'), rules);
289
+ },
290
+ web: async (validAgents, options) => {
291
+ const targetDir = path.join(process.cwd(), 'prompts');
292
+ await fsp.mkdir(targetDir, { recursive: true });
293
+
294
+ await Promise.all(
295
+ validAgents.map((agent) => {
296
+ const txt = toPlainSystemPrompt(agent, options);
297
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.txt`), txt);
298
+ })
299
+ );
300
+ },
301
+ opencode: async (validAgents, options) => {
302
+ const targetDir = path.join(process.cwd(), '.opencode', 'commands');
253
303
  await fsp.mkdir(targetDir, { recursive: true });
254
304
 
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
- }));
305
+ await Promise.all(
306
+ validAgents.map((agent) => {
307
+ const md = toOpenCodeAgent(agent, options);
308
+ return fsp.writeFile(path.join(targetDir, `${agent.slug}.md`), md);
309
+ })
310
+ );
311
+
312
+ // Generate AGENTS.md with interaction rules and agent location
313
+ const agentsMdPath = path.join(process.cwd(), 'AGENTS.md');
314
+ let agentsMdContent = `# Interaction Rules
315
+
316
+ - Always respond to the user in the language they initially interact in; if they interact in English, respond in English, if they interact in Portuguese, respond in Portuguese.
317
+ - If possible, display reasoning in the user's language as well.
318
+ - Be didactic when explaining things, focus on providing complete responses and not just summaries.
319
+ - Whenever possible, provide examples to illustrate concepts.
320
+
321
+ # Allowed Commands
322
+
323
+ - Never execute rm or rm -rf commands without confirming with the user.
324
+ - Whenever possible, use more specific commands instead of generic ones.
325
+ - Be cautious when using commands that may affect critical systems, such as shutdown or reboot.
326
+ - For commands that may affect files or directories, always confirm with the user before executing.
327
+ - Never execute commands that require administrative privileges (sudo, admin) without explicit permission from the user.
328
+ - Avoid running background processes or daemons unless explicitly requested.
329
+ - Be cautious when using commands that alter network settings, firewall configurations, or external connections.
330
+ - Always quote file paths that contain spaces to avoid interpretation errors.
331
+ - For package installation commands (npm install, pip install, etc.), confirm that the user has control over dependencies and versions.
332
+ - Avoid irreversible git operations (such as force push or reset --hard) without confirmation.
333
+
334
+ # Agent Location
335
+
336
+ Custom agents are located in .opencode/commands/`;
337
+
338
+ let userRules = '';
339
+ if (options.globalRules && options.globalRules.trim()) {
340
+ userRules = '\n\n# User Specified Rules\n\n' + options.globalRules.split('\n').filter(line => line.trim()).map(line => '- ' + line.trim()).join('\n');
341
+ }
342
+ agentsMdContent += userRules;
343
+
344
+ await fsp.writeFile(agentsMdPath, agentsMdContent);
259
345
  }
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);
346
+ };
347
+
348
+ for (const tool of tools) {
349
+ const handler = toolHandlers[tool];
350
+ if (handler) {
351
+ await handler(validAgents, options);
352
+ }
353
+ }
354
+
355
+ s.stop(t('INSTALL.FINISHED'));
356
+
357
+ if (tools.includes('roo') || tools.includes('cline')) {
358
+ note(t('INSTALL.ROO_WARNING'), t('INSTALL.ROO_WARNING_TITLE'));
359
+ }
360
+ } catch (e) {
361
+ s.stop(pc.red(`${t('INSTALL.FAILED')}: ${e.message}`));
362
+ process.exit(1);
363
+ }
364
+ }
365
+
366
+ main().catch(console.error);