opencode-agent-kit 1.0.18 → 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2025 defuj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Agent Kit — Setup Guide
6
6
 
7
- Complete setup guide for the **Agent Kit** — a portable multi-stack AI agent system for OpenCode. Includes 13 specialized agents, 62 skill playbooks, 37 slash commands, and 7 MCP servers.
7
+ Complete setup guide for the **Agent Kit** — a portable multi-stack AI agent system for OpenCode. Includes 13 specialized agents, 63 skill playbooks, 39 slash commands, and 8 MCP servers.
8
8
 
9
9
  ```bash
10
10
  npx opencode-agent-kit init # One command. Full team.
@@ -191,6 +191,10 @@ After installing `.opencode/`, the following slash commands are available:
191
191
 
192
192
  # Quality
193
193
  /sonarqube-scan [options] # SonarQube quality scan (issues, security, coverage)
194
+
195
+ # Memory
196
+ /recall [query] # Search past observations and lessons
197
+ /remember [text] # Save insight/decision to persistent memory
194
198
  ```
195
199
 
196
200
  ## Using the `.opencode/` Folder
@@ -340,6 +344,7 @@ Skills are stored in `.opencode/skills/` (local in the repo) — no need to sear
340
344
  | SEO Specialist | frontend-patterns, web-design-guidelines, nuxt-ui |
341
345
  | **Android Developer** | coding-standards, android-jetpack-compose, edge-to-edge, navigation-3, firebase-basics, play-billing, camera1-to-camerax, r8-analyzer, migrate-xml-views-to-jetpack-compose, gpc-setup, gpc-release-flow, gpc-preflight, gpc-vitals-monitoring |
342
346
  | **Flutter Developer** | coding-standards, flutter (patterns), 10 Flutter skills, 9 Dart skills, firebase-basics |
347
+ | **All Agents** | agentmemory (persistent cross-session memory, 53 MCP tools) |
343
348
 
344
349
  ### Skills Not Required for Core Stack
345
350
 
@@ -363,6 +368,7 @@ These can be kept if your team uses multi-stack, but are optional.
363
368
  - `verification-loop` — Agent verification cycle
364
369
  - `nutrient-document-processing` — Document processing API
365
370
  - `project-guidelines-example` — Project guidelines example
371
+ - `agentmemory` — Persistent cross-session memory with 53 MCP tools
366
372
 
367
373
  ## Skill Locations
368
374
 
@@ -431,8 +437,9 @@ From `.opencode/config.json`, agents use the following MCP servers:
431
437
  | `nuxt-ui` | remote | enabled | Nuxt UI component docs & examples |
432
438
  | `playwright` | stdio | enabled | Browser automation & E2E testing |
433
439
  | `postman` | remote | enabled | Postman API management (collections, requests, docs) |
434
- | `figma` | stdio | disabled | Figma design file access (optional) |
440
+ | `figma` | remote | disabled | Figma design file access (optional) |
435
441
  | `stitch` | remote | disabled | Google Stitch AI design generation (optional) |
442
+ | `agentmemory`| local | enabled | Persistent cross-session memory (53 memory tools) |
436
443
 
437
444
  To enable Figma MCP:
438
445
 
@@ -440,6 +447,15 @@ To enable Figma MCP:
440
447
  export FIGMA_ACCESS_TOKEN="your-token"
441
448
  ```
442
449
 
450
+ To enable agentmemory:
451
+
452
+ ```bash
453
+ npm install -g @agentmemory/agentmemory
454
+ agentmemory # Start the memory server on :3111
455
+ ```
456
+
457
+ Open `http://localhost:3113` for the real-time memory viewer.
458
+
443
459
  To enable Google Stitch MCP:
444
460
 
445
461
  ```bash
@@ -0,0 +1,164 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { execSync } from 'child_process';
4
+
5
+ const PKG_NAME = 'opencode-agent-kit';
6
+
7
+ function check(condition, passMsg, failMsg) {
8
+ if (condition) {
9
+ console.log(` ✓ ${passMsg}`);
10
+ return true;
11
+ }
12
+ console.log(` ✗ ${failMsg}`);
13
+ return false;
14
+ }
15
+
16
+ function warn(msg) {
17
+ console.log(` ⚠ ${msg}`);
18
+ }
19
+
20
+ export async function doctor(options) {
21
+ const targetDir = options.dir || process.cwd();
22
+ const { fix } = options;
23
+
24
+ console.log(`\n ${PKG_NAME} doctor`);
25
+ console.log(` ${'─'.repeat(50)}`);
26
+ console.log(` Target: ${targetDir}\n`);
27
+
28
+ let allGood = true;
29
+
30
+ // 1. Check Node.js version
31
+ const nodeVersion = process.version;
32
+ const major = parseInt(nodeVersion.slice(1).split('.')[0], 10);
33
+ allGood &= check(
34
+ major >= 18,
35
+ `Node.js ${nodeVersion} (>= 18)`,
36
+ `Node.js ${nodeVersion} — need >= 18`,
37
+ );
38
+
39
+ // 2. Check package.json exists
40
+ const pkgPath = join(targetDir, 'package.json');
41
+ allGood &= check(
42
+ existsSync(pkgPath),
43
+ 'package.json found',
44
+ 'package.json not found',
45
+ );
46
+
47
+ // 3. Check opencode.json
48
+ const configPath = join(targetDir, 'opencode.json');
49
+ const hasConfig = existsSync(configPath);
50
+ allGood &= check(hasConfig, 'opencode.json found', 'opencode.json not found');
51
+
52
+ if (hasConfig) {
53
+ try {
54
+ JSON.parse(readFileSync(configPath, 'utf-8'));
55
+ check(true, 'opencode.json is valid JSON', '');
56
+ } catch {
57
+ allGood = false;
58
+ console.log(' ✗ opencode.json is not valid JSON');
59
+ }
60
+ }
61
+
62
+ // 4. Check .opencode directory
63
+ const opencodeDir = join(targetDir, '.opencode');
64
+ const hasOpencode = existsSync(opencodeDir);
65
+ allGood &= check(hasOpencode, '.opencode/ directory found', '.opencode/ directory not found');
66
+
67
+ if (hasOpencode) {
68
+ // 4a. Check agents
69
+ const agentsDir = join(opencodeDir, 'agents');
70
+ check(
71
+ existsSync(agentsDir),
72
+ '.opencode/agents/ found',
73
+ '.opencode/agents/ missing',
74
+ );
75
+
76
+ // 4b. Check instructions
77
+ const instrDir = join(opencodeDir, 'instructions');
78
+ check(
79
+ existsSync(instrDir),
80
+ '.opencode/instructions/ found',
81
+ '.opencode/instructions/ missing',
82
+ );
83
+
84
+ // 4c. Check skills
85
+ const skillsDir = join(opencodeDir, 'skills');
86
+ check(
87
+ existsSync(skillsDir),
88
+ '.opencode/skills/ found',
89
+ '.opencode/skills/ missing',
90
+ );
91
+
92
+ // 4d. Count agents
93
+ if (existsSync(agentsDir)) {
94
+ const agents = readdirSync(agentsDir).filter(f => f.endsWith('.md'));
95
+ check(
96
+ agents.length >= 10,
97
+ `${agents.length} agent prompts found (expected 14)`,
98
+ `Only ${agents.length} agent prompts found (expected 14)`,
99
+ );
100
+ }
101
+
102
+ // 4e. Check kit-version
103
+ const versionFile = join(opencodeDir, '.kit-version');
104
+ if (existsSync(versionFile)) {
105
+ const installedVersion = readFileSync(versionFile, 'utf-8').trim();
106
+ try {
107
+ const latest = execSync(`npm view ${PKG_NAME} version`, {
108
+ encoding: 'utf-8',
109
+ timeout: 5000,
110
+ }).trim();
111
+ if (installedVersion !== latest) {
112
+ warn(`Kit v${installedVersion} installed, v${latest} available`);
113
+ if (fix) {
114
+ console.log(' → Run `npx opencode-agent-kit upgrade` to update');
115
+ }
116
+ } else {
117
+ check(true, `Kit version v${installedVersion} (up to date)`, '');
118
+ }
119
+ } catch {
120
+ warn('Could not check latest version (npm registry unreachable)');
121
+ }
122
+ } else {
123
+ warn('.kit-version not found');
124
+ }
125
+ }
126
+
127
+ // 5. Check .gitignore
128
+ const gitignorePath = join(targetDir, '.gitignore');
129
+ if (existsSync(gitignorePath)) {
130
+ const content = readFileSync(gitignorePath, 'utf-8');
131
+ check(
132
+ content.includes('.opencode') || content.includes('opencode.json'),
133
+ '.gitignore covers .opencode/ files',
134
+ '.gitignore may not cover .opencode/ files',
135
+ );
136
+ } else {
137
+ warn('.gitignore not found');
138
+ if (fix) {
139
+ console.log(' → Run `opencode-agent-kit init` to create one');
140
+ }
141
+ }
142
+
143
+ // 6. Check global agentmemory
144
+ try {
145
+ execSync('agentmemory --version', { stdio: 'pipe', timeout: 3000 });
146
+ check(true, 'agentmemory (global) is installed', '');
147
+ } catch {
148
+ warn('agentmemory not installed globally');
149
+ if (fix) {
150
+ console.log(' → Run: npm install -g @agentmemory/agentmemory');
151
+ }
152
+ }
153
+
154
+ // Summary
155
+ console.log(`\n ${'─'.repeat(50)}`);
156
+ if (allGood) {
157
+ console.log(`\n ✅ All checks passed. Your setup looks good!\n`);
158
+ } else {
159
+ console.log(`\n ⚠ Some checks failed. Review the issues above.\n`);
160
+ if (!fix) {
161
+ console.log(` Tip: Run with --fix to enable actionable suggestions.\n`);
162
+ }
163
+ }
164
+ }
@@ -6,15 +6,43 @@ import {
6
6
  writeFileSync,
7
7
  readdirSync,
8
8
  appendFileSync,
9
- } from "fs";
10
- import { join, dirname } from "path";
11
- import { fileURLToPath } from "url";
12
- import { execSync } from "child_process";
9
+ } from 'fs';
10
+ import { join, dirname } from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import { execSync } from 'child_process';
13
13
 
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = dirname(__filename);
16
- const PKG_ROOT = join(__dirname, "..", "..");
17
- const TEMPLATE_DIR = join(PKG_ROOT, "template");
16
+ const PKG_ROOT = join(__dirname, '..', '..');
17
+ const TEMPLATE_DIR = join(PKG_ROOT, 'template');
18
+ const PKG_NAME = 'opencode-agent-kit';
19
+
20
+ // ANSI colors
21
+ const C = {
22
+ reset: '\x1b[0m',
23
+ dim: '\x1b[2m',
24
+ green: '\x1b[32m',
25
+ yellow: '\x1b[33m',
26
+ red: '\x1b[31m',
27
+ cyan: '\x1b[36m',
28
+ bold: '\x1b[1m',
29
+ };
30
+
31
+ function info(msg) {
32
+ console.log(` ${C.cyan}→${C.reset} ${msg}`);
33
+ }
34
+
35
+ function success(msg) {
36
+ console.log(` ${C.green}✓${C.reset} ${msg}`);
37
+ }
38
+
39
+ function warn(msg) {
40
+ console.log(` ${C.yellow}⚠${C.reset} ${msg}`);
41
+ }
42
+
43
+ function error(msg) {
44
+ console.log(` ${C.red}✗${C.reset} ${msg}`);
45
+ }
18
46
 
19
47
  function copyRecursive(src, dest) {
20
48
  if (!existsSync(src)) return;
@@ -146,130 +174,130 @@ export async function init(options) {
146
174
  const force = options.force;
147
175
  const skipInstall = options.skipInstall;
148
176
 
149
- console.log(`\n opencode-agent-kit init`);
177
+ console.log(`\n ${C.bold}${PKG_NAME} init${C.reset}`);
178
+ console.log(` ${C.dim}${'─'.repeat(50)}${C.reset}`);
150
179
 
151
180
  // 1. Validate target
152
181
  if (!existsSync(targetDir)) {
153
182
  console.error(` ✗ Target directory does not exist: ${targetDir}`);
183
+ error(`Target directory does not exist: ${targetDir}`);
154
184
  process.exit(1);
155
185
  }
156
186
 
157
187
  // 2. Check if .opencode already exists
158
- const opencodeDir = join(targetDir, ".opencode");
159
- const userConfigPath = join(targetDir, "opencode.json");
188
+ const opencodeDir = join(targetDir, '.opencode');
189
+ const userConfigPath = join(targetDir, 'opencode.json');
160
190
 
161
191
  if (existsSync(opencodeDir) && !force) {
162
- console.log(` \n ⚠ .opencode/ already exists in ${targetDir}`);
163
- const rl = await import("readline/promises");
192
+ warn(`.opencode/ already exists in ${targetDir}`);
193
+ const rl = await import('readline/promises');
164
194
  const readline = rl.createInterface({
165
195
  input: process.stdin,
166
196
  output: process.stdout,
167
197
  });
168
- const answer = await readline.question(
169
- ` ? Overwrite existing files? [y/N] `,
170
- );
198
+ const answer = await readline.question(` ? Overwrite existing files? [y/N] `);
171
199
  readline.close();
172
- if (answer.toLowerCase() !== "y") {
173
- console.log(` ✗ Aborted.`);
200
+ if (answer.toLowerCase() !== 'y') {
201
+ error('Aborted.');
174
202
  process.exit(0);
175
203
  }
176
204
  }
177
205
 
178
206
  // 3. Validate template exists
179
207
  if (!existsSync(TEMPLATE_DIR)) {
180
- console.error(`Template directory not found at ${TEMPLATE_DIR}`);
181
- console.error(` This is a bug. Please reinstall the package.`);
208
+ error(`Template directory not found at ${TEMPLATE_DIR}`);
209
+ error('This is a bug. Please reinstall the package.');
182
210
  process.exit(1);
183
211
  }
184
212
 
185
213
  // 4. Copy .opencode/ from template
186
- console.log(` \n 📁 Copying .opencode/ configuration...`);
187
- copyRecursive(join(TEMPLATE_DIR, ".opencode"), opencodeDir);
214
+ info('Copying .opencode/ configuration...');
215
+ copyRecursive(join(TEMPLATE_DIR, '.opencode'), opencodeDir);
188
216
 
189
217
  // 5. Merge opencode.json
190
- const templateConfigPath = join(TEMPLATE_DIR, "opencode.json");
218
+ const templateConfigPath = join(TEMPLATE_DIR, 'opencode.json');
191
219
  if (existsSync(templateConfigPath)) {
192
- console.log(` 📝 Merging opencode.json...`);
193
- const merged = mergeOencodeConfig(
194
- templateConfigPath,
195
- userConfigPath,
196
- force,
197
- );
198
- writeFileSync(
199
- userConfigPath,
200
- JSON.stringify(merged, null, 2) + "\n",
201
- "utf-8",
202
- );
220
+ info('Merging opencode.json...');
221
+ const merged = mergeOencodeConfig(templateConfigPath, userConfigPath, force);
222
+ writeFileSync(userConfigPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
203
223
  }
204
224
 
205
225
  // 6. Copy opencode.example.json
206
- const exampleSrc = join(TEMPLATE_DIR, "opencode.example.json");
207
- const exampleDest = join(targetDir, "opencode.example.json");
226
+ const exampleSrc = join(TEMPLATE_DIR, 'opencode.example.json');
227
+ const exampleDest = join(targetDir, 'opencode.example.json');
208
228
  if (existsSync(exampleSrc)) {
209
- console.log(` 📄 Copying opencode.example.json...`);
229
+ info('Copying opencode.example.json...');
210
230
  copyFileSync(exampleSrc, exampleDest);
211
231
  }
212
232
 
213
233
  // 7. Install dependencies
214
234
  if (!skipInstall) {
215
235
  const pm = detectPackageManager(opencodeDir);
216
- console.log(` 📦 Installing .opencode/ dependencies with ${pm}...`);
236
+ info(`Installing .opencode/ dependencies with ${pm}...`);
217
237
  try {
218
- execSync(`${pm} install`, { cwd: opencodeDir, stdio: "pipe" });
238
+ execSync(`${pm} install`, { cwd: opencodeDir, stdio: 'pipe' });
239
+ success('.opencode/ dependencies installed');
219
240
  } catch (err) {
220
- console.error(`Dependency install failed: ${err.message}`);
221
- console.error(` You can run "${pm} install" manually in .opencode/`);
241
+ warn(`Dependency install failed: ${err.message}`);
242
+ warn(`You can run "${pm} install" manually in .opencode/`);
222
243
  }
223
244
  }
224
245
 
225
- // 8. Update .gitignore
226
- const gitignorePath = join(targetDir, ".gitignore");
227
- const gitignoreEntries = [
228
- ".opencode/*",
229
- "opencode.json",
230
- "opencode.example.json",
231
- ];
246
+ // 8. Install agentmemory globally
247
+ if (!skipInstall) {
248
+ info('Setting up agentmemory (persistent memory)...');
249
+ try {
250
+ execSync('agentmemory --version', { stdio: 'pipe' });
251
+ success('agentmemory already installed');
252
+ } catch {
253
+ try {
254
+ execSync('npm install -g @agentmemory/agentmemory', {
255
+ stdio: 'pipe',
256
+ timeout: 60000,
257
+ });
258
+ success('agentmemory installed globally');
259
+ } catch (err) {
260
+ warn(`agentmemory global install failed: ${err.message}`);
261
+ warn('Run "npm install -g @agentmemory/agentmemory" manually');
262
+ }
263
+ }
264
+ }
265
+
266
+ // 9. Update .gitignore
267
+ const gitignorePath = join(targetDir, '.gitignore');
268
+ const gitignoreEntries = ['.opencode/*', 'opencode.json', 'opencode.example.json', 'data/'];
232
269
  if (!existsSync(gitignorePath)) {
233
- writeFileSync(gitignorePath, gitignoreEntries.join("\n") + "\n", "utf-8");
234
- console.log(` 📄 Created .gitignore...`);
270
+ writeFileSync(gitignorePath, gitignoreEntries.join('\n') + '\n', 'utf-8');
271
+ success('Created .gitignore');
235
272
  } else {
236
- const gitignoreContent = readFileSync(gitignorePath, "utf-8");
273
+ const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
237
274
  let appended = false;
238
275
  for (const entry of gitignoreEntries) {
239
276
  if (!gitignoreContent.includes(entry)) {
240
- appendFileSync(gitignorePath, entry + "\n", "utf-8");
277
+ appendFileSync(gitignorePath, entry + '\n', 'utf-8');
241
278
  appended = true;
242
279
  }
243
280
  }
244
- if (appended) {
245
- console.log(` 📄 Updated .gitignore...`);
246
- }
281
+ if (appended) success('Updated .gitignore');
247
282
  }
248
283
 
249
- // 9. Write .kit-version for agent update checking
250
- const pkgJson = JSON.parse(
251
- readFileSync(join(PKG_ROOT, "package.json"), "utf-8"),
252
- );
253
- const versionFile = join(opencodeDir, ".kit-version");
254
- writeFileSync(versionFile, pkgJson.version + "\n", "utf-8");
255
-
256
- // 10. Done
257
- console.log(`\n ✅ opencode-agent-kit v${pkgJson.version} installed!\n`);
258
- console.log(` Location: ${targetDir}`);
259
- console.log(` What you got:`);
260
- console.log(
261
- ` • opencode.json 13 agents config with MCP servers`,
262
- );
263
- console.log(
264
- ` • opencode.example.json Example config for reference`,
265
- );
266
- console.log(` • .opencode/agents 14 agent prompt files`);
267
- console.log(` • .opencode/skills/ — 60+ skill playbooks`);
268
- console.log(` • .opencode/commands/ — 35+ slash commands`);
269
- console.log(` • .opencode/rules/ — Scoped coding rules`);
270
- console.log(` • .opencode/contexts/ — Dev/review/research contexts`);
271
- console.log(` • .opencode/docs/— Agent documentation`);
272
- console.log(`\n Next steps:`);
273
- console.log(` cd ${targetDir}`);
274
- console.log(` opencode\n`);
284
+ // 10. Write .kit-version for agent update checking
285
+ const pkgJson = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf-8'));
286
+ const versionFile = join(opencodeDir, '.kit-version');
287
+ writeFileSync(versionFile, pkgJson.version + '\n', 'utf-8');
288
+
289
+ // 11. Done summary
290
+ console.log(`\n ${C.bold}${C.green}✅ opencode-agent-kit v${pkgJson.version} installed!${C.reset}\n`);
291
+ console.log(` ${C.dim}Location:${C.reset} ${targetDir}`);
292
+ console.log(` ${C.dim}${'─'.repeat(30)}${C.reset}`);
293
+ console.log(` ${C.bold}What you got:${C.reset}`);
294
+ console.log(` ${C.cyan}opencode.json${C.reset} — 13 agents + MCP servers`);
295
+ console.log(` • ${C.cyan}.opencode/agents/${C.reset} — 14 agent prompt files`);
296
+ console.log(`${C.cyan}.opencode/skills/${C.reset} 60+ skill playbooks`);
297
+ console.log(` • ${C.cyan}.opencode/commands/${C.reset} — 35+ slash commands`);
298
+ console.log(` • ${C.cyan}.opencode/rules/${C.reset} — Scoped coding rules`);
299
+ console.log(`${C.cyan}.opencode/hooks/${C.reset} Automation hooks`);
300
+ console.log(` • ${C.cyan}.opencode/docs/${C.reset} — Agent documentation`);
301
+ console.log(` ${C.cyan}agentmemory${C.reset} (global) Persistent memory`);
302
+ console.log(`\n ${C.bold}${C.green}→${C.reset} Next step: run ${C.cyan}opencode${C.reset} to start\n`);
275
303
  }
@@ -0,0 +1,79 @@
1
+ import { existsSync, readFileSync, unlinkSync, rmSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { createInterface } from 'readline/promises';
4
+
5
+ const PKG_NAME = 'opencode-agent-kit';
6
+
7
+ export async function uninstall(options) {
8
+ const targetDir = options.dir || process.cwd();
9
+ const force = options.force;
10
+
11
+ console.log(`\n ${PKG_NAME} uninstall`);
12
+ console.log(` ${'─'.repeat(50)}`);
13
+ console.log(` Target: ${targetDir}\n`);
14
+
15
+ if (!force) {
16
+ const readline = createInterface({ input: process.stdin, output: process.stdout });
17
+ const answer = await readline.question(
18
+ ` ⚠ This will remove .opencode/ and opencode.json from\n ${targetDir}\n\n ? Continue? [y/N] `,
19
+ );
20
+ readline.close();
21
+ if (answer.toLowerCase() !== 'y') {
22
+ console.log(` ✗ Aborted.\n`);
23
+ process.exit(0);
24
+ }
25
+ }
26
+
27
+ // Remove .opencode/
28
+ const opencodeDir = join(targetDir, '.opencode');
29
+ if (existsSync(opencodeDir)) {
30
+ console.log(` Removing .opencode/ ...`);
31
+ rmSync(opencodeDir, { recursive: true, force: true });
32
+ console.log(` ✓ .opencode/ removed`);
33
+ } else {
34
+ console.log(` - .opencode/ not found, skipping`);
35
+ }
36
+
37
+ // Remove opencode.json
38
+ const configPath = join(targetDir, 'opencode.json');
39
+ if (existsSync(configPath)) {
40
+ console.log(` Removing opencode.json ...`);
41
+ unlinkSync(configPath);
42
+ console.log(` ✓ opencode.json removed`);
43
+ } else {
44
+ console.log(` - opencode.json not found, skipping`);
45
+ }
46
+
47
+ // Remove opencode.example.json
48
+ const examplePath = join(targetDir, 'opencode.example.json');
49
+ if (existsSync(examplePath)) {
50
+ console.log(` Removing opencode.example.json ...`);
51
+ unlinkSync(examplePath);
52
+ console.log(` ✓ opencode.example.json removed`);
53
+ }
54
+
55
+ // Clean up .gitignore entries (remove lines containing these patterns)
56
+ const gitignorePath = join(targetDir, '.gitignore');
57
+ if (existsSync(gitignorePath)) {
58
+ const entriesToRemove = ['.opencode/', 'opencode.json', 'opencode.example.json'];
59
+ const content = readFileSync(gitignorePath, 'utf-8');
60
+ const lines = content.split('\n').filter(line => {
61
+ return !entriesToRemove.some(e => line.trim().startsWith(e));
62
+ });
63
+ const newContent = lines.join('\n');
64
+ if (newContent !== content) {
65
+ // Write back without these entries
66
+ const { writeFileSync } = await import('fs');
67
+ writeFileSync(gitignorePath, newContent, 'utf-8');
68
+ console.log(` ✓ .gitignore cleaned`);
69
+ }
70
+ }
71
+
72
+ // Clean .kit-version
73
+ const versionFile = join(opencodeDir, '.kit-version');
74
+ if (existsSync(versionFile)) {
75
+ unlinkSync(versionFile);
76
+ }
77
+
78
+ console.log(`\n ✅ opencode-agent-kit has been removed from ${targetDir}\n`);
79
+ }
@@ -0,0 +1,65 @@
1
+ import { execSync } from 'child_process';
2
+ import { readFileSync } from 'fs';
3
+ const PKG_NAME = 'opencode-agent-kit';
4
+
5
+ /**
6
+ * Get the currently installed version from package.json
7
+ */
8
+ function getCurrentVersion() {
9
+ const pkgPath = new globalThis.URL('../../package.json', import.meta.url);
10
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
11
+ return pkg.version;
12
+ }
13
+
14
+ /**
15
+ * Get the latest available version from npm
16
+ */
17
+ function getLatestVersion() {
18
+ try {
19
+ return execSync(`npm view ${PKG_NAME} version`, {
20
+ encoding: 'utf-8',
21
+ timeout: 10000,
22
+ }).trim();
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ export async function upgrade(options) {
29
+ const { verbose } = options;
30
+
31
+ console.log(`\n ${PKG_NAME} upgrade`);
32
+ console.log(` ${'─'.repeat(50)}\n`);
33
+
34
+ const current = getCurrentVersion();
35
+ console.log(` Current version: v${current}`);
36
+
37
+ console.log(` Checking npm registry...`);
38
+ const latest = getLatestVersion();
39
+
40
+ if (!latest) {
41
+ console.error(` \n ✗ Could not reach npm registry.`);
42
+ console.error(` Check your internet connection and try again.`);
43
+ process.exit(1);
44
+ }
45
+
46
+ console.log(` Latest version: v${latest}`);
47
+
48
+ if (current === latest) {
49
+ console.log(`\n ✓ You're up to date (v${current}).\n`);
50
+ return;
51
+ }
52
+
53
+ console.log(`\n → Upgrading from v${current} to v${latest}...`);
54
+ try {
55
+ execSync(`npm install -g ${PKG_NAME}@latest`, {
56
+ stdio: verbose ? 'inherit' : 'pipe',
57
+ timeout: 60000,
58
+ });
59
+ console.log(` ✓ Upgraded to v${latest}\n`);
60
+ } catch (err) {
61
+ console.error(` ✗ Upgrade failed: ${err.message}`);
62
+ console.error(` Try: npm install -g ${PKG_NAME}@latest`);
63
+ process.exit(1);
64
+ }
65
+ }