opencode-onboard 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +266 -214
- package/content/.agents/agents/basic-engineer.md +30 -0
- package/content/.agents/agents/devops-manager.md +38 -29
- package/content/.agents/session-log.json +41 -0
- package/content/.agents/skills/ob-default/SKILL.md +21 -0
- package/content/.agents/skills/ob-generic-guardrails/SKILL.md +32 -0
- package/content/.agents/skills/ob-global/SKILL.md +49 -0
- package/content/.agents/skills/ob-pullrequest-az/SKILL.md +11 -21
- package/content/.agents/skills/ob-pullrequest-gh/SKILL.md +14 -24
- package/content/.agents/skills/ob-userstory-az/SKILL.md +8 -14
- package/content/.agents/skills/ob-userstory-gh/SKILL.md +6 -14
- package/content/.opencode/commands/opsx-apply.md +50 -33
- package/content/.opencode/opencode.json +3 -3
- package/content/.opencode/plugins/session-log.js +1 -1
- package/content/.opencode/skills/openspec-apply-change/SKILL.md +50 -33
- package/content/AGENTS.md +95 -141
- package/content/skills-lock.json +4 -0
- package/package.json +6 -1
- package/src/index.js +112 -191
- package/src/presets/browser.json +18 -0
- package/src/presets/clean.json +21 -0
- package/src/presets/models.json +33 -0
- package/src/presets/optimization.json +22 -0
- package/src/presets/platforms.json +29 -2
- package/src/presets/quota.json +14 -0
- package/src/presets/source.json +17 -0
- package/src/steps/browser/browser.test.js +81 -0
- package/src/steps/{install-browser.js → browser/index.js} +12 -15
- package/src/steps/{__tests__/clean-ai-files.test.js → clean/clean.test.js} +28 -13
- package/src/steps/{clean-ai-files.js → clean/index.js} +32 -30
- package/src/steps/{patch-agents-md.js → copy/agents.js} +41 -20
- package/src/steps/{__tests__/copy-content.test.js → copy/copy.test.js} +10 -1
- package/src/steps/copy/index.js +33 -0
- package/src/steps/copy/skills.js +55 -0
- package/src/steps/{write-onboard-config.js → metadata/index.js} +3 -3
- package/src/steps/metadata/metadata.test.js +96 -0
- package/src/steps/models/format.js +60 -0
- package/src/steps/models/format.test.js +74 -0
- package/src/steps/models/index.js +52 -0
- package/src/steps/models/write.js +54 -0
- package/src/steps/models/write.test.js +119 -0
- package/src/steps/{init-openspec.js → openspec/ensemble.js} +27 -61
- package/src/steps/openspec/ensemble.test.js +79 -0
- package/src/steps/openspec/index.js +32 -0
- package/src/steps/optimization/caveman-guidance.js +11 -0
- package/src/steps/{install-caveman.js → optimization/caveman.js} +5 -19
- package/src/steps/optimization/global.js +64 -0
- package/src/steps/optimization/index.js +101 -0
- package/src/steps/{__tests__/token-optimization.test.js → optimization/optimization.test.js} +19 -24
- package/src/steps/{install-quota.js → optimization/quota.js} +12 -10
- package/src/steps/platform/index.js +81 -0
- package/src/steps/platform/platform.test.js +129 -0
- package/src/steps/{choose-source-scope.js → source/index.js} +11 -17
- package/src/steps/source/source.test.js +89 -0
- package/src/utils/__tests__/copy.test.js +12 -5
- package/src/utils/copy.js +4 -24
- package/src/utils/exec-spinner.js +47 -0
- package/src/utils/exec.js +120 -162
- package/src/utils/models-cache.js +25 -68
- package/src/utils/models-pricing.js +42 -0
- package/src/utils/models-pricing.test.js +94 -0
- package/content/.agents/agents/back-engineer.md +0 -87
- package/content/.agents/agents/front-engineer.md +0 -86
- package/content/.agents/agents/infra-engineer.md +0 -85
- package/content/.agents/agents/quality-engineer.md +0 -86
- package/content/.agents/agents/security-auditor.md +0 -86
- package/src/steps/__tests__/check-env.test.js +0 -70
- package/src/steps/__tests__/check-platform.test.js +0 -104
- package/src/steps/__tests__/check-rtk.test.js +0 -38
- package/src/steps/__tests__/choose-platform.test.js +0 -38
- package/src/steps/check-env.js +0 -26
- package/src/steps/check-platform.js +0 -80
- package/src/steps/check-rtk.js +0 -38
- package/src/steps/choose-models.js +0 -163
- package/src/steps/choose-platform.js +0 -22
- package/src/steps/choose-skills-provider.js +0 -79
- package/src/steps/copy-content.js +0 -89
- package/src/steps/enable-caveman-guidance.js +0 -93
- package/src/steps/token-optimization.js +0 -59
package/src/index.js
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import chalk from 'chalk'
|
|
3
|
+
import fse from 'fs-extra'
|
|
3
4
|
import { createRequire } from 'node:module'
|
|
4
5
|
import path from 'node:path'
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { copyContentStep } from './steps/copy-content.js'
|
|
15
|
-
import { initOpenspec } from './steps/init-openspec.js'
|
|
16
|
-
import { patchAgentsMd } from './steps/patch-agents-md.js'
|
|
17
|
-
import { installQuota } from './steps/install-quota.js'
|
|
18
|
-
import { installCaveman } from './steps/install-caveman.js'
|
|
19
|
-
import { enableCavemanGuidance } from './steps/enable-caveman-guidance.js'
|
|
20
|
-
import { installBrowser } from './steps/install-browser.js'
|
|
21
|
-
import { writeOnboardConfig } from './steps/write-onboard-config.js'
|
|
22
|
-
import { loading } from './utils/exec.js'
|
|
23
|
-
import { tokenOptimizationStep } from './steps/token-optimization.js'
|
|
6
|
+
import { installBrowser } from './steps/browser/index.js'
|
|
7
|
+
import { cleanAiFiles } from './steps/clean/index.js'
|
|
8
|
+
import { copyContentStep } from './steps/copy/index.js'
|
|
9
|
+
import { chooseModels } from './steps/models/index.js'
|
|
10
|
+
import { initOpenspec } from './steps/openspec/index.js'
|
|
11
|
+
import { tokenOptimizationStep } from './steps/optimization/index.js'
|
|
12
|
+
import { choosePlatform } from './steps/platform/index.js'
|
|
13
|
+
import { chooseSourceScope } from './steps/source/index.js'
|
|
14
|
+
import { writeOnboardConfig } from './steps/metadata/index.js'
|
|
24
15
|
|
|
25
16
|
function printHelp(version) {
|
|
26
17
|
console.log(`opencode-onboard v${version}`)
|
|
@@ -34,12 +25,8 @@ function printHelp(version) {
|
|
|
34
25
|
console.log(' platform Run platform selection step')
|
|
35
26
|
console.log(' copy Run content copy step')
|
|
36
27
|
console.log(' openspec Run OpenSpec initialization step')
|
|
37
|
-
console.log(' skills Run skills install step')
|
|
38
28
|
console.log(' models Run models selection step')
|
|
39
29
|
console.log(' optimization Run token optimization tools step')
|
|
40
|
-
console.log(' quota Run opencode-quota installer step')
|
|
41
|
-
console.log(' rtk Run rtk check step')
|
|
42
|
-
console.log(' caveman Run caveman install + guidance steps')
|
|
43
30
|
console.log(' browser Run opencode-browser installer step')
|
|
44
31
|
console.log(' metadata Write onboarding metadata step')
|
|
45
32
|
console.log()
|
|
@@ -70,78 +57,46 @@ async function runSingleCommand(command) {
|
|
|
70
57
|
const platform = savedWizard?.platform
|
|
71
58
|
const resolvedPlatform = platform === 'azure' || platform === 'github' ? platform : 'github'
|
|
72
59
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (command === 'rtk') {
|
|
115
|
-
await checkRtk()
|
|
116
|
-
return true
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (command === 'caveman') {
|
|
120
|
-
const caveman = await installCaveman({ skillsProvider: savedWizard?.additionalSkillsProvider })
|
|
121
|
-
await enableCavemanGuidance(caveman)
|
|
122
|
-
return true
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (command === 'browser') {
|
|
126
|
-
await installBrowser()
|
|
127
|
-
return true
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (command === 'metadata') {
|
|
131
|
-
await writeOnboardConfig({
|
|
132
|
-
...ctx,
|
|
133
|
-
platform: resolvedPlatform,
|
|
134
|
-
additionalSkillsProvider: savedWizard?.additionalSkillsProvider ?? 'none',
|
|
135
|
-
planModel: savedWizard?.models?.plan ?? null,
|
|
136
|
-
buildModel: savedWizard?.models?.build ?? null,
|
|
137
|
-
fastModel: savedWizard?.models?.fast ?? null,
|
|
138
|
-
optionalTools: savedWizard?.optionalTools ?? null,
|
|
139
|
-
cavemanGuidance: savedWizard?.cavemanGuidance ?? null,
|
|
140
|
-
})
|
|
141
|
-
return true
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return false
|
|
60
|
+
const handlers = {
|
|
61
|
+
clean: async () => {
|
|
62
|
+
await cleanAiFiles()
|
|
63
|
+
},
|
|
64
|
+
platform: async () => {
|
|
65
|
+
await choosePlatform()
|
|
66
|
+
},
|
|
67
|
+
copy: async () => {
|
|
68
|
+
await copyContentStep(resolvedPlatform, ctx)
|
|
69
|
+
},
|
|
70
|
+
openspec: async () => {
|
|
71
|
+
await initOpenspec()
|
|
72
|
+
},
|
|
73
|
+
models: async () => {
|
|
74
|
+
await chooseModels()
|
|
75
|
+
},
|
|
76
|
+
optimization: async () => {
|
|
77
|
+
await tokenOptimizationStep({ ctx })
|
|
78
|
+
},
|
|
79
|
+
browser: async () => {
|
|
80
|
+
await installBrowser()
|
|
81
|
+
},
|
|
82
|
+
metadata: async () => {
|
|
83
|
+
await writeOnboardConfig({
|
|
84
|
+
...ctx,
|
|
85
|
+
platform: resolvedPlatform,
|
|
86
|
+
additionalSkillsProvider: 'npx-skills',
|
|
87
|
+
planModel: savedWizard?.models?.plan ?? null,
|
|
88
|
+
buildModel: savedWizard?.models?.build ?? null,
|
|
89
|
+
fastModel: savedWizard?.models?.fast ?? null,
|
|
90
|
+
optionalTools: savedWizard?.optionalTools ?? null,
|
|
91
|
+
cavemanGuidance: savedWizard?.cavemanGuidance ?? null,
|
|
92
|
+
})
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const handler = handlers[command]
|
|
97
|
+
if (!handler) return false
|
|
98
|
+
await handler()
|
|
99
|
+
return true
|
|
145
100
|
}
|
|
146
101
|
|
|
147
102
|
if (process.stdout.isTTY) console.clear()
|
|
@@ -168,117 +123,83 @@ if (args.length > 0) {
|
|
|
168
123
|
|
|
169
124
|
const logo = chalk.hex('#fe3d57')
|
|
170
125
|
const bannerLines = [
|
|
171
|
-
logo(' '),
|
|
172
|
-
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
|
|
173
|
-
logo(' ▒▒▓ ▓▒▓ '),
|
|
174
|
-
logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
|
|
175
|
-
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
176
|
-
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
|
|
177
|
-
logo(' ▓▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '),
|
|
178
|
-
logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
|
|
179
|
-
logo(' ▓▓▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
|
|
180
|
-
logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
181
|
-
logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
182
|
-
logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
|
|
183
|
-
'',
|
|
184
|
-
chalk.bold(' 🧰 opencode-onboard') + chalk.dim(` v${version}`),
|
|
185
|
-
chalk.dim(' Prepare your codebase for AI agents'),
|
|
186
|
-
]
|
|
187
|
-
|
|
188
|
-
for (const line of bannerLines) console.log(line)
|
|
189
|
-
console.log()
|
|
190
|
-
console.log(' This tool will set up your project with a team of AI agents,')
|
|
191
|
-
console.log(' install skills, select models, and configure OpenCode.')
|
|
192
|
-
console.log()
|
|
193
|
-
|
|
194
|
-
// Only wait for Enter in a real interactive TTY
|
|
195
|
-
if (process.stdin.isTTY) {
|
|
196
|
-
console.log(chalk.bold(' Press Enter to begin...'))
|
|
197
|
-
console.log()
|
|
198
|
-
await new Promise(resolve => {
|
|
199
|
-
process.stdin.resume()
|
|
200
|
-
process.stdin.once('data', () => {
|
|
201
|
-
process.stdin.pause()
|
|
202
|
-
resolve()
|
|
203
|
-
})
|
|
204
|
-
})
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
// 1. Check Node + pnpm
|
|
209
|
-
await checkEnv()
|
|
210
|
-
loading('preparing next step...')
|
|
126
|
+
logo(' '),
|
|
127
|
+
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
|
|
128
|
+
logo(' ▒▒▓ ▓▒▓ '),
|
|
129
|
+
logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
|
|
130
|
+
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
131
|
+
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
|
|
132
|
+
logo(' ▓▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '),
|
|
133
|
+
logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
|
|
134
|
+
logo(' ▓▓▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
|
|
135
|
+
logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
136
|
+
logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
137
|
+
logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
|
|
138
|
+
'',
|
|
139
|
+
chalk.bold(' 🧰 opencode-onboard') + chalk.dim(` v${version}`),
|
|
140
|
+
chalk.dim(' Prepare your codebase for AI agents'),
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
for (const line of bannerLines) console.log(line)
|
|
144
|
+
console.log()
|
|
145
|
+
console.log(' This tool will set up your project with a team of AI agents,')
|
|
146
|
+
console.log(' install skills, select models, and configure OpenCode.')
|
|
147
|
+
console.log()
|
|
148
|
+
|
|
149
|
+
// Only wait for Enter in a real interactive TTY
|
|
150
|
+
if (process.stdin.isTTY) {
|
|
151
|
+
console.log(chalk.bold(' Press Enter to begin...'))
|
|
152
|
+
console.log()
|
|
153
|
+
await new Promise(resolve => {
|
|
154
|
+
process.stdin.resume()
|
|
155
|
+
process.stdin.once('data', () => {
|
|
156
|
+
process.stdin.pause()
|
|
157
|
+
resolve()
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
}
|
|
211
161
|
|
|
212
|
-
|
|
162
|
+
try {
|
|
213
163
|
const scope = await chooseSourceScope()
|
|
214
|
-
loading('preparing next step...')
|
|
215
164
|
|
|
216
|
-
// 3. Clean existing AI config files, detect preserved state
|
|
217
165
|
const preserve = await cleanAiFiles()
|
|
218
166
|
const ctx = { ...preserve, ...scope }
|
|
219
|
-
loading('preparing next step...')
|
|
220
167
|
|
|
221
|
-
// 4. Choose platform
|
|
222
168
|
const platform = await choosePlatform()
|
|
223
|
-
loading('preparing next step...')
|
|
224
|
-
|
|
225
|
-
// 5. Check platform CLI (az or gh)
|
|
226
|
-
await checkPlatform(platform)
|
|
227
|
-
loading('preparing next step...')
|
|
228
169
|
|
|
229
|
-
// 6. Copy content
|
|
230
170
|
await copyContentStep(platform, ctx)
|
|
231
|
-
loading('preparing next step...')
|
|
232
|
-
|
|
233
|
-
// 6b. Patch AGENTS.md to skip steps for already-existing files
|
|
234
|
-
await patchAgentsMd(ctx)
|
|
235
|
-
loading('preparing next step...')
|
|
236
171
|
|
|
237
|
-
// 7. Init OpenSpec
|
|
238
172
|
await initOpenspec()
|
|
239
|
-
loading('preparing next step...')
|
|
240
173
|
|
|
241
|
-
// 8. Install skills
|
|
242
|
-
const skillsSelection = await chooseSkillsProvider()
|
|
243
|
-
loading('preparing next step...')
|
|
244
|
-
|
|
245
|
-
// 9. Choose models
|
|
246
174
|
const selectedModels = await chooseModels()
|
|
247
|
-
loading('preparing next step...')
|
|
248
175
|
|
|
249
|
-
|
|
250
|
-
const tokenOpt = await tokenOptimizationStep({ skillsProvider: skillsSelection.additionalSkillsProvider })
|
|
176
|
+
const tokenOpt = await tokenOptimizationStep({ ctx })
|
|
251
177
|
const { rtk, quota, caveman, cavemanGuidance } = tokenOpt
|
|
252
|
-
loading('preparing next step...')
|
|
253
178
|
|
|
254
|
-
// 11. Install opencode-browser
|
|
255
179
|
await installBrowser()
|
|
256
|
-
loading('preparing next step...')
|
|
257
180
|
|
|
258
|
-
// 12. Write onboarding metadata
|
|
259
181
|
await writeOnboardConfig({
|
|
260
182
|
...ctx,
|
|
261
183
|
platform,
|
|
262
|
-
|
|
184
|
+
additionalSkillsProvider: 'npx-skills',
|
|
263
185
|
...selectedModels,
|
|
264
186
|
optionalTools: { rtk, quota, caveman },
|
|
265
187
|
cavemanGuidance,
|
|
266
188
|
})
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
!ctx.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
console.log()
|
|
275
|
-
console.log(chalk.bold.green('
|
|
276
|
-
console.log(chalk.bold.green('
|
|
277
|
-
console.log(
|
|
278
|
-
console.log()
|
|
279
|
-
console.log('
|
|
280
|
-
console.log(
|
|
281
|
-
console.log()
|
|
189
|
+
|
|
190
|
+
const toGenerate = [
|
|
191
|
+
!ctx.hasDesign && 'DESIGN.md',
|
|
192
|
+
!ctx.hasArchitecture && 'ARCHITECTURE.md',
|
|
193
|
+
].filter(Boolean)
|
|
194
|
+
|
|
195
|
+
console.log()
|
|
196
|
+
console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
|
|
197
|
+
console.log(chalk.bold.green(' Onboarding complete!'))
|
|
198
|
+
console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
|
|
199
|
+
console.log()
|
|
200
|
+
console.log(' Open this project in OpenCode and type:')
|
|
201
|
+
console.log(chalk.bold(' "init"'))
|
|
202
|
+
console.log()
|
|
282
203
|
if (toGenerate.length > 0) {
|
|
283
204
|
console.log(` OpenCode will generate ${toGenerate.join(' and ')}`)
|
|
284
205
|
console.log(' from your actual codebase, then activate the agent team.')
|
|
@@ -287,12 +208,12 @@ try {
|
|
|
287
208
|
}
|
|
288
209
|
console.log(` Source scope: ${ctx.sourceMode === 'parent-selected' ? ctx.sourceRoots.map(p => `../${p.split(/[/\\]/).pop()}`).join(', ') : 'current folder'}`)
|
|
289
210
|
console.log()
|
|
290
|
-
} catch (err) {
|
|
291
|
-
if (err.name === 'ExitPromptError') {
|
|
292
|
-
console.log()
|
|
293
|
-
console.log(chalk.yellow('Cancelled.'))
|
|
294
|
-
} else {
|
|
295
|
-
console.error(chalk.red('\nUnexpected error:'), err.message)
|
|
296
|
-
process.exit(1)
|
|
297
|
-
}
|
|
298
|
-
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (err.name === 'ExitPromptError') {
|
|
213
|
+
console.log()
|
|
214
|
+
console.log(chalk.yellow('Cancelled.'))
|
|
215
|
+
} else {
|
|
216
|
+
console.error(chalk.red('\nUnexpected error:'), err.message)
|
|
217
|
+
process.exit(1)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"installer": {
|
|
3
|
+
"command": "npx",
|
|
4
|
+
"args": ["@different-ai/opencode-browser", "install"]
|
|
5
|
+
},
|
|
6
|
+
"output": {
|
|
7
|
+
"showAfter": "To load the extension",
|
|
8
|
+
"hideAfter": "Press Enter when"
|
|
9
|
+
},
|
|
10
|
+
"autoAnswers": [
|
|
11
|
+
{ "trigger": "Press Enter when", "response": "" },
|
|
12
|
+
{ "trigger": "Choose config location", "response": "2" },
|
|
13
|
+
{ "trigger": "Add plugin automatically?", "response": "y" },
|
|
14
|
+
{ "trigger": "Create one?", "response": "y" },
|
|
15
|
+
{ "trigger": "Add browser-automation skill", "response": "n" },
|
|
16
|
+
{ "trigger": "Check broker", "response": "n" }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"preserve": ["DESIGN.md", "ARCHITECTURE.md", "openspec"],
|
|
3
|
+
"detectFiles": [
|
|
4
|
+
"AGENTS.md",
|
|
5
|
+
"CLAUDE.md",
|
|
6
|
+
"ARCHITECTURE.md",
|
|
7
|
+
"DESIGN.md",
|
|
8
|
+
".cursorrules",
|
|
9
|
+
".clinerules",
|
|
10
|
+
".windsurfrules",
|
|
11
|
+
".github/copilot-instructions.md",
|
|
12
|
+
"copilot-instructions.md",
|
|
13
|
+
".aider.conf.yml",
|
|
14
|
+
".aider",
|
|
15
|
+
".opencode",
|
|
16
|
+
".agents"
|
|
17
|
+
],
|
|
18
|
+
"directoryTargets": [".opencode", ".agents"],
|
|
19
|
+
"preserveSubfolders": ["skills"],
|
|
20
|
+
"selectionMessage": "Select AI config files to remove:"
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"costTiers": [
|
|
3
|
+
{ "max": 1, "label": "[$]" },
|
|
4
|
+
{ "max": 10, "label": "[$$]" },
|
|
5
|
+
{ "label": "[$$$]" }
|
|
6
|
+
],
|
|
7
|
+
"roles": {
|
|
8
|
+
"plan": {
|
|
9
|
+
"prompt": "Plan model:",
|
|
10
|
+
"info": [
|
|
11
|
+
"PLAN model: used by the main agent to read issues, write proposals, coordinate the team.",
|
|
12
|
+
"This model needs to be strong. Use Claude Sonnet/Opus, GPT-4o, o3, or equivalent.",
|
|
13
|
+
"A weak model here will silently skip steps and break the workflow."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"build": {
|
|
17
|
+
"prompt": "Build model:",
|
|
18
|
+
"info": [
|
|
19
|
+
"BUILD model: used by front-engineer, back-engineer, infra-engineer, quality-engineer, security-auditor.",
|
|
20
|
+
"Needs to be capable for implementation work. Claude Sonnet, GPT-4o, or equivalent."
|
|
21
|
+
],
|
|
22
|
+
"agents": ["front-engineer", "back-engineer", "infra-engineer", "quality-engineer", "security-auditor"]
|
|
23
|
+
},
|
|
24
|
+
"fast": {
|
|
25
|
+
"prompt": "Fast model:",
|
|
26
|
+
"info": [
|
|
27
|
+
"FAST model: used by devops-manager for reading issues and classifying PR comments.",
|
|
28
|
+
"Something fast and cheap is fine here, no heavy reasoning needed."
|
|
29
|
+
],
|
|
30
|
+
"agents": ["devops-manager"]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"message": "Enable tools:",
|
|
3
|
+
"info": "Choose which optimization tools to enable (recommended: all).",
|
|
4
|
+
"timeoutMs": 30000,
|
|
5
|
+
"choices": [
|
|
6
|
+
{
|
|
7
|
+
"name": "RTK check (recommended)",
|
|
8
|
+
"value": "rtk",
|
|
9
|
+
"checked": true
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "opencode-quota plugin (recommended)",
|
|
13
|
+
"value": "quota",
|
|
14
|
+
"checked": true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "caveman concise mode (recommended)",
|
|
18
|
+
"value": "caveman",
|
|
19
|
+
"checked": true
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
3
|
"value": "github",
|
|
4
|
-
"name": "GitHub"
|
|
4
|
+
"name": "GitHub",
|
|
5
|
+
"cli": {
|
|
6
|
+
"command": "gh",
|
|
7
|
+
"displayName": "GitHub CLI (gh)",
|
|
8
|
+
"installUrl": "https://cli.github.com",
|
|
9
|
+
"authCheck": {
|
|
10
|
+
"args": ["auth", "status"],
|
|
11
|
+
"notAuthenticatedMessage": "GitHub CLI not authenticated. Run:",
|
|
12
|
+
"commands": ["gh auth login"]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
5
15
|
},
|
|
6
16
|
{
|
|
7
17
|
"value": "azure",
|
|
8
|
-
"name": "Azure DevOps"
|
|
18
|
+
"name": "Azure DevOps",
|
|
19
|
+
"cli": {
|
|
20
|
+
"command": "az",
|
|
21
|
+
"displayName": "Azure CLI (az)",
|
|
22
|
+
"installUrl": "https://learn.microsoft.com/en-us/cli/azure/install-azure-cli",
|
|
23
|
+
"extensionCheck": {
|
|
24
|
+
"args": ["extension", "list", "--query", "[?name=='azure-devops']", "-o", "tsv"],
|
|
25
|
+
"expectedOutput": "azure-devops",
|
|
26
|
+
"missingMessage": "azure-devops extension not found. Run:",
|
|
27
|
+
"errorMessage": "Could not check azure-devops extension. Run:",
|
|
28
|
+
"commands": [
|
|
29
|
+
"az extension add --name azure-devops",
|
|
30
|
+
"az config set extension.dynamic_install_allow_preview=true",
|
|
31
|
+
"az login",
|
|
32
|
+
"az devops login --organization https://dev.azure.com/<your-org>"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
9
36
|
}
|
|
10
37
|
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"plugin": "@slkiser/opencode-quota@latest",
|
|
3
|
+
"prompt": {
|
|
4
|
+
"message": "Install opencode-quota with recommended defaults?",
|
|
5
|
+
"default": true,
|
|
6
|
+
"timeoutMs": 20000
|
|
7
|
+
},
|
|
8
|
+
"defaults": {
|
|
9
|
+
"enabledProviders": "auto",
|
|
10
|
+
"formatStyle": "singleWindow",
|
|
11
|
+
"percentDisplayMode": "used",
|
|
12
|
+
"showSessionTokens": true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"message": "Source code location:",
|
|
3
|
+
"default": "current",
|
|
4
|
+
"choices": [
|
|
5
|
+
{
|
|
6
|
+
"name": "Current folder (default)",
|
|
7
|
+
"value": "current",
|
|
8
|
+
"description": "Use this repository only"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "Select folders in parent (../)",
|
|
12
|
+
"value": "parent",
|
|
13
|
+
"description": "Use when this repo only contains agent config"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"parentSelectionMessage": "Select source folders from parent directory:"
|
|
17
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
|
|
3
|
+
vi.mock('../../utils/exec.js', () => ({
|
|
4
|
+
header: vi.fn(),
|
|
5
|
+
info: vi.fn(),
|
|
6
|
+
success: vi.fn(),
|
|
7
|
+
warn: vi.fn(),
|
|
8
|
+
error: vi.fn(),
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
vi.mock('fs-extra', () => ({
|
|
12
|
+
default: {
|
|
13
|
+
readJson: vi.fn().mockResolvedValue({
|
|
14
|
+
installer: { command: 'npx', args: ['@different-ai/opencode-browser', 'install'] },
|
|
15
|
+
output: { showAfter: '===', hideAfter: '===' },
|
|
16
|
+
autoAnswers: [
|
|
17
|
+
{ trigger: 'Install', response: 'y' },
|
|
18
|
+
],
|
|
19
|
+
}),
|
|
20
|
+
},
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
vi.mock('execa', () => ({
|
|
24
|
+
execa: vi.fn(),
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
import fse from 'fs-extra'
|
|
28
|
+
import { installBrowser } from './index.js'
|
|
29
|
+
|
|
30
|
+
describe('installBrowser()', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
vi.clearAllMocks()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('calls installer command from preset', async () => {
|
|
36
|
+
const { execa } = await import('execa')
|
|
37
|
+
const mockChild = {
|
|
38
|
+
stdout: { on: vi.fn() },
|
|
39
|
+
stderr: { on: vi.fn() },
|
|
40
|
+
stdin: { write: vi.fn() },
|
|
41
|
+
then: (cb) => cb({ exitCode: 0 }),
|
|
42
|
+
}
|
|
43
|
+
execa.mockReturnValue(mockChild)
|
|
44
|
+
|
|
45
|
+
await installBrowser()
|
|
46
|
+
|
|
47
|
+
expect(execa).toHaveBeenCalledWith('npx', expect.arrayContaining('@different-ai/opencode-browser'), expect.any(Object))
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('logs success when exit code is 0', async () => {
|
|
51
|
+
const { execa } = await import('execa')
|
|
52
|
+
const mockChild = {
|
|
53
|
+
stdout: { on: vi.fn() },
|
|
54
|
+
stderr: { on: vi.fn() },
|
|
55
|
+
stdin: { write: vi.fn() },
|
|
56
|
+
then: (cb) => cb({ exitCode: 0 }),
|
|
57
|
+
}
|
|
58
|
+
execa.mockReturnValue(mockChild)
|
|
59
|
+
const { success } = await import('../../utils/exec.js')
|
|
60
|
+
|
|
61
|
+
await installBrowser()
|
|
62
|
+
|
|
63
|
+
expect(success).toHaveBeenCalledWith('opencode-browser installed')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('logs warning when exit code is non-zero', async () => {
|
|
67
|
+
const { execa } = await import('execa')
|
|
68
|
+
const mockChild = {
|
|
69
|
+
stdout: { on: vi.fn() },
|
|
70
|
+
stderr: { on: vi.fn() },
|
|
71
|
+
stdin: { write: vi.fn() },
|
|
72
|
+
then: (cb) => cb({ exitCode: 1 }),
|
|
73
|
+
}
|
|
74
|
+
execa.mockReturnValue(mockChild)
|
|
75
|
+
const { warn } = await import('../../utils/exec.js')
|
|
76
|
+
|
|
77
|
+
await installBrowser()
|
|
78
|
+
|
|
79
|
+
expect(warn).toHaveBeenCalledWith('opencode-browser install exited with non-zero code')
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
import { execa } from 'execa'
|
|
2
|
-
import
|
|
2
|
+
import fse from 'fs-extra'
|
|
3
|
+
import { header, info, success, warn, error } from '../../utils/exec.js'
|
|
3
4
|
import os from 'os'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import { fileURLToPath } from 'url'
|
|
4
7
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{ trigger: 'Add plugin automatically?', response: 'y' },
|
|
9
|
-
{ trigger: 'Create one?', response: 'y' },
|
|
10
|
-
{ trigger: 'Add browser-automation skill', response: 'n' },
|
|
11
|
-
{ trigger: 'Check broker', response: 'n' },
|
|
12
|
-
]
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
const BROWSER_PRESET_PATH = path.resolve(__dirname, '../../presets/browser.json')
|
|
10
|
+
const browserPreset = await fse.readJson(BROWSER_PRESET_PATH)
|
|
13
11
|
|
|
14
12
|
export async function installBrowser() {
|
|
15
|
-
header('Step
|
|
13
|
+
header('Step 9, Installing opencode-browser')
|
|
16
14
|
|
|
17
15
|
try {
|
|
18
|
-
const child = execa(
|
|
16
|
+
const child = execa(browserPreset.installer.command, browserPreset.installer.args, {
|
|
19
17
|
cwd: os.homedir(),
|
|
20
18
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
21
19
|
reject: false,
|
|
22
20
|
})
|
|
23
21
|
|
|
24
|
-
const pendingTriggers = [...
|
|
22
|
+
const pendingTriggers = [...browserPreset.autoAnswers]
|
|
25
23
|
let show = false
|
|
26
24
|
|
|
27
25
|
child.stdout.on('data', (chunk) => {
|
|
28
26
|
const text = chunk.toString()
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
if (text.includes(
|
|
32
|
-
if (text.includes('Press Enter when')) show = false
|
|
28
|
+
if (text.includes(browserPreset.output.showAfter)) show = true
|
|
29
|
+
if (text.includes(browserPreset.output.hideAfter)) show = false
|
|
33
30
|
|
|
34
31
|
if (show) process.stdout.write(chunk)
|
|
35
32
|
|