opencode-onboard 0.3.3 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +278 -214
  2. package/content/.agents/agents/basic-engineer.md +30 -0
  3. package/content/.agents/agents/devops-manager.md +38 -29
  4. package/content/.agents/session-log.json +41 -0
  5. package/content/.agents/skills/ob-default/SKILL.md +21 -0
  6. package/content/.agents/skills/ob-generic-guardrails/SKILL.md +32 -0
  7. package/content/.agents/skills/ob-global/SKILL.md +49 -0
  8. package/content/.agents/skills/ob-pullrequest-az/SKILL.md +11 -21
  9. package/content/.agents/skills/ob-pullrequest-gh/SKILL.md +14 -24
  10. package/content/.agents/skills/ob-userstory-az/SKILL.md +8 -14
  11. package/content/.agents/skills/ob-userstory-gh/SKILL.md +6 -14
  12. package/content/.opencode/commands/init.md +8 -0
  13. package/content/.opencode/commands/main.md +17 -0
  14. package/content/.opencode/commands/opsx-apply.md +50 -33
  15. package/content/.opencode/commands/plan.md +37 -0
  16. package/content/.opencode/plugins/session-log.js +1 -1
  17. package/content/.opencode/skills/openspec-apply-change/SKILL.md +50 -33
  18. package/content/AGENTS.md +94 -144
  19. package/content/skills-lock.json +4 -0
  20. package/package.json +6 -1
  21. package/src/commands/join.js +43 -0
  22. package/src/commands/shared.js +12 -0
  23. package/src/commands/shared.test.js +56 -0
  24. package/src/commands/single.js +64 -0
  25. package/src/commands/wizard.js +99 -0
  26. package/src/index.js +25 -202
  27. package/src/presets/browser.json +18 -0
  28. package/src/presets/clean.json +21 -0
  29. package/src/presets/models.json +33 -0
  30. package/src/presets/optimization.json +22 -0
  31. package/src/presets/platforms.json +29 -2
  32. package/src/presets/quota.json +14 -0
  33. package/src/presets/source.json +17 -0
  34. package/src/steps/browser/browser.test.js +81 -0
  35. package/src/steps/{install-browser.js → browser/index.js} +12 -15
  36. package/src/steps/{__tests__/clean-ai-files.test.js → clean/clean.test.js} +28 -13
  37. package/src/steps/{clean-ai-files.js → clean/index.js} +32 -30
  38. package/src/steps/copy/agents.js +106 -0
  39. package/src/steps/{__tests__/copy-content.test.js → copy/copy.test.js} +10 -1
  40. package/src/steps/copy/index.js +33 -0
  41. package/src/steps/copy/skills.js +55 -0
  42. package/src/steps/{write-onboard-config.js → metadata/index.js} +3 -3
  43. package/src/steps/metadata/metadata.test.js +99 -0
  44. package/src/steps/models/format.js +60 -0
  45. package/src/steps/models/format.test.js +75 -0
  46. package/src/steps/models/index.js +52 -0
  47. package/src/steps/models/write.js +54 -0
  48. package/src/steps/models/write.test.js +117 -0
  49. package/src/steps/{init-openspec.js → openspec/ensemble.js} +20 -57
  50. package/src/steps/openspec/ensemble.test.js +79 -0
  51. package/src/steps/openspec/index.js +32 -0
  52. package/src/steps/optimization/caveman-guidance.js +11 -0
  53. package/src/steps/{install-caveman.js → optimization/caveman.js} +5 -19
  54. package/src/steps/optimization/global.js +64 -0
  55. package/src/steps/optimization/index.js +101 -0
  56. package/src/steps/{__tests__/token-optimization.test.js → optimization/optimization.test.js} +37 -22
  57. package/src/steps/{install-quota.js → optimization/quota.js} +12 -10
  58. package/src/steps/platform/index.js +81 -0
  59. package/src/steps/platform/platform.test.js +129 -0
  60. package/src/steps/{choose-source-scope.js → source/index.js} +11 -17
  61. package/src/steps/source/source.test.js +91 -0
  62. package/src/utils/__tests__/copy.test.js +12 -5
  63. package/src/utils/copy.js +4 -24
  64. package/src/utils/exec-spinner.js +47 -0
  65. package/src/utils/exec.js +120 -162
  66. package/src/utils/models-cache.js +25 -68
  67. package/src/utils/models-pricing.js +42 -0
  68. package/src/utils/models-pricing.test.js +93 -0
  69. package/content/.agents/agents/back-engineer.md +0 -87
  70. package/content/.agents/agents/front-engineer.md +0 -86
  71. package/content/.agents/agents/infra-engineer.md +0 -85
  72. package/content/.agents/agents/quality-engineer.md +0 -86
  73. package/content/.agents/agents/security-auditor.md +0 -86
  74. package/src/steps/__tests__/check-env.test.js +0 -70
  75. package/src/steps/__tests__/check-platform.test.js +0 -104
  76. package/src/steps/__tests__/check-rtk.test.js +0 -38
  77. package/src/steps/__tests__/choose-platform.test.js +0 -38
  78. package/src/steps/check-env.js +0 -26
  79. package/src/steps/check-platform.js +0 -80
  80. package/src/steps/check-rtk.js +0 -38
  81. package/src/steps/choose-models.js +0 -165
  82. package/src/steps/choose-platform.js +0 -22
  83. package/src/steps/choose-skills-provider.js +0 -79
  84. package/src/steps/copy-content.js +0 -89
  85. package/src/steps/enable-caveman-guidance.js +0 -78
  86. package/src/steps/patch-agents-md.js +0 -153
  87. package/src/steps/token-optimization.js +0 -59
package/content/AGENTS.md CHANGED
@@ -97,7 +97,7 @@ After restarting you are ready to work.
97
97
  - Do NOT implement any features
98
98
  - Do NOT create branches or PRs
99
99
  - Do NOT modify any project source files
100
- - Do NOT create RTK files, scripts, or wrappers, RTK is already defined in AGENTS.md and agent files
100
+ - Do NOT create CLI wrapper files or scripts
101
101
  - Only read source files for analysis, write only to ARCHITECTURE.md, DESIGN.md, AGENTS.md, and openspec/
102
102
 
103
103
  <!-- AGENTS-TEMPLATE-START -->
@@ -112,17 +112,20 @@ This file provides guidance to AI agents when working in this repository.
112
112
  This is the agent orchestration layer for your project. It provides:
113
113
  - Universal agent team for development workflow
114
114
  - OpenSpec change management
115
- - Skills for platform-specific knowledge
115
+ - Mandatory global baseline skill (`ob-global`) for all agents
116
+ - Additional skills for platform/task-specific knowledge
116
117
 
117
118
  ## Source Scope
118
119
 
119
- - Read source scope from `.agents/source-roots.json`.
120
- - Use those roots for codebase analysis tasks (design, architecture, project-history, exploration).
121
- - If missing, default to current folder.
120
+ Source scope is defined by mandatory `ob-global` skill.
121
+
122
+ - Load `ob-global` first.
123
+ - Follow the generated `## Source Roots` section from that skill.
124
+ - Do not duplicate source-scope rules here.
122
125
 
123
126
  ## I Am the Lead, Full Workflow Ownership
124
127
 
125
- When the user provides a work item URL or says "implement the plan" or "I've added comments to the PR", **I own the full lifecycle**. I load the appropriate skill and use ensemble tools to coordinate the agent team.
128
+ When the user provides a work item URL or says "implement the plan" or "I've added comments to the PR", **I own the full lifecycle**. I load `ob-global` skill first, then the appropriate userstory skill, and use ensemble tools to coordinate the agent team.
126
129
 
127
130
  Trigger patterns, I recognize ALL of these, exact wording does not matter:
128
131
  - User pastes or mentions a GitHub Issue URL → load `ob-userstory-gh` skill → parse issue → run `/opsx-propose` → confirm with user → run `/opsx-apply` → ship
@@ -135,26 +138,15 @@ Trigger patterns, I recognize ALL of these, exact wording does not matter:
135
138
 
136
139
  **Never delegate without a plan. Never write implementation code directly, always spawn specialists, no exceptions. "Small feature", "faster to do it directly", or "environment issues" are not valid reasons to skip ensemble.**
137
140
 
138
- ## Multi-Agent Execution, opencode-ensemble
139
-
140
- Parallel execution uses the `opencode-ensemble` plugin (`team_create`, `team_spawn`, etc.).
141
- Works on **all platforms** (Windows, macOS, Linux) via OpenCode's built-in worktree support.
142
-
143
- | Tool | What it does |
144
- |------|-------------|
145
- | `team_create` | Create a team (caller becomes lead) |
146
- | `team_spawn` | Start a teammate asynchronously |
147
- | `team_shutdown` | Stop a teammate, preserve their branch |
148
- | `team_merge` | Merge a teammate's branch into working dir |
149
- | `team_cleanup` | Tear down the team |
150
- | `team_results` | Retrieve full message content (delivery is a ping only) |
151
- | `team_message` | Send a direct message to a teammate or lead |
152
- | `team_broadcast` | Message all teammates |
153
- | `team_status` | View all members and task summary |
154
- | `team_tasks_list` | View the shared task board |
155
- | `team_tasks_add` | Add tasks to shared board |
156
- | `team_tasks_complete` | Mark task done, auto-unblocks dependents |
157
- | `team_claim` | Atomically claim a pending task (teammates use this) |
141
+ ## Multi-Agent Execution, opencode-ensemble
142
+
143
+ Parallel execution uses the `opencode-ensemble` plugin (`team_create`, `team_spawn`, etc.).
144
+ Works on **all platforms** (Windows, macOS, Linux) via OpenCode's built-in worktree support.
145
+
146
+ Core tools used in this workflow:
147
+ - `team_create`, `team_spawn`, `team_shutdown`, `team_merge`, `team_cleanup`
148
+ - `team_tasks_add`, `team_tasks_list`, `team_claim`, `team_tasks_complete`
149
+ - `team_message`, `team_results`, `team_status`
158
150
 
159
151
  **Dashboard**: Monitor running agents at **http://localhost:4747/**
160
152
 
@@ -171,30 +163,25 @@ If a teammate stalls due to model quota/rate-limit exhaustion:
171
163
 
172
164
  ---
173
165
 
174
- ## Pipeline
166
+ ## Pipeline
175
167
 
176
168
  ```
177
- devops-manager (read mode)
178
- → parse work item via skill → structured summary
169
+ devops-manager (lead mode)
170
+ load ob-global + parse work item via skill
179
171
 
180
172
  openspec-propose
181
173
  → proposal.md + specs + tasks
182
174
 
183
175
  [confirm with user]
184
176
 
185
- back-engineer front-engineer infra-engineer ← sequential, one at a time, only spawn what the task needs
186
-
187
- quality-engineer (worktree:false)
188
- → tests, build, lint, acceptance criteria
189
-
190
- security-auditor (worktree:false)
191
- → vulnerability audit, secrets, auth gaps
177
+ basic-engineer + custom-engineer-* (parallel as needed)
178
+ → claim tasks + load abilities + implement
192
179
 
193
180
  devops-manager (ship mode)
194
- screenshots → commit → push → PR → post comment
181
+ verify completion → commit → push → PR → post comment
195
182
  ```
196
183
 
197
- ### Phase 1, Parse & Propose
184
+ ### Phase 1, Parse & Propose
198
185
 
199
186
  ```
200
187
  1. Detect URL type → load matching skill (ob-userstory-gh or ob-userstory-az)
@@ -204,60 +191,44 @@ devops-manager (ship mode)
204
191
  5. STOP. Ask user: "Ready to implement? (yes/no)", DO NOT proceed until confirmed.
205
192
  ```
206
193
 
207
- ### Phase 2, Implement
208
-
209
- ```
210
- 1. Run /opsx-apply, handles context reading, ensemble orchestration, and task marking.
211
- - Lead adds all tasks to board, then spawns specialists ONE AT A TIME (not parallel)
212
- - Each specialist claims tasks, implements, completes tasks, messages lead when done
213
- - Lead merges each branch after shutdown, then marks tasks done in tasks.md
214
- 2. After /opsx-apply completes, proceed to quality check.
215
- ```
216
-
217
- ### Phase 3, Quality
218
-
219
- ```
220
- 3. team_spawn name:quality agent:quality-engineer worktree:false → tests, build, lint
221
- 4. Wait → team_results → fix any blockers → team_shutdown (no merge, worktree:false)
222
- ```
194
+ ### Phase 2, Implement
195
+
196
+ ```
197
+ 1. Run /opsx-apply.
198
+ - Lead adds all tasks to board.
199
+ - Lead spawns one or more engineers (`basic-engineer` and/or custom engineers) in parallel where safe.
200
+ - Each engineer must claim task IDs, load relevant abilities, implement, and complete tasks.
201
+ - Lead merges each engineer branch after shutdown, then marks tasks done in tasks.md.
202
+ 2. Verify with tests/build/lint according to task scope.
203
+ ```
223
204
 
224
- ### Phase 4, Security
205
+ ### Phase 3, Ship
225
206
 
226
207
  ```
227
- 5. team_spawn name:security agent:security-auditor worktree:false → audit full change
228
- 6. Wait team_resultsfix Critical findings team_shutdown (no merge, worktree:false)
208
+ 3. team_spawn name:devops agent:devops-manager (ship mode)
209
+ commit & pushcreate PRpost comment
210
+ 4. Wait → team_results → report PR URL to user
211
+ 5. team_cleanup
229
212
  ```
230
213
 
231
- ### Phase 5, Ship
232
-
233
- ```
234
- 11. team_spawn name:devops agent:devops-manager (ship mode)
235
- → screenshots → commit & push → create PR → post comment
236
- 12. Wait → team_results → report PR URL to user
237
- 13. team_cleanup
238
- ```
239
-
240
- ### Phase 6, PR Feedback Loop
214
+ ### Phase 4, PR Feedback Loop
241
215
 
242
216
  ```
243
217
  When user says "I've added comments to the PR" or asks to fix PR comments from PR URLs:
244
218
  1. team_create "pr-feedback-<id>-<random>"
245
219
  2. team_tasks_add with at least these lead-managed tasks:
246
220
  - Parse and classify PR feedback (devops-manager)
247
- - Implement Api feedback items (back-engineer, if needed)
248
- - Implement App feedback items (front-engineer, if needed)
249
- - Infra feedback items (infra-engineer, if needed)
250
- - Verify with tests/build (quality-engineer)
221
+ - Implement feedback items (basic-engineer and/or custom engineers)
222
+ - Verify with tests/build/lint (implementation worker or dedicated verifier if available)
251
223
  - Push updates and post PR replies (devops-manager)
252
224
  3. team_spawn devops-manager (feedback mode) with explicit task IDs, then team_message "Start now"
253
225
  4. Wait for message → team_results
254
- 5. Add/update implementation tasks on board from parsed checklist (Api/App/Infra), then spawn needed specialists in parallel with explicit task IDs + team_message "Start now"
255
- 6. Wait for specialist results → team_shutdown + team_merge per specialist
256
- 7. team_spawn quality-engineer worktree:false with verification task ID + team_message "Start now"
257
- 8. Wait team_results fix blockers if any
258
- 9. team_spawn devops-manager (ship mode) with "push + update PR threads" task ID + team_message "Start now"
259
- 10. Wait → team_results → report what was updated
260
- 11. team_cleanup
226
+ 5. Add/update implementation tasks on board, then spawn needed engineers in parallel with explicit task IDs + team_message "Start now"
227
+ 6. Wait for engineer results → team_shutdown + team_merge per engineer
228
+ 7. Run verification tasks (tests/build/lint) and fix blockers if any
229
+ 8. team_spawn devops-manager (ship mode) with "push + update PR threads" task ID + team_message "Start now"
230
+ 9. Wait team_results report what was updated
231
+ 10. team_cleanup
261
232
  ```
262
233
 
263
234
  ---
@@ -269,28 +240,43 @@ All agents are universal, no project-specific knowledge. Platform and tech knowl
269
240
  | Agent | File | Role |
270
241
  |-------|------|------|
271
242
  | `devops-manager` | .agents/agents/devops-manager.md | Reads work items, creates PRs, handles review feedback |
272
- | `front-engineer` | .agents/agents/front-engineer.md | Web, mobile, UI implementation |
273
- | `back-engineer` | .agents/agents/back-engineer.md | APIs, services, data, AI implementation |
274
- | `infra-engineer` | .agents/agents/infra-engineer.md | Terraform, pipelines, cloud infrastructure |
275
- | `quality-engineer` | .agents/agents/quality-engineer.md | Unit, integration, e2e tests across all layers |
276
- | `security-auditor` | .agents/agents/security-auditor.md | Vulnerability audit, secrets, auth gaps |
243
+ | `basic-engineer` | .agents/agents/basic-engineer.md | Generic implementation worker using ability-loaded skills |
244
+
245
+ User can add more custom engineer agents and run them in parallel. Keep behavior ability-driven via skill mappings.
246
+
247
+ Default `basic-engineer` abilities:
248
+
249
+ ```
250
+ ## Abilities
251
+ - Guardrails: @ob-generic-guardrails, @ob-default
252
+ - Development: @ob-default
253
+ - Testing: @ob-default
254
+ - Infrastructure: @ob-default
255
+ ```
277
256
 
278
- ## Skills
257
+ ## Skills
279
258
 
280
- Skills provide platform and tech-specific knowledge. Agents detect and load them automatically, the user never specifies which skill to use.
259
+ Skills provide platform and tech-specific knowledge. Agents detect and load them automatically, **you never tell an agent which skill to use**.
260
+
261
+ `ob-global` is always loaded first, it provides baseline rules for all agents.
281
262
 
282
263
  Skills are located in `.agents/skills/`. Each skill has a `SKILL.md` with a description the agent reads to determine relevance.
283
264
 
284
265
  | Skill | Purpose |
285
266
  |-------|---------|
267
+ | `ob-global` | Generic skill, baseline rules loaded by all agents. Context, source roots, git/secrets guardrails, token opt rules |
268
+ | `ob-default` | Fallback skill, when no other skill matches |
269
+ | `ob-generic-guardrails` | Minimal foundation for user guardrails skills |
286
270
  | `ob-userstory-az` | Parse Azure DevOps work item URL |
287
271
  | `ob-userstory-gh` | Parse GitHub Issue URL |
288
272
  | `ob-pullrequest-az` | Create PR on Azure DevOps |
289
273
  | `ob-pullrequest-gh` | Create PR on GitHub |
290
274
  | `openspec-propose` | Propose change artifacts (proposal, specs, tasks) |
291
275
  | `openspec-apply-change` | Implement change with agent team |
292
- | `openspec-archive-change` | Archive completed change |
293
- | `browser-automation` | Browser automation for localhost UI, screenshots, clicks, queries |
276
+ | `openspec-archive-change` | Archive completed change |
277
+ | `browser-automation` | Browser automation for localhost UI, screenshots, clicks, queries |
278
+
279
+ Execution rules live in skills. Keep AGENTS.md focused on orchestration and routing.
294
280
 
295
281
  ---
296
282
 
@@ -299,7 +285,7 @@ Skills are located in `.agents/skills/`. Each skill has a `SKILL.md` with a desc
299
285
  Format: `feature/{issue-id}-{slug}`
300
286
  Example: `feature/42-add-user-auth`
301
287
 
302
- When `## Source Roots` lists multiple roots, each root is an independent git repository. The same branch name must be created in every repo that will have changes. Git operations (`branch`, `commit`, `push`) run once per repository there is no shared git history.
288
+ When `## Source Roots` lists multiple roots, each root is an independent git repository. The same branch name must be created in every repo that will have changes. Git operations (`branch`, `commit`, `push`) run once per repository, there is no shared git history.
303
289
 
304
290
  ---
305
291
 
@@ -309,7 +295,16 @@ When `## Source Roots` lists multiple roots, each root is an independent git rep
309
295
  [project-root]/
310
296
  ├── .agents/
311
297
  │ ├── agents/ # Agent definitions (universal, no project knowledge)
312
- └── skills/ # Skills (platform/tech specific knowledge)
298
+ │ ├── devops-manager.md
299
+ │ │ ├── basic-engineer.md
300
+ │ │ └── custom-engineer-*.md # optional, user-defined workers
301
+ │ └── skills/ # Skills (platform/tech specific knowledge)
302
+ │ ├── ob-global/ ← baseline skill, load first
303
+ │ ├── ob-default/ ← fallback skill
304
+ │ ├── ob-generic-guardrails/ ← foundation for user guardrails
305
+ │ ├── ob-userstory-gh/
306
+ │ ├── ob-userstory-az/
307
+ │ └── browser-automation/
313
308
  ├── openspec/
314
309
  │ ├── specs/
315
310
  │ └── changes/
@@ -322,61 +317,16 @@ When `## Source Roots` lists multiple roots, each root is an independent git rep
322
317
 
323
318
  ---
324
319
 
325
- ## RTK
326
-
327
- Use `rtk` wrapper for ALL CLI commands. Never run git, az, gh, or openspec commands directly.
328
-
329
- - `rtk git add` NOT `git add`
330
- - `rtk git commit` NOT `git commit`
331
- - `rtk git push` NOT `git push`
332
- - `rtk az boards work-item show` NOT `az boards work-item show`
333
- - `rtk az repos pr create` NOT `az repos pr create`
334
- - `rtk gh issue view` NOT `gh issue view`
335
- - `rtk gh pr create` NOT `gh pr create`
336
- - `rtk openspec new change` NOT `openspec new change`
337
-
338
- ---
339
-
340
- ## Guardrails
341
-
342
- ### Git Operations
343
-
344
- Agents CAN:
345
- - ✅ Commit to feature branches
346
- - ✅ Push to feature branches
347
-
348
- Agents CANNOT:
349
- - ❌ Commit or push to `main`, FORBIDDEN
350
- - ❌ Force push, FORBIDDEN
351
- - ❌ Merge PRs, human-only
352
- - ❌ Create or delete branches other than `feature/*`
353
-
354
- **Multi-repo**: When `## Source Roots` lists multiple roots, each is an independent git repository with its own history. All `rtk git` commands must be issued per repository. Never assume one `git` context covers all repos. Create the feature branch in each repo, commit per repo, push per repo, open one PR per repo.
355
-
356
- ### Platform CLI
357
-
358
- ALL platform interactions via CLI only. Browser MCP and webfetch FORBIDDEN for any DevOps or GitHub operation, use `gh` or `az` CLI exclusively, never fall back to HTTP requests.
359
-
360
- | Operation | Azure DevOps | GitHub |
361
- |-----------|-------------|--------|
362
- | Read issue | `az boards work-item show --id <id>` | `gh issue view <number>` |
363
- | Read PR threads | `az devops invoke ...` | `gh pr view <number> --comments` |
364
- | Create PR | `az repos pr create ...` | `gh pr create ...` |
365
-
366
- Browser MCP tools permitted only for screenshots of **local running app** on `localhost` URLs.
367
-
368
- ### Security
369
-
370
- Agents CANNOT:
371
- - ❌ Access `.env` or config files with secrets
372
- - ❌ Log or output credentials, API keys, or tokens
373
- - ❌ Commit secrets to git
374
-
375
- ### Scope
376
-
377
- - Max 10 files per change
378
- - No architecture changes without human approval
379
- - No pipeline modifications without human approval
320
+ ## Guardrails
321
+
322
+ Guardrails are mandatory via `ob-global` and ability-loaded skills.
323
+
324
+ Minimal non-negotiables:
325
+ - Never commit or push to `main`.
326
+ - Never force push.
327
+ - Never expose or commit secrets.
328
+ - Use `gh`/`az` CLI for platform operations.
329
+ - In multi-repo source scope, run git operations per repository.
380
330
 
381
331
  ---
382
332
 
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": 1,
3
+ "skills": {}
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-onboard",
3
- "version": "0.3.3",
3
+ "version": "0.4.2",
4
4
  "description": "Prepare any brownfield codebase for AI agent workflows using OpenCode, OpenSpec, and ensemble orchestration.",
5
5
  "keywords": [
6
6
  "opencode",
@@ -33,12 +33,17 @@
33
33
  "ora": "^8.0.0"
34
34
  },
35
35
  "devDependencies": {
36
+ "@eslint/js": "^9.0.0",
37
+ "eslint": "^9.0.0",
38
+ "globals": "^15.0.0",
36
39
  "vitest": "^4.1.5"
37
40
  },
38
41
  "vitest": {
39
42
  "environment": "node"
40
43
  },
41
44
  "scripts": {
45
+ "lint": "eslint .",
46
+ "lint:fix": "eslint . --fix",
42
47
  "test": "vitest run",
43
48
  "test:watch": "vitest"
44
49
  }
@@ -0,0 +1,43 @@
1
+ import chalk from 'chalk'
2
+ import { header, info } from '../utils/exec.js'
3
+ import { installBrowser } from '../steps/browser/index.js'
4
+ import { checkRtk } from '../steps/optimization/index.js'
5
+ import { choosePlatform, checkPlatform } from '../steps/platform/index.js'
6
+ import { readOnboardConfig } from './shared.js'
7
+
8
+ export async function runJoin() {
9
+ const logo = chalk.hex('#fe3d57')
10
+ console.log()
11
+ console.log(logo(' 🤝 opencode-onboard join'))
12
+ console.log(chalk.dim(' New team member setup — checks & local installs only'))
13
+ console.log(chalk.dim(' This will NOT modify any project files.'))
14
+ console.log()
15
+
16
+ // Step 1: Platform CLI check
17
+ header('Step 1, Platform CLI check')
18
+ const saved = await readOnboardConfig()
19
+ const savedPlatform = saved?.wizard?.platform
20
+ if (savedPlatform) {
21
+ info(`Detected project platform: ${savedPlatform === 'github' ? 'GitHub' : 'Azure DevOps'}`)
22
+ await checkPlatform(savedPlatform)
23
+ } else {
24
+ const platform = await choosePlatform()
25
+ void platform // result not persisted in join mode
26
+ }
27
+
28
+ // Step 2: rtk check
29
+ header('Step 2, Checking rtk')
30
+ await checkRtk({ skipHeader: true })
31
+
32
+ // Step 3: Browser extension
33
+ await installBrowser()
34
+
35
+ console.log()
36
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
37
+ console.log(chalk.bold.green(' Join setup complete!'))
38
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
39
+ console.log()
40
+ console.log(' Your local environment is ready.')
41
+ console.log(' Open the project in OpenCode and start coding!')
42
+ console.log()
43
+ }
@@ -0,0 +1,12 @@
1
+ import fse from 'fs-extra'
2
+ import path from 'node:path'
3
+
4
+ export async function readOnboardConfig() {
5
+ const cfgPath = path.join(process.cwd(), '.opencode', 'opencode-onboard.json')
6
+ if (!await fse.pathExists(cfgPath)) return null
7
+ try {
8
+ return await fse.readJson(cfgPath)
9
+ } catch {
10
+ return null
11
+ }
12
+ }
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import os from 'node:os'
5
+ import { readOnboardConfig } from './shared.js'
6
+
7
+ describe('readOnboardConfig()', () => {
8
+ let tmpDir, originalCwd
9
+
10
+ beforeEach(() => {
11
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'shared-test-'))
12
+ originalCwd = process.cwd()
13
+ process.chdir(tmpDir)
14
+ })
15
+
16
+ afterEach(() => {
17
+ process.chdir(originalCwd)
18
+ fs.rmSync(tmpDir, { recursive: true, force: true })
19
+ })
20
+
21
+ it('returns null when config file does not exist', async () => {
22
+ const result = await readOnboardConfig()
23
+
24
+ expect(result).toBeNull()
25
+ })
26
+
27
+ it('returns parsed config when file exists', async () => {
28
+ const configDir = path.join(tmpDir, '.opencode')
29
+ fs.mkdirSync(configDir)
30
+ fs.writeFileSync(
31
+ path.join(configDir, 'opencode-onboard.json'),
32
+ JSON.stringify({ schema: 1, wizard: { platform: 'github' } }),
33
+ 'utf-8'
34
+ )
35
+
36
+ const result = await readOnboardConfig()
37
+
38
+ expect(result).not.toBeNull()
39
+ expect(result.schema).toBe(1)
40
+ expect(result.wizard.platform).toBe('github')
41
+ })
42
+
43
+ it('returns null when file contains invalid JSON', async () => {
44
+ const configDir = path.join(tmpDir, '.opencode')
45
+ fs.mkdirSync(configDir)
46
+ fs.writeFileSync(
47
+ path.join(configDir, 'opencode-onboard.json'),
48
+ 'not valid json',
49
+ 'utf-8'
50
+ )
51
+
52
+ const result = await readOnboardConfig()
53
+
54
+ expect(result).toBeNull()
55
+ })
56
+ })
@@ -0,0 +1,64 @@
1
+ import { cleanAiFiles } from '../steps/clean/index.js'
2
+ import { copyContentStep } from '../steps/copy/index.js'
3
+ import { chooseModels } from '../steps/models/index.js'
4
+ import { initOpenspec } from '../steps/openspec/index.js'
5
+ import { tokenOptimizationStep } from '../steps/optimization/index.js'
6
+ import { choosePlatform } from '../steps/platform/index.js'
7
+ import { installBrowser } from '../steps/browser/index.js'
8
+ import { writeOnboardConfig } from '../steps/metadata/index.js'
9
+ import { readOnboardConfig } from './shared.js'
10
+
11
+ export async function runSingleCommand(command) {
12
+ const saved = await readOnboardConfig()
13
+ const savedWizard = saved?.wizard ?? {}
14
+ const ctx = {
15
+ hasDesign: !!savedWizard?.preserved?.design,
16
+ hasArchitecture: !!savedWizard?.preserved?.architecture,
17
+ hasOpenspec: !!savedWizard?.preserved?.openspec,
18
+ sourceMode: savedWizard?.sourceMode ?? 'current',
19
+ sourceRoots: Array.isArray(savedWizard?.sourceRoots) ? savedWizard.sourceRoots : [],
20
+ }
21
+ const platform = savedWizard?.platform
22
+ const resolvedPlatform = platform === 'azure' || platform === 'github' ? platform : 'github'
23
+
24
+ const handlers = {
25
+ clean: async () => {
26
+ await cleanAiFiles()
27
+ },
28
+ platform: async () => {
29
+ await choosePlatform()
30
+ },
31
+ copy: async () => {
32
+ await copyContentStep(resolvedPlatform, ctx)
33
+ },
34
+ openspec: async () => {
35
+ await initOpenspec()
36
+ },
37
+ models: async () => {
38
+ await chooseModels()
39
+ },
40
+ optimization: async () => {
41
+ await tokenOptimizationStep({ ctx })
42
+ },
43
+ browser: async () => {
44
+ await installBrowser()
45
+ },
46
+ metadata: async () => {
47
+ await writeOnboardConfig({
48
+ ...ctx,
49
+ platform: resolvedPlatform,
50
+ additionalSkillsProvider: 'npx-skills',
51
+ planModel: savedWizard?.models?.plan ?? null,
52
+ buildModel: savedWizard?.models?.build ?? null,
53
+ fastModel: savedWizard?.models?.fast ?? null,
54
+ optionalTools: savedWizard?.optionalTools ?? null,
55
+ cavemanGuidance: savedWizard?.cavemanGuidance ?? null,
56
+ })
57
+ },
58
+ }
59
+
60
+ const handler = handlers[command]
61
+ if (!handler) return false
62
+ await handler()
63
+ return true
64
+ }
@@ -0,0 +1,99 @@
1
+ import chalk from 'chalk'
2
+ import { chooseSourceScope } from '../steps/source/index.js'
3
+ import { cleanAiFiles } from '../steps/clean/index.js'
4
+ import { choosePlatform } from '../steps/platform/index.js'
5
+ import { copyContentStep } from '../steps/copy/index.js'
6
+ import { initOpenspec } from '../steps/openspec/index.js'
7
+ import { chooseModels } from '../steps/models/index.js'
8
+ import { tokenOptimizationStep } from '../steps/optimization/index.js'
9
+ import { installBrowser } from '../steps/browser/index.js'
10
+ import { writeOnboardConfig } from '../steps/metadata/index.js'
11
+
12
+ export async function runWizard(version) {
13
+ const logo = chalk.hex('#fe3d57')
14
+ const bannerLines = [
15
+ logo(' '),
16
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
17
+ logo(' ▒▒▓ ▓▒▓ '),
18
+ logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
19
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
20
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
21
+ logo(' ▓▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '),
22
+ logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
23
+ logo(' ▓▓▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
24
+ logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
25
+ logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
26
+ logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
27
+ '',
28
+ chalk.bold(' 🧰 opencode-onboard') + chalk.dim(` v${version}`),
29
+ chalk.dim(' Prepare your codebase for AI agents'),
30
+ ]
31
+
32
+ for (const line of bannerLines) console.log(line)
33
+ console.log()
34
+ console.log(' This tool will set up your project with a team of AI agents,')
35
+ console.log(' install skills, select models, and configure OpenCode.')
36
+ console.log()
37
+
38
+ // Only wait for Enter in a real interactive TTY
39
+ if (process.stdin.isTTY) {
40
+ console.log(chalk.bold(' Press Enter to begin...'))
41
+ console.log()
42
+ await new Promise(resolve => {
43
+ process.stdin.resume()
44
+ process.stdin.once('data', () => {
45
+ process.stdin.pause()
46
+ resolve()
47
+ })
48
+ })
49
+ }
50
+
51
+ const scope = await chooseSourceScope()
52
+
53
+ const preserve = await cleanAiFiles()
54
+ const ctx = { ...preserve, ...scope }
55
+
56
+ const platform = await choosePlatform()
57
+
58
+ await copyContentStep(platform, ctx)
59
+
60
+ await initOpenspec()
61
+
62
+ const selectedModels = await chooseModels()
63
+
64
+ const tokenOpt = await tokenOptimizationStep({ ctx })
65
+ const { rtk, quota, caveman, cavemanGuidance } = tokenOpt
66
+
67
+ await installBrowser()
68
+
69
+ await writeOnboardConfig({
70
+ ...ctx,
71
+ platform,
72
+ additionalSkillsProvider: 'npx-skills',
73
+ ...selectedModels,
74
+ optionalTools: { rtk, quota, caveman },
75
+ cavemanGuidance,
76
+ })
77
+
78
+ const toGenerate = [
79
+ !ctx.hasDesign && 'DESIGN.md',
80
+ !ctx.hasArchitecture && 'ARCHITECTURE.md',
81
+ ].filter(Boolean)
82
+
83
+ console.log()
84
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
85
+ console.log(chalk.bold.green(' Onboarding complete!'))
86
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
87
+ console.log()
88
+ console.log(' Open this project in OpenCode and type:')
89
+ console.log(chalk.bold(' "init"'))
90
+ console.log()
91
+ if (toGenerate.length > 0) {
92
+ console.log(` OpenCode will generate ${toGenerate.join(' and ')}`)
93
+ console.log(' from your actual codebase, then activate the agent team.')
94
+ } else {
95
+ console.log(' OpenCode will activate the agent team.')
96
+ }
97
+ console.log(` Source scope: ${ctx.sourceMode === 'parent-selected' ? ctx.sourceRoots.map(p => `../${p.split(/[/\\]/).pop()}`).join(', ') : 'current folder'}`)
98
+ console.log()
99
+ }