opencode-agent-kit 1.0.19 → 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.
@@ -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,157 +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
- // 7.6 Install agentmemory globally
246
+ // 8. Install agentmemory globally
226
247
  if (!skipInstall) {
227
- console.log(` 🧠 Installing agentmemory (persistent memory)...`);
248
+ info('Setting up agentmemory (persistent memory)...');
228
249
  try {
229
- execSync(`agentmemory --version`, { stdio: "pipe" });
230
- console.log(` ✓ agentmemory already installed`);
250
+ execSync('agentmemory --version', { stdio: 'pipe' });
251
+ success('agentmemory already installed');
231
252
  } catch {
232
253
  try {
233
- execSync(`npm install -g @agentmemory/agentmemory`, {
234
- stdio: "pipe",
254
+ execSync('npm install -g @agentmemory/agentmemory', {
255
+ stdio: 'pipe',
235
256
  timeout: 60000,
236
257
  });
237
- console.log(` ✓ agentmemory installed globally`);
258
+ success('agentmemory installed globally');
238
259
  } catch (err) {
239
- console.error(`agentmemory global install failed: ${err.message}`);
240
- console.error(
241
- ` Run "npm install -g @agentmemory/agentmemory" manually`,
242
- );
260
+ warn(`agentmemory global install failed: ${err.message}`);
261
+ warn('Run "npm install -g @agentmemory/agentmemory" manually');
243
262
  }
244
263
  }
245
264
  }
246
265
 
247
- // 8. Update .gitignore
248
- const gitignorePath = join(targetDir, ".gitignore");
249
- const gitignoreEntries = [
250
- ".opencode/*",
251
- "opencode.json",
252
- "opencode.example.json",
253
- "data/",
254
- ];
266
+ // 9. Update .gitignore
267
+ const gitignorePath = join(targetDir, '.gitignore');
268
+ const gitignoreEntries = ['.opencode/*', 'opencode.json', 'opencode.example.json', 'data/'];
255
269
  if (!existsSync(gitignorePath)) {
256
- writeFileSync(gitignorePath, gitignoreEntries.join("\n") + "\n", "utf-8");
257
- console.log(` 📄 Created .gitignore...`);
270
+ writeFileSync(gitignorePath, gitignoreEntries.join('\n') + '\n', 'utf-8');
271
+ success('Created .gitignore');
258
272
  } else {
259
- const gitignoreContent = readFileSync(gitignorePath, "utf-8");
273
+ const gitignoreContent = readFileSync(gitignorePath, 'utf-8');
260
274
  let appended = false;
261
275
  for (const entry of gitignoreEntries) {
262
276
  if (!gitignoreContent.includes(entry)) {
263
- appendFileSync(gitignorePath, entry + "\n", "utf-8");
277
+ appendFileSync(gitignorePath, entry + '\n', 'utf-8');
264
278
  appended = true;
265
279
  }
266
280
  }
267
- if (appended) {
268
- console.log(` 📄 Updated .gitignore...`);
269
- }
281
+ if (appended) success('Updated .gitignore');
270
282
  }
271
283
 
272
- // 9. Write .kit-version for agent update checking
273
- const pkgJson = JSON.parse(
274
- readFileSync(join(PKG_ROOT, "package.json"), "utf-8"),
275
- );
276
- const versionFile = join(opencodeDir, ".kit-version");
277
- writeFileSync(versionFile, pkgJson.version + "\n", "utf-8");
278
-
279
- // 10. Done
280
- console.log(`\n ✅ opencode-agent-kit v${pkgJson.version} installed!\n`);
281
- console.log(` Location: ${targetDir}`);
282
- console.log(` What you got:`);
283
- console.log(
284
- ` • opencode.json 13 agents config with MCP servers`,
285
- );
286
- console.log(
287
- ` • opencode.example.json Example config for reference`,
288
- );
289
- console.log(` • .opencode/agents 14 agent prompt files`);
290
- console.log(` • .opencode/skills/ — 60+ skill playbooks`);
291
- console.log(` • .opencode/commands/ — 35+ slash commands`);
292
- console.log(` • .opencode/rules/ — Scoped coding rules`);
293
- console.log(` • .opencode/contexts/ — Dev/review/research contexts`);
294
- console.log(` • .opencode/docs/ — Agent documentation`);
295
- console.log(` • .opencode/plugins/ — agentmemory capture plugin (22 hooks)`);
296
- console.log(` • .opencode/hooks/ — agentmemory auto-start wrapper`);
297
- console.log(` • agentmemory (global) — Persistent cross-session memory`);
298
- console.log(`\n Next steps:`);
299
- console.log(` cd ${targetDir}`);
300
- console.log(` opencode # agentmemory auto-starts on first use`);
301
- console.log(` # Viewer: http://localhost:3113`);
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`);
302
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
+ }
package/bin/init.mjs CHANGED
@@ -1,21 +1,44 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from "commander";
4
- import { init } from "./commands/init.mjs";
3
+ import { Command } from 'commander';
4
+ import { init } from './commands/init.mjs';
5
+ import { upgrade } from './commands/upgrade.mjs';
6
+ import { doctor } from './commands/doctor.mjs';
7
+ import { uninstall } from './commands/uninstall.mjs';
5
8
 
6
9
  const program = new Command();
7
10
 
8
11
  program
9
- .name("opencode-agent-kit")
10
- .description("Install OpenCode multi-agent toolkit into your project")
11
- .version("1.0.0");
12
+ .name('opencode-agent-kit')
13
+ .description('Multi-stack OpenCode agent toolkit 13 specialized AI agents, 60+ skills, 35+ commands')
14
+ .version('1.0.0');
12
15
 
13
16
  program
14
- .command("init")
15
- .description("Initialize .opencode/ configuration in current project")
16
- .option("-f, --force", "Overwrite existing files without prompt")
17
- .option("-d, --dir <path>", "Target project directory", process.cwd())
18
- .option("--skip-install", "Skip npm/bun install step in .opencode/")
17
+ .command('init')
18
+ .description('Initialize .opencode/ configuration in current project')
19
+ .option('-f, --force', 'Overwrite existing files without prompt')
20
+ .option('-d, --dir <path>', 'Target project directory', process.cwd())
21
+ .option('--skip-install', 'Skip npm/bun install step in .opencode/')
19
22
  .action(init);
20
23
 
24
+ program
25
+ .command('upgrade')
26
+ .description('Check for and apply the latest version of opencode-agent-kit')
27
+ .option('-v, --verbose', 'Show detailed upgrade output')
28
+ .action(upgrade);
29
+
30
+ program
31
+ .command('doctor')
32
+ .description('Diagnose common setup issues in the target project')
33
+ .option('-d, --dir <path>', 'Target project directory', process.cwd())
34
+ .option('--fix', 'Show actionable fix suggestions')
35
+ .action(doctor);
36
+
37
+ program
38
+ .command('uninstall')
39
+ .description('Remove opencode-agent-kit configuration from the target project')
40
+ .option('-d, --dir <path>', 'Target project directory', process.cwd())
41
+ .option('-f, --force', 'Skip confirmation prompt')
42
+ .action(uninstall);
43
+
21
44
  program.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-agent-kit",
3
- "version": "1.0.19",
4
- "description": "Multi-stack OpenCode agent toolkit 13 specialized AI agents (Nuxt, React, Node.js, Laravel, CI3, Android, Flutter, DevOps, SEO, SonarQube) with 62 skills, 37 commands, and 7 MCP servers",
3
+ "version": "1.1.0",
4
+ "description": "Multi-stack OpenCode agent toolkit \u2014 13 specialized AI agents (Nuxt, React, Node.js, Laravel, CI3, Android, Flutter, DevOps, SEO, SonarQube) with 62 skills, 37 commands, and 7 MCP servers",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opencode-agent-kit": "./bin/init.mjs"
@@ -25,6 +25,13 @@
25
25
  "engines": {
26
26
  "node": ">=18"
27
27
  },
28
+ "scripts": {
29
+ "lint": "eslint bin/ --ext .mjs,.js",
30
+ "format": "prettier --write 'bin/**/*.mjs' '*.md' '*.json'",
31
+ "format:check": "prettier --check 'bin/**/*.mjs' '*.md' '*.json'",
32
+ "generate-template": "bash scripts/generate-template.sh",
33
+ "prepare": "node -e \"try { require('husky').install() } catch {}\""
34
+ },
28
35
  "keywords": [
29
36
  "opencode",
30
37
  "ai-agent",
@@ -56,5 +63,11 @@
56
63
  "dependencies": {
57
64
  "commander": "^13.0.0",
58
65
  "fs-extra": "^11.0.0"
66
+ },
67
+ "devDependencies": {
68
+ "@eslint/js": "^10.0.1",
69
+ "eslint": "^9.0.0",
70
+ "eslint-config-prettier": "^9.1.0",
71
+ "prettier": "^3.4.0"
59
72
  }
60
73
  }
@@ -10,7 +10,6 @@
10
10
  "*.env.*": "deny",
11
11
  "*.env.example": "allow"
12
12
  },
13
- "edit": "allow",
14
13
  "webfetch": "allow",
15
14
  "skill": {
16
15
  "*": "allow"
@@ -24,7 +23,7 @@
24
23
  ".opencode/instructions/INSTRUCTIONS.md",
25
24
  ".opencode/skills/coding-standards/SKILL.md",
26
25
  ".opencode/skills/security-review/SKILL.md",
27
- ".opencode/skills/frontend-design/SKILL.md"
26
+ ".opencode/skills/impeccable/SKILL.md"
28
27
  ],
29
28
  "mcp": {
30
29
  "nuxt": {
@@ -74,8 +73,15 @@
74
73
  "Authorization": "Bearer ${SONARQUBE_TOKEN}",
75
74
  "SONARQUBE_TOOLSETS": "analysis,issues,security-hotspots,quality-gates,rules,duplications,measures,dependency-risks,coverage,sources,languages,portfolios,system,webhooks"
76
75
  }
76
+ },
77
+ "agentmemory": {
78
+ "type": "local",
79
+ "command": ["bash", ".opencode/hooks/agentmemory-start.sh"],
80
+ "enabled": true,
81
+ "description": "Persistent cross-session memory with 53 tools (save, recall, smart search, sessions, file history, lessons)"
77
82
  }
78
83
  },
84
+ "plugin": [".opencode/plugins/agentmemory-capture.ts"],
79
85
  "agent": {
80
86
  "leader": {
81
87
  "description": "IT Leader & Technical Project Manager — analyzes requirements, designs architecture, decomposes tasks, delegates to subagents, and unifies outputs",
@@ -101,6 +107,7 @@
101
107
  "figma_*": "ask",
102
108
  "playwright_*": "allow",
103
109
  "postman_*": "allow",
110
+ "agentmemory_*": "allow",
104
111
  "task": { "*": "allow" }
105
112
  }
106
113
  },
@@ -126,7 +133,8 @@
126
133
  "nuxt_*": "allow",
127
134
  "nuxt-ui_*": "allow",
128
135
  "figma_*": "ask",
129
- "playwright_*": "allow"
136
+ "playwright_*": "allow",
137
+ "agentmemory_*": "allow"
130
138
  }
131
139
  },
132
140
  "frontend-react": {
@@ -149,7 +157,8 @@
149
157
  "npx playwright*": "allow"
150
158
  },
151
159
  "figma_*": "ask",
152
- "playwright_*": "allow"
160
+ "playwright_*": "allow",
161
+ "agentmemory_*": "allow"
153
162
  }
154
163
  },
155
164
  "backend": {
@@ -170,7 +179,8 @@
170
179
  "bun *": "allow",
171
180
  "yarn *": "allow"
172
181
  },
173
- "postman_*": "allow"
182
+ "postman_*": "allow",
183
+ "agentmemory_*": "allow"
174
184
  }
175
185
  },
176
186
  "ci3": {
@@ -210,7 +220,8 @@
210
220
  "git diff": "allow",
211
221
  "git log*": "allow"
212
222
  },
213
- "postman_*": "allow"
223
+ "postman_*": "allow",
224
+ "agentmemory_*": "allow"
214
225
  }
215
226
  },
216
227
  "designer": {
@@ -232,7 +243,8 @@
232
243
  },
233
244
  "stitch_*": "allow",
234
245
  "figma_*": "ask",
235
- "nuxt-ui_*": "allow"
246
+ "nuxt-ui_*": "allow",
247
+ "agentmemory_*": "allow"
236
248
  }
237
249
  },
238
250
  "reviewer": {
@@ -254,7 +266,8 @@
254
266
  "yarn *": "allow",
255
267
  "npx playwright*": "allow"
256
268
  },
257
- "playwright_*": "allow"
269
+ "playwright_*": "allow",
270
+ "agentmemory_*": "allow"
258
271
  }
259
272
  },
260
273
  "database": {
@@ -274,28 +287,8 @@
274
287
  "pnpm *": "allow",
275
288
  "bun *": "allow",
276
289
  "yarn *": "allow"
277
- }
278
- }
279
- },
280
- "devops": {
281
- "description": "DevOps Engineer specializing in CI/CD, deployment, Docker, monitoring, and infrastructure (subagent of IT Leader)",
282
- "mode": "subagent",
283
- "prompt": "{file:.opencode/agents/devops-specialist.md}",
284
- "model": "opencode/claude-sonnet-4.5",
285
- "temperature": 0.4,
286
- "color": "#6366f1",
287
- "permission": {
288
- "edit": "allow",
289
- "webfetch": "allow",
290
- "skill": { "*": "allow" },
291
- "bash": {
292
- "*": "ask",
293
- "npm *": "allow",
294
- "pnpm *": "allow",
295
- "bun *": "allow",
296
- "yarn *": "allow",
297
- "docker *": "allow"
298
- }
290
+ },
291
+ "agentmemory_*": "allow"
299
292
  }
300
293
  },
301
294
  "seo": {
@@ -315,7 +308,8 @@
315
308
  "git diff": "allow",
316
309
  "git log*": "allow"
317
310
  },
318
- "nuxt_*": "allow"
311
+ "nuxt_*": "allow",
312
+ "agentmemory_*": "allow"
319
313
  }
320
314
  },
321
315
  "android": {
@@ -335,7 +329,8 @@
335
329
  "gradle *": "allow"
336
330
  },
337
331
  "figma_*": "ask",
338
- "playwright_*": "allow"
332
+ "playwright_*": "allow",
333
+ "agentmemory_*": "allow"
339
334
  }
340
335
  },
341
336
  "flutter": {
@@ -355,7 +350,8 @@
355
350
  "dart *": "allow"
356
351
  },
357
352
  "figma_*": "ask",
358
- "playwright_*": "allow"
353
+ "playwright_*": "allow",
354
+ "agentmemory_*": "allow"
359
355
  }
360
356
  }
361
357
  }
@@ -22,7 +22,7 @@
22
22
  ".opencode/instructions/INSTRUCTIONS.md",
23
23
  ".opencode/skills/coding-standards/SKILL.md",
24
24
  ".opencode/skills/security-review/SKILL.md",
25
- ".opencode/skills/frontend-design/SKILL.md"
25
+ ".opencode/skills/impeccable/SKILL.md"
26
26
  ],
27
27
  "mcp": {
28
28
  "nuxt": {
@@ -93,7 +93,7 @@
93
93
  "webfetch": "allow",
94
94
  "skill": { "*": "allow" },
95
95
  "bash": {
96
- "*": "allow",
96
+ "*": "ask",
97
97
  "npm *": "allow",
98
98
  "pnpm *": "allow",
99
99
  "bun *": "allow",
@@ -119,7 +119,7 @@
119
119
  "webfetch": "allow",
120
120
  "skill": { "*": "allow" },
121
121
  "bash": {
122
- "*": "allow",
122
+ "*": "ask",
123
123
  "npm *": "allow",
124
124
  "pnpm *": "allow",
125
125
  "bun *": "allow",
@@ -143,7 +143,7 @@
143
143
  "webfetch": "allow",
144
144
  "skill": { "*": "allow" },
145
145
  "bash": {
146
- "*": "allow",
146
+ "*": "ask",
147
147
  "npm *": "allow",
148
148
  "pnpm *": "allow",
149
149
  "bun *": "allow",
@@ -165,7 +165,7 @@
165
165
  "webfetch": "allow",
166
166
  "skill": { "*": "allow" },
167
167
  "bash": {
168
- "*": "allow",
168
+ "*": "ask",
169
169
  "npm *": "allow",
170
170
  "pnpm *": "allow",
171
171
  "bun *": "allow",
@@ -185,7 +185,7 @@
185
185
  "webfetch": "allow",
186
186
  "skill": { "*": "allow" },
187
187
  "bash": {
188
- "*": "allow",
188
+ "*": "ask",
189
189
  "git status": "allow",
190
190
  "git diff": "allow",
191
191
  "git log*": "allow"
@@ -204,7 +204,7 @@
204
204
  "webfetch": "allow",
205
205
  "skill": { "*": "allow" },
206
206
  "bash": {
207
- "*": "allow",
207
+ "*": "ask",
208
208
  "git status": "allow",
209
209
  "git diff": "allow",
210
210
  "git log*": "allow"
@@ -223,7 +223,7 @@
223
223
  "webfetch": "allow",
224
224
  "skill": { "*": "allow" },
225
225
  "bash": {
226
- "*": "allow",
226
+ "*": "ask",
227
227
  "git status": "allow",
228
228
  "git diff": "allow",
229
229
  "git log*": "allow"
@@ -244,7 +244,7 @@
244
244
  "webfetch": "allow",
245
245
  "skill": { "*": "allow" },
246
246
  "bash": {
247
- "*": "allow",
247
+ "*": "ask",
248
248
  "npm *": "allow",
249
249
  "pnpm *": "allow",
250
250
  "bun *": "allow",
@@ -265,7 +265,7 @@
265
265
  "webfetch": "allow",
266
266
  "skill": { "*": "allow" },
267
267
  "bash": {
268
- "*": "allow",
268
+ "*": "ask",
269
269
  "npm *": "allow",
270
270
  "pnpm *": "allow",
271
271
  "bun *": "allow",
@@ -284,7 +284,7 @@
284
284
  "webfetch": "allow",
285
285
  "skill": { "*": "allow" },
286
286
  "bash": {
287
- "*": "allow",
287
+ "*": "ask",
288
288
  "npm *": "allow",
289
289
  "pnpm *": "allow",
290
290
  "bun *": "allow",
@@ -304,7 +304,7 @@
304
304
  "webfetch": "allow",
305
305
  "skill": { "*": "allow" },
306
306
  "bash": {
307
- "*": "allow",
307
+ "*": "ask",
308
308
  "git status": "allow",
309
309
  "git diff": "allow",
310
310
  "git log*": "allow"
@@ -323,7 +323,7 @@
323
323
  "webfetch": "allow",
324
324
  "skill": { "*": "allow" },
325
325
  "bash": {
326
- "*": "allow",
326
+ "*": "ask",
327
327
  "./gradlew *": "allow",
328
328
  "gradle *": "allow"
329
329
  },
@@ -342,7 +342,7 @@
342
342
  "webfetch": "allow",
343
343
  "skill": { "*": "allow" },
344
344
  "bash": {
345
- "*": "allow",
345
+ "*": "ask",
346
346
  "flutter *": "allow",
347
347
  "dart *": "allow"
348
348
  },