opencode-cc10x 6.0.21

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/bun.lock +20 -0
  4. package/dist/agents.d.ts +3 -0
  5. package/dist/agents.d.ts.map +1 -0
  6. package/dist/agents.js +483 -0
  7. package/dist/agents.js.map +1 -0
  8. package/dist/compatibility-layer.d.ts +18 -0
  9. package/dist/compatibility-layer.d.ts.map +1 -0
  10. package/dist/compatibility-layer.js +150 -0
  11. package/dist/compatibility-layer.js.map +1 -0
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +1459 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/intent-detection.d.ts +14 -0
  17. package/dist/intent-detection.d.ts.map +1 -0
  18. package/dist/intent-detection.js +121 -0
  19. package/dist/intent-detection.js.map +1 -0
  20. package/dist/memory.d.ts +41 -0
  21. package/dist/memory.d.ts.map +1 -0
  22. package/dist/memory.js +330 -0
  23. package/dist/memory.js.map +1 -0
  24. package/dist/router.d.ts +15 -0
  25. package/dist/router.d.ts.map +1 -0
  26. package/dist/router.js +208 -0
  27. package/dist/router.js.map +1 -0
  28. package/dist/skills.d.ts +3 -0
  29. package/dist/skills.d.ts.map +1 -0
  30. package/dist/skills.js +2790 -0
  31. package/dist/skills.js.map +1 -0
  32. package/dist/task-orchestrator.d.ts +46 -0
  33. package/dist/task-orchestrator.d.ts.map +1 -0
  34. package/dist/task-orchestrator.js +262 -0
  35. package/dist/task-orchestrator.js.map +1 -0
  36. package/dist/workflow-executor.d.ts +29 -0
  37. package/dist/workflow-executor.d.ts.map +1 -0
  38. package/dist/workflow-executor.js +414 -0
  39. package/dist/workflow-executor.js.map +1 -0
  40. package/install-from-github.mjs +152 -0
  41. package/install-from-github.sh +106 -0
  42. package/install.sh +295 -0
  43. package/opencode.json +118 -0
  44. package/package.json +41 -0
  45. package/tsconfig.json +23 -0
@@ -0,0 +1,414 @@
1
+ import { taskOrchestrator } from './task-orchestrator';
2
+ import { memoryManager } from './memory';
3
+ export class WorkflowExecutor {
4
+ async executeWorkflow(input, options) {
5
+ const { intent, userRequest, memory, workflowTaskId, activeForm } = options;
6
+ console.log(`🚀 Executing ${intent} workflow for: "${userRequest}"`);
7
+ try {
8
+ switch (intent) {
9
+ case 'BUILD':
10
+ await this.executeBuildWorkflow(input, { userRequest, memory, workflowTaskId });
11
+ break;
12
+ case 'DEBUG':
13
+ await this.executeDebugWorkflow(input, { userRequest, memory, workflowTaskId });
14
+ break;
15
+ case 'REVIEW':
16
+ await this.executeReviewWorkflow(input, { userRequest, memory, workflowTaskId });
17
+ break;
18
+ case 'PLAN':
19
+ await this.executePlanWorkflow(input, { userRequest, memory, workflowTaskId });
20
+ break;
21
+ }
22
+ // Final memory update (workflow-final)
23
+ await this.executeMemoryUpdate(input, workflowTaskId);
24
+ // Mark workflow complete
25
+ await taskOrchestrator.completeWorkflow(workflowTaskId);
26
+ console.log(`✅ ${intent} workflow completed successfully`);
27
+ }
28
+ catch (error) {
29
+ console.error(`❌ ${intent} workflow failed:`, error);
30
+ await this.handleWorkflowFailure(input, workflowTaskId, error);
31
+ }
32
+ }
33
+ async executeBuildWorkflow(input, options) {
34
+ const { userRequest, memory, workflowTaskId } = options;
35
+ // Step 1: component-builder (TDD)
36
+ await this.invokeAgent(input, {
37
+ agentName: 'component-builder',
38
+ taskId: `${workflowTaskId}-builder`,
39
+ prompt: this.buildBuilderPrompt(userRequest, memory),
40
+ waitForCompletion: true
41
+ });
42
+ // Step 2: Parallel execution - code-reviewer and silent-failure-hunter
43
+ await this.invokeParallelAgents(input, {
44
+ agentNames: ['code-reviewer', 'silent-failure-hunter'],
45
+ baseTaskId: workflowTaskId,
46
+ sharedPrompt: this.buildReviewAndHuntPrompt(userRequest, memory),
47
+ waitForCompletion: true
48
+ });
49
+ // Step 3: integration-verifier
50
+ await this.invokeAgent(input, {
51
+ agentName: 'integration-verifier',
52
+ taskId: `${workflowTaskId}-verifier`,
53
+ prompt: await this.buildVerifierPrompt(input, workflowTaskId),
54
+ waitForCompletion: true
55
+ });
56
+ }
57
+ async executeDebugWorkflow(input, options) {
58
+ const { userRequest, memory, workflowTaskId } = options;
59
+ // Step 1: bug-investigator (log-first)
60
+ await this.invokeAgent(input, {
61
+ agentName: 'bug-investigator',
62
+ taskId: `${workflowTaskId}-investigator`,
63
+ prompt: this.buildDebugPrompt(userRequest, memory),
64
+ waitForCompletion: true
65
+ });
66
+ // Step 2: code-reviewer (validate fix)
67
+ await this.invokeAgent(input, {
68
+ agentName: 'code-reviewer',
69
+ taskId: `${workflowTaskId}-reviewer`,
70
+ prompt: this.buildReviewFixPrompt(userRequest, memory),
71
+ waitForCompletion: true
72
+ });
73
+ // Step 3: integration-verifier
74
+ await this.invokeAgent(input, {
75
+ agentName: 'integration-verifier',
76
+ taskId: `${workflowTaskId}-verifier`,
77
+ prompt: await this.buildVerifierPrompt(input, workflowTaskId),
78
+ waitForCompletion: true
79
+ });
80
+ }
81
+ async executeReviewWorkflow(input, options) {
82
+ const { userRequest, memory, workflowTaskId } = options;
83
+ // Single step: code-reviewer
84
+ await this.invokeAgent(input, {
85
+ agentName: 'code-reviewer',
86
+ taskId: `${workflowTaskId}-reviewer`,
87
+ prompt: this.buildReviewPrompt(userRequest, memory),
88
+ waitForCompletion: true
89
+ });
90
+ }
91
+ async executePlanWorkflow(input, options) {
92
+ const { userRequest, memory, workflowTaskId } = options;
93
+ // Single step: planner
94
+ await this.invokeAgent(input, {
95
+ agentName: 'planner',
96
+ taskId: `${workflowTaskId}-planner`,
97
+ prompt: this.buildPlanPrompt(userRequest, memory),
98
+ waitForCompletion: true
99
+ });
100
+ }
101
+ async executeMemoryUpdate(input, workflowTaskId) {
102
+ // This is the workflow-final memory persistence
103
+ // The main assistant (router) handles this
104
+ console.log('💾 Executing workflow-final memory update');
105
+ // Persist any accumulated memory notes
106
+ await memoryManager.persistAccumulatedNotes(input);
107
+ // Update progress.md with completion
108
+ await memoryManager.updateProgress(input, {
109
+ completed: [`Workflow ${workflowTaskId} completed with verification`]
110
+ });
111
+ }
112
+ async invokeAgent(input, options) {
113
+ const { agentName, taskId, prompt, waitForCompletion } = options;
114
+ console.log(`🤖 Invoking agent: ${agentName} (task: ${taskId})`);
115
+ try {
116
+ // Update task status to in_progress
117
+ await taskOrchestrator.updateTaskStatus(input, taskId, 'in_progress');
118
+ // Invoke the agent using OpenCode's agent system
119
+ const result = await input.client.app.agent.invoke(agentName, {
120
+ prompt: prompt,
121
+ taskId: taskId
122
+ });
123
+ // Update task status to completed
124
+ await taskOrchestrator.updateTaskStatus(input, taskId, 'completed', result);
125
+ console.log(`✅ Agent ${agentName} completed`);
126
+ }
127
+ catch (error) {
128
+ console.error(`❌ Agent ${agentName} failed:`, error);
129
+ await taskOrchestrator.updateTaskStatus(input, taskId, 'blocked');
130
+ throw error;
131
+ }
132
+ }
133
+ async invokeParallelAgents(input, options) {
134
+ const { agentNames, baseTaskId, sharedPrompt, waitForCompletion } = options;
135
+ console.log(`⚡ Invoking parallel agents: ${agentNames.join(', ')}`);
136
+ // Create promises for all agents
137
+ const agentPromises = agentNames.map(agentName => this.invokeAgent(input, {
138
+ agentName,
139
+ taskId: `${baseTaskId}-${agentName.split('-').pop()}`,
140
+ prompt: sharedPrompt,
141
+ waitForCompletion: true // Each agent waits for its own completion
142
+ }));
143
+ // Wait for all to complete
144
+ await Promise.all(agentPromises);
145
+ }
146
+ buildBuilderPrompt(userRequest, memory) {
147
+ return `
148
+ # Component Builder (TDD)
149
+
150
+ ## User Request
151
+ ${userRequest}
152
+
153
+ ## Memory Context
154
+ ${this.formatMemoryContext(memory)}
155
+
156
+ ## Instructions
157
+ Follow the TDD cycle strictly:
158
+ 1. RED: Write a failing test first (must exit with code 1)
159
+ 2. GREEN: Write minimal code to pass (must exit with code 0)
160
+ 3. REFACTOR: Clean up while keeping tests green
161
+ 4. VERIFY: All tests must pass
162
+
163
+ ## Pre-Implementation Checklist
164
+ - API: CORS? Auth middleware? Input validation? Rate limiting?
165
+ - UI: Loading states? Error boundaries? Accessibility?
166
+ - DB: Migrations? N+1 queries? Transactions?
167
+ - All: Edge cases listed? Error handling planned?
168
+
169
+ ## Output Requirements
170
+ - Provide TDD evidence with exact commands and exit codes
171
+ - Include Dev Journal with decisions and assumptions
172
+ - Follow the Router Contract format exactly
173
+ - Update memory via Edit tool (permission-free)
174
+ `.trim();
175
+ }
176
+ buildReviewAndHuntPrompt(userRequest, memory) {
177
+ return `
178
+ # Code Review & Silent Failure Hunt
179
+
180
+ ## User Request
181
+ ${userRequest}
182
+
183
+ ## Memory Context
184
+ ${this.formatMemoryContext(memory)}
185
+
186
+ ## Instructions
187
+ Analyze the implementation from the component-builder. Focus on:
188
+
189
+ ### Code Reviewer Focus
190
+ - Code quality and best practices
191
+ - Security vulnerabilities (OWASP top 10)
192
+ - Performance implications (N+1 queries, etc.)
193
+ - Maintainability and readability
194
+ - API design and contracts
195
+
196
+ ### Silent Failure Hunter Focus
197
+ - Empty catch blocks
198
+ - Missing error handling
199
+ - Unvalidated inputs
200
+ - Resource leaks
201
+ - Race conditions
202
+ - Edge cases not covered by tests
203
+
204
+ ## Confidence Scoring
205
+ Only report issues with ≥80% confidence. Provide file:line citations.
206
+
207
+ ## Output Requirements
208
+ - Critical Issues section with confidence scores
209
+ - Verdict: APPROVED or CHANGES REQUESTED
210
+ - Include "### Memory Notes" section for workflow persistence
211
+ `.trim();
212
+ }
213
+ async buildVerifierPrompt(input, workflowTaskId) {
214
+ // Collect findings from previous agents
215
+ const reviewerTaskId = `${workflowTaskId}-reviewer`;
216
+ const hunterTaskId = `${workflowTaskId}-hunter`;
217
+ // In a full implementation, would fetch task results
218
+ // For now, provide template
219
+ return `
220
+ # Integration Verifier
221
+
222
+ ## Task Context
223
+ - Workflow: ${workflowTaskId}
224
+ - Previous agents: code-reviewer, silent-failure-hunter
225
+
226
+ ## Instructions
227
+ Verify the implementation considering ALL findings from previous agents.
228
+
229
+ ### Verification Checklist
230
+ - [ ] All tests pass (exit code 0)
231
+ - [ ] No critical security issues
232
+ - [ ] No silent failures detected
233
+ - [ ] Error handling is comprehensive
234
+ - [ ] Performance is acceptable
235
+ - [ ] Code follows project patterns
236
+
237
+ ### Critical Issues
238
+ Any CRITICAL issues should block PASS verdict.
239
+
240
+ ## Output Requirements
241
+ - Verdict: PASS or FAIL with reasoning
242
+ - Include verification evidence (commands + exit codes)
243
+ - Provide "### Memory Notes" section
244
+ `.trim();
245
+ }
246
+ buildDebugPrompt(userRequest, memory) {
247
+ return `
248
+ # Bug Investigator (LOG FIRST)
249
+
250
+ ## User Request
251
+ ${userRequest}
252
+
253
+ ## Memory Context
254
+ ${this.formatMemoryContext(memory)}
255
+
256
+ ## Iron Law: LOG FIRST
257
+ Never fix without evidence. Follow this process:
258
+
259
+ 1. **Reproduce** - Get exact error conditions
260
+ 2. **Log** - Gather all relevant logs, stack traces, system state
261
+ 3. **Analyze** - Root cause analysis using debugging patterns
262
+ 4. **Fix** - Minimal change to resolve
263
+ 5. **Verify** - Confirm fix works and doesn't break other things
264
+
265
+ ## Common Debugging Patterns
266
+ - Check recent changes (git diff)
267
+ - Examine error logs and stack traces
268
+ - Validate assumptions with print statements
269
+ - Isolate the failing component
270
+ - Check for null/undefined values
271
+ - Verify data types and formats
272
+
273
+ ## Output Requirements
274
+ - Evidence before any fix proposal
275
+ - Root cause analysis with confidence
276
+ - Minimal fix with verification
277
+ - Update memory with common gotchas if discovered
278
+ `.trim();
279
+ }
280
+ buildReviewFixPrompt(userRequest, memory) {
281
+ return `
282
+ # Code Reviewer (Fix Validation)
283
+
284
+ ## User Request
285
+ ${userRequest}
286
+
287
+ ## Memory Context
288
+ ${this.formatMemoryContext(memory)}
289
+
290
+ ## Instructions
291
+ Review the bug fix from bug-investigator. Focus on:
292
+
293
+ - Fix correctness: Does it actually solve the problem?
294
+ - Side effects: Does it introduce new issues?
295
+ - Code quality: Is the fix clean and maintainable?
296
+ - Testing: Are there tests for the fix?
297
+ - Security: Does the fix introduce vulnerabilities?
298
+
299
+ ## Confidence Scoring
300
+ Only report issues with ≥80% confidence.
301
+
302
+ ## Output Requirements
303
+ - Verdict: APPROVED or CHANGES REQUESTED
304
+ - Critical Issues with file:line citations
305
+ - Include "### Memory Notes" section
306
+ `.trim();
307
+ }
308
+ buildReviewPrompt(userRequest, memory) {
309
+ return `
310
+ # Code Reviewer (Comprehensive Review)
311
+
312
+ ## User Request
313
+ ${userRequest}
314
+
315
+ ## Memory Context
316
+ ${this.formatMemoryContext(memory)}
317
+
318
+ ## Instructions
319
+ Perform comprehensive code review with 80%+ confidence threshold.
320
+
321
+ ### Review Dimensions
322
+ - **Security**: OWASP top 10, input validation, authentication/authorization
323
+ - **Performance**: Algorithm efficiency, database queries, memory usage
324
+ - **Maintainability**: Code structure, naming, documentation
325
+ - **Reliability**: Error handling, edge cases, resource management
326
+ - **Testing**: Test coverage, test quality, edge case coverage
327
+
328
+ ## Output Requirements
329
+ - Only report issues with ≥80% confidence
330
+ - File:line citations for every finding
331
+ - Verdict: APPROVED or CHANGES REQUESTED
332
+ - Include "### Memory Notes" section
333
+ `.trim();
334
+ }
335
+ buildPlanPrompt(userRequest, memory) {
336
+ return `
337
+ # Planner (Comprehensive Planning)
338
+
339
+ ## User Request
340
+ ${userRequest}
341
+
342
+ ## Memory Context
343
+ ${this.formatMemoryContext(memory)}
344
+
345
+ ## Planning Requirements
346
+ Create a comprehensive plan that includes:
347
+
348
+ ### 1. Analysis
349
+ - Current state assessment
350
+ - Requirements clarification
351
+ - Constraints and dependencies
352
+ - Risk assessment
353
+
354
+ ### 2. Architecture
355
+ - System design decisions
356
+ - Technology choices with rationale
357
+ - API design (if applicable)
358
+ - Data model (if applicable)
359
+
360
+ ### 3. Implementation Plan
361
+ - Phased approach with milestones
362
+ - Specific files to create/modify
363
+ - Testing strategy
364
+ - Rollback plan
365
+
366
+ ### 4. Research Needs
367
+ - External packages to investigate
368
+ - Best practices to research
369
+ - Alternatives to evaluate
370
+
371
+ ## Output Requirements
372
+ - Save plan to docs/plans/YYYY-MM-DD-<topic>-plan.md
373
+ - Update activeContext.md with plan reference
374
+ - Include research phase if needed (github-research skill)
375
+ - Provide clear next steps
376
+ `.trim();
377
+ }
378
+ formatMemoryContext(memory) {
379
+ if (!memory)
380
+ return 'No memory available';
381
+ const parts = [];
382
+ if (memory.activeContext) {
383
+ const focusMatch = memory.activeContext.match(/## Current Focus\n([\s\S]*?)(?=\n##|\n$)/);
384
+ if (focusMatch) {
385
+ parts.push(`Current Focus: ${focusMatch[1].trim()}`);
386
+ }
387
+ }
388
+ if (memory.patterns) {
389
+ const gotchasMatch = memory.patterns.match(/## Common Gotchas\n([\s\S]*?)(?=\n##|\n$)/);
390
+ if (gotchasMatch) {
391
+ parts.push(`Common Gotchas: ${gotchasMatch[1].trim().substring(0, 200)}...`);
392
+ }
393
+ }
394
+ if (memory.progress) {
395
+ const completedMatch = memory.progress.match(/## Completed\n([\s\S]*?)(?=\n##|\n$)/);
396
+ if (completedMatch) {
397
+ parts.push(`Recent Completions: ${completedMatch[1].trim().substring(0, 200)}...`);
398
+ }
399
+ }
400
+ return parts.length > 0 ? parts.join('\n') : 'Memory files empty or not loaded';
401
+ }
402
+ async handleWorkflowFailure(input, workflowTaskId, error) {
403
+ console.error(`Workflow ${workflowTaskId} failed:`, error);
404
+ // Update memory with failure
405
+ await memoryManager.updateActiveContext(input, {
406
+ recentChanges: [`Workflow ${workflowTaskId} failed: ${error.message}`],
407
+ blockers: [`Workflow failure: ${error.message}`]
408
+ });
409
+ // Could create TODO task for remediation
410
+ // This would use OpenCode's task system
411
+ }
412
+ }
413
+ export const workflowExecutor = new WorkflowExecutor();
414
+ //# sourceMappingURL=workflow-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-executor.js","sourceRoot":"","sources":["../src/workflow-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { mkdtempSync, rmSync, mkdirSync, existsSync, readFileSync, writeFileSync, copyFileSync, readdirSync, statSync } from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { tmpdir } from "node:os";
7
+ import { spawnSync } from "node:child_process";
8
+
9
+ const REPO = process.env.CC10X_REPO || "Chaim12345/cc10x";
10
+ const REF = process.env.CC10X_REF || "main";
11
+ const RAW_BASE = `https://raw.githubusercontent.com/${REPO}/${REF}/project/opencode-cc10x-plugin`;
12
+ const TARBALL_URL = `https://codeload.github.com/${REPO}/tar.gz/${REF}`;
13
+ const PLUGIN_NAME = "opencode-cc10x";
14
+
15
+ function log(msg) {
16
+ process.stdout.write(`${msg}\n`);
17
+ }
18
+
19
+ function fail(msg) {
20
+ process.stderr.write(`${msg}\n`);
21
+ process.exit(1);
22
+ }
23
+
24
+ function run(cmd, args, cwd = process.cwd()) {
25
+ const result = spawnSync(cmd, args, { cwd, stdio: "inherit", shell: process.platform === "win32" });
26
+ if (result.status !== 0) {
27
+ fail(`Command failed: ${cmd} ${args.join(" ")}`);
28
+ }
29
+ }
30
+
31
+ function detectConfigBase() {
32
+ if (process.platform === "win32") {
33
+ return process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
34
+ }
35
+ return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
36
+ }
37
+
38
+ function isTempHome(home) {
39
+ if (!home) return false;
40
+ if (process.platform === "win32") {
41
+ const normalized = home.toLowerCase();
42
+ return normalized.startsWith(path.join(process.env.TEMP || "", "").toLowerCase());
43
+ }
44
+ return home.startsWith("/tmp/");
45
+ }
46
+
47
+ async function downloadText(url) {
48
+ const res = await fetch(url);
49
+ if (!res.ok) {
50
+ throw new Error(`HTTP ${res.status} for ${url}`);
51
+ }
52
+ return await res.text();
53
+ }
54
+
55
+ async function downloadFile(url, targetPath) {
56
+ const res = await fetch(url);
57
+ if (!res.ok) {
58
+ throw new Error(`HTTP ${res.status} for ${url}`);
59
+ }
60
+ const buf = Buffer.from(await res.arrayBuffer());
61
+ writeFileSync(targetPath, buf);
62
+ }
63
+
64
+ function findPluginDir(rootDir) {
65
+ const stack = [rootDir];
66
+ while (stack.length > 0) {
67
+ const current = stack.pop();
68
+ if (!current) break;
69
+ if (path.basename(current) === "opencode-cc10x-plugin") return current;
70
+ let entries = [];
71
+ try {
72
+ entries = readdirSync(current);
73
+ } catch {
74
+ continue;
75
+ }
76
+ for (const entry of entries) {
77
+ const full = path.join(current, entry);
78
+ try {
79
+ if (statSync(full).isDirectory()) {
80
+ stack.push(full);
81
+ }
82
+ } catch {
83
+ continue;
84
+ }
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function upsertConfig(configFile, pluginName) {
91
+ let cfg = {};
92
+ if (existsSync(configFile)) {
93
+ try {
94
+ cfg = JSON.parse(readFileSync(configFile, "utf8"));
95
+ } catch {
96
+ cfg = {};
97
+ }
98
+ }
99
+ if (!Array.isArray(cfg.plugin)) cfg.plugin = [];
100
+ if (!cfg.plugin.includes(pluginName)) cfg.plugin.push(pluginName);
101
+ if (!cfg.$schema) cfg.$schema = "https://opencode.ai/config.json";
102
+ writeFileSync(configFile, `${JSON.stringify(cfg, null, 2)}\n`);
103
+ }
104
+
105
+ async function main() {
106
+ log(`Installing ${PLUGIN_NAME} from GitHub (${REPO}@${REF})...`);
107
+
108
+ if (isTempHome(os.homedir()) && process.env.CC10X_ALLOW_TMP_HOME !== "1") {
109
+ fail(`Refusing to install into temporary HOME (${os.homedir()}). Set CC10X_ALLOW_TMP_HOME=1 for testing.`);
110
+ }
111
+
112
+ const configBase = detectConfigBase();
113
+ const pluginDir = path.join(configBase, "opencode", "plugins");
114
+ const configDir = path.join(configBase, "opencode");
115
+ const configFile = path.join(configDir, "opencode.json");
116
+ const pluginFile = path.join(pluginDir, `${PLUGIN_NAME}.js`);
117
+
118
+ mkdirSync(pluginDir, { recursive: true });
119
+ mkdirSync(configDir, { recursive: true });
120
+
121
+ let installed = false;
122
+ try {
123
+ const source = await downloadText(`${RAW_BASE}/dist/index.js`);
124
+ writeFileSync(pluginFile, source);
125
+ log("Downloaded prebuilt plugin artifact.");
126
+ installed = true;
127
+ } catch {
128
+ log("Prebuilt artifact not found. Building from source...");
129
+ const workdir = mkdtempSync(path.join(tmpdir(), "cc10x-"));
130
+ try {
131
+ const tgzPath = path.join(workdir, "cc10x.tar.gz");
132
+ await downloadFile(TARBALL_URL, tgzPath);
133
+ run("tar", ["-xzf", tgzPath, "-C", workdir]);
134
+ const srcDir = findPluginDir(workdir);
135
+ if (!srcDir) fail("Could not locate plugin source in tarball.");
136
+ run("npm", ["install"], srcDir);
137
+ run("npm", ["run", "build"], srcDir);
138
+ copyFileSync(path.join(srcDir, "dist", "index.js"), pluginFile);
139
+ installed = true;
140
+ } finally {
141
+ rmSync(workdir, { recursive: true, force: true });
142
+ }
143
+ }
144
+
145
+ if (!installed) fail("Installation failed.");
146
+ upsertConfig(configFile, PLUGIN_NAME);
147
+
148
+ log(`Installed ${PLUGIN_NAME} to ${pluginFile}`);
149
+ log("Restart OpenCode to load the plugin.");
150
+ }
151
+
152
+ main().catch((err) => fail(err instanceof Error ? err.message : String(err)));
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ REPO="${CC10X_REPO:-Chaim12345/cc10x}"
6
+ REF="${CC10X_REF:-main}"
7
+ RAW_BASE="https://raw.githubusercontent.com/${REPO}/${REF}/project/opencode-cc10x-plugin"
8
+ TARBALL_URL="https://codeload.github.com/${REPO}/tar.gz/${REF}"
9
+ OS_NAME="$(uname -s 2>/dev/null || echo unknown)"
10
+ if [[ "${OS_NAME}" == MINGW* || "${OS_NAME}" == MSYS* || "${OS_NAME}" == CYGWIN* ]]; then
11
+ if [[ -n "${APPDATA:-}" ]]; then
12
+ if command -v cygpath >/dev/null 2>&1; then
13
+ CONFIG_BASE="$(cygpath -u "${APPDATA}")"
14
+ else
15
+ CONFIG_BASE="${APPDATA//\\//}"
16
+ fi
17
+ else
18
+ CONFIG_BASE="${HOME}/AppData/Roaming"
19
+ fi
20
+ else
21
+ CONFIG_BASE="${XDG_CONFIG_HOME:-${HOME}/.config}"
22
+ fi
23
+ PLUGIN_DIR="${CONFIG_BASE}/opencode/plugins"
24
+ CONFIG_DIR="${CONFIG_BASE}/opencode"
25
+ CONFIG_FILE="${CONFIG_DIR}/opencode.json"
26
+ PLUGIN_NAME="opencode-cc10x"
27
+
28
+ echo "Installing ${PLUGIN_NAME} from GitHub (${REPO}@${REF})..."
29
+
30
+ if [[ "${HOME}" == /tmp/* ]] && [[ "${CC10X_ALLOW_TMP_HOME:-0}" != "1" ]]; then
31
+ echo "Refusing to install into temporary HOME (${HOME})."
32
+ echo "Run with your real user environment, or set CC10X_ALLOW_TMP_HOME=1 for testing."
33
+ exit 1
34
+ fi
35
+
36
+ if ! command -v opencode >/dev/null 2>&1; then
37
+ echo "OpenCode binary not found in PATH; continuing with plugin/config installation."
38
+ fi
39
+
40
+ if ! command -v curl >/dev/null 2>&1; then
41
+ echo "curl is required for GitHub installation."
42
+ exit 1
43
+ fi
44
+
45
+ mkdir -p "${PLUGIN_DIR}" "${CONFIG_DIR}"
46
+
47
+ if curl -fsSL "${RAW_BASE}/dist/index.js" -o "${PLUGIN_DIR}/${PLUGIN_NAME}.js"; then
48
+ echo "Downloaded prebuilt plugin artifact."
49
+ else
50
+ echo "Prebuilt artifact not found. Building from source..."
51
+ if ! command -v npm >/dev/null 2>&1; then
52
+ echo "npm is required to build from source."
53
+ exit 1
54
+ fi
55
+ WORKDIR="$(mktemp -d)"
56
+ curl -fsSL "${TARBALL_URL}" -o "${WORKDIR}/cc10x.tar.gz"
57
+ tar -xzf "${WORKDIR}/cc10x.tar.gz" -C "${WORKDIR}"
58
+ SRC_DIR="$(find "${WORKDIR}" -maxdepth 5 -type d -name 'opencode-cc10x-plugin' | head -n 1)"
59
+ if [ -z "${SRC_DIR}" ]; then
60
+ echo "Could not locate plugin source in tarball."
61
+ exit 1
62
+ fi
63
+ (cd "${SRC_DIR}" && npm install && npm run build)
64
+ cp "${SRC_DIR}/dist/index.js" "${PLUGIN_DIR}/${PLUGIN_NAME}.js"
65
+ rm -rf "${WORKDIR}"
66
+ fi
67
+
68
+ if command -v node >/dev/null 2>&1; then
69
+ CONFIG_FILE="${CONFIG_FILE}" PLUGIN_NAME="${PLUGIN_NAME}" node <<'NODE'
70
+ const fs = require('fs');
71
+ const path = require('path');
72
+
73
+ const configFile = process.env.CONFIG_FILE;
74
+ const pluginName = process.env.PLUGIN_NAME;
75
+
76
+ let cfg = {};
77
+ if (fs.existsSync(configFile)) {
78
+ try {
79
+ cfg = JSON.parse(fs.readFileSync(configFile, 'utf8'));
80
+ } catch {
81
+ cfg = {};
82
+ }
83
+ }
84
+
85
+ if (!Array.isArray(cfg.plugin)) cfg.plugin = [];
86
+ if (!cfg.plugin.includes(pluginName)) cfg.plugin.push(pluginName);
87
+ if (!cfg.$schema) cfg.$schema = 'https://opencode.ai/config.json';
88
+
89
+ fs.writeFileSync(configFile, JSON.stringify(cfg, null, 2) + '\n');
90
+ NODE
91
+ else
92
+ if [ ! -f "${CONFIG_FILE}" ]; then
93
+ cat > "${CONFIG_FILE}" <<EOF
94
+ {
95
+ "\$schema": "https://opencode.ai/config.json",
96
+ "plugin": ["${PLUGIN_NAME}"]
97
+ }
98
+ EOF
99
+ elif ! grep -q "\"${PLUGIN_NAME}\"" "${CONFIG_FILE}"; then
100
+ echo "Plugin installed, but ${PLUGIN_NAME} was not added to ${CONFIG_FILE} automatically."
101
+ echo "Please add it manually under the \"plugin\" array."
102
+ fi
103
+ fi
104
+
105
+ echo "Installed ${PLUGIN_NAME} to ${PLUGIN_DIR}/${PLUGIN_NAME}.js"
106
+ echo "Restart OpenCode to load the plugin."