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.
- package/README.md +278 -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/init.md +8 -0
- package/content/.opencode/commands/main.md +17 -0
- package/content/.opencode/commands/opsx-apply.md +50 -33
- package/content/.opencode/commands/plan.md +37 -0
- package/content/.opencode/plugins/session-log.js +1 -1
- package/content/.opencode/skills/openspec-apply-change/SKILL.md +50 -33
- package/content/AGENTS.md +94 -144
- package/content/skills-lock.json +4 -0
- package/package.json +6 -1
- package/src/commands/join.js +43 -0
- package/src/commands/shared.js +12 -0
- package/src/commands/shared.test.js +56 -0
- package/src/commands/single.js +64 -0
- package/src/commands/wizard.js +99 -0
- package/src/index.js +25 -202
- 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/copy/agents.js +106 -0
- 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 +99 -0
- package/src/steps/models/format.js +60 -0
- package/src/steps/models/format.test.js +75 -0
- package/src/steps/models/index.js +52 -0
- package/src/steps/models/write.js +54 -0
- package/src/steps/models/write.test.js +117 -0
- package/src/steps/{init-openspec.js → openspec/ensemble.js} +20 -57
- 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} +37 -22
- 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 +91 -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 +93 -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 -165
- 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 -78
- package/src/steps/patch-agents-md.js +0 -153
- 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
|
|
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
|
-
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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 (
|
|
178
|
-
→ parse work item via skill
|
|
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
|
-
|
|
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
|
-
→
|
|
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
|
|
211
|
-
- Lead adds all tasks to board
|
|
212
|
-
-
|
|
213
|
-
-
|
|
214
|
-
|
|
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
|
|
205
|
+
### Phase 3, Ship
|
|
225
206
|
|
|
226
207
|
```
|
|
227
|
-
|
|
228
|
-
|
|
208
|
+
3. team_spawn name:devops agent:devops-manager (ship mode)
|
|
209
|
+
→ commit & push → create PR → post comment
|
|
210
|
+
4. Wait → team_results → report PR URL to user
|
|
211
|
+
5. team_cleanup
|
|
229
212
|
```
|
|
230
213
|
|
|
231
|
-
### Phase
|
|
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
|
|
248
|
-
-
|
|
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
|
|
255
|
-
6. Wait for
|
|
256
|
-
7.
|
|
257
|
-
8.
|
|
258
|
-
9.
|
|
259
|
-
10.
|
|
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
|
-
| `
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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,
|
|
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
|
|
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
|
-
│
|
|
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
|
-
##
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
-
|
|
330
|
-
-
|
|
331
|
-
-
|
|
332
|
-
-
|
|
333
|
-
- `
|
|
334
|
-
-
|
|
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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-onboard",
|
|
3
|
-
"version": "0.
|
|
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
|
+
}
|