opencode-deepa 1.0.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/README.md +38 -0
- package/dist/cli/config-io.d.ts +23 -0
- package/dist/cli/config-io.d.ts.map +1 -0
- package/dist/cli/config-manager.d.ts +6 -0
- package/dist/cli/config-manager.d.ts.map +1 -0
- package/dist/cli/custom-skills.d.ts +26 -0
- package/dist/cli/custom-skills.d.ts.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +706 -0
- package/dist/cli/install.d.ts +3 -0
- package/dist/cli/install.d.ts.map +1 -0
- package/dist/cli/paths.d.ts +10 -0
- package/dist/cli/paths.d.ts.map +1 -0
- package/dist/cli/providers.d.ts +3 -0
- package/dist/cli/providers.d.ts.map +1 -0
- package/dist/cli/system.d.ts +7 -0
- package/dist/cli/system.d.ts.map +1 -0
- package/dist/cli/types.d.ts +37 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/config/agent-mcps.d.ts +19 -0
- package/dist/config/agent-mcps.d.ts.map +1 -0
- package/dist/config/mcp-types.d.ts +4 -0
- package/dist/config/mcp-types.d.ts.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +305 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/skills/index.d.ts +53 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/tools/codeindexScan.d.ts +7 -0
- package/dist/tools/codeindexScan.d.ts.map +1 -0
- package/dist/tools/deepaSummary/index.d.ts +7 -0
- package/dist/tools/deepaSummary/index.d.ts.map +1 -0
- package/dist/tools/fastcode.d.ts +7 -0
- package/dist/tools/fastcode.d.ts.map +1 -0
- package/dist/tools/gitingest.d.ts +10 -0
- package/dist/tools/gitingest.d.ts.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/opencode-deepa.schema.json +74 -0
- package/package.json +68 -0
- package/src/skills/deepa-planning/SKILL.md +98 -0
- package/src/skills/index.ts +142 -0
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// src/cli/config-io.ts
|
|
5
|
+
import {
|
|
6
|
+
copyFileSync,
|
|
7
|
+
existsSync as existsSync2,
|
|
8
|
+
readFileSync,
|
|
9
|
+
renameSync,
|
|
10
|
+
statSync,
|
|
11
|
+
writeFileSync
|
|
12
|
+
} from "fs";
|
|
13
|
+
|
|
14
|
+
// src/cli/paths.ts
|
|
15
|
+
import { existsSync, mkdirSync } from "fs";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
function getConfigDir() {
|
|
19
|
+
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
20
|
+
return join(userConfigDir, "opencode");
|
|
21
|
+
}
|
|
22
|
+
function getConfigJson() {
|
|
23
|
+
return join(getConfigDir(), "opencode.json");
|
|
24
|
+
}
|
|
25
|
+
function getConfigJsonc() {
|
|
26
|
+
return join(getConfigDir(), "opencode.jsonc");
|
|
27
|
+
}
|
|
28
|
+
function getLiteConfig() {
|
|
29
|
+
return join(getConfigDir(), "opencode-deepa.json");
|
|
30
|
+
}
|
|
31
|
+
function getExistingConfigPath() {
|
|
32
|
+
const jsonPath = getConfigJson();
|
|
33
|
+
if (existsSync(jsonPath))
|
|
34
|
+
return jsonPath;
|
|
35
|
+
const jsoncPath = getConfigJsonc();
|
|
36
|
+
if (existsSync(jsoncPath))
|
|
37
|
+
return jsoncPath;
|
|
38
|
+
return jsonPath;
|
|
39
|
+
}
|
|
40
|
+
function ensureConfigDir() {
|
|
41
|
+
const configDir = getConfigDir();
|
|
42
|
+
if (!existsSync(configDir)) {
|
|
43
|
+
mkdirSync(configDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/config/agent-mcps.ts
|
|
48
|
+
var DEFAULT_AGENT_MCPS = {
|
|
49
|
+
deepa: ["websearch", "context7", "grep_app", "gitingest", "deepaSummary", "filesystem"]
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/skills/index.ts
|
|
53
|
+
var RECOMMENDED_SKILLS = [
|
|
54
|
+
{
|
|
55
|
+
name: "simplify",
|
|
56
|
+
repo: "https://github.com/brianlovin/claude-config",
|
|
57
|
+
skillName: "simplify",
|
|
58
|
+
allowedAgents: ["deepa", "deepa-builder"],
|
|
59
|
+
description: "YAGNI code simplification expert"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "agent-browser",
|
|
63
|
+
repo: "https://github.com/vercel-labs/agent-browser",
|
|
64
|
+
skillName: "agent-browser",
|
|
65
|
+
allowedAgents: ["deepa", "deepa-planner", "deepa-builder"],
|
|
66
|
+
description: "High-performance browser automation",
|
|
67
|
+
postInstallCommands: [
|
|
68
|
+
"npm install -g agent-browser",
|
|
69
|
+
"agent-browser install"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "ask-questions-if-underspecified",
|
|
74
|
+
repo: "https://github.com/smartfrog/opencode-froggy",
|
|
75
|
+
skillName: "ask-questions-if-underspecified",
|
|
76
|
+
allowedAgents: ["deepa", "deepa-planner", "deepa-builder"],
|
|
77
|
+
description: "Clarify requirements before implementing. Use when serious doubts arise."
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
function installSkill(skill) {
|
|
81
|
+
console.log(`Would install skill: ${skill.name} from ${skill.repo}`);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// src/cli/providers.ts
|
|
86
|
+
function generateLiteConfig(installConfig) {
|
|
87
|
+
const config = {
|
|
88
|
+
preset: "openai",
|
|
89
|
+
presets: {}
|
|
90
|
+
};
|
|
91
|
+
const createAgentConfig = (agentName, modelInfo) => {
|
|
92
|
+
const isOrchestrator = agentName === "deepa";
|
|
93
|
+
const skills = isOrchestrator ? ["*"] : RECOMMENDED_SKILLS.filter((s) => s.allowedAgents.includes("*") || s.allowedAgents.includes(agentName)).map((s) => s.skillName);
|
|
94
|
+
let agentSkills = skills;
|
|
95
|
+
if (agentName === "deepa-planner") {
|
|
96
|
+
agentSkills = [...new Set([...skills, "ask-questions-if-underspecified"])];
|
|
97
|
+
} else if (agentName === "deepa-builder") {
|
|
98
|
+
agentSkills = [...new Set([...skills, "simplify"])];
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
model: modelInfo.model,
|
|
102
|
+
variant: modelInfo.variant,
|
|
103
|
+
skills: agentSkills,
|
|
104
|
+
mcps: DEFAULT_AGENT_MCPS[agentName] ?? []
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
config.presets.openai = {
|
|
108
|
+
deepa: createAgentConfig("deepa", {}),
|
|
109
|
+
deepa_planner: createAgentConfig("deepa-planner", {}),
|
|
110
|
+
deepa_builder: createAgentConfig("deepa-builder", {})
|
|
111
|
+
};
|
|
112
|
+
config.presets.kimi = {
|
|
113
|
+
deepa: createAgentConfig("deepa", {}),
|
|
114
|
+
deepa_planner: createAgentConfig("deepa-planner", {}),
|
|
115
|
+
deepa_builder: createAgentConfig("deepa-builder", {})
|
|
116
|
+
};
|
|
117
|
+
config.presets.copilot = {
|
|
118
|
+
deepa: createAgentConfig("deepa", {}),
|
|
119
|
+
deepa_planner: createAgentConfig("deepa-planner", {}),
|
|
120
|
+
deepa_builder: createAgentConfig("deepa-builder", {})
|
|
121
|
+
};
|
|
122
|
+
config.presets["zai-plan"] = {
|
|
123
|
+
deepa: createAgentConfig("deepa", {}),
|
|
124
|
+
deepa_planner: createAgentConfig("deepa-planner", {}),
|
|
125
|
+
deepa_builder: createAgentConfig("deepa-builder", {})
|
|
126
|
+
};
|
|
127
|
+
if (installConfig.hasTmux) {
|
|
128
|
+
config.tmux = {
|
|
129
|
+
enabled: true,
|
|
130
|
+
layout: "main-vertical",
|
|
131
|
+
main_pane_size: 60
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return config;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/cli/config-io.ts
|
|
138
|
+
var PACKAGE_NAME = "opencode-deepa";
|
|
139
|
+
function stripJsonComments(json) {
|
|
140
|
+
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
141
|
+
const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
|
|
142
|
+
return json.replace(commentPattern, (match, commentGroup) => commentGroup ? "" : match).replace(trailingCommaPattern, (match, comma, closing) => comma ? closing : match);
|
|
143
|
+
}
|
|
144
|
+
function parseConfigFile(path) {
|
|
145
|
+
try {
|
|
146
|
+
if (!existsSync2(path))
|
|
147
|
+
return { config: null };
|
|
148
|
+
const stat = statSync(path);
|
|
149
|
+
if (stat.size === 0)
|
|
150
|
+
return { config: null };
|
|
151
|
+
const content = readFileSync(path, "utf-8");
|
|
152
|
+
if (content.trim().length === 0)
|
|
153
|
+
return { config: null };
|
|
154
|
+
return { config: JSON.parse(stripJsonComments(content)) };
|
|
155
|
+
} catch (err) {
|
|
156
|
+
return { config: null, error: String(err) };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function parseConfig(path) {
|
|
160
|
+
const result = parseConfigFile(path);
|
|
161
|
+
if (result.config || result.error)
|
|
162
|
+
return result;
|
|
163
|
+
if (path.endsWith(".json")) {
|
|
164
|
+
const jsoncPath = path.replace(/\.json$/, ".jsonc");
|
|
165
|
+
return parseConfigFile(jsoncPath);
|
|
166
|
+
}
|
|
167
|
+
return { config: null };
|
|
168
|
+
}
|
|
169
|
+
function writeConfig(configPath, config) {
|
|
170
|
+
if (configPath.endsWith(".jsonc")) {
|
|
171
|
+
console.warn("[config-manager] Writing to .jsonc file - comments will not be preserved");
|
|
172
|
+
}
|
|
173
|
+
const tmpPath = `${configPath}.tmp`;
|
|
174
|
+
const bakPath = `${configPath}.bak`;
|
|
175
|
+
const content = `${JSON.stringify(config, null, 2)}
|
|
176
|
+
`;
|
|
177
|
+
if (existsSync2(configPath)) {
|
|
178
|
+
copyFileSync(configPath, bakPath);
|
|
179
|
+
}
|
|
180
|
+
writeFileSync(tmpPath, content);
|
|
181
|
+
renameSync(tmpPath, configPath);
|
|
182
|
+
}
|
|
183
|
+
async function addPluginToOpenCodeConfig() {
|
|
184
|
+
try {
|
|
185
|
+
ensureConfigDir();
|
|
186
|
+
} catch (err) {
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
configPath: getConfigDir(),
|
|
190
|
+
error: `Failed to create config directory: ${err}`
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const configPath = getExistingConfigPath();
|
|
194
|
+
try {
|
|
195
|
+
const { config: parsedConfig, error } = parseConfig(configPath);
|
|
196
|
+
if (error) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
configPath,
|
|
200
|
+
error: `Failed to parse config: ${error}`
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const config = parsedConfig ?? {};
|
|
204
|
+
const plugins = config.plugin ?? [];
|
|
205
|
+
const filteredPlugins = plugins.filter((p) => p !== PACKAGE_NAME && !p.startsWith(`${PACKAGE_NAME}@`));
|
|
206
|
+
filteredPlugins.push(PACKAGE_NAME);
|
|
207
|
+
config.plugin = filteredPlugins;
|
|
208
|
+
writeConfig(configPath, config);
|
|
209
|
+
return { success: true, configPath };
|
|
210
|
+
} catch (err) {
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
configPath,
|
|
214
|
+
error: `Failed to update opencode config: ${err}`
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function writeLiteConfig(installConfig) {
|
|
219
|
+
const configPath = getLiteConfig();
|
|
220
|
+
try {
|
|
221
|
+
ensureConfigDir();
|
|
222
|
+
const config = generateLiteConfig(installConfig);
|
|
223
|
+
const tmpPath = `${configPath}.tmp`;
|
|
224
|
+
const bakPath = `${configPath}.bak`;
|
|
225
|
+
const content = `${JSON.stringify(config, null, 2)}
|
|
226
|
+
`;
|
|
227
|
+
if (existsSync2(configPath)) {
|
|
228
|
+
copyFileSync(configPath, bakPath);
|
|
229
|
+
}
|
|
230
|
+
writeFileSync(tmpPath, content);
|
|
231
|
+
renameSync(tmpPath, configPath);
|
|
232
|
+
return { success: true, configPath };
|
|
233
|
+
} catch (err) {
|
|
234
|
+
return {
|
|
235
|
+
success: false,
|
|
236
|
+
configPath,
|
|
237
|
+
error: `Failed to write lite config: ${err}`
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function disableDefaultAgents() {
|
|
242
|
+
const configPath = getExistingConfigPath();
|
|
243
|
+
try {
|
|
244
|
+
ensureConfigDir();
|
|
245
|
+
const { config: parsedConfig, error } = parseConfig(configPath);
|
|
246
|
+
if (error) {
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
configPath,
|
|
250
|
+
error: `Failed to parse config: ${error}`
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const config = parsedConfig ?? {};
|
|
254
|
+
const agent = config.agent ?? {};
|
|
255
|
+
agent.explore = { disable: true };
|
|
256
|
+
agent.general = { disable: true };
|
|
257
|
+
config.agent = agent;
|
|
258
|
+
writeConfig(configPath, config);
|
|
259
|
+
return { success: true, configPath };
|
|
260
|
+
} catch (err) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
configPath,
|
|
264
|
+
error: `Failed to disable default agents: ${err}`
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function detectCurrentConfig() {
|
|
269
|
+
const result = {
|
|
270
|
+
isInstalled: false,
|
|
271
|
+
hasKimi: false,
|
|
272
|
+
hasOpenAI: false,
|
|
273
|
+
hasAnthropic: false,
|
|
274
|
+
hasCopilot: false,
|
|
275
|
+
hasZaiPlan: false,
|
|
276
|
+
hasAntigravity: false,
|
|
277
|
+
hasChutes: false,
|
|
278
|
+
hasOpencodeZen: false,
|
|
279
|
+
hasTmux: false
|
|
280
|
+
};
|
|
281
|
+
const { config } = parseConfig(getExistingConfigPath());
|
|
282
|
+
if (!config)
|
|
283
|
+
return result;
|
|
284
|
+
const plugins = config.plugin ?? [];
|
|
285
|
+
result.isInstalled = plugins.some((p) => p.startsWith(PACKAGE_NAME));
|
|
286
|
+
result.hasAntigravity = plugins.some((p) => p.startsWith("opencode-antigravity-auth"));
|
|
287
|
+
const providers = config.provider;
|
|
288
|
+
result.hasKimi = !!providers?.kimi;
|
|
289
|
+
result.hasAnthropic = !!providers?.anthropic;
|
|
290
|
+
result.hasCopilot = !!providers?.["github-copilot"];
|
|
291
|
+
result.hasZaiPlan = !!providers?.["zai-coding-plan"];
|
|
292
|
+
result.hasChutes = !!providers?.chutes;
|
|
293
|
+
if (providers?.google)
|
|
294
|
+
result.hasAntigravity = true;
|
|
295
|
+
const { config: liteConfig } = parseConfig(getLiteConfig());
|
|
296
|
+
if (liteConfig && typeof liteConfig === "object") {
|
|
297
|
+
const configObj = liteConfig;
|
|
298
|
+
const presetName = configObj.preset;
|
|
299
|
+
const presets = configObj.presets;
|
|
300
|
+
const agents = presets?.[presetName];
|
|
301
|
+
if (agents) {
|
|
302
|
+
const models = Object.values(agents).map((a) => a?.model).filter(Boolean);
|
|
303
|
+
result.hasOpenAI = models.some((m) => m?.startsWith("openai/"));
|
|
304
|
+
result.hasAnthropic = models.some((m) => m?.startsWith("anthropic/"));
|
|
305
|
+
result.hasCopilot = models.some((m) => m?.startsWith("github-copilot/"));
|
|
306
|
+
result.hasZaiPlan = models.some((m) => m?.startsWith("zai-coding-plan/"));
|
|
307
|
+
result.hasOpencodeZen = models.some((m) => m?.startsWith("opencode/"));
|
|
308
|
+
if (models.some((m) => m?.startsWith("google/"))) {
|
|
309
|
+
result.hasAntigravity = true;
|
|
310
|
+
}
|
|
311
|
+
if (models.some((m) => m?.startsWith("chutes/"))) {
|
|
312
|
+
result.hasChutes = true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (configObj.tmux && typeof configObj.tmux === "object") {
|
|
316
|
+
const tmuxConfig = configObj.tmux;
|
|
317
|
+
result.hasTmux = tmuxConfig.enabled === true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
// src/cli/system.ts
|
|
323
|
+
import { statSync as statSync2 } from "fs";
|
|
324
|
+
var cachedOpenCodePath = null;
|
|
325
|
+
function getOpenCodePaths() {
|
|
326
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
327
|
+
return [
|
|
328
|
+
"opencode",
|
|
329
|
+
`${home}/.local/bin/opencode`,
|
|
330
|
+
`${home}/.opencode/bin/opencode`,
|
|
331
|
+
`${home}/bin/opencode`,
|
|
332
|
+
"/usr/local/bin/opencode",
|
|
333
|
+
"/opt/opencode/bin/opencode",
|
|
334
|
+
"/usr/bin/opencode",
|
|
335
|
+
"/bin/opencode",
|
|
336
|
+
"/Applications/OpenCode.app/Contents/MacOS/opencode",
|
|
337
|
+
`${home}/Applications/OpenCode.app/Contents/MacOS/opencode`,
|
|
338
|
+
"/opt/homebrew/bin/opencode",
|
|
339
|
+
"/home/linuxbrew/.linuxbrew/bin/opencode",
|
|
340
|
+
`${home}/homebrew/bin/opencode`,
|
|
341
|
+
`${home}/Library/Application Support/opencode/bin/opencode`,
|
|
342
|
+
"/snap/bin/opencode",
|
|
343
|
+
"/var/snap/opencode/current/bin/opencode",
|
|
344
|
+
"/var/lib/flatpak/exports/bin/ai.opencode.OpenCode",
|
|
345
|
+
`${home}/.local/share/flatpak/exports/bin/ai.opencode.OpenCode`,
|
|
346
|
+
"/nix/store/opencode/bin/opencode",
|
|
347
|
+
`${home}/.nix-profile/bin/opencode`,
|
|
348
|
+
"/run/current-system/sw/bin/opencode",
|
|
349
|
+
`${home}/.cargo/bin/opencode`,
|
|
350
|
+
`${home}/.npm-global/bin/opencode`,
|
|
351
|
+
"/usr/local/lib/node_modules/opencode/bin/opencode",
|
|
352
|
+
`${home}/.yarn/bin/opencode`,
|
|
353
|
+
`${home}/.pnpm-global/bin/opencode`
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
function resolveOpenCodePath() {
|
|
357
|
+
if (cachedOpenCodePath) {
|
|
358
|
+
return cachedOpenCodePath;
|
|
359
|
+
}
|
|
360
|
+
const paths = getOpenCodePaths();
|
|
361
|
+
for (const opencodePath of paths) {
|
|
362
|
+
if (opencodePath === "opencode")
|
|
363
|
+
continue;
|
|
364
|
+
try {
|
|
365
|
+
const stat = statSync2(opencodePath);
|
|
366
|
+
if (stat.isFile()) {
|
|
367
|
+
cachedOpenCodePath = opencodePath;
|
|
368
|
+
return opencodePath;
|
|
369
|
+
}
|
|
370
|
+
} catch {}
|
|
371
|
+
}
|
|
372
|
+
return "opencode";
|
|
373
|
+
}
|
|
374
|
+
async function isOpenCodeInstalled() {
|
|
375
|
+
const paths = getOpenCodePaths();
|
|
376
|
+
for (const opencodePath of paths) {
|
|
377
|
+
try {
|
|
378
|
+
const proc = Bun.spawn([opencodePath, "--version"], {
|
|
379
|
+
stdout: "pipe",
|
|
380
|
+
stderr: "pipe"
|
|
381
|
+
});
|
|
382
|
+
await proc.exited;
|
|
383
|
+
if (proc.exitCode === 0) {
|
|
384
|
+
cachedOpenCodePath = opencodePath;
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
} catch {}
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
async function getOpenCodeVersion() {
|
|
392
|
+
const opencodePath = resolveOpenCodePath();
|
|
393
|
+
try {
|
|
394
|
+
const proc = Bun.spawn([opencodePath, "--version"], {
|
|
395
|
+
stdout: "pipe",
|
|
396
|
+
stderr: "pipe"
|
|
397
|
+
});
|
|
398
|
+
const output = await new Response(proc.stdout).text();
|
|
399
|
+
await proc.exited;
|
|
400
|
+
if (proc.exitCode === 0) {
|
|
401
|
+
return output.trim();
|
|
402
|
+
}
|
|
403
|
+
} catch {}
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
function getOpenCodePath() {
|
|
407
|
+
const path = resolveOpenCodePath();
|
|
408
|
+
return path === "opencode" ? null : path;
|
|
409
|
+
}
|
|
410
|
+
// src/cli/custom-skills.ts
|
|
411
|
+
var CUSTOM_SKILLS = [
|
|
412
|
+
{
|
|
413
|
+
name: "deepa-planning",
|
|
414
|
+
repo: "https://github.com/yourusername/opencode-deepa",
|
|
415
|
+
skillName: "deepa-planning",
|
|
416
|
+
allowedAgents: ["deepa"],
|
|
417
|
+
description: "Strategic planning and task decomposition for complex coding tasks"
|
|
418
|
+
}
|
|
419
|
+
];
|
|
420
|
+
function installCustomSkill(skill) {
|
|
421
|
+
const args = [
|
|
422
|
+
"skills",
|
|
423
|
+
"add",
|
|
424
|
+
skill.repo,
|
|
425
|
+
"--skill",
|
|
426
|
+
skill.skillName,
|
|
427
|
+
"-a",
|
|
428
|
+
"opencode",
|
|
429
|
+
"-y",
|
|
430
|
+
"--global"
|
|
431
|
+
];
|
|
432
|
+
try {
|
|
433
|
+
const result = Bun.spawnSync(["npx", ...args]);
|
|
434
|
+
if (result.exitCode !== 0) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (skill.postInstallCommands && skill.postInstallCommands.length > 0) {
|
|
438
|
+
console.log(`Running post-install commands for ${skill.name}...`);
|
|
439
|
+
for (const cmd of skill.postInstallCommands) {
|
|
440
|
+
console.log(`> ${cmd}`);
|
|
441
|
+
const [command, ...cmdArgs] = cmd.split(" ");
|
|
442
|
+
const cmdResult = Bun.spawnSync([command, ...cmdArgs]);
|
|
443
|
+
if (cmdResult.exitCode !== 0) {
|
|
444
|
+
console.warn(`Post-install command failed: ${cmd}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return true;
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error(`Failed to install skill: ${skill.name}`, error);
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/cli/install.ts
|
|
456
|
+
var GREEN = "\x1B[32m";
|
|
457
|
+
var BLUE = "\x1B[34m";
|
|
458
|
+
var YELLOW = "\x1B[33m";
|
|
459
|
+
var RED = "\x1B[31m";
|
|
460
|
+
var BOLD = "\x1B[1m";
|
|
461
|
+
var DIM = "\x1B[2m";
|
|
462
|
+
var RESET = "\x1B[0m";
|
|
463
|
+
var SYMBOLS = {
|
|
464
|
+
check: `${GREEN}\u2713${RESET}`,
|
|
465
|
+
cross: `${RED}\u2717${RESET}`,
|
|
466
|
+
arrow: `${BLUE}\u2192${RESET}`,
|
|
467
|
+
bullet: `${DIM}\u2022${RESET}`,
|
|
468
|
+
info: `${BLUE}\u2139${RESET}`,
|
|
469
|
+
warn: `${YELLOW}\u26A0${RESET}`,
|
|
470
|
+
star: `${YELLOW}\u2605${RESET}`
|
|
471
|
+
};
|
|
472
|
+
function printHeader(isUpdate) {
|
|
473
|
+
console.log();
|
|
474
|
+
console.log(`${BOLD}opencode-deepa ${isUpdate ? "Update" : "Install"}${RESET}`);
|
|
475
|
+
console.log("=".repeat(30));
|
|
476
|
+
console.log();
|
|
477
|
+
}
|
|
478
|
+
function printStep(step, total, message) {
|
|
479
|
+
console.log(`${DIM}[${step}/${total}]${RESET} ${message}`);
|
|
480
|
+
}
|
|
481
|
+
function printSuccess(message) {
|
|
482
|
+
console.log(`${SYMBOLS.check} ${message}`);
|
|
483
|
+
}
|
|
484
|
+
function printError(message) {
|
|
485
|
+
console.log(`${SYMBOLS.cross} ${RED}${message}${RESET}`);
|
|
486
|
+
}
|
|
487
|
+
function printInfo(message) {
|
|
488
|
+
console.log(`${SYMBOLS.info} ${message}`);
|
|
489
|
+
}
|
|
490
|
+
async function checkOpenCodeInstalled() {
|
|
491
|
+
const installed = await isOpenCodeInstalled();
|
|
492
|
+
if (!installed) {
|
|
493
|
+
printError("OpenCode is not installed on this system.");
|
|
494
|
+
printInfo("Install it with:");
|
|
495
|
+
console.log(` ${BLUE}curl -fsSL https://opencode.ai/install | bash${RESET}`);
|
|
496
|
+
console.log();
|
|
497
|
+
printInfo("Or if already installed, add it to your PATH:");
|
|
498
|
+
console.log(` ${BLUE}export PATH="$HOME/.local/bin:$PATH"${RESET}`);
|
|
499
|
+
console.log(` ${BLUE}export PATH="$HOME/.opencode/bin:$PATH"${RESET}`);
|
|
500
|
+
return { ok: false };
|
|
501
|
+
}
|
|
502
|
+
const version = await getOpenCodeVersion();
|
|
503
|
+
const path = getOpenCodePath();
|
|
504
|
+
printSuccess(`OpenCode ${version ?? ""} detected${path ? ` (${DIM}${path}${RESET})` : ""}`);
|
|
505
|
+
return { ok: true, version: version ?? undefined, path: path ?? undefined };
|
|
506
|
+
}
|
|
507
|
+
function handleStepResult(result, successMsg) {
|
|
508
|
+
if (!result.success) {
|
|
509
|
+
printError(`Failed: ${result.error}`);
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
printSuccess(`${successMsg} ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`);
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
function formatConfigSummary() {
|
|
516
|
+
const lines = [];
|
|
517
|
+
lines.push(`${BOLD}Configuration Summary${RESET}`);
|
|
518
|
+
lines.push("");
|
|
519
|
+
lines.push(` ${BOLD}Preset:${RESET} ${BLUE}openai${RESET}`);
|
|
520
|
+
lines.push(` ${SYMBOLS.check} OpenAI (default)`);
|
|
521
|
+
lines.push(` ${DIM}\u25CB Kimi \u2014 see docs/provider-configurations.md${RESET}`);
|
|
522
|
+
lines.push(` ${DIM}\u25CB GitHub Copilot \u2014 see docs/provider-configurations.md${RESET}`);
|
|
523
|
+
lines.push(` ${DIM}\u25CB ZAI Coding Plan \u2014 see docs/provider-configurations.md${RESET}`);
|
|
524
|
+
return lines.join(`
|
|
525
|
+
`);
|
|
526
|
+
}
|
|
527
|
+
async function runInstall(config) {
|
|
528
|
+
const detected = detectCurrentConfig();
|
|
529
|
+
const isUpdate = detected.isInstalled;
|
|
530
|
+
printHeader(isUpdate);
|
|
531
|
+
let totalSteps = 4;
|
|
532
|
+
if (config.installSkills)
|
|
533
|
+
totalSteps += 1;
|
|
534
|
+
if (config.installCustomSkills)
|
|
535
|
+
totalSteps += 1;
|
|
536
|
+
let step = 1;
|
|
537
|
+
printStep(step++, totalSteps, "Checking OpenCode installation...");
|
|
538
|
+
if (config.dryRun) {
|
|
539
|
+
printInfo("Dry run mode - skipping OpenCode check");
|
|
540
|
+
} else {
|
|
541
|
+
const { ok } = await checkOpenCodeInstalled();
|
|
542
|
+
if (!ok)
|
|
543
|
+
return 1;
|
|
544
|
+
}
|
|
545
|
+
{
|
|
546
|
+
printStep(step++, totalSteps, "Adding opencode-deepa plugin...");
|
|
547
|
+
if (config.dryRun) {
|
|
548
|
+
printInfo("Dry run mode - skipping plugin installation");
|
|
549
|
+
} else {
|
|
550
|
+
const pluginResult = await addPluginToOpenCodeConfig();
|
|
551
|
+
if (!handleStepResult(pluginResult, "Plugin added"))
|
|
552
|
+
return 1;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
{
|
|
556
|
+
printStep(step++, totalSteps, "Disabling OpenCode default agents...");
|
|
557
|
+
if (config.dryRun) {
|
|
558
|
+
printInfo("Dry run mode - skipping agent disabling");
|
|
559
|
+
} else {
|
|
560
|
+
const agentResult = disableDefaultAgents();
|
|
561
|
+
if (!handleStepResult(agentResult, "Default agents disabled"))
|
|
562
|
+
return 1;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
printStep(step++, totalSteps, "Writing opencode-deepa configuration...");
|
|
566
|
+
if (config.dryRun) {
|
|
567
|
+
const liteConfig = generateLiteConfig(config);
|
|
568
|
+
printInfo("Dry run mode - configuration that would be written:");
|
|
569
|
+
console.log(`
|
|
570
|
+
${JSON.stringify(liteConfig, null, 2)}
|
|
571
|
+
`);
|
|
572
|
+
} else {
|
|
573
|
+
const liteResult = writeLiteConfig(config);
|
|
574
|
+
if (!handleStepResult(liteResult, "Config written"))
|
|
575
|
+
return 1;
|
|
576
|
+
}
|
|
577
|
+
if (config.installSkills) {
|
|
578
|
+
printStep(step++, totalSteps, "Installing recommended skills...");
|
|
579
|
+
if (config.dryRun) {
|
|
580
|
+
printInfo("Dry run mode - would install skills:");
|
|
581
|
+
for (const skill of RECOMMENDED_SKILLS) {
|
|
582
|
+
printInfo(` - ${skill.name}`);
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
let skillsInstalled = 0;
|
|
586
|
+
for (const skill of RECOMMENDED_SKILLS) {
|
|
587
|
+
printInfo(`Installing ${skill.name}...`);
|
|
588
|
+
if (installSkill(skill)) {
|
|
589
|
+
printSuccess(`Installed: ${skill.name}`);
|
|
590
|
+
skillsInstalled++;
|
|
591
|
+
} else {
|
|
592
|
+
printInfo(`Skipped: ${skill.name} (already installed)`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
printSuccess(`${skillsInstalled}/${RECOMMENDED_SKILLS.length} skills processed`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (config.installCustomSkills) {
|
|
599
|
+
printStep(step++, totalSteps, "Installing custom skills...");
|
|
600
|
+
if (config.dryRun) {
|
|
601
|
+
printInfo("Dry run mode - would install custom skills:");
|
|
602
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
603
|
+
printInfo(` - ${skill.name}`);
|
|
604
|
+
}
|
|
605
|
+
} else {
|
|
606
|
+
let customSkillsInstalled = 0;
|
|
607
|
+
for (const skill of CUSTOM_SKILLS) {
|
|
608
|
+
printInfo(`Installing ${skill.name}...`);
|
|
609
|
+
if (installCustomSkill(skill)) {
|
|
610
|
+
printSuccess(`Installed: ${skill.name}`);
|
|
611
|
+
customSkillsInstalled++;
|
|
612
|
+
} else {
|
|
613
|
+
printInfo(`Skipped: ${skill.name} (already installed)`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
printSuccess(`${customSkillsInstalled}/${CUSTOM_SKILLS.length} custom skills processed`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
console.log();
|
|
620
|
+
console.log(formatConfigSummary());
|
|
621
|
+
console.log();
|
|
622
|
+
console.log(`${SYMBOLS.star} ${BOLD}${GREEN}${isUpdate ? "Configuration updated!" : "Installation complete!"}${RESET}`);
|
|
623
|
+
console.log();
|
|
624
|
+
console.log(`${BOLD}Next steps:${RESET}`);
|
|
625
|
+
console.log();
|
|
626
|
+
console.log(` 1. Start OpenCode:`);
|
|
627
|
+
console.log(` ${BLUE}$ opencode${RESET}`);
|
|
628
|
+
console.log();
|
|
629
|
+
console.log(`${BOLD}Default configuration uses OpenAI models (gpt-5.4 / gpt-5-codex).${RESET}`);
|
|
630
|
+
console.log(`${BOLD}For alternative providers (Kimi, GitHub Copilot, ZAI Coding Plan), see:${RESET}`);
|
|
631
|
+
console.log(` ${BLUE}https://github.com/yourusername/opencode-deepa/blob/main/docs/provider-configurations.md${RESET}`);
|
|
632
|
+
console.log();
|
|
633
|
+
return 0;
|
|
634
|
+
}
|
|
635
|
+
async function install(args) {
|
|
636
|
+
const config = {
|
|
637
|
+
hasTmux: args.tmux === "yes",
|
|
638
|
+
installSkills: args.skills === "yes",
|
|
639
|
+
installCustomSkills: args.skills === "yes",
|
|
640
|
+
dryRun: args.dryRun
|
|
641
|
+
};
|
|
642
|
+
return runInstall(config);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/cli/index.ts
|
|
646
|
+
function parseArgs(args) {
|
|
647
|
+
const result = {
|
|
648
|
+
tui: true
|
|
649
|
+
};
|
|
650
|
+
for (const arg of args) {
|
|
651
|
+
if (arg === "--no-tui") {
|
|
652
|
+
result.tui = false;
|
|
653
|
+
} else if (arg.startsWith("--tmux=")) {
|
|
654
|
+
result.tmux = arg.split("=")[1];
|
|
655
|
+
} else if (arg.startsWith("--skills=")) {
|
|
656
|
+
result.skills = arg.split("=")[1];
|
|
657
|
+
} else if (arg === "--dry-run") {
|
|
658
|
+
result.dryRun = true;
|
|
659
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
660
|
+
printHelp();
|
|
661
|
+
process.exit(0);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return result;
|
|
665
|
+
}
|
|
666
|
+
function printHelp() {
|
|
667
|
+
console.log(`
|
|
668
|
+
opencode-deepa installer
|
|
669
|
+
|
|
670
|
+
Usage: bunx opencode-deepa install [OPTIONS]
|
|
671
|
+
|
|
672
|
+
Options:
|
|
673
|
+
--tmux=yes|no Enable tmux integration (yes/no)
|
|
674
|
+
--skills=yes|no Install recommended skills (yes/no)
|
|
675
|
+
--no-tui Non-interactive mode
|
|
676
|
+
--dry-run Simulate install without writing files
|
|
677
|
+
-h, --help Show this help message
|
|
678
|
+
|
|
679
|
+
The installer generates an OpenAI configuration by default.
|
|
680
|
+
For alternative providers, see docs/provider-configurations.md.
|
|
681
|
+
|
|
682
|
+
Examples:
|
|
683
|
+
bunx opencode-deepa install
|
|
684
|
+
bunx opencode-deepa install --no-tui --tmux=no --skills=yes
|
|
685
|
+
`);
|
|
686
|
+
}
|
|
687
|
+
async function main() {
|
|
688
|
+
const args = process.argv.slice(2);
|
|
689
|
+
if (args.length === 0 || args[0] === "install") {
|
|
690
|
+
const hasSubcommand = args[0] === "install";
|
|
691
|
+
const installArgs = parseArgs(args.slice(hasSubcommand ? 1 : 0));
|
|
692
|
+
const exitCode = await install(installArgs);
|
|
693
|
+
process.exit(exitCode);
|
|
694
|
+
} else if (args[0] === "-h" || args[0] === "--help") {
|
|
695
|
+
printHelp();
|
|
696
|
+
process.exit(0);
|
|
697
|
+
} else {
|
|
698
|
+
console.error(`Unknown command: ${args[0]}`);
|
|
699
|
+
console.error("Run with --help for usage information");
|
|
700
|
+
process.exit(1);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
main().catch((err) => {
|
|
704
|
+
console.error("Fatal error:", err);
|
|
705
|
+
process.exit(1);
|
|
706
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/cli/install.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAqB,WAAW,EAAiB,MAAM,SAAS,CAAC;AAwO7E,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAShE"}
|