opencode-setup 0.1.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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/chunk-PKHQD5QT.js +1106 -0
  4. package/dist/cli.js +339 -0
  5. package/dist/index.js +170 -0
  6. package/dist/templates/agents/planner.md +10 -0
  7. package/dist/templates/agents/reviewer.md +12 -0
  8. package/dist/templates/agents/tester.md +5 -0
  9. package/dist/templates/agents-md/backend-go.md +28 -0
  10. package/dist/templates/agents-md/backend-python.md +27 -0
  11. package/dist/templates/agents-md/base.md +29 -0
  12. package/dist/templates/agents-md/frontend-ts.md +39 -0
  13. package/dist/templates/agents-md/fullstack.md +25 -0
  14. package/dist/templates/commands/lint.md +2 -0
  15. package/dist/templates/commands/plan.md +10 -0
  16. package/dist/templates/commands/review.md +6 -0
  17. package/dist/templates/commands/test.md +2 -0
  18. package/dist/templates/configs/balanced.json +10 -0
  19. package/dist/templates/configs/budget.json +10 -0
  20. package/dist/templates/configs/google-only.json +10 -0
  21. package/dist/templates/configs/minimax.json +10 -0
  22. package/dist/templates/configs/power.json +10 -0
  23. package/dist/templates/skills/code-review/SKILL.md +27 -0
  24. package/dist/templates/skills/frontend-design/SKILL.md +20 -0
  25. package/dist/templates/skills/testing/SKILL.md +19 -0
  26. package/package.json +83 -0
  27. package/templates/agents/planner.md +10 -0
  28. package/templates/agents/reviewer.md +12 -0
  29. package/templates/agents/tester.md +5 -0
  30. package/templates/agents-md/backend-go.md +28 -0
  31. package/templates/agents-md/backend-python.md +27 -0
  32. package/templates/agents-md/base.md +29 -0
  33. package/templates/agents-md/frontend-ts.md +39 -0
  34. package/templates/agents-md/fullstack.md +25 -0
  35. package/templates/commands/lint.md +2 -0
  36. package/templates/commands/plan.md +10 -0
  37. package/templates/commands/review.md +6 -0
  38. package/templates/commands/test.md +2 -0
  39. package/templates/configs/balanced.json +10 -0
  40. package/templates/configs/budget.json +10 -0
  41. package/templates/configs/google-only.json +10 -0
  42. package/templates/configs/minimax.json +10 -0
  43. package/templates/configs/power.json +10 -0
  44. package/templates/skills/code-review/SKILL.md +27 -0
  45. package/templates/skills/frontend-design/SKILL.md +20 -0
  46. package/templates/skills/testing/SKILL.md +19 -0
package/dist/cli.js ADDED
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ applyPreset,
4
+ autoMigrate,
5
+ formatReport,
6
+ generateAgents,
7
+ generateAgentsMD,
8
+ generateCommands,
9
+ generateEnvExample,
10
+ generateSkills,
11
+ listPresets,
12
+ runAllChecks,
13
+ runMigration,
14
+ runValidation,
15
+ writeAgents,
16
+ writeAgentsMD,
17
+ writeCommands,
18
+ writeEnvExample,
19
+ writeGlobalConfig,
20
+ writeProjectConfig,
21
+ writeSkills
22
+ } from "./chunk-PKHQD5QT.js";
23
+
24
+ // src/cli.ts
25
+ import { Command } from "commander";
26
+
27
+ // src/prompt/wizard.ts
28
+ import { homedir } from "os";
29
+
30
+ // src/prompt/questions.ts
31
+ import { select, checkbox, input, confirm } from "@inquirer/prompts";
32
+ async function askExperience() {
33
+ const answer = await select({
34
+ message: "AI \uCF54\uB529 \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD55C \uC801\uC774 \uC788\uB098\uC694?",
35
+ choices: [
36
+ { name: "\uC544\uB2C8\uC624, \uCC98\uC74C\uC785\uB2C8\uB2E4", value: "new" },
37
+ { name: "\uD55C\uBC88\uC529 \uC368\uBD24\uC5B4\uC694", value: "beginner" },
38
+ { name: "\uC790\uC8FC \uC0AC\uC6A9\uD569\uB2C8\uB2E4", value: "intermediate" },
39
+ { name: "\uC804\uBB38\uAC00\uC785\uB2C8\uB2E4", value: "advanced" }
40
+ ]
41
+ });
42
+ return answer;
43
+ }
44
+ async function askPreviousTool() {
45
+ const answer = await select({
46
+ message: "\uC774\uC804\uC5D0 \uC0AC\uC6A9\uD558\uB358 AI \uCF54\uB529 \uB3C4\uAD6C\uAC00 \uC788\uB098\uC694?",
47
+ choices: [
48
+ { name: "\uC5C6\uC2B5\uB2C8\uB2E4", value: "none" },
49
+ { name: "Claude Code", value: "claude-code" },
50
+ { name: "Cursor", value: "cursor" },
51
+ { name: "Aider", value: "aider" },
52
+ { name: "\uAE30\uD0C0", value: "other" }
53
+ ]
54
+ });
55
+ return answer;
56
+ }
57
+ async function askProviders() {
58
+ const answer = await checkbox({
59
+ message: "\uC5B4\uB5A4 AI \uC11C\uBE44\uC2A4\uB97C \uC0AC\uC6A9\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? (\uBCF5\uC218 \uC120\uD0DD \uAC00\uB2A5)",
60
+ choices: [
61
+ { name: "OpenCode Go (\uBB34\uB8CC)", value: "opencode-go", checked: true },
62
+ { name: "OpenCode Zen (\uC720\uB8CC \uD050\uB808\uC774\uC158)", value: "opencode-zen" },
63
+ { name: "Anthropic (Claude)", value: "anthropic" },
64
+ { name: "Google (Gemini)", value: "google" },
65
+ { name: "OpenAI", value: "openai" },
66
+ { name: "DeepSeek", value: "deepseek" },
67
+ { name: "\uB85C\uCEEC \uBAA8\uB378 (Ollama/LM Studio)", value: "local" },
68
+ { name: "\uAE30\uD0C0", value: "other" }
69
+ ]
70
+ });
71
+ return answer;
72
+ }
73
+ async function askBudget() {
74
+ const answer = await select({
75
+ message: "\uC6D4 \uC608\uC0B0\uC740 \uC5BC\uB9C8\uB098 \uB418\uC2DC\uB098\uC694?",
76
+ choices: [
77
+ { name: "\uBB34\uB8CC (Big Pickle)", value: "free" },
78
+ { name: "$5-10/\uC6D4 (MiniMax \uCD94\uCC9C)", value: "low" },
79
+ { name: "$20-50/\uC6D4 (Sonnet + Flash)", value: "mid" },
80
+ { name: "$50+/\uC6D4 (Sonnet + Opus)", value: "high" }
81
+ ]
82
+ });
83
+ return answer;
84
+ }
85
+ async function askProjectLanguage() {
86
+ const answer = await select({
87
+ message: "\uD504\uB85C\uC81D\uD2B8 \uC8FC \uC5B8\uC5B4\uB294 \uBB34\uC5C7\uC778\uAC00\uC694?",
88
+ choices: [
89
+ { name: "TypeScript", value: "typescript" },
90
+ { name: "JavaScript", value: "javascript" },
91
+ { name: "Go", value: "go" },
92
+ { name: "Python", value: "python" },
93
+ { name: "Rust", value: "rust" },
94
+ { name: "\uAE30\uD0C0", value: "other" }
95
+ ]
96
+ });
97
+ return answer;
98
+ }
99
+ async function askProjectFramework() {
100
+ const answer = await input({
101
+ message: "\uD504\uB808\uC784\uC6CC\uD06C/\uB77C\uC774\uBE0C\uB7EC\uB9AC\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694 (\uC608: nextjs, react, fastapi, gin)",
102
+ default: "nextjs"
103
+ });
104
+ return answer;
105
+ }
106
+ async function askTestRunner() {
107
+ const answer = await select({
108
+ message: "\uD14C\uC2A4\uD2B8 \uB3C4\uAD6C\uB294 \uBB34\uC5C7\uC744 \uC0AC\uC6A9\uD558\uC2DC\uB098\uC694?",
109
+ choices: [
110
+ { name: "Vitest", value: "vitest" },
111
+ { name: "Jest", value: "jest" },
112
+ { name: "Playwright", value: "playwright" },
113
+ { name: "Go test", value: "go test" },
114
+ { name: "Pytest", value: "pytest" },
115
+ { name: "\uAE30\uD0C0", value: "other" }
116
+ ]
117
+ });
118
+ return answer;
119
+ }
120
+ async function askLinter() {
121
+ const answer = await select({
122
+ message: "\uB9B0\uD130/\uD3EC\uB9F7\uD130\uB294 \uBB34\uC5C7\uC744 \uC0AC\uC6A9\uD558\uC2DC\uB098\uC694?",
123
+ choices: [
124
+ { name: "Biome", value: "biome" },
125
+ { name: "ESLint", value: "eslint" },
126
+ { name: "Prettier", value: "prettier" },
127
+ { name: "Golangci-lint", value: "golangci-lint" },
128
+ { name: "Ruff", value: "ruff" },
129
+ { name: "\uAE30\uD0C0", value: "other" }
130
+ ]
131
+ });
132
+ return answer;
133
+ }
134
+ async function askProjectScale() {
135
+ const answer = await select({
136
+ message: "\uD504\uB85C\uC81D\uD2B8 \uADDC\uBAA8\uB294 \uC5B4\uB5A4\uAC00\uC694?",
137
+ choices: [
138
+ { name: "\uB2E8\uC77C \uD504\uB85C\uC81D\uD2B8", value: "single" },
139
+ { name: "\uBAA8\uB178\uB808\uD3EC", value: "monorepo" }
140
+ ]
141
+ });
142
+ return answer;
143
+ }
144
+ async function askMCPServers() {
145
+ const addServer = await confirm({
146
+ message: "MCP \uC11C\uBC84\uB97C \uC5F0\uACB0\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
147
+ default: false
148
+ });
149
+ if (!addServer) {
150
+ return [];
151
+ }
152
+ const servers = await checkbox({
153
+ message: "\uC5B4\uB5A4 MCP \uC11C\uBC84\uB97C \uC5F0\uACB0\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
154
+ choices: [
155
+ { name: "Figma", value: "figma" },
156
+ { name: "Notion", value: "notion" },
157
+ { name: "GitHub", value: "github" },
158
+ { name: "Sentry", value: "sentry" },
159
+ { name: "Filesystem", value: "filesystem" }
160
+ ]
161
+ });
162
+ return servers.map((name) => ({
163
+ name,
164
+ config: {}
165
+ }));
166
+ }
167
+ async function askPlugins() {
168
+ const answer = await checkbox({
169
+ message: "\uD50C\uB7EC\uADF8\uC778\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
170
+ choices: [
171
+ { name: "oh-my-opencode (\uBA40\uD2F0 \uC5D0\uC774\uC804\uD2B8 \uC624\uCF00\uC2A4\uD2B8\uB808\uC774\uC158)", value: "oh-my-opencode" },
172
+ { name: "opencode-skillful (Skill \uAD00\uB9AC \uD655\uC7A5)", value: "opencode-skillful" },
173
+ { name: "opencode-wakatime (\uCF54\uB529 \uC2DC\uAC04 \uCD94\uC801)", value: "opencode-wakatime" }
174
+ ]
175
+ });
176
+ return answer;
177
+ }
178
+ async function askPermissionLevel() {
179
+ const answer = await select({
180
+ message: "\uC790\uB3D9\uD654 \uC218\uC900\uC744 \uC120\uD0DD\uD574\uC8FC\uC138\uC694",
181
+ choices: [
182
+ { name: "\uC548\uC804 - \uBAA8\uB4E0 \uC791\uC5C5 \uD655\uC778 \uC694\uCCAD (\uC2E0\uADDC \uC0AC\uC6A9\uC790 \uCD94\uCC9C)", value: "safe" },
183
+ { name: "\uADE0\uD615 - \uC548\uC804\uC740 \uD655\uC778, \uC77C\uC0C1\uC801 \uC791\uC5C5\uC740 \uC790\uB3D9", value: "balanced" },
184
+ { name: "\uC790\uB3D9 - \uB300\uBD80\uBD84 \uC790\uB3D9 \uC2B9\uC778 (\uC219\uB828 \uC0AC\uC6A9\uC790)", value: "auto" }
185
+ ]
186
+ });
187
+ return answer;
188
+ }
189
+ async function askConfirmGeneration(files) {
190
+ console.log("\n\u{1F4C1} \uB2E4\uC74C \uD30C\uC77C\uB4E4\uC774 \uC0DD\uC131\uB429\uB2C8\uB2E4:\n");
191
+ for (const file of files) {
192
+ console.log(` \u2022 ${file}`);
193
+ }
194
+ console.log("");
195
+ const answer = await confirm({
196
+ message: "\uC704 \uD30C\uC77C\uB4E4\uC744 \uC0DD\uC131\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
197
+ default: true
198
+ });
199
+ return answer;
200
+ }
201
+
202
+ // src/prompt/wizard.ts
203
+ function getProjectDir() {
204
+ return process.cwd();
205
+ }
206
+ function getHomeDir() {
207
+ return homedir();
208
+ }
209
+ async function runInitWizard() {
210
+ console.log("\u{1F527} OpenCode \uD658\uACBD \uC138\uD305 \uB9C8\uBC95\uC0AC\uC5D0 \uC624\uC2E0 \uAC83\uC744 \uD658\uC601\uD569\uB2C8\uB2E4!\n");
211
+ const experience = await askExperience();
212
+ const previousTool = await askPreviousTool();
213
+ if (previousTool !== "none") {
214
+ console.log(`
215
+ \u26A0\uFE0F ${previousTool}\uC5D0\uC11C \uC804\uD658\uD558\uC2DC\uB294\uAD70\uC694!`);
216
+ console.log(" 'opencode-setup migrate' \uBA85\uB839\uC73C\uB85C \uB9C8\uC774\uADF8\uB808\uC774\uC158\uC744 \uBA3C\uC800 \uC9C4\uD589\uD574\uC8FC\uC138\uC694.\n");
217
+ return;
218
+ }
219
+ console.log("\n\u{1F4E6} Provider \uC120\uD0DD");
220
+ const providers = await askProviders();
221
+ const budget = await askBudget();
222
+ console.log("\n\u{1F6E0}\uFE0F \uD504\uB85C\uC81D\uD2B8 \uC815\uBCF4");
223
+ const projectLanguage = await askProjectLanguage();
224
+ const projectFramework = await askProjectFramework();
225
+ const testRunner = await askTestRunner();
226
+ const linter = await askLinter();
227
+ console.log("\n\u{1F4CA} \uD504\uB85C\uC81D\uD2B8 \uADDC\uBAA8");
228
+ const projectScale = await askProjectScale();
229
+ console.log("\n\u{1F50C} MCP \uC11C\uBC84 (\uC120\uD0DD)");
230
+ const mcpServers = await askMCPServers();
231
+ console.log("\n\u{1F527} \uD50C\uB7EC\uADF8\uC778");
232
+ const plugins = await askPlugins();
233
+ console.log("\n\u{1F512} \uAD8C\uD55C \uC124\uC815");
234
+ const permissionLevel = await askPermissionLevel();
235
+ const profile = {
236
+ experienceLevel: experience,
237
+ previousTool,
238
+ providers,
239
+ budget,
240
+ projectLanguage,
241
+ projectFramework,
242
+ testRunner,
243
+ linter,
244
+ projectScale,
245
+ mcpServers,
246
+ plugins,
247
+ permissionLevel
248
+ };
249
+ const projectDir = getProjectDir();
250
+ const homeDir = getHomeDir();
251
+ const filesToCreate = [
252
+ `${homeDir}/.config/opencode/opencode.json (\uAE00\uB85C\uBC8C \uC124\uC815)`,
253
+ `${projectDir}/opencode.json (\uD504\uB85C\uC81D\uD2B8 \uC124\uC815)`,
254
+ `${projectDir}/AGENTS.md (\uD504\uB85C\uC81D\uD2B8 \uAC00\uC774\uB4DC)`,
255
+ `${projectDir}/.opencode/commands/ (\uCEE4\uC2A4\uD140 \uCEE4\uB9E8\uB4DC)`,
256
+ `${projectDir}/.opencode/agents/ (\uCEE4\uC2A4\uD140 \uC5D0\uC774\uC804\uD2B8)`,
257
+ `${projectDir}/.opencode/skills/ (\uC2A4\uD0AC)`,
258
+ `${projectDir}/.env.example (\uD658\uACBD\uBCC0\uC218 \uD15C\uD50C\uB9BF)`
259
+ ];
260
+ const confirmed = await askConfirmGeneration(filesToCreate);
261
+ if (!confirmed) {
262
+ console.log("\n\u274C \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
263
+ return;
264
+ }
265
+ console.log("\n\u2728 \uD30C\uC77C \uC0DD\uC131 \uC911...\n");
266
+ try {
267
+ writeGlobalConfig(profile, homeDir);
268
+ console.log(" \u2713 \uAE00\uB85C\uBC8C \uC124\uC815 \uC0DD\uC131\uB428");
269
+ writeProjectConfig(profile, projectDir);
270
+ console.log(" \u2713 \uD504\uB85C\uC81D\uD2B8 \uC124\uC815 \uC0DD\uC131\uB428");
271
+ const agentsMD = generateAgentsMD(profile);
272
+ writeAgentsMD(agentsMD, projectDir);
273
+ console.log(" \u2713 AGENTS.md \uC0DD\uC131\uB428");
274
+ const commands = generateCommands();
275
+ writeCommands(commands, projectDir);
276
+ console.log(" \u2713 \uCEE4\uC2A4\uD140 \uCEE4\uB9E8\uB4DC \uC0DD\uC131\uB428");
277
+ const agents = generateAgents();
278
+ writeAgents(agents, projectDir);
279
+ console.log(" \u2713 \uCEE4\uC2A4\uD140 \uC5D0\uC774\uC804\uD2B8 \uC0DD\uC131\uB428");
280
+ const skills = generateSkills(profile);
281
+ writeSkills(skills, projectDir);
282
+ console.log(" \u2713 \uC2A4\uD0AC \uC0DD\uC131\uB428");
283
+ const envContent = generateEnvExample(profile);
284
+ writeEnvExample(envContent, projectDir);
285
+ console.log(" \u2713 .env.example \uC0DD\uC131\uB428");
286
+ console.log("\n\u2705 \uC0DD\uC131 \uC644\uB8CC!\n");
287
+ console.log("\uB2E4\uC74C \uB2E8\uACC4:");
288
+ console.log("1. .env.example\uC744 .env\uB85C \uBCF5\uC0AC\uD558\uACE0 API \uD0A4\uB97C \uC124\uC815\uD558\uC138\uC694");
289
+ console.log("2. OpenCode\uB97C \uC7AC\uC2DC\uC791\uD558\uAC70\uB098 'opencode --reload'\uB97C \uC2E4\uD589\uD558\uC138\uC694");
290
+ console.log("3. \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C 'opencode' \uBA85\uB839\uC5B4\uB97C \uC0AC\uC6A9\uD574\uBCF4\uC138\uC694!\n");
291
+ } catch (error) {
292
+ console.error("\n\u274C \uD30C\uC77C \uC0DD\uC131 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4:");
293
+ console.error(error instanceof Error ? error.message : String(error));
294
+ }
295
+ }
296
+
297
+ // src/cli.ts
298
+ var program = new Command();
299
+ program.name("opencode-setup").description("OpenCode \uCD08\uAE30 \uD658\uACBD \uC138\uD305 \uB3C4\uAD6C").version("0.1.0");
300
+ program.command("init").description("\uB300\uD654\uD615 \uCD08\uAE30 \uC138\uD305").action(runInitWizard);
301
+ var preset = program.command("preset").description("\uD504\uB9AC\uC14B \uAD00\uB9AC");
302
+ preset.command("list").description("\uD504\uB9AC\uC14B \uBAA9\uB85D").action(() => {
303
+ console.log(listPresets());
304
+ });
305
+ preset.command("apply <name>").description("\uD504\uB9AC\uC14B \uC801\uC6A9").action(async (name) => {
306
+ const result = await applyPreset(name, process.cwd());
307
+ console.log(JSON.stringify(result, null, 2));
308
+ });
309
+ program.command("migrate [tool]").description("\uAE30\uC874 \uB3C4\uAD6C \uB9C8\uC774\uADF8\uB808\uC774\uC158 (claude-code, cursor, aider). \uB3C4\uAD6C \uBBF8\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0").action(async (tool) => {
310
+ const projectDir = process.cwd();
311
+ if (!tool) {
312
+ console.log("\u{1F50D} \uC790\uB3D9 \uAC10\uC9C0 \uC911...");
313
+ console.log(autoMigrate(projectDir));
314
+ return;
315
+ }
316
+ const result = await runMigration(tool, projectDir);
317
+ console.log(result);
318
+ });
319
+ program.command("validate").description("\uC124\uC815 \uAC80\uC99D").action(async () => {
320
+ const result = await runValidation(process.cwd());
321
+ console.log(result);
322
+ });
323
+ program.command("doctor").description("\uD658\uACBD \uC9C4\uB2E8").action(async () => {
324
+ const results = await runAllChecks(process.cwd());
325
+ console.log(formatReport(results));
326
+ });
327
+ program.parse();
328
+ process.on("SIGINT", () => {
329
+ console.log("\nCancelled by user");
330
+ process.exit(130);
331
+ });
332
+ process.on("uncaughtException", (error) => {
333
+ console.error(`Fatal error: ${error.message}`);
334
+ process.exit(1);
335
+ });
336
+ process.on("unhandledRejection", (reason) => {
337
+ console.error(`Unhandled rejection: ${reason}`);
338
+ process.exit(1);
339
+ });
package/dist/index.js ADDED
@@ -0,0 +1,170 @@
1
+ import {
2
+ applyPreset,
3
+ autoMigrate,
4
+ formatReport,
5
+ generateAgents,
6
+ generateAgentsMD,
7
+ generateCommands,
8
+ generateEnvExample,
9
+ generateSkills,
10
+ listPresets,
11
+ runAllChecks,
12
+ runMigration,
13
+ runValidation,
14
+ writeAgents,
15
+ writeAgentsMD,
16
+ writeCommands,
17
+ writeEnvExample,
18
+ writeGlobalConfig,
19
+ writeProjectConfig,
20
+ writeSkills
21
+ } from "./chunk-PKHQD5QT.js";
22
+
23
+ // src/tools/setup-init.ts
24
+ import { tool } from "@opencode-ai/plugin";
25
+ import { homedir } from "os";
26
+ var { schema } = tool;
27
+ var setupInit = tool({
28
+ description: "Initialize OpenCode environment with custom profile. Accepts a UserProfile JSON string.",
29
+ args: {
30
+ profile: schema.string().describe("UserProfile as JSON string")
31
+ },
32
+ async execute(args, context) {
33
+ let profile;
34
+ try {
35
+ profile = JSON.parse(args.profile);
36
+ } catch {
37
+ return "\u274C Invalid profile JSON. Please provide a valid UserProfile object.";
38
+ }
39
+ const projectDir = context.directory;
40
+ const homeDir = homedir();
41
+ const generatedFiles = [];
42
+ try {
43
+ writeGlobalConfig(profile, homeDir);
44
+ generatedFiles.push(`${homeDir}/.config/opencode/opencode.json`);
45
+ writeProjectConfig(profile, projectDir);
46
+ generatedFiles.push(`${projectDir}/opencode.json`);
47
+ const agentsMD = generateAgentsMD(profile);
48
+ writeAgentsMD(agentsMD, projectDir);
49
+ generatedFiles.push(`${projectDir}/AGENTS.md`);
50
+ const commands = generateCommands();
51
+ writeCommands(commands, projectDir);
52
+ generatedFiles.push(`${projectDir}/.opencode/commands/`);
53
+ const agents = generateAgents();
54
+ writeAgents(agents, projectDir);
55
+ generatedFiles.push(`${projectDir}/.opencode/agents/`);
56
+ const skills = generateSkills(profile);
57
+ writeSkills(skills, projectDir);
58
+ generatedFiles.push(`${projectDir}/.opencode/skills/`);
59
+ const envContent = generateEnvExample(profile);
60
+ writeEnvExample(envContent, projectDir);
61
+ generatedFiles.push(`${projectDir}/.env.example`);
62
+ const filesList = generatedFiles.map((f) => ` \u2022 ${f}`).join("\n");
63
+ return `\u2705 OpenCode environment initialized successfully!
64
+
65
+ Generated files:
66
+ ${filesList}
67
+
68
+ Next steps:
69
+ 1. Copy .env.example to .env and set your API keys
70
+ 2. Restart OpenCode or run 'opencode --reload'
71
+ 3. Start using OpenCode in your project!`;
72
+ } catch (error) {
73
+ return `\u274C Failed to initialize environment:
74
+ ${error instanceof Error ? error.message : String(error)}
75
+
76
+ Generated files so far:
77
+ ${generatedFiles.map((f) => ` \u2022 ${f}`).join("\n")}`;
78
+ }
79
+ }
80
+ });
81
+
82
+ // src/tools/setup-preset.ts
83
+ import { tool as tool2 } from "@opencode-ai/plugin";
84
+ var { schema: schema2 } = tool2;
85
+ var setupPresetList = tool2({
86
+ description: "Available presets for OpenCode setup.",
87
+ args: {},
88
+ async execute() {
89
+ return listPresets();
90
+ }
91
+ });
92
+ var setupPresetApply = tool2({
93
+ description: "Apply a preset configuration to the project.",
94
+ args: {
95
+ name: schema2.string().describe("Preset name (e.g., 'budget', 'balanced', 'frontend-ts', 'backend-go')")
96
+ },
97
+ async execute(args, context) {
98
+ const result = await applyPreset(args.name, context.directory);
99
+ if (!result.success) {
100
+ return `\u274C Failed to apply preset '${args.name}'.
101
+
102
+ ${result.warnings.join("\n")}`;
103
+ }
104
+ const filesList = result.files.map((f) => ` \u2022 ${f}`).join("\n");
105
+ return `\u2705 Preset '${args.name}' applied successfully!
106
+
107
+ Generated files:
108
+ ${filesList}${result.warnings.length > 0 ? `
109
+
110
+ Warnings:
111
+ ${result.warnings.join("\n")}` : ""}`;
112
+ }
113
+ });
114
+
115
+ // src/tools/setup-migrate.ts
116
+ import { tool as tool3 } from "@opencode-ai/plugin";
117
+ var { schema: schema3 } = tool3;
118
+ var setupMigrate = tool3({
119
+ description: "Migrate configuration from existing tools (Claude Code, Cursor, Aider) to OpenCode.",
120
+ args: {
121
+ tool: schema3.string().optional().describe("Source tool: 'claude-code', 'cursor', or 'aider'. If omitted, auto-detects."),
122
+ sourcePath: schema3.string().optional().describe("Path to source config (default: current directory)")
123
+ },
124
+ async execute(args, context) {
125
+ const rootPath = args.sourcePath || context.directory;
126
+ if (!args.tool) {
127
+ return autoMigrate(rootPath);
128
+ }
129
+ return runMigration(args.tool, rootPath);
130
+ }
131
+ });
132
+
133
+ // src/tools/setup-validate.ts
134
+ import { tool as tool4 } from "@opencode-ai/plugin";
135
+ var setupValidate = tool4({
136
+ description: "Validate OpenCode configuration files. Checks opencode.json schema and AGENTS.md structure.",
137
+ args: {},
138
+ async execute(_args, context) {
139
+ return runValidation(context.directory);
140
+ }
141
+ });
142
+
143
+ // src/tools/setup-doctor.ts
144
+ import { tool as tool5 } from "@opencode-ai/plugin";
145
+ var setupDoctor = tool5({
146
+ description: "Diagnose OpenCode environment. Checks installation, API keys, authentication, config files, LSP servers, and plugins.",
147
+ args: {},
148
+ async execute(_args, context) {
149
+ const results = await runAllChecks(context.directory);
150
+ return formatReport(results);
151
+ }
152
+ });
153
+
154
+ // src/index.ts
155
+ var OcSetupPlugin = async (ctx) => {
156
+ void ctx;
157
+ return {
158
+ tool: {
159
+ setup_init: setupInit,
160
+ setup_preset_list: setupPresetList,
161
+ setup_preset_apply: setupPresetApply,
162
+ setup_migrate: setupMigrate,
163
+ setup_validate: setupValidate,
164
+ setup_doctor: setupDoctor
165
+ }
166
+ };
167
+ };
168
+ export {
169
+ OcSetupPlugin
170
+ };
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: "계획 수립 전문 에이전트"
3
+ permission:
4
+ write: deny
5
+ edit: deny
6
+ bash:
7
+ "*": deny
8
+ ---
9
+ You are a planning specialist. Analyze requirements and create detailed implementation plans.
10
+ Do not make any code changes. Only analyze and suggest.
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: "코드 리뷰 전문 에이전트"
3
+ permission:
4
+ write: deny
5
+ edit: deny
6
+ bash:
7
+ "git diff *": allow
8
+ "git log *": allow
9
+ "*": deny
10
+ ---
11
+ You are a code reviewer. Provide constructive feedback without making direct changes.
12
+ Focus on security, performance, readability, and test coverage.
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "테스트 코드 작성 전문 에이전트"
3
+ ---
4
+ You are a testing specialist. Write comprehensive test cases and run them to verify.
5
+ Follow the project's testing conventions in AGENTS.md.
@@ -0,0 +1,28 @@
1
+ # {{projectName}}
2
+
3
+ Go 백엔드 프로젝트.
4
+
5
+ ## Project Structure
6
+
7
+ - `cmd/` - 진입점
8
+ - `internal/` - 비즈니스 로직
9
+ - `pkg/` - 외부 공개 패키지
10
+ - `api/` - API 핸들러
11
+
12
+ ## Code Standards
13
+
14
+ - gofmt 준수
15
+ - 에러 래핑: fmt.Errorf("context: %w", err)
16
+ - 전역 상태 금지, 생성자 주입
17
+ - 모든 exported 함수에 GoDoc 주석
18
+
19
+ ## Testing
20
+
21
+ - go test ./...
22
+ - 테이블 기반 테스트
23
+ - testcontainers (DB 테스트)
24
+
25
+ ## Rules
26
+
27
+ - 핸들러에 비즈니스 로직 금지
28
+ - 모든 DB 쿼리는 repository 인터페이스 경유
@@ -0,0 +1,27 @@
1
+ # {{projectName}}
2
+
3
+ Python + FastAPI 프로젝트.
4
+
5
+ ## Project Structure
6
+
7
+ - `app/` - 메인 애플리케이션
8
+ - `app/routers/` - API 라우터
9
+ - `app/models/` - Pydantic 모델
10
+ - `app/services/` - 비즈니스 로직
11
+ - `tests/` - 테스트
12
+
13
+ ## Code Standards
14
+
15
+ - Python 3.12+
16
+ - Type hints 필수
17
+ - ruff로 린트/포맷
18
+
19
+ ## Testing
20
+
21
+ - pytest
22
+ - httpx (AsyncClient)
23
+
24
+ ## Rules
25
+
26
+ - 라우터에 비즈니스 로직 금지
27
+ - 모든 엔드포인트에 Pydantic 응답 모델 정의
@@ -0,0 +1,29 @@
1
+ # {{projectName}}
2
+
3
+ {{projectDescription}}
4
+
5
+ ## Project Structure
6
+
7
+ {{structure}}
8
+
9
+ ## Code Standards
10
+
11
+ {{codeStandards}}
12
+
13
+ ## Testing
14
+
15
+ {{testingGuidelines}}
16
+
17
+ ## Development Workflow
18
+
19
+ 1. Plan 모드에서 기능 계획 수립
20
+ 2. Build 모드에서 구현
21
+ 3. /test 커맨드로 테스트 실행
22
+ 4. /review 커맨드로 코드 리뷰
23
+ 5. 수동 확인 후 커밋
24
+
25
+ {{#if rules}}
26
+ ## Rules
27
+
28
+ {{rules}}
29
+ {{/if}}
@@ -0,0 +1,39 @@
1
+ # {{projectName}}
2
+
3
+ TypeScript + Next.js 프로젝트. App Router 사용.
4
+
5
+ ## Project Structure
6
+
7
+ - `src/app/` - App Router 페이지 및 레이아웃
8
+ - `src/components/` - 재사용 컴포넌트
9
+ - `src/lib/` - 유틸리티, API 클라이언트
10
+ - `src/hooks/` - 커스텀 React 훅
11
+ - `src/types/` - TypeScript 타입 정의
12
+
13
+ ## Code Standards
14
+
15
+ - TypeScript strict mode 활성화
16
+ - 함수형 컴포넌트 + React hooks 사용
17
+ - 네이밍: PascalCase (컴포넌트), camelCase (함수/변수), kebab-case (파일)
18
+ - 서버 컴포넌트 기본, 클라이언트는 필요시에만 'use client'
19
+
20
+ ## Testing
21
+
22
+ - 테스트 러너: {{testRunner}}
23
+ - E2E: playwright
24
+ - 린트/포맷: {{linter}}
25
+ - 4단계: lint check → unit → integration → e2e
26
+
27
+ ## Development Workflow
28
+
29
+ 1. Plan 모드에서 기능 계획 수립
30
+ 2. Build 모드에서 구현
31
+ 3. /test 커맨드로 테스트 실행
32
+ 4. /review 커맨드로 코드 리뷰
33
+ 5. 수동 확인 후 커밋
34
+
35
+ ## Rules
36
+
37
+ - 새 컴포넌트 작성 시 반드시 테스트 파일도 함께 생성
38
+ - API 호출은 src/lib/ 내 함수를 통해 수행
39
+ - 에러 바운더리를 페이지 단위로 설정
@@ -0,0 +1,25 @@
1
+ # {{projectName}}
2
+
3
+ 풀스택 프로젝트.
4
+
5
+ ## Project Structure
6
+
7
+ - `frontend/` - 프론트엔드
8
+ - `backend/` - 백엔드
9
+ - `shared/` - 공유 타입/유틸
10
+
11
+ ## Code Standards
12
+
13
+ - TypeScript strict mode (프론트+백)
14
+ - 공유 타입은 shared/ 경유
15
+
16
+ ## Testing
17
+
18
+ - 프론트: {{testRunner}}
19
+ - 백: 프레임워크별 테스트 러너
20
+ - E2E: playwright
21
+
22
+ ## Rules
23
+
24
+ - API 계약은 shared/에 타입으로 정의
25
+ - 프론트-백 간 직접 import 금지, shared만 사용