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 +14 -15
- package/content/.agents/agents/back-engineer.md +11 -11
- package/content/.agents/agents/devops-manager.md +18 -18
- package/content/.agents/agents/front-engineer.md +11 -11
- package/content/.agents/agents/infra-engineer.md +12 -12
- package/content/.agents/agents/quality-engineer.md +13 -13
- package/content/.agents/agents/security-auditor.md +17 -17
- package/package.json +1 -1
- package/src/index.js +105 -97
- package/src/steps/choose-models.js +6 -6
- package/src/steps/choose-skills-provider.js +4 -4
- package/src/utils/exec.js +161 -161
- package/src/utils/models-cache.js +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<img src="
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
32
|
+
## Skills, Auto-Detection
|
|
33
33
|
|
|
34
|
-
Skills are located in `.agents/skills/`. Detect and use relevant skills automatically
|
|
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
|
|
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
|
|
44
|
-
- If two skills apply, follow both
|
|
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
|
|
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
|
|
62
|
-
- Do not merge PRs
|
|
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
|
|
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
|
|
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
|
|
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
|
|
32
|
+
## Skills, Auto-Detection
|
|
33
33
|
|
|
34
|
-
Skills are located in `.agents/skills/`. Detect and use relevant skills automatically
|
|
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
|
|
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
|
|
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
|
|
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
|
|
72
|
-
- Does not push to `main
|
|
73
|
-
- Does not merge PRs
|
|
74
|
-
- Does not approve PRs
|
|
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
|
|
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
|
|
82
|
+
## DevOps Manager, Work Item Parsed
|
|
83
83
|
|
|
84
84
|
**Platform:** GitHub | Azure DevOps
|
|
85
|
-
**Item:** <id
|
|
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
|
|
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
|
|
102
|
+
## DevOps Manager, Feedback Classified
|
|
103
103
|
|
|
104
104
|
**Comments:** <total>
|
|
105
|
-
**Code changes needed:** <count
|
|
106
|
-
**Questions for human:** <count
|
|
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
|
|
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
|
|
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
|
|
32
|
+
## Skills, Auto-Detection
|
|
33
33
|
|
|
34
|
-
Skills are located in `.agents/skills/`. Detect and use relevant skills automatically
|
|
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
|
|
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
|
|
44
|
-
- If two skills apply, follow both
|
|
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
|
|
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
|
|
61
|
-
- Do not merge PRs
|
|
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
|
|
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
|
|
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
|
|
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
|
|
32
|
+
## Skills, Auto-Detection
|
|
33
33
|
|
|
34
|
-
Skills are located in `.agents/skills/`. Detect and use relevant skills automatically
|
|
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
|
|
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
|
|
44
|
-
- If two skills apply, follow both
|
|
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
|
|
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
|
|
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
|
|
61
|
-
- Do not merge PRs
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
32
|
+
## Skills, Auto-Detection
|
|
33
33
|
|
|
34
|
-
Skills are located in `.agents/skills/`. Detect and use relevant skills automatically
|
|
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
|
|
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
|
|
44
|
-
- If two skills apply, follow both
|
|
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
|
|
58
|
-
- Do not push to `main
|
|
59
|
-
- Do not merge PRs
|
|
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
|
|
61
|
+
- Report all failures, do not silently skip failing tests
|
|
62
62
|
|
|
63
63
|
## Output Format
|
|
64
64
|
|
|
65
65
|
```
|
|
66
|
-
## Quality Engineer
|
|
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
|
|
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
|
|
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
|
|
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
|
|
30
|
+
## Skills, Auto-Detection
|
|
31
31
|
|
|
32
|
-
Skills are located in `.agents/skills/`. Detect and use relevant skills automatically
|
|
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
|
|
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
|
|
42
|
-
- If two skills apply, follow both
|
|
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
|
|
58
|
-
- **High
|
|
59
|
-
- **Medium
|
|
60
|
-
- **Low
|
|
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
|
|
64
|
+
- Audit only, `edit: deny` enforced
|
|
65
65
|
- Do not push to `main`
|
|
66
|
-
- Do not merge PRs
|
|
67
|
-
- Critical findings must block the PR
|
|
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
|
|
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
|
|
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
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
console.log()
|
|
36
|
-
console.log(
|
|
37
|
-
console.log()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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)}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
107
|
-
info('Pick something fast and cheap
|
|
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
|
|
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
|
|
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
|
|
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
|
|
76
|
-
*/
|
|
77
|
-
export function header(text) {
|
|
78
|
-
// Rotate buffers
|
|
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
|
|
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
|
|
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)
|