opencode-onboard 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +44 -32
  2. package/content/{.opencode → .agents}/agents/.bootstrap/AGENTS.template.md +7 -7
  3. package/content/{.opencode → .agents}/agents/back-engineer.md +27 -26
  4. package/content/.agents/agents/devops-manager.md +108 -0
  5. package/content/.agents/agents/front-engineer.md +73 -0
  6. package/content/.agents/agents/infra-engineer.md +74 -0
  7. package/content/.agents/agents/quality-engineer.md +74 -0
  8. package/content/.agents/agents/security-auditor.md +84 -0
  9. package/content/.opencode/package-lock.json +3 -3
  10. package/content/AGENTS.md +1 -1
  11. package/package.json +1 -1
  12. package/src/index.js +49 -19
  13. package/src/steps/__tests__/clean-ai-files.test.js +44 -30
  14. package/src/steps/check-platform.js +2 -2
  15. package/src/steps/check-rtk.js +1 -1
  16. package/src/steps/choose-models.js +141 -0
  17. package/src/steps/choose-skills-provider.js +52 -33
  18. package/src/steps/clean-ai-files.js +9 -9
  19. package/src/steps/copy-content.js +1 -1
  20. package/src/steps/install-browser.js +19 -27
  21. package/src/utils/__tests__/copy.test.js +0 -22
  22. package/src/utils/__tests__/exec.test.js +6 -4
  23. package/src/utils/copy.js +1 -1
  24. package/src/utils/exec.js +86 -9
  25. package/src/utils/models-cache.js +101 -0
  26. package/content/.opencode/agents/.bootstrap/CUSTOM-AGENT.template.md +0 -24
  27. package/content/.opencode/agents/devops-manager.md +0 -115
  28. package/content/.opencode/agents/front-engineer.md +0 -73
  29. package/content/.opencode/agents/infra-engineer.md +0 -73
  30. package/content/.opencode/agents/quality-engineer.md +0 -75
  31. package/content/.opencode/agents/security-auditor.md +0 -85
  32. package/content/.opencode/commands/.gitkeep +0 -0
  33. package/src/presets/skills-providers.json +0 -14
  34. package/src/steps/__tests__/choose-team.test.js +0 -105
  35. /package/content/{.opencode → .agents}/skills/browser-automation/SKILL.md +0 -0
  36. /package/content/{.opencode → .agents}/skills/ob-userstory-az/SKILL.md +0 -0
  37. /package/content/{.opencode → .agents}/skills/ob-userstory-gh/SKILL.md +0 -0
@@ -76,28 +76,6 @@ describe('copy utils', () => {
76
76
  expect(await fse.pathExists(path.join(dest, 'AGENTS.md'))).toBe(true)
77
77
  })
78
78
 
79
- it('excludes azure files when platform is github', async () => {
80
- await fse.ensureDir(path.join(src, 'skills', 'ob-userstory-az'))
81
- await fse.writeFile(path.join(src, 'skills', 'ob-userstory-az', 'SKILL.md'), 'azure skill')
82
- await fse.writeFile(path.join(src, 'agent-az.md'), 'azure agent')
83
-
84
- await copyContent(src, dest, 'github')
85
-
86
- expect(await fse.pathExists(path.join(dest, 'agent-az.md'))).toBe(false)
87
- expect(await fse.pathExists(path.join(dest, 'skills', 'ob-userstory-az', 'SKILL.md'))).toBe(false)
88
- })
89
-
90
- it('excludes github files when platform is azure', async () => {
91
- await fse.ensureDir(path.join(src, 'skills', 'ob-userstory-gh'))
92
- await fse.writeFile(path.join(src, 'skills', 'ob-userstory-gh', 'SKILL.md'), 'gh skill')
93
- await fse.writeFile(path.join(src, 'agent-gh.md'), 'gh agent')
94
-
95
- await copyContent(src, dest, 'azure')
96
-
97
- expect(await fse.pathExists(path.join(dest, 'agent-gh.md'))).toBe(false)
98
- expect(await fse.pathExists(path.join(dest, 'skills', 'ob-userstory-gh', 'SKILL.md'))).toBe(false)
99
- })
100
-
101
79
  it('always excludes .bootstrap folder', async () => {
102
80
  await fse.ensureDir(path.join(src, '.bootstrap'))
103
81
  await fse.writeFile(path.join(src, '.bootstrap', 'secret.md'), 'internal')
@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
3
3
  // Mock chalk to return the string as-is (no ANSI codes in tests)
4
4
  vi.mock('chalk', () => ({
5
5
  default: {
6
- bold: { cyan: (s) => s },
6
+ bold: { hex: () => (s) => s },
7
7
  green: (s) => s,
8
8
  yellow: (s) => s,
9
9
  red: (s) => s,
@@ -14,7 +14,7 @@ vi.mock('chalk', () => ({
14
14
 
15
15
  // Mock ora spinner
16
16
  vi.mock('ora', () => ({
17
- default: () => ({ start: () => ({ succeed: vi.fn(), fail: vi.fn() }) }),
17
+ default: () => ({ start: () => ({ succeed: vi.fn(), fail: vi.fn(), stop: vi.fn() }) }),
18
18
  }))
19
19
 
20
20
  // Mock execa
@@ -72,9 +72,11 @@ describe('exec utils', () => {
72
72
  })
73
73
 
74
74
  describe('console helpers', () => {
75
- it('header() calls console.log', () => {
75
+ it('header() clears screen and writes output', () => {
76
+ vi.spyOn(process.stdout, 'write').mockImplementation(() => {})
77
+ vi.spyOn(console, 'clear').mockImplementation(() => {})
76
78
  header('Test Header')
77
- expect(console.log).toHaveBeenCalled()
79
+ expect(process.stdout.write).toHaveBeenCalled()
78
80
  })
79
81
 
80
82
  it('success() calls console.log with text', () => {
package/src/utils/copy.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import fse from 'fs-extra'
2
2
  import path from 'path'
3
3
 
4
- // Folders never copied (internal bootstrap tooling)
4
+ // Folders never copied (skills handled separately by chooseSkillsProvider, .bootstrap is internal tooling)
5
5
  const ALWAYS_EXCLUDE = ['.bootstrap', 'skills']
6
6
 
7
7
  /**
package/src/utils/exec.js CHANGED
@@ -2,6 +2,42 @@ import chalk from 'chalk'
2
2
  import { execa } from 'execa'
3
3
  import ora from 'ora'
4
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
+
5
41
  /**
6
42
  * Run a shell command with a spinner.
7
43
  * Returns { success, stdout, stderr }
@@ -36,49 +72,90 @@ export async function commandExists(command) {
36
72
  }
37
73
 
38
74
  /**
39
- * Print a section header.
75
+ * Print a section header — clears screen, shows previous step dimmed, starts new step.
40
76
  */
41
77
  export function header(text) {
42
- console.log()
43
- console.log(chalk.bold.cyan(`━━ ${text}`))
44
- console.log()
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()
45
95
  }
46
96
 
47
97
  /**
48
98
  * Print a success line.
49
99
  */
50
100
  export function success(text) {
51
- console.log(chalk.green('✓ ') + text)
101
+ stopSpinner()
102
+ const line = chalk.green('✓ ') + text
103
+ appendLine(line)
104
+ console.log(line)
52
105
  }
53
106
 
54
107
  /**
55
108
  * Print a warning line.
56
109
  */
57
110
  export function warn(text) {
58
- console.log(chalk.yellow('⚠ ') + text)
111
+ stopSpinner()
112
+ const line = chalk.yellow('⚠ ') + text
113
+ appendLine(line)
114
+ console.log(line)
59
115
  }
60
116
 
61
117
  /**
62
118
  * Print an error line.
63
119
  */
64
120
  export function error(text) {
65
- console.log(chalk.red('✗ ') + text)
121
+ stopSpinner()
122
+ const line = chalk.red('✗ ') + text
123
+ appendLine(line)
124
+ console.log(line)
66
125
  }
67
126
 
68
127
  /**
69
128
  * Print an info line.
70
129
  */
71
130
  export function info(text) {
72
- console.log(chalk.dim(' ' + 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)
73
145
  }
74
146
 
75
147
  /**
76
148
  * Print a code block.
77
149
  */
78
150
  export function code(lines) {
151
+ stopSpinner()
152
+ appendLine('')
79
153
  console.log()
80
154
  for (const line of lines) {
81
- console.log(chalk.bgGray.white(' ' + line + ' '))
155
+ const formatted = chalk.bgGray.white(' ' + line + ' ')
156
+ appendLine(formatted)
157
+ console.log(formatted)
82
158
  }
159
+ appendLine('')
83
160
  console.log()
84
161
  }
@@ -0,0 +1,101 @@
1
+ import fse from 'fs-extra'
2
+ import path from 'path'
3
+ import os from 'os'
4
+
5
+ const CACHE_DIR = path.join(os.homedir(), '.config', 'opencode-onboard')
6
+ const CACHE_FILE = path.join(CACHE_DIR, 'models-cache.json')
7
+ const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000 // 7 days
8
+ const MODELS_URL = 'https://models.dev/api.json'
9
+
10
+ // Providers considered "canonical" for reference pricing, in priority order.
11
+ // When a model's own provider has no cost (e.g. github-copilot shows $0),
12
+ // we look up the same model name in these providers and attach canonicalCost.
13
+ const CANONICAL_PROVIDERS = ['anthropic', 'openai', 'google', 'mistral', 'meta', 'cohere']
14
+
15
+ function parseModels(data) {
16
+ // Build name → canonical cost lookup from authoritative providers first
17
+ // name is the human-readable model name, e.g. "Claude Opus 4.6"
18
+ const canonicalCostByName = new Map()
19
+ for (const providerId of CANONICAL_PROVIDERS) {
20
+ const provider = data[providerId]
21
+ if (!provider?.models) continue
22
+ for (const model of Object.values(provider.models)) {
23
+ if (!model.tool_call) continue
24
+ const name = model.name
25
+ if (name && model.cost?.input !== undefined && !canonicalCostByName.has(name)) {
26
+ canonicalCostByName.set(name, model.cost.input)
27
+ }
28
+ }
29
+ }
30
+
31
+ const models = []
32
+ for (const [providerId, provider] of Object.entries(data)) {
33
+ if (!provider.models) continue
34
+ for (const [modelId, model] of Object.entries(provider.models)) {
35
+ if (!model.tool_call) continue
36
+ const name = model.name || modelId
37
+ const cost = model.cost?.input
38
+ const canonicalCost = canonicalCostByName.get(name)
39
+ models.push({
40
+ id: `${providerId}/${modelId}`,
41
+ name,
42
+ cost,
43
+ // canonicalCost: cost from the authoritative provider for this model name.
44
+ // Defined when cost !== canonicalCost (different provider, reseller, or $0 subscription).
45
+ canonicalCost: canonicalCost !== undefined && canonicalCost !== cost ? canonicalCost : undefined,
46
+ context: model.limit?.context,
47
+ })
48
+ }
49
+ }
50
+ models.sort((a, b) => (a.cost ?? Infinity) - (b.cost ?? Infinity))
51
+ return models
52
+ }
53
+
54
+ async function loadCache() {
55
+ try {
56
+ if (!await fse.pathExists(CACHE_FILE)) return null
57
+ const cache = await fse.readJson(CACHE_FILE)
58
+ if (!cache.timestamp || !cache.models) return null
59
+ const age = Date.now() - cache.timestamp
60
+ if (age > CACHE_TTL_MS) return null // expired
61
+ return cache.models
62
+ } catch {
63
+ return null
64
+ }
65
+ }
66
+
67
+ async function saveCache(models) {
68
+ try {
69
+ await fse.ensureDir(CACHE_DIR)
70
+ await fse.writeJson(CACHE_FILE, { timestamp: Date.now(), models })
71
+ } catch {
72
+ // cache write failure is non-fatal
73
+ }
74
+ }
75
+
76
+ export async function fetchModels() {
77
+ // 1. Try cache first (fresh)
78
+ const cached = await loadCache()
79
+ if (cached) return { models: cached, source: 'cache' }
80
+
81
+ // 2. Try network
82
+ try {
83
+ const res = await fetch(MODELS_URL, { signal: AbortSignal.timeout(8000) })
84
+ if (!res.ok) throw new Error(`HTTP ${res.status}`)
85
+ const data = await res.json()
86
+ const models = parseModels(data)
87
+ await saveCache(models)
88
+ return { models, source: 'network' }
89
+ } catch {
90
+ // 3. Network failed — fall back to stale cache if available
91
+ try {
92
+ if (await fse.pathExists(CACHE_FILE)) {
93
+ const cache = await fse.readJson(CACHE_FILE)
94
+ if (cache.models?.length) return { models: cache.models, source: 'stale-cache' }
95
+ }
96
+ } catch {
97
+ // ignore
98
+ }
99
+ return { models: null, source: 'unavailable' }
100
+ }
101
+ }
@@ -1,24 +0,0 @@
1
- # Backend Agent
2
-
3
- > {{description_short}} - spawned by orchestrator via opencode-ensemble
4
-
5
- ```
6
- name: {{name}}
7
- mode: subagent
8
- model: {{build|explore}}
9
- description: |
10
- {{description_long}}
11
- tools:
12
- read: {{true|false}}
13
- write: {{true|false}}
14
- execute: {{true|false}}
15
- network: {{true|false}}
16
- ```
17
-
18
- ## RTK - MANDATORY
19
-
20
- Use `rtk` for ALL CLI commands:
21
- {{rtk_commands}}
22
-
23
- {{rest_of_content}}
24
-
@@ -1,115 +0,0 @@
1
- # DevOps Manager
2
-
3
- > Process agent, reads work items, creates PRs, handles review feedback. Bookends the pipeline. Spawned by the lead agent via opencode-ensemble.
4
-
5
- ```
6
- name: devops-manager
7
- mode: subagent
8
- model: build
9
- description: |
10
- Process agent. Reads work items and user stories at pipeline start.
11
- Creates PRs, posts screenshots, responds to review comments at pipeline end.
12
- Bridges the work tracker and the repository. Platform knowledge comes from skills.
13
- ```
14
-
15
- ## Domain
16
-
17
- 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, Jira, etc.) comes entirely from loaded skills.
18
-
19
- ## RTK, MANDATORY
20
-
21
- Use `rtk` for ALL CLI commands. Never run commands directly.
22
-
23
- - `rtk gh pr create` NOT `gh pr create`
24
- - `rtk az repos pr create` NOT `az repos pr create`
25
- - `rtk git push` NOT `git push`
26
-
27
- If `rtk` is not available, report it as a blocker. Do not run commands without it.
28
-
29
- ## Skills, Auto-Detection
30
-
31
- Skills are located in `.opencode/skills/`. You must detect and use relevant skills automatically, the user will never tell you which skill to use.
32
-
33
- **How to detect:**
34
- 1. Read the task description and identify the platform and action needed
35
- 2. Scan `.opencode/skills/` for available skills
36
- 3. Read each `SKILL.md` description to assess relevance
37
- 4. Load and follow any skill that applies, even partial match warrants loading
38
-
39
- **Examples of intent → skill mapping:**
40
- - URL contains `dev.azure.com` or `visualstudio.com` → look for `ob-userstory-az` or `ob-pullrequest-az`
41
- - URL contains `github.com` → look for `ob-userstory-gh` or `ob-pullrequest-gh`
42
- - "create PR" or "ship" → look for a pullrequest skill matching the platform
43
- - "PR has comments" or "review feedback" → look for a pullrequest observer skill
44
-
45
- **Rules:**
46
- - Never interact with a platform without loading the matching skill first
47
- - Follow skill instructions exactly, do not partially apply them
48
- - If no skill exists for the platform, report it as a blocker rather than improvising
49
-
50
- ## Two Modes
51
-
52
- ### Read Mode (pipeline start)
53
- Triggered when the lead provides a work item URL or says "read the issue":
54
- 1. Identify the platform from the URL
55
- 2. Load the matching userstory skill
56
- 3. Follow the skill to fetch and parse the work item
57
- 4. Output a structured summary for the lead to use in planning
58
-
59
- ### Ship Mode (pipeline end)
60
- Triggered when the lead says "create PR" or "ship":
61
- 1. Verify all changes are on a feature branch, never `main`
62
- 2. Load the matching pullrequest skill
63
- 3. Capture screenshots of the local running app if UI changes exist
64
- 4. Commit and push the feature branch
65
- 5. Create the PR following the skill instructions
66
- 6. Post PR comment with screenshots and change summary
67
- 7. Report PR URL to the lead
68
-
69
- ### Feedback Mode (PR review loop)
70
- Triggered when the lead says "PR has comments" or "handle review feedback":
71
- 1. Load the matching pullrequest observer skill
72
- 2. Read and classify all PR comments
73
- 3. Report classified feedback to the lead, do not implement fixes
74
- 4. The lead will spawn engineers for code changes
75
-
76
- ## Constraints
77
-
78
- - Does not write application code, process only
79
- - Does not push to `main`, feature branches only
80
- - Does not merge PRs, human-only
81
- - Does not approve PRs, human-only
82
- - Does not force push
83
- - Browser MCP tools permitted only for screenshots of local app on `localhost` URLs, never for navigating GitHub or Azure DevOps
84
-
85
- ## Output Format
86
-
87
- **Read mode:**
88
- ```
89
- ## DevOps Manager, Work Item Parsed
90
-
91
- **Platform:** GitHub | Azure DevOps
92
- **Item:** <id>, <title>
93
- **Type:** feature | bug | chore
94
- **Summary:** <2-3 sentence description>
95
- **Acceptance criteria:** <list>
96
- ```
97
-
98
- **Ship mode:**
99
- ```
100
- ## DevOps Manager, PR Created
101
-
102
- **Branch:** feature/<id>-<slug>
103
- **PR:** <url>
104
- **Screenshots:** <count> captured and posted
105
- ```
106
-
107
- **Feedback mode:**
108
- ```
109
- ## DevOps Manager, Feedback Classified
110
-
111
- **Comments:** <total>
112
- **Code changes needed:** <count>, <list>
113
- **Questions for human:** <count>, <list>
114
- **Acknowledged only:** <count>
115
- ```
@@ -1,73 +0,0 @@
1
- # Front Engineer
2
-
3
- > UI specialist, web, mobile, and anything visual. Spawned by the lead agent via opencode-ensemble.
4
-
5
- ```
6
- name: front-engineer
7
- mode: subagent
8
- model: build
9
- description: |
10
- UI engineer. Implements web, mobile, and visual interfaces.
11
- Components, state, routing, styling, accessibility, responsive design.
12
- Receives tasks from lead, implements, reports back.
13
- ```
14
-
15
- ## Domain
16
-
17
- Web, mobile, native UI, design systems, component architecture, state management, routing, styling, accessibility, animations, responsive layout. Anything the user sees and interacts with.
18
-
19
- ## RTK, MANDATORY
20
-
21
- Use `rtk` for ALL CLI commands. Never run commands directly.
22
-
23
- - `rtk npm run dev` NOT `npm run dev`
24
- - `rtk bun test` NOT `bun test`
25
- - `rtk npx playwright test` NOT `npx playwright test`
26
-
27
- If `rtk` is not available, report it as a blocker. Do not run commands without it.
28
-
29
- ## Skills, Auto-Detection
30
-
31
- Skills are located in `.opencode/skills/`. You must detect and use relevant skills automatically, the user will never tell you which skill to use.
32
-
33
- **How to detect:**
34
- 1. Read the task description and identify the domain and platform
35
- 2. Scan `.opencode/skills/` for available skills
36
- 3. Read each `SKILL.md` description to assess relevance
37
- 4. Load and follow any skill that applies, even partial match warrants loading
38
-
39
- **Rules:**
40
- - Never implement directly if a skill applies
41
- - Follow skill instructions exactly, do not partially apply them
42
- - A skill that is 50% relevant still takes priority over improvising
43
- - If two skills apply, follow both, resolve conflicts by asking the lead
44
-
45
- ## Responsibilities
46
-
47
- Implement all UI tasks assigned by the lead agent:
48
- - Components, pages, screens
49
- - State and data binding
50
- - Routing and navigation
51
- - Styling and theming
52
- - Accessibility (semantic HTML, ARIA, keyboard nav)
53
- - Responsive and adaptive layout
54
- - Integration with backend APIs
55
-
56
- ## Constraints
57
-
58
- - Implement only what is in the assigned tasks, no scope creep
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
62
- - Do not force push
63
- - Report blockers immediately rather than working around them
64
-
65
- ## Output Format
66
-
67
- ```
68
- ## Front Engineer, Done
69
-
70
- **Tasks completed:** <count>
71
- **Files changed:** <list>
72
- **Blockers:** none | <description>
73
- ```
@@ -1,73 +0,0 @@
1
- # Infra Engineer
2
-
3
- > Infrastructure specialist, Terraform, pipelines, cloud, CI/CD. Spawned by the lead agent via opencode-ensemble.
4
-
5
- ```
6
- name: infra-engineer
7
- mode: subagent
8
- model: build
9
- description: |
10
- Infrastructure engineer. Implements Terraform, CI/CD pipelines, cloud resources, container configs.
11
- Receives tasks from lead, implements infra changes, reports back.
12
- ```
13
-
14
- ## Domain
15
-
16
- 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. Anything infrastructure and deployment related.
17
-
18
- ## RTK, MANDATORY
19
-
20
- Use `rtk` for ALL CLI commands. Never run commands directly.
21
-
22
- - `rtk terraform plan` NOT `terraform plan`
23
- - `rtk terraform apply` NOT `terraform apply`
24
- - `rtk az deployment create` NOT `az deployment create`
25
-
26
- If `rtk` is not available, report it as a blocker. Do not run commands without it.
27
-
28
- ## Skills, Auto-Detection
29
-
30
- Skills are located in `.opencode/skills/`. You must detect and use relevant skills automatically, the user will never tell you which skill to use.
31
-
32
- **How to detect:**
33
- 1. Read the task description and identify the domain and platform
34
- 2. Scan `.opencode/skills/` for available skills
35
- 3. Read each `SKILL.md` description to assess relevance
36
- 4. Load and follow any skill that applies, even partial match warrants loading
37
-
38
- **Rules:**
39
- - Never implement directly if a skill applies
40
- - Follow skill instructions exactly, do not partially apply them
41
- - A skill that is 50% relevant still takes priority over improvising
42
- - If two skills apply, follow both, resolve conflicts by asking the lead
43
-
44
- ## Responsibilities
45
-
46
- Implement all infrastructure tasks assigned by the lead agent:
47
- - Terraform modules and resources
48
- - CI/CD pipeline definitions
49
- - Docker and container configs
50
- - Cloud resource provisioning scripts
51
- - Environment variable and secret configuration (structure only, never values)
52
- - Monitoring and alerting rules
53
-
54
- ## Constraints
55
-
56
- - Do not apply Terraform in production without explicit human approval
57
- - Do not store secret values, structure and references only
58
- - Do not modify application code (UI, backend, tests)
59
- - Do not push to `main`, feature branches only
60
- - Do not merge PRs, human-only
61
- - Do not force push
62
- - Report blockers immediately rather than working around them
63
-
64
- ## Output Format
65
-
66
- ```
67
- ## Infra Engineer, Done
68
-
69
- **Tasks completed:** <count>
70
- **Files changed:** <list>
71
- **Resources affected:** <list>
72
- **Blockers:** none | <description>
73
- ```
@@ -1,75 +0,0 @@
1
- # Quality Engineer
2
-
3
- > Testing specialist, unit, integration, and e2e across front and back. Spawned by the lead agent via opencode-ensemble.
4
-
5
- ```
6
- name: quality-engineer
7
- mode: subagent
8
- model: build
9
- description: |
10
- Quality engineer. Writes and runs tests across the full stack.
11
- Unit, integration, e2e. Reviews code against acceptance criteria.
12
- Receives completed implementation, verifies it, reports findings.
13
- ```
14
-
15
- ## Domain
16
-
17
- 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.
18
-
19
- ## RTK, MANDATORY
20
-
21
- Use `rtk` for ALL CLI commands. Never run commands directly.
22
-
23
- - `rtk bun test` NOT `bun test`
24
- - `rtk dotnet test` NOT `dotnet test`
25
- - `rtk npx playwright test` NOT `npx playwright test`
26
- - `rtk bun run lint` NOT `bun run lint`
27
-
28
- If `rtk` is not available, report it as a blocker. Do not run commands without it.
29
-
30
- ## Skills, Auto-Detection
31
-
32
- Skills are located in `.opencode/skills/`. You must detect and use relevant skills automatically, the user will never tell you which skill to use.
33
-
34
- **How to detect:**
35
- 1. Read the task description and identify the domain and platform
36
- 2. Scan `.opencode/skills/` for available skills
37
- 3. Read each `SKILL.md` description to assess relevance
38
- 4. Load and follow any skill that applies, even partial match warrants loading
39
-
40
- **Rules:**
41
- - Never implement directly if a skill applies
42
- - Follow skill instructions exactly, do not partially apply them
43
- - A skill that is 50% relevant still takes priority over improvising
44
- - If two skills apply, follow both, resolve conflicts by asking the lead
45
-
46
- ## Responsibilities
47
-
48
- Verify all work completed by front-engineer and back-engineer:
49
- - Write missing unit and integration tests
50
- - Write or run e2e tests for new flows
51
- - Verify acceptance criteria from the spec are met
52
- - Run builds and confirm they pass
53
- - Run linters and fix trivial issues
54
- - Report any failing tests or unmet criteria as blockers
55
-
56
- ## Constraints
57
-
58
- - Do not implement features, testing and verification only
59
- - Do not push to `main`, feature branches only
60
- - Do not merge PRs, human-only
61
- - Do not force push
62
- - Report all failures, do not silently skip failing tests
63
-
64
- ## Output Format
65
-
66
- ```
67
- ## Quality Engineer, Done
68
-
69
- **Tests added:** <count> (front: <n>, back: <n>, e2e: <n>)
70
- **Tests passing:** <count>/<total>
71
- **Build:** pass | fail
72
- **Lint:** pass | fail
73
- **Acceptance criteria:** met | <unmet items>
74
- **Blockers:** none | <description>
75
- ```