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.
- package/README.md +44 -32
- package/content/{.opencode → .agents}/agents/.bootstrap/AGENTS.template.md +7 -7
- package/content/{.opencode → .agents}/agents/back-engineer.md +27 -26
- package/content/.agents/agents/devops-manager.md +108 -0
- package/content/.agents/agents/front-engineer.md +73 -0
- package/content/.agents/agents/infra-engineer.md +74 -0
- package/content/.agents/agents/quality-engineer.md +74 -0
- package/content/.agents/agents/security-auditor.md +84 -0
- package/content/.opencode/package-lock.json +3 -3
- package/content/AGENTS.md +1 -1
- package/package.json +1 -1
- package/src/index.js +49 -19
- package/src/steps/__tests__/clean-ai-files.test.js +44 -30
- package/src/steps/check-platform.js +2 -2
- package/src/steps/check-rtk.js +1 -1
- package/src/steps/choose-models.js +141 -0
- package/src/steps/choose-skills-provider.js +52 -33
- package/src/steps/clean-ai-files.js +9 -9
- package/src/steps/copy-content.js +1 -1
- package/src/steps/install-browser.js +19 -27
- package/src/utils/__tests__/copy.test.js +0 -22
- package/src/utils/__tests__/exec.test.js +6 -4
- package/src/utils/copy.js +1 -1
- package/src/utils/exec.js +86 -9
- package/src/utils/models-cache.js +101 -0
- package/content/.opencode/agents/.bootstrap/CUSTOM-AGENT.template.md +0 -24
- package/content/.opencode/agents/devops-manager.md +0 -115
- package/content/.opencode/agents/front-engineer.md +0 -73
- package/content/.opencode/agents/infra-engineer.md +0 -73
- package/content/.opencode/agents/quality-engineer.md +0 -75
- package/content/.opencode/agents/security-auditor.md +0 -85
- package/content/.opencode/commands/.gitkeep +0 -0
- package/src/presets/skills-providers.json +0 -14
- package/src/steps/__tests__/choose-team.test.js +0 -105
- /package/content/{.opencode → .agents}/skills/browser-automation/SKILL.md +0 -0
- /package/content/{.opencode → .agents}/skills/ob-userstory-az/SKILL.md +0 -0
- /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: {
|
|
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()
|
|
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(
|
|
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 (
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|