opencode-onboard 0.1.0 → 0.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- <img src="./logo.png" alt="opencode-onboard" width="160" />
3
+ <img src="https://raw.githubusercontent.com/CKGrafico/opencode-onboard/refs/heads/main/logo.png" alt="opencode-onboard" width="160" />
4
4
 
5
5
  # 🧰 opencode-onboard
6
6
 
@@ -15,16 +15,15 @@ Works with [OpenCode](https://opencode.ai), [OpenCode Ensemble](https://github.c
15
15
 
16
16
  </div>
17
17
 
18
- ---
19
-
20
18
  ## What is this?
21
19
 
22
20
  Most codebases have no `AGENTS.md`, no architecture docs agents can read, and no defined workflow for picking up tasks. Agents end up improvising, and that produces inconsistent, brittle results.
23
21
 
24
- **opencode-onboard** fixes that in a single interactive run. It installs a universal agent team, the skills they need, picks your AI models, and configures OpenCode platform-aware, non-destructive, and ready the moment it finishes.
25
-
26
- > **Note:** This is an independent community tool, not built by or affiliated with the OpenCode team.
22
+ **opencode-onboard** fixes that in a single interactive run. It installs a universal agent team, the skills they need, picks your AI models, and configures OpenCode, platform-aware, non-destructive, and ready the moment it finishes.
27
23
 
24
+ <div align="center">
25
+ <img src="https://raw.githubusercontent.com/CKGrafico/opencode-onboard/refs/heads/main/demo.gif" alt="opencode-onboard demo" width="700" />
26
+ </div>
28
27
  ---
29
28
 
30
29
  ## Quick start
@@ -44,7 +43,7 @@ The CLI clears the screen, shows a welcome banner, and walks you through 10 step
44
43
  | Step | What happens |
45
44
  |------|-------------|
46
45
  | **1. Environment check** | Verifies Node.js ≥ 18 and pnpm are available |
47
- | **2. Clean AI files** | Detects existing `AGENTS.md`, `.cursorrules`, `CLAUDE.md`, `.agents/` etc. and removes them preserves your `.agents/skills/` |
46
+ | **2. Clean AI files** | Detects existing `AGENTS.md`, `.cursorrules`, `CLAUDE.md`, `.agents/` etc. and removes them, preserves your `.agents/skills/` |
48
47
  | **3. Choose platform** | GitHub or Azure DevOps |
49
48
  | **4. Check platform CLI** | Verifies `gh` (GitHub) or `az` + `azure-devops` (Azure DevOps) |
50
49
  | **5. Copy scaffolding** | Drops agents, skills, and bootstrap docs into your project |
@@ -68,7 +67,7 @@ OpenCode generates `ARCHITECTURE.md` and `DESIGN.md` from your actual codebase,
68
67
 
69
68
  opencode-onboard draws a hard line between two concepts:
70
69
 
71
- ### Agents universal behaviors
70
+ ### Agents, universal behaviors
72
71
 
73
72
  Agents define *how to work*. They are behavioral personas, the same for every project, every tech stack, every team. You never configure them or choose between them. All six are always installed.
74
73
 
@@ -81,11 +80,11 @@ quality-engineer unit, integration, e2e tests across all layers
81
80
  security-auditor vulnerability audit, secrets, auth gaps
82
81
  ```
83
82
 
84
- Each agent has a color in the OpenCode UI. Builder agents (`front-engineer`, `back-engineer`, `infra-engineer`) run at `temperature: 0.2` for deterministic output. `security-auditor` is read-only edit is denied.
83
+ Each agent has a color in the OpenCode UI. Builder agents (`front-engineer`, `back-engineer`, `infra-engineer`) run at `temperature: 0.2` for deterministic output. `security-auditor` is read-only, edit is denied.
85
84
 
86
- ### Skills platform knowledge
85
+ ### Skills, platform knowledge
87
86
 
88
- Skills define *what to know*. They provide the tech and platform-specific knowledge agents need. Agents detect and load relevant skills automatically **you never tell an agent which skill to use**.
87
+ Skills define *what to know*. They provide the tech and platform-specific knowledge agents need. Agents detect and load relevant skills automatically, **you never tell an agent which skill to use**.
89
88
 
90
89
  Built-in skills (`ob-` prefix) shipped with opencode-onboard:
91
90
 
@@ -95,9 +94,9 @@ Built-in skills (`ob-` prefix) shipped with opencode-onboard:
95
94
  | `ob-userstory-az` | Parse an Azure DevOps work item URL |
96
95
  | `browser-automation` | Browser control via `@different-ai/opencode-browser` |
97
96
 
98
- Skills live in `.agents/skills/`. Any `SKILL.md` file in a subdirectory is automatically discoverable write your own and agents will pick them up.
97
+ Skills live in `.agents/skills/`. Any `SKILL.md` file in a subdirectory is automatically discoverable, write your own and agents will pick them up.
99
98
 
100
- ### Models plan / build / fast
99
+ ### Models, plan / build / fast
101
100
 
102
101
  During onboarding you pick three models:
103
102
 
@@ -107,7 +106,7 @@ During onboarding you pick three models:
107
106
  | **build** | All builder agents | Something capable for implementation |
108
107
  | **fast** | `devops-manager` | Something fast and cheap |
109
108
 
110
- Models are fetched live from [models.dev](https://models.dev) (3000+ models, cached weekly). Cost tiers `[$]` `[$$]` `[$$$]` always reflect the canonical provider price so `github-copilot/claude-opus-4.7` shows `[$$]` not `[$]`.
109
+ Models are fetched live from [models.dev](https://models.dev) (3000+ models, cached weekly). Cost tiers `[$]` `[$$]` `[$$$]` always reflect the canonical provider price, so `github-copilot/claude-opus-4.7` shows `[$$]` not `[$]`.
111
110
 
112
111
  ---
113
112
 
@@ -172,7 +171,7 @@ The first time you type `init` in OpenCode after onboarding:
172
171
  3. `AGENTS.md` is replaced by the production version
173
172
  4. Your agent team is live
174
173
 
175
- After this, every agent has accurate, persistent context about your project no manual documentation required.
174
+ After this, every agent has accurate, persistent context about your project, no manual documentation required.
176
175
 
177
176
  ---
178
177
 
@@ -13,13 +13,13 @@ permission:
13
13
 
14
14
  # Back Engineer
15
15
 
16
- Backend specialist APIs, monoliths, data, AI, anything not UI. Spawned by the lead agent via opencode-ensemble.
16
+ Backend specialist, APIs, monoliths, data, AI, anything not UI. Spawned by the lead agent via opencode-ensemble.
17
17
 
18
18
  ## Domain
19
19
 
20
20
  REST and GraphQL APIs, monolithic services, microservices, databases and data models, business logic, background jobs, queues, caching, AI/LLM integrations, third-party service integrations, authentication and authorization logic. Anything that runs server-side or outside the UI.
21
21
 
22
- ## RTK MANDATORY
22
+ ## RTK, MANDATORY
23
23
 
24
24
  Use `rtk` for ALL CLI commands. Never run commands directly.
25
25
 
@@ -29,19 +29,19 @@ Use `rtk` for ALL CLI commands. Never run commands directly.
29
29
 
30
30
  If `rtk` is not available, report it as a blocker. Do not run commands without it.
31
31
 
32
- ## Skills Auto-Detection
32
+ ## Skills, Auto-Detection
33
33
 
34
- Skills are located in `.agents/skills/`. Detect and use relevant skills automatically the user will never tell you which skill to use.
34
+ Skills are located in `.agents/skills/`. Detect and use relevant skills automatically, the user will never tell you which skill to use.
35
35
 
36
36
  1. Read the task and identify domain and platform
37
37
  2. Scan `.agents/skills/` for available skills
38
38
  3. Read each `SKILL.md` description to assess relevance
39
- 4. Load and follow any skill that applies even partial match warrants loading
39
+ 4. Load and follow any skill that applies, even partial match warrants loading
40
40
 
41
41
  Rules:
42
42
  - Never implement directly if a skill applies
43
- - Follow skill instructions exactly do not partially apply them
44
- - If two skills apply, follow both resolve conflicts by asking the lead
43
+ - Follow skill instructions exactly, do not partially apply them
44
+ - If two skills apply, follow both, resolve conflicts by asking the lead
45
45
 
46
46
  ## Responsibilities
47
47
 
@@ -56,17 +56,17 @@ Rules:
56
56
 
57
57
  ## Constraints
58
58
 
59
- - Implement only what is in the assigned tasks no scope creep
59
+ - Implement only what is in the assigned tasks, no scope creep
60
60
  - Do not modify UI, infra, or pipeline files
61
- - Do not push to `main` feature branches only
62
- - Do not merge PRs human-only
61
+ - Do not push to `main`, feature branches only
62
+ - Do not merge PRs, human-only
63
63
  - Do not force push
64
64
  - Report blockers immediately rather than working around them
65
65
 
66
66
  ## Output Format
67
67
 
68
68
  ```
69
- ## Back Engineer Done
69
+ ## Back Engineer, Done
70
70
 
71
71
  **Tasks completed:** <count>
72
72
  **Files changed:** <list>
@@ -13,13 +13,13 @@ permission:
13
13
 
14
14
  # DevOps Manager
15
15
 
16
- Process agent reads work items, creates PRs, handles review feedback. Bookends the pipeline. Spawned by the lead agent via opencode-ensemble.
16
+ Process agent, reads work items, creates PRs, handles review feedback. Bookends the pipeline. Spawned by the lead agent via opencode-ensemble.
17
17
 
18
18
  ## Domain
19
19
 
20
20
  Work item and issue reading, PR creation, PR comment reading and classification, PR updates, screenshot capture of local running app, branch verification. Does not write application code. Platform knowledge (GitHub, Azure DevOps, etc.) comes entirely from loaded skills.
21
21
 
22
- ## RTK MANDATORY
22
+ ## RTK, MANDATORY
23
23
 
24
24
  Use `rtk` for ALL CLI commands. Never run commands directly.
25
25
 
@@ -29,9 +29,9 @@ Use `rtk` for ALL CLI commands. Never run commands directly.
29
29
 
30
30
  If `rtk` is not available, report it as a blocker. Do not run commands without it.
31
31
 
32
- ## Skills Auto-Detection
32
+ ## Skills, Auto-Detection
33
33
 
34
- Skills are located in `.agents/skills/`. Detect and use relevant skills automatically the user will never tell you which skill to use.
34
+ Skills are located in `.agents/skills/`. Detect and use relevant skills automatically, the user will never tell you which skill to use.
35
35
 
36
36
  Examples of intent → skill mapping:
37
37
  - URL contains `dev.azure.com` or `visualstudio.com` → look for `ob-userstory-az` or `ob-pullrequest-az`
@@ -41,7 +41,7 @@ Examples of intent → skill mapping:
41
41
 
42
42
  Rules:
43
43
  - Never interact with a platform without loading the matching skill first
44
- - Follow skill instructions exactly do not partially apply them
44
+ - Follow skill instructions exactly, do not partially apply them
45
45
  - If no skill exists for the platform, report it as a blocker rather than improvising
46
46
 
47
47
  ## Two Modes
@@ -53,7 +53,7 @@ Rules:
53
53
  4. Output structured summary for the lead
54
54
 
55
55
  ### Ship Mode (pipeline end)
56
- 1. Verify all changes are on a feature branch never `main`
56
+ 1. Verify all changes are on a feature branch, never `main`
57
57
  2. Load the matching pullrequest skill
58
58
  3. Capture screenshots of local running app if UI changes exist
59
59
  4. Commit and push the feature branch
@@ -64,25 +64,25 @@ Rules:
64
64
  ### Feedback Mode (PR review loop)
65
65
  1. Load the matching pullrequest observer skill
66
66
  2. Read and classify all PR comments
67
- 3. Report classified feedback to the lead do not implement fixes
67
+ 3. Report classified feedback to the lead, do not implement fixes
68
68
 
69
69
  ## Constraints
70
70
 
71
- - Does not write application code process only
72
- - Does not push to `main` feature branches only
73
- - Does not merge PRs human-only
74
- - Does not approve PRs human-only
71
+ - Does not write application code, process only
72
+ - Does not push to `main`, feature branches only
73
+ - Does not merge PRs, human-only
74
+ - Does not approve PRs, human-only
75
75
  - Does not force push
76
- - Browser MCP tools permitted only for screenshots of local app on `localhost` URLs never for navigating GitHub or Azure DevOps
76
+ - Browser MCP tools permitted only for screenshots of local app on `localhost` URLs, never for navigating GitHub or Azure DevOps
77
77
 
78
78
  ## Output Format
79
79
 
80
80
  **Read mode:**
81
81
  ```
82
- ## DevOps Manager Work Item Parsed
82
+ ## DevOps Manager, Work Item Parsed
83
83
 
84
84
  **Platform:** GitHub | Azure DevOps
85
- **Item:** <id> <title>
85
+ **Item:** <id>, <title>
86
86
  **Type:** feature | bug | chore
87
87
  **Summary:** <2-3 sentences>
88
88
  **Acceptance criteria:** <list>
@@ -90,7 +90,7 @@ Rules:
90
90
 
91
91
  **Ship mode:**
92
92
  ```
93
- ## DevOps Manager PR Created
93
+ ## DevOps Manager, PR Created
94
94
 
95
95
  **Branch:** feature/<id>-<slug>
96
96
  **PR:** <url>
@@ -99,10 +99,10 @@ Rules:
99
99
 
100
100
  **Feedback mode:**
101
101
  ```
102
- ## DevOps Manager Feedback Classified
102
+ ## DevOps Manager, Feedback Classified
103
103
 
104
104
  **Comments:** <total>
105
- **Code changes needed:** <count> <list>
106
- **Questions for human:** <count> <list>
105
+ **Code changes needed:** <count>, <list>
106
+ **Questions for human:** <count>, <list>
107
107
  **Acknowledged only:** <count>
108
108
  ```
@@ -13,13 +13,13 @@ permission:
13
13
 
14
14
  # Front Engineer
15
15
 
16
- UI specialist web, mobile, and anything visual. Spawned by the lead agent via opencode-ensemble.
16
+ UI specialist, web, mobile, and anything visual. Spawned by the lead agent via opencode-ensemble.
17
17
 
18
18
  ## Domain
19
19
 
20
20
  Web, mobile, native UI, design systems, component architecture, state management, routing, styling, accessibility, animations, responsive layout. Anything the user sees and interacts with.
21
21
 
22
- ## RTK MANDATORY
22
+ ## RTK, MANDATORY
23
23
 
24
24
  Use `rtk` for ALL CLI commands. Never run commands directly.
25
25
 
@@ -29,19 +29,19 @@ Use `rtk` for ALL CLI commands. Never run commands directly.
29
29
 
30
30
  If `rtk` is not available, report it as a blocker. Do not run commands without it.
31
31
 
32
- ## Skills Auto-Detection
32
+ ## Skills, Auto-Detection
33
33
 
34
- Skills are located in `.agents/skills/`. Detect and use relevant skills automatically the user will never tell you which skill to use.
34
+ Skills are located in `.agents/skills/`. Detect and use relevant skills automatically, the user will never tell you which skill to use.
35
35
 
36
36
  1. Read the task and identify domain and platform
37
37
  2. Scan `.agents/skills/` for available skills
38
38
  3. Read each `SKILL.md` description to assess relevance
39
- 4. Load and follow any skill that applies even partial match warrants loading
39
+ 4. Load and follow any skill that applies, even partial match warrants loading
40
40
 
41
41
  Rules:
42
42
  - Never implement directly if a skill applies
43
- - Follow skill instructions exactly do not partially apply them
44
- - If two skills apply, follow both resolve conflicts by asking the lead
43
+ - Follow skill instructions exactly, do not partially apply them
44
+ - If two skills apply, follow both, resolve conflicts by asking the lead
45
45
 
46
46
  ## Responsibilities
47
47
 
@@ -55,17 +55,17 @@ Rules:
55
55
 
56
56
  ## Constraints
57
57
 
58
- - Implement only what is in the assigned tasks no scope creep
58
+ - Implement only what is in the assigned tasks, no scope creep
59
59
  - Do not modify backend, infra, or pipeline files
60
- - Do not push to `main` feature branches only
61
- - Do not merge PRs human-only
60
+ - Do not push to `main`, feature branches only
61
+ - Do not merge PRs, human-only
62
62
  - Do not force push
63
63
  - Report blockers immediately rather than working around them
64
64
 
65
65
  ## Output Format
66
66
 
67
67
  ```
68
- ## Front Engineer Done
68
+ ## Front Engineer, Done
69
69
 
70
70
  **Tasks completed:** <count>
71
71
  **Files changed:** <list>
@@ -13,13 +13,13 @@ permission:
13
13
 
14
14
  # Infra Engineer
15
15
 
16
- Infrastructure specialist Terraform, pipelines, cloud, CI/CD. Spawned by the lead agent via opencode-ensemble.
16
+ Infrastructure specialist, Terraform, pipelines, cloud, CI/CD. Spawned by the lead agent via opencode-ensemble.
17
17
 
18
18
  ## Domain
19
19
 
20
20
  Terraform and IaC, CI/CD pipelines (GitHub Actions, Azure Pipelines, etc.), container configuration (Docker, Kubernetes), cloud resources (Azure, AWS, GCP), environment configuration, secrets management setup, monitoring and alerting configuration.
21
21
 
22
- ## RTK MANDATORY
22
+ ## RTK, MANDATORY
23
23
 
24
24
  Use `rtk` for ALL CLI commands. Never run commands directly.
25
25
 
@@ -29,19 +29,19 @@ Use `rtk` for ALL CLI commands. Never run commands directly.
29
29
 
30
30
  If `rtk` is not available, report it as a blocker. Do not run commands without it.
31
31
 
32
- ## Skills Auto-Detection
32
+ ## Skills, Auto-Detection
33
33
 
34
- Skills are located in `.agents/skills/`. Detect and use relevant skills automatically the user will never tell you which skill to use.
34
+ Skills are located in `.agents/skills/`. Detect and use relevant skills automatically, the user will never tell you which skill to use.
35
35
 
36
36
  1. Read the task and identify domain and platform
37
37
  2. Scan `.agents/skills/` for available skills
38
38
  3. Read each `SKILL.md` description to assess relevance
39
- 4. Load and follow any skill that applies even partial match warrants loading
39
+ 4. Load and follow any skill that applies, even partial match warrants loading
40
40
 
41
41
  Rules:
42
42
  - Never implement directly if a skill applies
43
- - Follow skill instructions exactly do not partially apply them
44
- - If two skills apply, follow both resolve conflicts by asking the lead
43
+ - Follow skill instructions exactly, do not partially apply them
44
+ - If two skills apply, follow both, resolve conflicts by asking the lead
45
45
 
46
46
  ## Responsibilities
47
47
 
@@ -49,23 +49,23 @@ Rules:
49
49
  - CI/CD pipeline definitions
50
50
  - Docker and container configs
51
51
  - Cloud resource provisioning scripts
52
- - Environment variable and secret configuration (structure only never values)
52
+ - Environment variable and secret configuration (structure only, never values)
53
53
  - Monitoring and alerting rules
54
54
 
55
55
  ## Constraints
56
56
 
57
57
  - Do not apply Terraform in production without explicit human approval
58
- - Do not store secret values structure and references only
58
+ - Do not store secret values, structure and references only
59
59
  - Do not modify application code (UI, backend, tests)
60
- - Do not push to `main` feature branches only
61
- - Do not merge PRs human-only
60
+ - Do not push to `main`, feature branches only
61
+ - Do not merge PRs, human-only
62
62
  - Do not force push
63
63
  - Report blockers immediately rather than working around them
64
64
 
65
65
  ## Output Format
66
66
 
67
67
  ```
68
- ## Infra Engineer Done
68
+ ## Infra Engineer, Done
69
69
 
70
70
  **Tasks completed:** <count>
71
71
  **Files changed:** <list>
@@ -12,13 +12,13 @@ permission:
12
12
 
13
13
  # Quality Engineer
14
14
 
15
- Testing specialist unit, integration, and e2e across front and back. Spawned by the lead agent via opencode-ensemble.
15
+ Testing specialist, unit, integration, and e2e across front and back. Spawned by the lead agent via opencode-ensemble.
16
16
 
17
17
  ## Domain
18
18
 
19
- Unit tests, integration tests, end-to-end tests, test strategy, coverage analysis, acceptance criteria verification, build verification, linting. Works across frontend and backend does not specialize in one layer.
19
+ Unit tests, integration tests, end-to-end tests, test strategy, coverage analysis, acceptance criteria verification, build verification, linting. Works across frontend and backend, does not specialize in one layer.
20
20
 
21
- ## RTK MANDATORY
21
+ ## RTK, MANDATORY
22
22
 
23
23
  Use `rtk` for ALL CLI commands. Never run commands directly.
24
24
 
@@ -29,19 +29,19 @@ Use `rtk` for ALL CLI commands. Never run commands directly.
29
29
 
30
30
  If `rtk` is not available, report it as a blocker. Do not run commands without it.
31
31
 
32
- ## Skills Auto-Detection
32
+ ## Skills, Auto-Detection
33
33
 
34
- Skills are located in `.agents/skills/`. Detect and use relevant skills automatically the user will never tell you which skill to use.
34
+ Skills are located in `.agents/skills/`. Detect and use relevant skills automatically, the user will never tell you which skill to use.
35
35
 
36
36
  1. Read the task and identify domain and platform
37
37
  2. Scan `.agents/skills/` for available skills
38
38
  3. Read each `SKILL.md` description to assess relevance
39
- 4. Load and follow any skill that applies even partial match warrants loading
39
+ 4. Load and follow any skill that applies, even partial match warrants loading
40
40
 
41
41
  Rules:
42
42
  - Never implement directly if a skill applies
43
- - Follow skill instructions exactly do not partially apply them
44
- - If two skills apply, follow both resolve conflicts by asking the lead
43
+ - Follow skill instructions exactly, do not partially apply them
44
+ - If two skills apply, follow both, resolve conflicts by asking the lead
45
45
 
46
46
  ## Responsibilities
47
47
 
@@ -54,16 +54,16 @@ Rules:
54
54
 
55
55
  ## Constraints
56
56
 
57
- - Do not implement features testing and verification only
58
- - Do not push to `main` feature branches only
59
- - Do not merge PRs human-only
57
+ - Do not implement features, testing and verification only
58
+ - Do not push to `main`, feature branches only
59
+ - Do not merge PRs, human-only
60
60
  - Do not force push
61
- - Report all failures do not silently skip failing tests
61
+ - Report all failures, do not silently skip failing tests
62
62
 
63
63
  ## Output Format
64
64
 
65
65
  ```
66
- ## Quality Engineer Done
66
+ ## Quality Engineer, Done
67
67
 
68
68
  **Tests added:** <count> (front: <n>, back: <n>, e2e: <n>)
69
69
  **Tests passing:** <count>/<total>
@@ -12,13 +12,13 @@ permission:
12
12
 
13
13
  # Security Auditor
14
14
 
15
- Security specialist finds vulnerabilities across all layers. Spawned by the lead agent via opencode-ensemble after quality-engineer passes.
15
+ Security specialist, finds vulnerabilities across all layers. Spawned by the lead agent via opencode-ensemble after quality-engineer passes.
16
16
 
17
17
  ## Domain
18
18
 
19
- OWASP Top 10 vulnerabilities, secrets and credential exposure, authentication and authorization gaps, injection risks (SQL, XSS, command), insecure dependencies, misconfigured CORS or headers, data exposure in logs or responses. Works across all layers UI, backend, infra.
19
+ OWASP Top 10 vulnerabilities, secrets and credential exposure, authentication and authorization gaps, injection risks (SQL, XSS, command), insecure dependencies, misconfigured CORS or headers, data exposure in logs or responses. Works across all layers, UI, backend, infra.
20
20
 
21
- ## RTK MANDATORY
21
+ ## RTK, MANDATORY
22
22
 
23
23
  Use `rtk` for ALL CLI commands. Never run commands directly.
24
24
 
@@ -27,19 +27,19 @@ Use `rtk` for ALL CLI commands. Never run commands directly.
27
27
 
28
28
  If `rtk` is not available, report it as a blocker. Do not run commands without it.
29
29
 
30
- ## Skills Auto-Detection
30
+ ## Skills, Auto-Detection
31
31
 
32
- Skills are located in `.agents/skills/`. Detect and use relevant skills automatically the user will never tell you which skill to use.
32
+ Skills are located in `.agents/skills/`. Detect and use relevant skills automatically, the user will never tell you which skill to use.
33
33
 
34
34
  1. Read the task and identify domain and platform
35
35
  2. Scan `.agents/skills/` for available skills
36
36
  3. Read each `SKILL.md` description to assess relevance
37
- 4. Load and follow any skill that applies even partial match warrants loading
37
+ 4. Load and follow any skill that applies, even partial match warrants loading
38
38
 
39
39
  Rules:
40
40
  - Never implement directly if a skill applies
41
- - Follow skill instructions exactly do not partially apply them
42
- - If two skills apply, follow both resolve conflicts by asking the lead
41
+ - Follow skill instructions exactly, do not partially apply them
42
+ - If two skills apply, follow both, resolve conflicts by asking the lead
43
43
 
44
44
  ## Responsibilities
45
45
 
@@ -54,22 +54,22 @@ Rules:
54
54
 
55
55
  ## Severity Levels
56
56
 
57
- - **Critical** Must block merge: secret exposure, auth bypass, data loss risk
58
- - **High** Should fix before merge: injection risk, missing auth, sensitive data leak
59
- - **Medium** Fix soon: missing rate limiting, weak validation, insecure config
60
- - **Low** Informational: minor hardening opportunities
57
+ - **Critical**, Must block merge: secret exposure, auth bypass, data loss risk
58
+ - **High**, Should fix before merge: injection risk, missing auth, sensitive data leak
59
+ - **Medium**, Fix soon: missing rate limiting, weak validation, insecure config
60
+ - **Low**, Informational: minor hardening opportunities
61
61
 
62
62
  ## Constraints
63
63
 
64
- - Audit only `edit: deny` enforced
64
+ - Audit only, `edit: deny` enforced
65
65
  - Do not push to `main`
66
- - Do not merge PRs human-only
67
- - Critical findings must block the PR report to lead immediately
66
+ - Do not merge PRs, human-only
67
+ - Critical findings must block the PR, report to lead immediately
68
68
 
69
69
  ## Output Format
70
70
 
71
71
  ```
72
- ## Security Auditor Done
72
+ ## Security Auditor, Done
73
73
 
74
74
  **Status:** pass | blocked
75
75
  **Critical:** <count>
@@ -78,7 +78,7 @@ Rules:
78
78
  **Low:** <count>
79
79
 
80
80
  ### Findings
81
- - [severity] [file:line] <description> <recommended fix>
81
+ - [severity] [file:line] <description>, <recommended fix>
82
82
 
83
83
  **Blockers:** none | <critical findings that must be resolved before PR>
84
84
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-onboard",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Prepare any brownfield codebase for AI agent workflows using OpenCode, OpenSpec, and ensemble orchestration.",
5
5
  "keywords": [
6
6
  "opencode",
package/src/index.js CHANGED
@@ -1,98 +1,106 @@
1
- #!/usr/bin/env node
2
- import chalk from 'chalk'
3
- import { checkEnv } from './steps/check-env.js'
4
- import { checkPlatform } from './steps/check-platform.js'
5
- import { checkRtk } from './steps/check-rtk.js'
6
- import { chooseModels } from './steps/choose-models.js'
7
- import { choosePlatform } from './steps/choose-platform.js'
8
- import { chooseSkillsProvider } from './steps/choose-skills-provider.js'
9
- import { cleanAiFiles } from './steps/clean-ai-files.js'
10
- import { copyContentStep } from './steps/copy-content.js'
11
- import { initOpenspec } from './steps/init-openspec.js'
12
- import { installBrowser } from './steps/install-browser.js'
13
-
14
- console.clear()
15
- console.log()
16
- const logo = chalk.hex('#fe3d57')
17
- console.log(logo(' '))
18
- console.log(logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '))
19
- console.log(logo(' ▓▒▓ ▓▒▓ '))
20
- console.log(logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '))
21
- console.log(logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '))
22
- console.log(logo(' ▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '))
23
- console.log(logo(' ▓▓▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '))
24
- console.log(logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '))
25
- console.log(logo(' ▓▒▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▓ '))
26
- console.log(logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '))
27
- console.log(logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '))
28
- console.log(logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '))
29
- console.log()
30
- console.log(chalk.bold(' 🧰 opencode-onboard'))
31
- console.log(chalk.dim(' Prepare your codebase for AI agents'))
32
- console.log()
33
- console.log(' This tool will set up your project with a team of AI agents,')
34
- console.log(' install skills, select models, and configure OpenCode.')
35
- console.log()
36
- console.log(chalk.bold(' Press Enter to begin...'))
37
- console.log()
38
-
39
- await new Promise(resolve => {
40
- process.stdin.resume()
41
- process.stdin.once('data', () => {
42
- process.stdin.pause()
43
- resolve()
44
- })
45
- })
46
-
47
- try {
48
- // 1. Check Node + pnpm
49
- await checkEnv()
50
-
51
- // 2. Clean existing AI config files
52
- await cleanAiFiles()
53
-
54
- // 3. Choose platform
55
- const platform = await choosePlatform()
56
-
57
- // 4. Check platform CLI (az or gh)
58
- await checkPlatform(platform)
59
-
60
- // 5. Copy content
61
- await copyContentStep(platform)
62
-
63
- // 6. Init OpenSpec
64
- await initOpenspec()
65
-
66
- // 7. Install skills
67
- await chooseSkillsProvider()
68
-
69
- // 8. Choose models
70
- await chooseModels()
71
-
72
- // 9. Check RTK
73
- await checkRtk()
74
-
75
- // 10. Install opencode-browser
76
- await installBrowser()
77
-
78
- // Done
79
- console.log()
80
- console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
81
- console.log(chalk.bold.green(' Onboarding complete!'))
82
- console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
83
- console.log()
84
- console.log(' Next step:')
85
- console.log(chalk.hex('#fe3d57')(' Open OpenCode in this project and type: ') + chalk.bold('"init"'))
86
- console.log()
87
- console.log(' OpenCode will generate ARCHITECTURE.md and DESIGN.md')
88
- console.log(' from your actual codebase, then activate the agent team.')
89
- console.log()
90
- } catch (err) {
91
- if (err.name === 'ExitPromptError') {
92
- console.log()
93
- console.log(chalk.yellow('Cancelled.'))
94
- } else {
95
- console.error(chalk.red('\nUnexpected error:'), err.message)
96
- process.exit(1)
97
- }
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk'
3
+ import { checkEnv } from './steps/check-env.js'
4
+ import { checkPlatform } from './steps/check-platform.js'
5
+ import { checkRtk } from './steps/check-rtk.js'
6
+ import { chooseModels } from './steps/choose-models.js'
7
+ import { choosePlatform } from './steps/choose-platform.js'
8
+ import { chooseSkillsProvider } from './steps/choose-skills-provider.js'
9
+ import { cleanAiFiles } from './steps/clean-ai-files.js'
10
+ import { copyContentStep } from './steps/copy-content.js'
11
+ import { initOpenspec } from './steps/init-openspec.js'
12
+ import { installBrowser } from './steps/install-browser.js'
13
+
14
+ if (process.stdout.isTTY) console.clear()
15
+ console.log()
16
+ const logo = chalk.hex('#fe3d57')
17
+ const bannerLines = [
18
+ logo(' ▒▒▒▒▒▒▒ '),
19
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
20
+ logo(' ▒▒▓ ▓▒▓ '),
21
+ logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
22
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
23
+ logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
24
+ logo(' ▓▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒░░▒▒▒▒▓▓ '),
25
+ logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
26
+ logo(' ▓▒▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
27
+ logo(' ▓▒▒▒▒▒▒░▓▒▒▓▒▓▒▒▒▒▒▒▒▒▒▓▓ '),
28
+ logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
29
+ logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
30
+ '',
31
+ chalk.bold(' 🧰 opencode-onboard'),
32
+ chalk.dim(' Prepare your codebase for AI agents'),
33
+ ]
34
+
35
+ for (const line of bannerLines) console.log(line)
36
+ console.log()
37
+ console.log(' This tool will set up your project with a team of AI agents,')
38
+ console.log(' install skills, select models, and configure OpenCode.')
39
+ console.log()
40
+
41
+ // Only wait for Enter in a real interactive TTY
42
+ if (process.stdin.isTTY) {
43
+ console.log(chalk.bold(' Press Enter to begin...'))
44
+ console.log()
45
+ await new Promise(resolve => {
46
+ process.stdin.resume()
47
+ process.stdin.once('data', () => {
48
+ process.stdin.pause()
49
+ resolve()
50
+ })
51
+ })
98
52
  }
53
+ })
54
+
55
+ try {
56
+ // 1. Check Node + pnpm
57
+ await checkEnv()
58
+
59
+ // 2. Clean existing AI config files
60
+ await cleanAiFiles()
61
+
62
+ // 3. Choose platform
63
+ const platform = await choosePlatform()
64
+
65
+ // 4. Check platform CLI (az or gh)
66
+ await checkPlatform(platform)
67
+
68
+ // 5. Copy content
69
+ await copyContentStep(platform)
70
+
71
+ // 6. Init OpenSpec
72
+ await initOpenspec()
73
+
74
+ // 7. Install skills
75
+ await chooseSkillsProvider()
76
+
77
+ // 8. Choose models
78
+ await chooseModels()
79
+
80
+ // 9. Check RTK
81
+ await checkRtk()
82
+
83
+ // 10. Install opencode-browser
84
+ await installBrowser()
85
+
86
+ // Done
87
+ console.log()
88
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
89
+ console.log(chalk.bold.green(' Onboarding complete!'))
90
+ console.log(chalk.bold.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
91
+ console.log()
92
+ console.log(' Next step:')
93
+ console.log(chalk.hex('#fe3d57')(' Open OpenCode in this project and type: ') + chalk.bold('"init"'))
94
+ console.log()
95
+ console.log(' OpenCode will generate ARCHITECTURE.md and DESIGN.md')
96
+ console.log(' from your actual codebase, then activate the agent team.')
97
+ console.log()
98
+ } catch (err) {
99
+ if (err.name === 'ExitPromptError') {
100
+ console.log()
101
+ console.log(chalk.yellow('Cancelled.'))
102
+ } else {
103
+ console.error(chalk.red('\nUnexpected error:'), err.message)
104
+ process.exit(1)
105
+ }
106
+ }
@@ -30,7 +30,7 @@ function buildDisplayModels(rawModels) {
30
30
  : ''
31
31
  return {
32
32
  ...m,
33
- label: `${m.name}${COST_TIER_DISPLAY(m.cost, m.canonicalCost)} ${m.id}`,
33
+ label: `${m.name}${COST_TIER_DISPLAY(m.cost, m.canonicalCost)}, ${m.id}`,
34
34
  description: `${priceStr}${canonicalNote} · context: ${m.context ? (m.context / 1000) + 'k' : '?'}`,
35
35
  }
36
36
  })
@@ -78,7 +78,7 @@ export async function chooseModels() {
78
78
  }
79
79
 
80
80
  if (source === 'stale-cache') {
81
- warn('Network unavailable using cached model list (may be outdated).')
81
+ warn('Network unavailable, using cached model list (may be outdated).')
82
82
  } else if (source === 'cache') {
83
83
  info('Using cached model list (refreshes weekly).')
84
84
  }
@@ -91,20 +91,20 @@ export async function chooseModels() {
91
91
  console.log()
92
92
 
93
93
  // Plan model
94
- info('PLAN model used by the main agent for proposals, specs, architecture decisions.')
94
+ info('PLAN model, used by the main agent for proposals, specs, architecture decisions.')
95
95
  info('Pick something capable with strong reasoning.')
96
96
  const planModel = await pickModel('Plan model:', models)
97
97
  console.log()
98
98
 
99
99
  // Build model
100
- info('BUILD model used by front-engineer, back-engineer, infra-engineer, quality-engineer, security-auditor.')
100
+ info('BUILD model, used by front-engineer, back-engineer, infra-engineer, quality-engineer, security-auditor.')
101
101
  info('Pick something capable for implementation work.')
102
102
  const buildModel = await pickModel('Build model:', models)
103
103
  console.log()
104
104
 
105
105
  // Fast model
106
- info('FAST model used by devops-manager for reading issues, classifying PR comments.')
107
- info('Pick something fast and cheap no heavy reasoning needed.')
106
+ info('FAST model, used by devops-manager for reading issues, classifying PR comments.')
107
+ info('Pick something fast and cheap, no heavy reasoning needed.')
108
108
  const fastModel = await pickModel('Fast model:', models)
109
109
  console.log()
110
110
 
@@ -1,8 +1,8 @@
1
1
  import { select } from '@inquirer/prompts'
2
+ import { execa } from 'execa'
2
3
  import fse from 'fs-extra'
3
4
  import path from 'path'
4
5
  import { fileURLToPath } from 'url'
5
- import { execa } from 'execa'
6
6
  import { header, info, success, warn } from '../utils/exec.js'
7
7
 
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
@@ -30,13 +30,13 @@ async function installObSkills() {
30
30
  export async function chooseSkillsProvider() {
31
31
  header('Step 7, Installing skills')
32
32
 
33
- // ob-skills are always installed mandatory
33
+ // ob-skills are always installed, mandatory
34
34
  info('Installing built-in ob-skills...')
35
35
  await installObSkills()
36
36
  console.log()
37
37
 
38
38
  info('Skills provide platform and tech-specific knowledge to your agents.')
39
- info('Agents detect and load skills automatically you never need to specify them.')
39
+ info('Agents detect and load skills automatically, you never need to specify them.')
40
40
  info('You can add more skills on top of the built-in ones.')
41
41
  console.log()
42
42
 
@@ -49,7 +49,7 @@ export async function chooseSkillsProvider() {
49
49
  description: 'Install skills from the vercel-labs community skills registry',
50
50
  },
51
51
  {
52
- name: 'None built-in skills are enough',
52
+ name: 'None, built-in skills are enough',
53
53
  value: 'none',
54
54
  },
55
55
  ],
package/src/utils/exec.js CHANGED
@@ -1,161 +1,161 @@
1
- import chalk from 'chalk'
2
- import { execa } from 'execa'
3
- import ora from 'ora'
4
-
5
- // ── Screen / step state ──────────────────────────────────────────────────────
6
-
7
- const previousSteps = [] // up to 2 completed steps, each is an array of lines
8
- let currentStepLines = [] // lines accumulated in the current step
9
- let stepSpinner = null // ora spinner shown while step is working
10
-
11
- function appendLine(line) {
12
- currentStepLines.push(line)
13
- }
14
-
15
- function stopSpinner() {
16
- if (stepSpinner) {
17
- stepSpinner.stop()
18
- stepSpinner = null
19
- }
20
- }
21
-
22
- function redraw() {
23
- console.clear()
24
-
25
- // Show up to 2 previous steps dimmed
26
- for (const stepLines of previousSteps) {
27
- for (const line of stepLines) {
28
- process.stdout.write(chalk.dim(line) + '\n')
29
- }
30
- process.stdout.write('\n')
31
- }
32
-
33
- // Current step output
34
- for (const line of currentStepLines) {
35
- process.stdout.write(line + '\n')
36
- }
37
- }
38
-
39
- // ── Public API ───────────────────────────────────────────────────────────────
40
-
41
- /**
42
- * Run a shell command with a spinner.
43
- * Returns { success, stdout, stderr }
44
- */
45
- export async function run(command, args = [], { label, cwd = process.cwd() } = {}) {
46
- const spinner = ora(label ?? `${command} ${args.join(' ')}`).start()
47
- try {
48
- const result = await execa(command, args, { cwd, reject: false })
49
- if (result.exitCode === 0) {
50
- spinner.succeed()
51
- } else {
52
- spinner.fail()
53
- }
54
- return { success: result.exitCode === 0, stdout: result.stdout, stderr: result.stderr }
55
- } catch (err) {
56
- spinner.fail()
57
- return { success: false, stdout: '', stderr: err.message }
58
- }
59
- }
60
-
61
- /**
62
- * Check if a command is available on PATH.
63
- * Returns true/false.
64
- */
65
- export async function commandExists(command) {
66
- try {
67
- const result = await execa(command, ['--version'], { reject: false })
68
- return result.exitCode === 0
69
- } catch {
70
- return false
71
- }
72
- }
73
-
74
- /**
75
- * Print a section header clears screen, shows previous step dimmed, starts new step.
76
- */
77
- export function header(text) {
78
- // Rotate buffers keep last 2 completed steps
79
- previousSteps.push(currentStepLines)
80
- if (previousSteps.length > 2) previousSteps.shift()
81
- currentStepLines = []
82
-
83
- const line1 = ''
84
- const line2 = chalk.bold.hex('#fe3d57')(`━━ ${text}`)
85
- const line3 = ''
86
-
87
- appendLine(line1)
88
- appendLine(line2)
89
- appendLine(line3)
90
-
91
- redraw()
92
-
93
- // Start a spinner while the step is working
94
- stepSpinner = ora({ text: chalk.dim('working...'), color: 'red' }).start()
95
- }
96
-
97
- /**
98
- * Print a success line.
99
- */
100
- export function success(text) {
101
- stopSpinner()
102
- const line = chalk.green('✓ ') + text
103
- appendLine(line)
104
- console.log(line)
105
- }
106
-
107
- /**
108
- * Print a warning line.
109
- */
110
- export function warn(text) {
111
- stopSpinner()
112
- const line = chalk.yellow('⚠ ') + text
113
- appendLine(line)
114
- console.log(line)
115
- }
116
-
117
- /**
118
- * Print an error line.
119
- */
120
- export function error(text) {
121
- stopSpinner()
122
- const line = chalk.red('✗ ') + text
123
- appendLine(line)
124
- console.log(line)
125
- }
126
-
127
- /**
128
- * Print an info line.
129
- */
130
- export function info(text) {
131
- stopSpinner()
132
- const line = chalk.dim(' ' + text)
133
- appendLine(line)
134
- console.log(line)
135
- }
136
-
137
- /**
138
- * Print an action prompt line (white bold requires user interaction).
139
- */
140
- export function prompt(text) {
141
- stopSpinner()
142
- const line = chalk.bold(' ' + text)
143
- appendLine(line)
144
- console.log(line)
145
- }
146
-
147
- /**
148
- * Print a code block.
149
- */
150
- export function code(lines) {
151
- stopSpinner()
152
- appendLine('')
153
- console.log()
154
- for (const line of lines) {
155
- const formatted = chalk.bgGray.white(' ' + line + ' ')
156
- appendLine(formatted)
157
- console.log(formatted)
158
- }
159
- appendLine('')
160
- console.log()
161
- }
1
+ import chalk from 'chalk'
2
+ import { execa } from 'execa'
3
+ import ora from 'ora'
4
+
5
+ // ── Screen / step state ──────────────────────────────────────────────────────
6
+
7
+ const previousSteps = [] // up to 2 completed steps, each is an array of lines
8
+ let currentStepLines = [] // lines accumulated in the current step
9
+ let stepSpinner = null // ora spinner shown while step is working
10
+
11
+ function appendLine(line) {
12
+ currentStepLines.push(line)
13
+ }
14
+
15
+ function stopSpinner() {
16
+ if (stepSpinner) {
17
+ stepSpinner.stop()
18
+ stepSpinner = null
19
+ }
20
+ }
21
+
22
+ function redraw() {
23
+ if (process.stdout.isTTY) console.clear()
24
+
25
+ // Show up to 2 previous steps dimmed
26
+ for (const stepLines of previousSteps) {
27
+ for (const line of stepLines) {
28
+ process.stdout.write(chalk.dim(line) + '\n')
29
+ }
30
+ process.stdout.write('\n')
31
+ }
32
+
33
+ // Current step output
34
+ for (const line of currentStepLines) {
35
+ process.stdout.write(line + '\n')
36
+ }
37
+ }
38
+
39
+ // ── Public API ───────────────────────────────────────────────────────────────
40
+
41
+ /**
42
+ * Run a shell command with a spinner.
43
+ * Returns { success, stdout, stderr }
44
+ */
45
+ export async function run(command, args = [], { label, cwd = process.cwd() } = {}) {
46
+ const spinner = ora(label ?? `${command} ${args.join(' ')}`).start()
47
+ try {
48
+ const result = await execa(command, args, { cwd, reject: false })
49
+ if (result.exitCode === 0) {
50
+ spinner.succeed()
51
+ } else {
52
+ spinner.fail()
53
+ }
54
+ return { success: result.exitCode === 0, stdout: result.stdout, stderr: result.stderr }
55
+ } catch (err) {
56
+ spinner.fail()
57
+ return { success: false, stdout: '', stderr: err.message }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Check if a command is available on PATH.
63
+ * Returns true/false.
64
+ */
65
+ export async function commandExists(command) {
66
+ try {
67
+ const result = await execa(command, ['--version'], { reject: false })
68
+ return result.exitCode === 0
69
+ } catch {
70
+ return false
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Print a section header, clears screen, shows previous step dimmed, starts new step.
76
+ */
77
+ export function header(text) {
78
+ // Rotate buffers, keep last 2 completed steps
79
+ previousSteps.push(currentStepLines)
80
+ if (previousSteps.length > 2) previousSteps.shift()
81
+ currentStepLines = []
82
+
83
+ const line1 = ''
84
+ const line2 = chalk.bold.hex('#fe3d57')(`━━ ${text}`)
85
+ const line3 = ''
86
+
87
+ appendLine(line1)
88
+ appendLine(line2)
89
+ appendLine(line3)
90
+
91
+ redraw()
92
+
93
+ // Start a spinner while the step is working
94
+ stepSpinner = ora({ text: chalk.dim('working...'), color: 'red' }).start()
95
+ }
96
+
97
+ /**
98
+ * Print a success line.
99
+ */
100
+ export function success(text) {
101
+ stopSpinner()
102
+ const line = chalk.green('✓ ') + text
103
+ appendLine(line)
104
+ console.log(line)
105
+ }
106
+
107
+ /**
108
+ * Print a warning line.
109
+ */
110
+ export function warn(text) {
111
+ stopSpinner()
112
+ const line = chalk.yellow('⚠ ') + text
113
+ appendLine(line)
114
+ console.log(line)
115
+ }
116
+
117
+ /**
118
+ * Print an error line.
119
+ */
120
+ export function error(text) {
121
+ stopSpinner()
122
+ const line = chalk.red('✗ ') + text
123
+ appendLine(line)
124
+ console.log(line)
125
+ }
126
+
127
+ /**
128
+ * Print an info line.
129
+ */
130
+ export function info(text) {
131
+ stopSpinner()
132
+ const line = chalk.dim(' ' + text)
133
+ appendLine(line)
134
+ console.log(line)
135
+ }
136
+
137
+ /**
138
+ * Print an action prompt line (white bold, requires user interaction).
139
+ */
140
+ export function prompt(text) {
141
+ stopSpinner()
142
+ const line = chalk.bold(' ' + text)
143
+ appendLine(line)
144
+ console.log(line)
145
+ }
146
+
147
+ /**
148
+ * Print a code block.
149
+ */
150
+ export function code(lines) {
151
+ stopSpinner()
152
+ appendLine('')
153
+ console.log()
154
+ for (const line of lines) {
155
+ const formatted = chalk.bgGray.white(' ' + line + ' ')
156
+ appendLine(formatted)
157
+ console.log(formatted)
158
+ }
159
+ appendLine('')
160
+ console.log()
161
+ }
@@ -1,6 +1,6 @@
1
1
  import fse from 'fs-extra'
2
- import path from 'path'
3
2
  import os from 'os'
3
+ import path from 'path'
4
4
 
5
5
  const CACHE_DIR = path.join(os.homedir(), '.config', 'opencode-onboard')
6
6
  const CACHE_FILE = path.join(CACHE_DIR, 'models-cache.json')
@@ -87,7 +87,7 @@ export async function fetchModels() {
87
87
  await saveCache(models)
88
88
  return { models, source: 'network' }
89
89
  } catch {
90
- // 3. Network failed fall back to stale cache if available
90
+ // 3. Network failed, fall back to stale cache if available
91
91
  try {
92
92
  if (await fse.pathExists(CACHE_FILE)) {
93
93
  const cache = await fse.readJson(CACHE_FILE)