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.
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/dist/chunk-PKHQD5QT.js +1106 -0
- package/dist/cli.js +339 -0
- package/dist/index.js +170 -0
- package/dist/templates/agents/planner.md +10 -0
- package/dist/templates/agents/reviewer.md +12 -0
- package/dist/templates/agents/tester.md +5 -0
- package/dist/templates/agents-md/backend-go.md +28 -0
- package/dist/templates/agents-md/backend-python.md +27 -0
- package/dist/templates/agents-md/base.md +29 -0
- package/dist/templates/agents-md/frontend-ts.md +39 -0
- package/dist/templates/agents-md/fullstack.md +25 -0
- package/dist/templates/commands/lint.md +2 -0
- package/dist/templates/commands/plan.md +10 -0
- package/dist/templates/commands/review.md +6 -0
- package/dist/templates/commands/test.md +2 -0
- package/dist/templates/configs/balanced.json +10 -0
- package/dist/templates/configs/budget.json +10 -0
- package/dist/templates/configs/google-only.json +10 -0
- package/dist/templates/configs/minimax.json +10 -0
- package/dist/templates/configs/power.json +10 -0
- package/dist/templates/skills/code-review/SKILL.md +27 -0
- package/dist/templates/skills/frontend-design/SKILL.md +20 -0
- package/dist/templates/skills/testing/SKILL.md +19 -0
- package/package.json +83 -0
- package/templates/agents/planner.md +10 -0
- package/templates/agents/reviewer.md +12 -0
- package/templates/agents/tester.md +5 -0
- package/templates/agents-md/backend-go.md +28 -0
- package/templates/agents-md/backend-python.md +27 -0
- package/templates/agents-md/base.md +29 -0
- package/templates/agents-md/frontend-ts.md +39 -0
- package/templates/agents-md/fullstack.md +25 -0
- package/templates/commands/lint.md +2 -0
- package/templates/commands/plan.md +10 -0
- package/templates/commands/review.md +6 -0
- package/templates/commands/test.md +2 -0
- package/templates/configs/balanced.json +10 -0
- package/templates/configs/budget.json +10 -0
- package/templates/configs/google-only.json +10 -0
- package/templates/configs/minimax.json +10 -0
- package/templates/configs/power.json +10 -0
- package/templates/skills/code-review/SKILL.md +27 -0
- package/templates/skills/frontend-design/SKILL.md +20 -0
- 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,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,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만 사용
|