super-opencode 1.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 (36) hide show
  1. package/.opencode/agents/architect.md +84 -0
  2. package/.opencode/agents/backend.md +124 -0
  3. package/.opencode/agents/frontend.md +137 -0
  4. package/.opencode/agents/optimizer.md +51 -0
  5. package/.opencode/agents/pm-agent.md +105 -0
  6. package/.opencode/agents/quality.md +107 -0
  7. package/.opencode/agents/researcher.md +105 -0
  8. package/.opencode/agents/reviewer.md +80 -0
  9. package/.opencode/agents/security.md +107 -0
  10. package/.opencode/agents/writer.md +136 -0
  11. package/.opencode/commands/soc-analyze.md +137 -0
  12. package/.opencode/commands/soc-brainstorm.md +110 -0
  13. package/.opencode/commands/soc-cleanup.md +107 -0
  14. package/.opencode/commands/soc-design.md +122 -0
  15. package/.opencode/commands/soc-explain.md +113 -0
  16. package/.opencode/commands/soc-git.md +104 -0
  17. package/.opencode/commands/soc-help.md +94 -0
  18. package/.opencode/commands/soc-implement.md +112 -0
  19. package/.opencode/commands/soc-improve.md +105 -0
  20. package/.opencode/commands/soc-pm.md +99 -0
  21. package/.opencode/commands/soc-research.md +105 -0
  22. package/.opencode/commands/soc-review.md +102 -0
  23. package/.opencode/commands/soc-test.md +109 -0
  24. package/.opencode/commands/soc-workflow.md +97 -0
  25. package/.opencode/settings.json +3 -0
  26. package/.opencode/skills/confidence-check/SKILL.md +97 -0
  27. package/.opencode/skills/debug-protocol/SKILL.md +83 -0
  28. package/.opencode/skills/reflexion/SKILL.md +108 -0
  29. package/.opencode/skills/security-audit/SKILL.md +90 -0
  30. package/.opencode/skills/self-check/SKILL.md +95 -0
  31. package/.opencode/skills/simplification/SKILL.md +92 -0
  32. package/AGENTS.md +175 -0
  33. package/LICENSE +21 -0
  34. package/README.md +288 -0
  35. package/dist/cli.js +403 -0
  36. package/package.json +45 -0
package/dist/cli.js ADDED
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env node
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import chalk from "chalk";
6
+ import fs from "fs-extra";
7
+ import inquirer from "inquirer";
8
+ import ora from "ora";
9
+ // ESM replacement for __dirname
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const FRAMEWORK_DIR = path.join(__dirname, "..");
13
+ const MCP_KEY = "mcp";
14
+ const MCP_SERVERS_KEY = "mcpServers";
15
+ const CONFIG_FILENAME = "opencode.json";
16
+ const FALLBACK_FILENAME = "mcp_settings.json";
17
+ /**
18
+ * Validates that a resolved path is within the allowed base directory
19
+ * @throws Error if path traversal is detected
20
+ */
21
+ function resolveSafePath(baseDir, targetPath) {
22
+ const resolvedBase = path.resolve(baseDir);
23
+ const resolvedTarget = path.resolve(targetPath);
24
+ if (!resolvedTarget.startsWith(resolvedBase + path.sep) &&
25
+ resolvedTarget !== resolvedBase) {
26
+ throw new Error(`Path traversal detected: ${resolvedTarget} is outside ${resolvedBase}`);
27
+ }
28
+ return resolvedTarget;
29
+ }
30
+ function sanitizePaths(pathInput) {
31
+ const paths = pathInput
32
+ .split(",")
33
+ .map((p) => p.trim())
34
+ .filter(Boolean);
35
+ return paths.map((p) => {
36
+ const resolved = path.resolve(p);
37
+ return resolved;
38
+ });
39
+ }
40
+ const AVAILABLE_MCP_SERVERS = [
41
+ {
42
+ name: "Context7 (Recommended)",
43
+ value: "context7",
44
+ env: [],
45
+ recommended: true,
46
+ },
47
+ { name: "Serena (Recommended)", value: "serena", env: [], recommended: true },
48
+ {
49
+ name: "Tavily Search (Recommended)",
50
+ value: "tavily",
51
+ env: [{ name: "TAVILY_API_KEY", optional: true }],
52
+ recommended: true,
53
+ },
54
+ {
55
+ name: "Filesystem (Recommended)",
56
+ value: "filesystem",
57
+ env: [],
58
+ recommended: true,
59
+ },
60
+ {
61
+ name: "Sequential Thinking (Recommended)",
62
+ value: "sequential-thinking",
63
+ env: [],
64
+ recommended: true,
65
+ },
66
+ {
67
+ name: "GitHub",
68
+ value: "github",
69
+ env: [{ name: "GITHUB_PERSONAL_ACCESS_TOKEN", optional: false }],
70
+ recommended: false,
71
+ },
72
+ { name: "SQLite", value: "sqlite", env: [], recommended: false },
73
+ {
74
+ name: "Chrome DevTools",
75
+ value: "chrome-devtools",
76
+ env: [],
77
+ recommended: false,
78
+ },
79
+ { name: "Playwright", value: "playwright", env: [], recommended: false },
80
+ ];
81
+ function getMcpServerConfig(serverId, envVars, targetDir) {
82
+ const configs = {
83
+ context7: {
84
+ type: "local",
85
+ command: ["npx", "-y", "@upstash/context7-mcp"],
86
+ environment: envVars,
87
+ },
88
+ serena: {
89
+ type: "local",
90
+ command: [
91
+ "uvx",
92
+ "--from",
93
+ "git+https://github.com/oraios/serena",
94
+ "serena",
95
+ "start-mcp-server",
96
+ "--context",
97
+ "ide-assistant",
98
+ "--enable-web-dashboard",
99
+ "false",
100
+ "--enable-gui-log-window",
101
+ "false",
102
+ ],
103
+ environment: envVars,
104
+ },
105
+ tavily: {
106
+ type: "local",
107
+ command: ["npx", "-y", "tavily-mcp@latest"],
108
+ environment: envVars,
109
+ },
110
+ "sequential-thinking": {
111
+ type: "local",
112
+ command: [
113
+ "npx",
114
+ "-y",
115
+ "@modelcontextprotocol/server-sequential-thinking",
116
+ ],
117
+ environment: envVars,
118
+ },
119
+ github: {
120
+ type: "local",
121
+ command: ["npx", "-y", "@modelcontextprotocol/server-github"],
122
+ environment: envVars,
123
+ },
124
+ sqlite: {
125
+ type: "local",
126
+ command: ["uvx", "mcp-server-sqlite", "--memory"],
127
+ },
128
+ "chrome-devtools": {
129
+ type: "local",
130
+ command: ["npx", "-y", "chrome-devtools-mcp@latest"],
131
+ },
132
+ playwright: {
133
+ type: "local",
134
+ command: ["npx", "-y", "@playwright/mcp@latest"],
135
+ },
136
+ };
137
+ if (serverId === "filesystem") {
138
+ const sanitizedPaths = [targetDir];
139
+ return {
140
+ type: "local",
141
+ command: [
142
+ "npx",
143
+ "-y",
144
+ "@modelcontextprotocol/server-filesystem",
145
+ ...sanitizedPaths,
146
+ ],
147
+ };
148
+ }
149
+ return configs[serverId];
150
+ }
151
+ async function copyFile(src, dest, targetDir, overwrite) {
152
+ const srcPath = resolveSafePath(FRAMEWORK_DIR, path.join(FRAMEWORK_DIR, src));
153
+ const destPath = resolveSafePath(targetDir, path.join(targetDir, dest));
154
+ if (await fs.pathExists(srcPath)) {
155
+ try {
156
+ await fs.copy(srcPath, destPath, { overwrite });
157
+ }
158
+ catch (err) {
159
+ if (!overwrite && (await fs.pathExists(destPath))) {
160
+ return;
161
+ }
162
+ throw err;
163
+ }
164
+ }
165
+ }
166
+ async function installModules(modules, targetDir, overwrite) {
167
+ const moduleMap = {
168
+ core: ["README.md", "README.md"],
169
+ agents: [".opencode/agents", ".opencode/agents"],
170
+ commands: [".opencode/commands", ".opencode/commands"],
171
+ skills: [".opencode/skills", ".opencode/skills"],
172
+ };
173
+ for (const module of modules) {
174
+ const [src, dest] = moduleMap[module] || [];
175
+ if (src && dest) {
176
+ await copyFile(src, dest, targetDir, overwrite);
177
+ if (module === "core") {
178
+ await fs.ensureDir(path.join(targetDir, ".opencode"));
179
+ await copyFile("AGENTS.md", "AGENTS.md", targetDir, overwrite);
180
+ await copyFile("LICENSE", "LICENSE", targetDir, overwrite);
181
+ await copyFile(".opencode/settings.json", ".opencode/settings.json", targetDir, overwrite);
182
+ }
183
+ }
184
+ }
185
+ }
186
+ async function promptForMcpServers() {
187
+ const { installMcp } = await inquirer.prompt([
188
+ {
189
+ type: "confirm",
190
+ name: "installMcp",
191
+ message: "Would you like to configure MCP Servers?",
192
+ default: false,
193
+ },
194
+ ]);
195
+ if (!installMcp) {
196
+ return { installMcp: false, selectedServers: [] };
197
+ }
198
+ const { selectedServers } = await inquirer.prompt([
199
+ {
200
+ type: "checkbox",
201
+ name: "selectedServers",
202
+ message: "Select MCP components to install:",
203
+ choices: AVAILABLE_MCP_SERVERS.map((s) => ({
204
+ name: s.name,
205
+ value: s.value,
206
+ checked: s.recommended,
207
+ })),
208
+ pageSize: 15,
209
+ },
210
+ ]);
211
+ return { installMcp: true, selectedServers };
212
+ }
213
+ async function collectEnvVars(serverDef) {
214
+ const envVars = {};
215
+ for (const envDef of serverDef.env) {
216
+ const envName = typeof envDef === "string" ? envDef : envDef.name;
217
+ const isOptional = typeof envDef === "string" ? false : envDef.optional;
218
+ const { value } = await inquirer.prompt([
219
+ {
220
+ type: "input",
221
+ name: "value",
222
+ message: `Enter ${envName} for ${serverDef.name}${isOptional ? " (Optional)" : ""}:`,
223
+ validate: (input) => {
224
+ if (isOptional)
225
+ return true;
226
+ return input.length > 0 ? true : `${envName} is required`;
227
+ },
228
+ },
229
+ ]);
230
+ if (value || !isOptional) {
231
+ envVars[envName] = value;
232
+ }
233
+ }
234
+ return envVars;
235
+ }
236
+ async function configureFilesystemMcp(targetDir) {
237
+ const { allowedPaths: pathsInput } = await inquirer.prompt([
238
+ {
239
+ type: "input",
240
+ name: "allowedPaths",
241
+ message: "Enter absolute paths for Filesystem MCP (comma separated):",
242
+ default: targetDir,
243
+ },
244
+ ]);
245
+ const sanitizedPaths = sanitizePaths(pathsInput);
246
+ return {
247
+ type: "local",
248
+ command: [
249
+ "npx",
250
+ "-y",
251
+ "@modelcontextprotocol/server-filesystem",
252
+ ...sanitizedPaths,
253
+ ],
254
+ };
255
+ }
256
+ async function collectMcpServerConfigs(selectedServers, targetDir) {
257
+ const mcpServers = {};
258
+ for (const serverId of selectedServers) {
259
+ const serverDef = AVAILABLE_MCP_SERVERS.find((s) => s.value === serverId);
260
+ if (!serverDef)
261
+ continue;
262
+ if (serverId === "filesystem") {
263
+ mcpServers.filesystem = await configureFilesystemMcp(targetDir);
264
+ }
265
+ else {
266
+ const envVars = await collectEnvVars(serverDef);
267
+ const config = getMcpServerConfig(serverId, envVars, targetDir);
268
+ if (config) {
269
+ mcpServers[serverId] = config;
270
+ }
271
+ }
272
+ }
273
+ return mcpServers;
274
+ }
275
+ async function writeMcpConfiguration(mcpServers, scope, targetDir) {
276
+ const configPath = scope === "global"
277
+ ? path.join(os.homedir(), ".config", "opencode", CONFIG_FILENAME)
278
+ : path.join(process.cwd(), CONFIG_FILENAME);
279
+ let finalSettings = {};
280
+ let writeTarget = configPath;
281
+ if (scope === "global") {
282
+ await fs.ensureDir(path.dirname(configPath));
283
+ }
284
+ if (await fs.pathExists(configPath)) {
285
+ try {
286
+ finalSettings = await fs.readJson(configPath);
287
+ console.log(chalk.blue(`ā„¹ļø Found existing configuration at ${configPath}, merging...`));
288
+ }
289
+ catch {
290
+ console.warn(chalk.yellow(`āš ļø Could not parse existing ${configPath} (might contain comments), writing to ${FALLBACK_FILENAME} instead to avoid data loss.`));
291
+ writeTarget = path.join(targetDir, FALLBACK_FILENAME);
292
+ finalSettings = { mcpServers: {} };
293
+ }
294
+ }
295
+ const isMainConfig = writeTarget.endsWith(CONFIG_FILENAME);
296
+ const mcpKey = isMainConfig ? MCP_KEY : MCP_SERVERS_KEY;
297
+ if (!finalSettings[mcpKey]) {
298
+ finalSettings[mcpKey] = {};
299
+ }
300
+ finalSettings[mcpKey] = {
301
+ ...finalSettings[mcpKey],
302
+ ...mcpServers,
303
+ };
304
+ await fs.writeJson(writeTarget, finalSettings, { spaces: 2 });
305
+ if (writeTarget !== configPath) {
306
+ console.log(chalk.green(`āœ… MCP configuration saved to ${writeTarget}`));
307
+ console.log(chalk.yellow(`šŸ‘‰ Please manually copy the contents of '${MCP_SERVERS_KEY}' into the '${MCP_KEY}' section of your ${configPath}`));
308
+ }
309
+ else {
310
+ console.log(chalk.green(`āœ… OpenCode configuration updated at ${writeTarget}`));
311
+ }
312
+ }
313
+ async function handleMcpInstallation(scope, targetDir) {
314
+ const { installMcp, selectedServers } = await promptForMcpServers();
315
+ if (!installMcp || selectedServers.length === 0) {
316
+ return;
317
+ }
318
+ const mcpServers = await collectMcpServerConfigs(selectedServers, targetDir);
319
+ if (Object.keys(mcpServers).length > 0) {
320
+ await writeMcpConfiguration(mcpServers, scope, targetDir);
321
+ }
322
+ }
323
+ async function main() {
324
+ console.log(chalk.cyan.bold("\nšŸš€ Welcome to Super-OpenCode Installer\n"));
325
+ const scopeAnswer = await inquirer.prompt([
326
+ {
327
+ type: "list",
328
+ name: "scope",
329
+ message: "Where would you like to install Super-OpenCode?",
330
+ choices: [
331
+ { name: "Global (Recommended for User-wide access)", value: "global" },
332
+ { name: "Project (Current Directory)", value: "project" },
333
+ ],
334
+ default: "global",
335
+ },
336
+ ]);
337
+ const targetDir = scopeAnswer.scope === "global"
338
+ ? path.join(os.homedir(), ".opencode")
339
+ : process.cwd();
340
+ const questions = [
341
+ {
342
+ type: "confirm",
343
+ name: "proceed",
344
+ message: `Install Super-OpenCode to ${chalk.yellow(targetDir)}?`,
345
+ default: true,
346
+ },
347
+ {
348
+ type: "checkbox",
349
+ name: "modules",
350
+ message: "Select components to install:",
351
+ choices: [
352
+ {
353
+ name: "Core (README, LICENSE, AGENTS.md, settings.json)",
354
+ value: "core",
355
+ checked: true,
356
+ },
357
+ {
358
+ name: "Agents (Specialized Personas)",
359
+ value: "agents",
360
+ checked: true,
361
+ },
362
+ { name: "Commands (Slash Commands)", value: "commands", checked: true },
363
+ { name: "Skills (capabilities)", value: "skills", checked: true },
364
+ ],
365
+ when: (answers) => answers.proceed,
366
+ },
367
+ {
368
+ type: "confirm",
369
+ name: "overwrite",
370
+ message: "Overwrite existing files if found?",
371
+ default: false,
372
+ when: (answers) => answers.proceed,
373
+ },
374
+ ];
375
+ const answers = await inquirer.prompt(questions);
376
+ if (!answers.proceed) {
377
+ console.log(chalk.yellow("Installation cancelled."));
378
+ return;
379
+ }
380
+ const spinner = ora("Installing Super-OpenCode...").start();
381
+ try {
382
+ const { modules, overwrite } = answers;
383
+ await installModules(modules, targetDir, overwrite);
384
+ spinner.stop();
385
+ await handleMcpInstallation(scopeAnswer.scope, targetDir);
386
+ spinner.start();
387
+ spinner.succeed(chalk.green("Installation complete!"));
388
+ console.log("\nNext steps:");
389
+ if (scopeAnswer.scope === "global") {
390
+ console.log(chalk.white(`1. Framework installed to: ${chalk.cyan(targetDir)}`));
391
+ console.log(chalk.white(`2. Configuration updated at: ${chalk.cyan(path.join(os.homedir(), ".config", "opencode", "opencode.json"))}`));
392
+ }
393
+ else {
394
+ console.log(chalk.white("1. Read AGENTS.md to understand the workflow."));
395
+ console.log(chalk.white("2. Configuration updated in ./opencode.json"));
396
+ }
397
+ }
398
+ catch (error) {
399
+ spinner.fail(chalk.red("Installation failed."));
400
+ console.error(error);
401
+ }
402
+ }
403
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "super-opencode",
3
+ "version": "1.1.0",
4
+ "description": "Super-OpenCode Framework Installer",
5
+ "type": "module",
6
+ "main": "dist/cli.js",
7
+ "bin": {
8
+ "super-opencode": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "AGENTS.md",
13
+ ".opencode",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "lint": "biome check src/",
20
+ "format": "biome check --write src/",
21
+ "prepublishOnly": "npm run build",
22
+ "test": "echo \"Error: no test specified\" && exit 1"
23
+ },
24
+ "keywords": [
25
+ "opencode",
26
+ "agent",
27
+ "framework",
28
+ "cli"
29
+ ],
30
+ "author": "lst97",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "chalk": "^5.6.2",
34
+ "fs-extra": "^11.3.3",
35
+ "inquirer": "^13.2.1",
36
+ "ora": "^9.1.0"
37
+ },
38
+ "devDependencies": {
39
+ "@biomejs/biome": "2.3.13",
40
+ "@types/fs-extra": "^11.0.4",
41
+ "@types/node": "^25.0.10",
42
+ "ts-node": "^10.9.2",
43
+ "typescript": "^5.9.3"
44
+ }
45
+ }