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 +21 -0
- package/README.md +18 -2
- package/bin/commands/doctor.mjs +164 -0
- package/bin/commands/init.mjs +107 -79
- package/bin/commands/uninstall.mjs +79 -0
- package/bin/commands/upgrade.mjs +65 -0
- package/bin/init.mjs +33 -10
- package/package.json +16 -2
- package/template/.opencode/commands/recall.md +19 -0
- package/template/.opencode/commands/remember.md +19 -0
- package/template/.opencode/hooks/agentmemory-start.sh +17 -0
- package/template/.opencode/instructions/INSTRUCTIONS.md +49 -0
- package/template/.opencode/plugins/agentmemory-capture.ts +651 -0
- package/template/.opencode/skills/agentmemory/SKILL.md +97 -0
- package/template/opencode.example.json +29 -33
- package/template/opencode.json +46 -26
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,
|
|
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` |
|
|
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
|
+
}
|
package/bin/commands/init.mjs
CHANGED
|
@@ -6,15 +6,43 @@ import {
|
|
|
6
6
|
writeFileSync,
|
|
7
7
|
readdirSync,
|
|
8
8
|
appendFileSync,
|
|
9
|
-
} from
|
|
10
|
-
import { join, dirname } from
|
|
11
|
-
import { fileURLToPath } from
|
|
12
|
-
import { execSync } from
|
|
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,
|
|
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
|
|
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,
|
|
159
|
-
const userConfigPath = join(targetDir,
|
|
188
|
+
const opencodeDir = join(targetDir, '.opencode');
|
|
189
|
+
const userConfigPath = join(targetDir, 'opencode.json');
|
|
160
190
|
|
|
161
191
|
if (existsSync(opencodeDir) && !force) {
|
|
162
|
-
|
|
163
|
-
const rl = await import(
|
|
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() !==
|
|
173
|
-
|
|
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
|
-
|
|
181
|
-
|
|
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
|
-
|
|
187
|
-
copyRecursive(join(TEMPLATE_DIR,
|
|
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,
|
|
218
|
+
const templateConfigPath = join(TEMPLATE_DIR, 'opencode.json');
|
|
191
219
|
if (existsSync(templateConfigPath)) {
|
|
192
|
-
|
|
193
|
-
const merged = mergeOencodeConfig(
|
|
194
|
-
|
|
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,
|
|
207
|
-
const exampleDest = join(targetDir,
|
|
226
|
+
const exampleSrc = join(TEMPLATE_DIR, 'opencode.example.json');
|
|
227
|
+
const exampleDest = join(targetDir, 'opencode.example.json');
|
|
208
228
|
if (existsSync(exampleSrc)) {
|
|
209
|
-
|
|
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
|
-
|
|
236
|
+
info(`Installing .opencode/ dependencies with ${pm}...`);
|
|
217
237
|
try {
|
|
218
|
-
execSync(`${pm} install`, { cwd: opencodeDir, stdio:
|
|
238
|
+
execSync(`${pm} install`, { cwd: opencodeDir, stdio: 'pipe' });
|
|
239
|
+
success('.opencode/ dependencies installed');
|
|
219
240
|
} catch (err) {
|
|
220
|
-
|
|
221
|
-
|
|
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.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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(
|
|
234
|
-
|
|
270
|
+
writeFileSync(gitignorePath, gitignoreEntries.join('\n') + '\n', 'utf-8');
|
|
271
|
+
success('Created .gitignore');
|
|
235
272
|
} else {
|
|
236
|
-
const gitignoreContent = readFileSync(gitignorePath,
|
|
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 +
|
|
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
|
-
//
|
|
250
|
-
const pkgJson = JSON.parse(
|
|
251
|
-
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
console.log(
|
|
258
|
-
console.log(`
|
|
259
|
-
console.log(`
|
|
260
|
-
console.log(
|
|
261
|
-
|
|
262
|
-
);
|
|
263
|
-
console.log(
|
|
264
|
-
|
|
265
|
-
);
|
|
266
|
-
console.log(`
|
|
267
|
-
console.log(
|
|
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
|
+
}
|