opencode-onboard 0.1.5 → 0.1.10
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/content/.agents/agents/back-engineer.md +0 -7
- package/content/.agents/agents/devops-manager.md +2 -8
- package/content/.agents/agents/front-engineer.md +0 -7
- package/content/.agents/agents/infra-engineer.md +0 -7
- package/content/.agents/agents/quality-engineer.md +0 -7
- package/content/.agents/agents/security-auditor.md +0 -7
- package/content/.agents/skills/browser-automation/SKILL.md +3 -3
- package/content/.agents/skills/ob-pullrequest-az/SKILL.md +6 -6
- package/content/.agents/skills/ob-pullrequest-gh/SKILL.md +11 -10
- package/content/.agents/skills/ob-userstory-az/SKILL.md +6 -6
- package/content/.agents/skills/ob-userstory-gh/SKILL.md +17 -15
- package/content/.opencode/ensemble.json +1 -5
- package/content/.opencode/opencode.json +2 -2
- package/content/.opencode/plugins/session-log.js +91 -0
- package/content/AGENTS.md +21 -117
- package/package.json +1 -1
- package/src/index.js +10 -7
- package/src/steps/choose-models.js +16 -1
- package/src/steps/clean-ai-files.js +34 -14
- package/src/steps/init-openspec.js +124 -0
- package/src/utils/copy.js +3 -1
|
@@ -72,10 +72,3 @@ Rules:
|
|
|
72
72
|
**Files changed:** <list>
|
|
73
73
|
**Blockers:** none | <description>
|
|
74
74
|
```
|
|
75
|
-
|
|
76
|
-
## Session Log
|
|
77
|
-
|
|
78
|
-
Append to `.agents/session-log.md` (create with header if missing, skip if `session-logging: disabled` in AGENTS.md):
|
|
79
|
-
- On start: `| {ISO timestamp} | back-engineer | started | {task summary} |`
|
|
80
|
-
- On skill load: `| {ISO timestamp} | back-engineer | skill-loaded | {skill-name} |`
|
|
81
|
-
- On done: `| {ISO timestamp} | back-engineer | completed | {files changed count} files |`
|
|
@@ -56,7 +56,7 @@ Rules:
|
|
|
56
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
|
-
4. Read `.agents/session-log.
|
|
59
|
+
4. Read `.agents/session-log.json` if it exists, parse the JSON array and include a "Session Activity" section in the PR description with agent names, task counts, and skills used
|
|
60
60
|
5. Commit and push the feature branch
|
|
61
61
|
6. Create the PR following the skill instructions
|
|
62
62
|
7. Post PR comment with screenshots and change summary
|
|
@@ -74,6 +74,7 @@ Rules:
|
|
|
74
74
|
- Does not merge PRs, human-only
|
|
75
75
|
- Does not approve PRs, human-only
|
|
76
76
|
- Does not force push
|
|
77
|
+
- ALL GitHub and Azure DevOps data MUST come from `gh` or `az` CLI, NEVER use webfetch or HTTP requests to fetch platform URLs, even as a fallback. If CLI is unavailable, report as a blocker.
|
|
77
78
|
- Browser MCP tools permitted only for screenshots of local app on `localhost` URLs, never for navigating GitHub or Azure DevOps
|
|
78
79
|
|
|
79
80
|
## Output Format
|
|
@@ -107,10 +108,3 @@ Rules:
|
|
|
107
108
|
**Questions for human:** <count>, <list>
|
|
108
109
|
**Acknowledged only:** <count>
|
|
109
110
|
```
|
|
110
|
-
|
|
111
|
-
## Session Log
|
|
112
|
-
|
|
113
|
-
Append to `.agents/session-log.md` (create with header if missing, skip if `session-logging: disabled` in AGENTS.md):
|
|
114
|
-
- On start: `| {ISO timestamp} | devops-manager | started | {mode} mode |`
|
|
115
|
-
- On skill load: `| {ISO timestamp} | devops-manager | skill-loaded | {skill-name} |`
|
|
116
|
-
- On done: `| {ISO timestamp} | devops-manager | completed | {summary} |`
|
|
@@ -71,10 +71,3 @@ Rules:
|
|
|
71
71
|
**Files changed:** <list>
|
|
72
72
|
**Blockers:** none | <description>
|
|
73
73
|
```
|
|
74
|
-
|
|
75
|
-
## Session Log
|
|
76
|
-
|
|
77
|
-
Append to `.agents/session-log.md` (create with header if missing, skip if `session-logging: disabled` in AGENTS.md):
|
|
78
|
-
- On start: `| {ISO timestamp} | front-engineer | started | {task summary} |`
|
|
79
|
-
- On skill load: `| {ISO timestamp} | front-engineer | skill-loaded | {skill-name} |`
|
|
80
|
-
- On done: `| {ISO timestamp} | front-engineer | completed | {files changed count} files |`
|
|
@@ -72,10 +72,3 @@ Rules:
|
|
|
72
72
|
**Resources affected:** <list>
|
|
73
73
|
**Blockers:** none | <description>
|
|
74
74
|
```
|
|
75
|
-
|
|
76
|
-
## Session Log
|
|
77
|
-
|
|
78
|
-
Append to `.agents/session-log.md` (create with header if missing, skip if `session-logging: disabled` in AGENTS.md):
|
|
79
|
-
- On start: `| {ISO timestamp} | infra-engineer | started | {task summary} |`
|
|
80
|
-
- On skill load: `| {ISO timestamp} | infra-engineer | skill-loaded | {skill-name} |`
|
|
81
|
-
- On done: `| {ISO timestamp} | infra-engineer | completed | {files changed count} files |`
|
|
@@ -72,10 +72,3 @@ Rules:
|
|
|
72
72
|
**Acceptance criteria:** met | <unmet items>
|
|
73
73
|
**Blockers:** none | <description>
|
|
74
74
|
```
|
|
75
|
-
|
|
76
|
-
## Session Log
|
|
77
|
-
|
|
78
|
-
Append to `.agents/session-log.md` (create with header if missing, skip if `session-logging: disabled` in AGENTS.md):
|
|
79
|
-
- On start: `| {ISO timestamp} | quality-engineer | started | {task summary} |`
|
|
80
|
-
- On skill load: `| {ISO timestamp} | quality-engineer | skill-loaded | {skill-name} |`
|
|
81
|
-
- On done: `| {ISO timestamp} | quality-engineer | completed | {tests added count} tests |`
|
|
@@ -82,10 +82,3 @@ Rules:
|
|
|
82
82
|
|
|
83
83
|
**Blockers:** none | <critical findings that must be resolved before PR>
|
|
84
84
|
```
|
|
85
|
-
|
|
86
|
-
## Session Log
|
|
87
|
-
|
|
88
|
-
Append to `.agents/session-log.md` (create with header if missing, skip if `session-logging: disabled` in AGENTS.md):
|
|
89
|
-
- On start: `| {ISO timestamp} | security-auditor | started | {task summary} |`
|
|
90
|
-
- On skill load: `| {ISO timestamp} | security-auditor | skill-loaded | {skill-name} |`
|
|
91
|
-
- On done: `| {ISO timestamp} | security-auditor | completed | {findings count} findings |`
|
|
@@ -74,6 +74,6 @@ Browser MCP tools are permitted ONLY for interactions with the LOCAL running app
|
|
|
74
74
|
|
|
75
75
|
- ✅ Screenshots of locally running app on `localhost` URLs
|
|
76
76
|
- ✅ Click, type, scroll, query on `localhost` pages
|
|
77
|
-
- ❌ Navigate to external services (github.com, dev.azure.com, npmjs.com, etc.)
|
|
78
|
-
- ❌ Use browser tools for any DevOps or GitHub operations
|
|
79
|
-
- ❌ Use browser tools to read or modify production systems
|
|
77
|
+
- ❌ Navigate to external services (github.com, dev.azure.com, npmjs.com, etc.), FORBIDDEN
|
|
78
|
+
- ❌ Use browser tools for any DevOps or GitHub operations, FORBIDDEN
|
|
79
|
+
- ❌ Use browser tools to read or modify production systems, FORBIDDEN
|
|
@@ -64,7 +64,7 @@ rtk az repos pr create \
|
|
|
64
64
|
--description "{description}"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
### Step 5: Link work item (MANDATORY, run sequentially
|
|
67
|
+
### Step 5: Link work item (MANDATORY, run sequentially, not in parallel)
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
70
|
rtk az repos pr work-item add --id {pr-id} --work-items {workitem-id}
|
|
@@ -151,7 +151,7 @@ rtk az devops invoke \
|
|
|
151
151
|
{
|
|
152
152
|
"comments": [{
|
|
153
153
|
"parentCommentId": 1,
|
|
154
|
-
"content": "Acknowledged
|
|
154
|
+
"content": "Acknowledged, applying this change now.",
|
|
155
155
|
"commentType": 1
|
|
156
156
|
}]
|
|
157
157
|
}
|
|
@@ -164,7 +164,7 @@ rtk az devops invoke \
|
|
|
164
164
|
- ✅ Commit and push to feature branches only
|
|
165
165
|
- ✅ Create and comment on PRs via az CLI
|
|
166
166
|
- ✅ Screenshots of localhost only via browser_screenshot
|
|
167
|
-
- ❌ Commit or push to `main
|
|
168
|
-
- ❌ Force push
|
|
169
|
-
- ❌ Merge or approve PRs
|
|
170
|
-
- ❌ Navigate browser to dev.azure.com
|
|
167
|
+
- ❌ Commit or push to `main`, FORBIDDEN
|
|
168
|
+
- ❌ Force push, FORBIDDEN
|
|
169
|
+
- ❌ Merge or approve PRs, human-only
|
|
170
|
+
- ❌ Navigate browser to dev.azure.com, FORBIDDEN
|
|
@@ -18,8 +18,8 @@ Use `rtk` wrapper for ALL CLI commands:
|
|
|
18
18
|
- `rtk gh pr comment` NOT `gh pr comment`
|
|
19
19
|
- `rtk gh api` NOT `gh api`
|
|
20
20
|
|
|
21
|
-
**
|
|
22
|
-
|
|
21
|
+
**ALL GitHub data MUST come from `gh` CLI. NEVER use webfetch, HTTP requests, or browser MCP tools for GitHub operations, even if gh CLI fails. If `gh` is unavailable, report as a blocker.**
|
|
22
|
+
Always pass `--repo {owner}/{repo}` explicitly, never rely on git context to resolve the repo.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
@@ -85,13 +85,13 @@ Triggered when user says "I've added comments to the PR" or "check PR feedback".
|
|
|
85
85
|
|
|
86
86
|
If PR link provided, extract number from URL. Otherwise:
|
|
87
87
|
```bash
|
|
88
|
-
rtk gh pr list --state open --limit 1
|
|
88
|
+
rtk gh pr list --repo {owner}/{repo} --state open --limit 1
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
### Step 2: Read comment threads
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
rtk gh pr view {pr-number} --comments
|
|
94
|
+
rtk gh pr view {pr-number} --repo {owner}/{repo} --comments
|
|
95
95
|
# Or structured output:
|
|
96
96
|
rtk gh api repos/{owner}/{repo}/pulls/{pr-number}/comments
|
|
97
97
|
rtk gh api repos/{owner}/{repo}/pulls/{pr-number}/reviews
|
|
@@ -121,7 +121,7 @@ Update: `openspec/changes/{change}/proposal.md`, `design.md`, or `tasks.md` as a
|
|
|
121
121
|
# Reply to a review comment
|
|
122
122
|
rtk gh api repos/{owner}/{repo}/pulls/{pr-number}/comments/{comment-id}/replies \
|
|
123
123
|
--method POST \
|
|
124
|
-
--field body="Acknowledged
|
|
124
|
+
--field body="Acknowledged, applying this change now."
|
|
125
125
|
|
|
126
126
|
# Or post a general PR comment
|
|
127
127
|
rtk gh pr comment {pr-number} --body "Updated design.md to reflect feedback."
|
|
@@ -132,9 +132,10 @@ rtk gh pr comment {pr-number} --body "Updated design.md to reflect feedback."
|
|
|
132
132
|
## Guardrails
|
|
133
133
|
|
|
134
134
|
- ✅ Commit and push to feature branches only
|
|
135
|
-
- ✅ Create and comment on PRs via gh CLI
|
|
135
|
+
- ✅ Create and comment on PRs via gh CLI with explicit `--repo {owner}/{repo}`
|
|
136
136
|
- ✅ Screenshots of localhost only via browser_screenshot
|
|
137
|
-
- ❌ Commit or push to `main
|
|
138
|
-
- ❌ Force push
|
|
139
|
-
- ❌ Merge or approve PRs
|
|
140
|
-
- ❌ Navigate browser to github.com
|
|
137
|
+
- ❌ Commit or push to `main`, FORBIDDEN
|
|
138
|
+
- ❌ Force push, FORBIDDEN
|
|
139
|
+
- ❌ Merge or approve PRs, human-only
|
|
140
|
+
- ❌ Navigate browser to github.com, FORBIDDEN
|
|
141
|
+
- ❌ webfetch or HTTP requests to GitHub URLs, FORBIDDEN
|
|
@@ -156,17 +156,17 @@ https://dev.azure.com/{org}/{project}/_git/{repo}/pullrequest/{pr-id}
|
|
|
156
156
|
**State:** {state}
|
|
157
157
|
|
|
158
158
|
**Change Created:** us-{id}-{slug}
|
|
159
|
-
|
|
160
|
-
### Next Steps
|
|
161
|
-
1. Review the proposal
|
|
162
|
-
2. Say "implement the plan" to start implementation
|
|
163
159
|
```
|
|
164
160
|
|
|
161
|
+
After outputting the above, the lead MUST run `/opsx-propose` to generate the proposal, specs, and tasks. After `/opsx-propose` completes, STOP and ask the user: **"Ready to implement? (yes/no)"**, do NOT proceed to `/opsx-apply` until confirmed.
|
|
162
|
+
|
|
165
163
|
---
|
|
166
164
|
|
|
167
165
|
## Guardrails
|
|
168
166
|
|
|
169
167
|
- ✅ Parse Azure DevOps URL and create OpenSpec change
|
|
170
168
|
- ✅ Use `rtk` for all Azure CLI operations
|
|
171
|
-
-
|
|
172
|
-
-
|
|
169
|
+
- ✅ Always run `/opsx-propose` after parsing, never skip to implementation
|
|
170
|
+
- ✅ Always stop and confirm with user after propose, before running `/opsx-apply`
|
|
171
|
+
- ❌ Browser MCP tools for Azure DevOps operations, FORBIDDEN
|
|
172
|
+
- ❌ Jump to implementation without user confirmation, FORBIDDEN
|
|
@@ -16,7 +16,7 @@ Use `rtk` wrapper for ALL CLI commands:
|
|
|
16
16
|
- `rtk gh issue edit` NOT `gh issue edit`
|
|
17
17
|
- `rtk openspec new change` NOT `openspec new change`
|
|
18
18
|
|
|
19
|
-
**
|
|
19
|
+
**ALL GitHub data MUST come from `gh` CLI. NEVER use webfetch, HTTP requests, or browser MCP tools to fetch GitHub URLs, even if gh CLI fails. If `gh` is unavailable, report it as a blocker.**
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -36,13 +36,14 @@ gh auth status
|
|
|
36
36
|
|
|
37
37
|
## Steps
|
|
38
38
|
|
|
39
|
-
1. **Extract
|
|
40
|
-
- `https://github.com/{owner}/{repo}/issues/42` → number: 42
|
|
39
|
+
1. **Extract owner, repo, and issue number** from URL
|
|
40
|
+
- `https://github.com/{owner}/{repo}/issues/42` → owner: `{owner}`, repo: `{repo}`, number: `42`
|
|
41
41
|
|
|
42
|
-
2. **Fetch Issue
|
|
42
|
+
2. **Fetch Issue**, always pass `--repo` explicitly, never rely on git context:
|
|
43
43
|
```bash
|
|
44
|
-
rtk gh issue view 42 --json number,title,body,labels,milestone,state
|
|
44
|
+
rtk gh issue view 42 --repo {owner}/{repo} --json number,title,body,labels,milestone,state
|
|
45
45
|
```
|
|
46
|
+
If this returns an auth error or 404, report as a blocker, do NOT fall back to webfetch or web search.
|
|
46
47
|
|
|
47
48
|
3. **Extract Key Fields** from JSON response:
|
|
48
49
|
- `number` → Issue number
|
|
@@ -61,18 +62,18 @@ gh auth status
|
|
|
61
62
|
|
|
62
63
|
## Full GitHub CLI Reference
|
|
63
64
|
|
|
64
|
-
Use these for ALL GitHub operations, browser MCP
|
|
65
|
+
Use these for ALL GitHub operations, browser MCP and webfetch are FORBIDDEN. Always pass `--repo {owner}/{repo}`, never rely on git context.
|
|
65
66
|
|
|
66
67
|
### Issues
|
|
67
68
|
```bash
|
|
68
69
|
# Read issue
|
|
69
|
-
rtk gh issue view <number>
|
|
70
|
+
rtk gh issue view <number> --repo {owner}/{repo}
|
|
70
71
|
|
|
71
72
|
# List open issues
|
|
72
|
-
rtk gh issue list --state open --limit 10
|
|
73
|
+
rtk gh issue list --repo {owner}/{repo} --state open --limit 10
|
|
73
74
|
|
|
74
75
|
# Update issue
|
|
75
|
-
rtk gh issue edit <number> --add-label "in-progress"
|
|
76
|
+
rtk gh issue edit <number> --repo {owner}/{repo} --add-label "in-progress"
|
|
76
77
|
```
|
|
77
78
|
|
|
78
79
|
---
|
|
@@ -119,17 +120,18 @@ https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}
|
|
|
119
120
|
**Milestone:** {milestone}
|
|
120
121
|
|
|
121
122
|
**Change Created:** gh-{number}-{slug}
|
|
122
|
-
|
|
123
|
-
### Next Steps
|
|
124
|
-
1. Review the proposal
|
|
125
|
-
2. Say "implement the plan" to start implementation
|
|
126
123
|
```
|
|
127
124
|
|
|
125
|
+
After outputting the above, the lead MUST run `/opsx-propose` to generate the proposal, specs, and tasks. After `/opsx-propose` completes, STOP and ask the user: **"Ready to implement? (yes/no)"**, do NOT proceed to `/opsx-apply` until confirmed.
|
|
126
|
+
|
|
128
127
|
---
|
|
129
128
|
|
|
130
129
|
## Guardrails
|
|
131
130
|
|
|
132
131
|
- ✅ Parse GitHub Issue URL and create OpenSpec change
|
|
133
132
|
- ✅ Use `rtk gh` for all GitHub CLI operations
|
|
134
|
-
-
|
|
135
|
-
-
|
|
133
|
+
- ✅ Always run `/opsx-propose` after parsing, never skip to implementation
|
|
134
|
+
- ✅ Always stop and confirm with user after propose, before running `/opsx-apply`
|
|
135
|
+
- ❌ `webfetch` or HTTP requests to GitHub URLs, FORBIDDEN, use `gh` CLI only
|
|
136
|
+
- ❌ Browser MCP tools for GitHub operations, FORBIDDEN
|
|
137
|
+
- ❌ Jump to implementation without user confirmation, FORBIDDEN
|
|
@@ -3,10 +3,6 @@
|
|
|
3
3
|
"mergeOnCleanup": true,
|
|
4
4
|
"timeoutMs": 1800000,
|
|
5
5
|
"stallThresholdMs": 300000,
|
|
6
|
-
"
|
|
7
|
-
"modelsByAgent": {
|
|
8
|
-
"build": "anthropic/claude-sonnet-4-6",
|
|
9
|
-
"explore": "anthropic/claude-haiku-4.5"
|
|
10
|
-
},
|
|
6
|
+
"modelsByAgent": {},
|
|
11
7
|
"modelAssignment": "default"
|
|
12
8
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
const LOG_FILE = ".agents/session-log.json"
|
|
5
|
+
|
|
6
|
+
// Per-session state: editCount and skills loaded
|
|
7
|
+
const sessionState = new Map()
|
|
8
|
+
|
|
9
|
+
function ts() {
|
|
10
|
+
return new Date().toISOString()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function appendEntry(directory, entry) {
|
|
14
|
+
const logPath = path.join(directory, LOG_FILE)
|
|
15
|
+
const dir = path.dirname(logPath)
|
|
16
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
17
|
+
|
|
18
|
+
let entries = []
|
|
19
|
+
if (fs.existsSync(logPath)) {
|
|
20
|
+
try { entries = JSON.parse(fs.readFileSync(logPath, "utf8")) } catch (_) {}
|
|
21
|
+
}
|
|
22
|
+
entries.push(entry)
|
|
23
|
+
fs.writeFileSync(logPath, JSON.stringify(entries, null, 2), "utf8")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveAgentName(session) {
|
|
27
|
+
const agentPath = session?.agent
|
|
28
|
+
if (agentPath) {
|
|
29
|
+
const base = path.basename(agentPath, ".md")
|
|
30
|
+
if (base) return base
|
|
31
|
+
}
|
|
32
|
+
return "lead"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const SessionLogPlugin = async ({ client, directory }) => {
|
|
36
|
+
return {
|
|
37
|
+
event: async ({ event }) => {
|
|
38
|
+
try {
|
|
39
|
+
if (event?.type === "session.created") {
|
|
40
|
+
const sessionId = event.properties?.id ?? event.properties?.sessionID
|
|
41
|
+
if (!sessionId) return
|
|
42
|
+
|
|
43
|
+
const res = await client.session.get({ path: { id: sessionId } })
|
|
44
|
+
const session = res?.data
|
|
45
|
+
const agentName = resolveAgentName(session)
|
|
46
|
+
|
|
47
|
+
sessionState.set(sessionId, { agentName, editCount: 0, skills: [] })
|
|
48
|
+
appendEntry(directory, { ts: ts(), agent: agentName, action: "started", sessionId })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (event?.type === "file.edited") {
|
|
52
|
+
const sessionId = event.properties?.sessionID ?? event.properties?.id
|
|
53
|
+
if (!sessionId) return
|
|
54
|
+
const state = sessionState.get(sessionId)
|
|
55
|
+
if (state) state.editCount++
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (event?.type === "session.idle") {
|
|
59
|
+
const sessionId = event.properties?.id ?? event.properties?.sessionID
|
|
60
|
+
if (!sessionId) return
|
|
61
|
+
const state = sessionState.get(sessionId)
|
|
62
|
+
if (!state) return
|
|
63
|
+
|
|
64
|
+
const { agentName, editCount, skills } = state
|
|
65
|
+
appendEntry(directory, { ts: ts(), agent: agentName, action: "completed", filesEdited: editCount, skills })
|
|
66
|
+
sessionState.delete(sessionId)
|
|
67
|
+
}
|
|
68
|
+
} catch (_) {}
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
"tool.execute.after": async (input, output) => {
|
|
72
|
+
try {
|
|
73
|
+
const sessionId = input?.sessionID ?? input?.session_id
|
|
74
|
+
if (!sessionId) return
|
|
75
|
+
|
|
76
|
+
const state = sessionState.get(sessionId)
|
|
77
|
+
if (!state) return
|
|
78
|
+
|
|
79
|
+
if (input?.tool === "read") {
|
|
80
|
+
const filePath = input?.args?.filePath ?? ""
|
|
81
|
+
const match = filePath.match(/[/\\]skills[/\\]([^/\\]+)[/\\]SKILL\.md$/i)
|
|
82
|
+
if (match) {
|
|
83
|
+
const skillName = match[1]
|
|
84
|
+
if (!state.skills.includes(skillName)) state.skills.push(skillName)
|
|
85
|
+
appendEntry(directory, { ts: ts(), agent: state.agentName, action: "skill-loaded", skill: skillName })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch (_) {}
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
}
|
package/content/AGENTS.md
CHANGED
|
@@ -35,99 +35,35 @@ openspec archive "project-history"
|
|
|
35
35
|
|
|
36
36
|
### Step 2, Generate DESIGN.md
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
`DESIGN.md` contains a prompt. You MUST follow this exact sequence, do not skip or reorder steps:
|
|
39
39
|
|
|
40
|
-
1.
|
|
41
|
-
2.
|
|
42
|
-
3.
|
|
43
|
-
4.
|
|
40
|
+
1. **Read `DESIGN.md` now** using a file read tool. The file contains a prompt with instructions and an output format.
|
|
41
|
+
2. **Store the full prompt text** in your context.
|
|
42
|
+
3. **Overwrite `DESIGN.md` with an empty string** (zero bytes). Do this before generating any content.
|
|
43
|
+
4. **Analyze the actual codebase**: read CSS files, Tailwind config, component files, token definitions. Do not rely on prior knowledge, read the files.
|
|
44
|
+
5. **Write the result into `DESIGN.md`** following exactly the format and sections described in the stored prompt.
|
|
44
45
|
|
|
45
|
-
The output must be a real, populated `DESIGN.md
|
|
46
|
+
The output must be a real, populated `DESIGN.md` based on what you found in the codebase, not from memory or assumptions.
|
|
46
47
|
|
|
47
48
|
---
|
|
48
49
|
|
|
49
50
|
### Step 3, Generate ARCHITECTURE.md
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
`ARCHITECTURE.md` contains a prompt. You MUST follow this exact sequence, do not skip or reorder steps:
|
|
52
53
|
|
|
53
|
-
1.
|
|
54
|
-
2.
|
|
55
|
-
3.
|
|
56
|
-
4.
|
|
54
|
+
1. **Read `ARCHITECTURE.md` now** using a file read tool. The file contains a prompt with instructions and an output format.
|
|
55
|
+
2. **Store the full prompt text** in your context.
|
|
56
|
+
3. **Overwrite `ARCHITECTURE.md` with an empty string** (zero bytes). Do this before generating any content.
|
|
57
|
+
4. **Analyze the actual codebase**: read folder structure, config files, route definitions, data models, integration points. Do not rely on prior knowledge, read the files.
|
|
58
|
+
5. **Write the result into `ARCHITECTURE.md`** following exactly the format and sections described in the stored prompt.
|
|
57
59
|
|
|
58
|
-
The output must be a real, populated `ARCHITECTURE.md`
|
|
60
|
+
The output must be a real, populated `ARCHITECTURE.md` based on what you found in the codebase, covering all sections the prompt describes.
|
|
59
61
|
|
|
60
62
|
---
|
|
61
63
|
|
|
62
64
|
### Step 4, Rewrite this file
|
|
63
65
|
|
|
64
|
-
Replace the entire contents of this file (`AGENTS.md`) with everything below the line `<!-- AGENTS-TEMPLATE-START -->` in this same file. Delete the bootstrap section and the template marker
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
### Step 4b, Patch opsx-apply for ensemble
|
|
69
|
-
|
|
70
|
-
Read `.opencode/commands/opsx-apply.md`. Find the step that instructs the agent to **implement tasks directly** — it will contain phrases like "make the code changes", "implement tasks", "loop until done or blocked". This step tells the agent to write code itself.
|
|
71
|
-
|
|
72
|
-
**Replace that step and everything after it** (completion output, pause output, guardrails, fluid workflow) with the following:
|
|
73
|
-
|
|
74
|
-
```markdown
|
|
75
|
-
6. **Implement via ensemble team**
|
|
76
|
-
|
|
77
|
-
NEVER implement tasks directly. Always delegate to specialists via ensemble.
|
|
78
|
-
|
|
79
|
-
a. Create feature branch if not already on one: `feature/{id}-{slug}`
|
|
80
|
-
b. Create team:
|
|
81
|
-
```
|
|
82
|
-
team_create "<change-name>"
|
|
83
|
-
```
|
|
84
|
-
Announce: "Team running. Monitor at http://localhost:4747/"
|
|
85
|
-
|
|
86
|
-
c. Spawn only what the tasks require (in parallel):
|
|
87
|
-
```
|
|
88
|
-
team_spawn name:front agent:front-engineer → UI/frontend tasks
|
|
89
|
-
team_spawn name:back agent:back-engineer → backend/API tasks
|
|
90
|
-
team_spawn name:infra agent:infra-engineer → infra/pipeline tasks
|
|
91
|
-
```
|
|
92
|
-
Pass each specialist: their relevant tasks + all context file paths from step above.
|
|
93
|
-
Log each spawn to `.agents/session-log.md` (see Session Log section).
|
|
94
|
-
|
|
95
|
-
d. Wait for all → `team_results` → `team_shutdown` + `team_merge`
|
|
96
|
-
|
|
97
|
-
7. **Quality check**
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
team_spawn name:quality agent:quality-engineer
|
|
101
|
-
```
|
|
102
|
-
Wait → `team_results` → fix blockers → `team_shutdown`
|
|
103
|
-
|
|
104
|
-
8. **Mark tasks complete in openspec**
|
|
105
|
-
|
|
106
|
-
After specialists finish, update the tasks file: `- [ ]` → `- [x]` for each completed task.
|
|
107
|
-
Run `openspec status --change "<name>" --json` to confirm progress.
|
|
108
|
-
|
|
109
|
-
9. **On completion or pause, show status**
|
|
110
|
-
|
|
111
|
-
Display:
|
|
112
|
-
- Tasks completed this session
|
|
113
|
-
- Overall progress: "N/M tasks complete"
|
|
114
|
-
- If all done: suggest archive with `/opsx-archive`
|
|
115
|
-
- If paused: explain why and wait for guidance
|
|
116
|
-
|
|
117
|
-
Then run `team_cleanup`.
|
|
118
|
-
|
|
119
|
-
**Guardrails**
|
|
120
|
-
- NEVER implement tasks directly — always use `team_create` + `team_spawn`
|
|
121
|
-
- Always read context files before spawning (from the apply instructions output)
|
|
122
|
-
- Always pass context file paths and task list to spawned specialists
|
|
123
|
-
- Mark tasks complete in openspec AFTER specialists finish, not before
|
|
124
|
-
- If task is ambiguous, pause and ask before spawning
|
|
125
|
-
- If implementation reveals issues, pause and suggest artifact updates
|
|
126
|
-
- Pause on errors, blockers, or unclear requirements — don't guess
|
|
127
|
-
- Use contextFiles from CLI output, don't assume specific file names
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Keep all steps before the implementation step unchanged — they are openspec's domain (select change, check status, get instructions, read context, show progress).
|
|
66
|
+
Replace the entire contents of this file (`AGENTS.md`) with everything below the line `<!-- AGENTS-TEMPLATE-START -->` in this same file. Delete the bootstrap section and the template marker, the file should contain only the template content when done.
|
|
131
67
|
|
|
132
68
|
---
|
|
133
69
|
|
|
@@ -153,6 +89,7 @@ You're ready to work.
|
|
|
153
89
|
- Do NOT implement any features
|
|
154
90
|
- Do NOT create branches or PRs
|
|
155
91
|
- Do NOT modify any project source files
|
|
92
|
+
- Do NOT create RTK files, scripts, or wrappers, RTK is already defined in AGENTS.md and agent files
|
|
156
93
|
- Only read source files for analysis, write only to ARCHITECTURE.md, DESIGN.md, AGENTS.md, and openspec/
|
|
157
94
|
|
|
158
95
|
<!-- AGENTS-TEMPLATE-START -->
|
|
@@ -179,7 +116,7 @@ Trigger patterns:
|
|
|
179
116
|
- `implement the plan` → run `/opsx-apply` (ensemble orchestration is built into the command) → ship
|
|
180
117
|
- `I've added comments to the PR` → spawn `devops-manager` in feedback mode → fix → update PR
|
|
181
118
|
|
|
182
|
-
**Never delegate without a plan. Never write implementation code directly, always spawn specialists.**
|
|
119
|
+
**Never delegate without a plan. Never write implementation code directly, always spawn specialists, no exceptions. "Small feature", "faster to do it directly", or "environment issues" are not valid reasons to skip ensemble.**
|
|
183
120
|
|
|
184
121
|
## Multi-Agent Execution, opencode-ensemble
|
|
185
122
|
|
|
@@ -233,15 +170,13 @@ devops-manager (ship mode)
|
|
|
233
170
|
2. Load skill: openspec-propose → generate proposal.md, specs/, tasks.md
|
|
234
171
|
- team_create → spawn design + specs in parallel → merge → write tasks.md
|
|
235
172
|
3. Show the plan: change name, schema, total tasks, task list summary
|
|
236
|
-
4. STOP. Ask user: "Ready to implement? (yes/no)"
|
|
173
|
+
4. STOP. Ask user: "Ready to implement? (yes/no)", DO NOT proceed until confirmed.
|
|
237
174
|
```
|
|
238
175
|
|
|
239
176
|
### Phase 2, Implement
|
|
240
177
|
|
|
241
178
|
```
|
|
242
|
-
1. Run /opsx-apply
|
|
243
|
-
The command handles context reading, ensemble orchestration, and task marking automatically.
|
|
244
|
-
DO NOT implement tasks directly — the command spawns specialists via ensemble.
|
|
179
|
+
1. Run /opsx-apply, handles context reading, ensemble orchestration, and task marking.
|
|
245
180
|
2. After /opsx-apply completes, proceed to quality check.
|
|
246
181
|
```
|
|
247
182
|
|
|
@@ -309,7 +244,7 @@ Skills are located in `.agents/skills/`. Each skill has a `SKILL.md` with a desc
|
|
|
309
244
|
| `openspec-propose` | Propose change artifacts (proposal, specs, tasks) |
|
|
310
245
|
| `openspec-apply-change` | Implement change with agent team |
|
|
311
246
|
| `openspec-archive-change` | Archive completed change |
|
|
312
|
-
| `browser-automation` | Browser automation for localhost UI
|
|
247
|
+
| `browser-automation` | Browser automation for localhost UI, screenshots, clicks, queries |
|
|
313
248
|
|
|
314
249
|
---
|
|
315
250
|
|
|
@@ -339,37 +274,6 @@ Example: `feature/42-add-user-auth`
|
|
|
339
274
|
|
|
340
275
|
---
|
|
341
276
|
|
|
342
|
-
## Session Log
|
|
343
|
-
|
|
344
|
-
<!-- session-logging: enabled -->
|
|
345
|
-
|
|
346
|
-
All agents MUST log their activity to `.agents/session-log.md`. This file is gitignored and temporary.
|
|
347
|
-
|
|
348
|
-
**Check before logging:** Read the `session-logging` comment above. If it says `disabled`, skip all logging.
|
|
349
|
-
|
|
350
|
-
**On first write per session**, create the file with the header:
|
|
351
|
-
```markdown
|
|
352
|
-
# Session Log
|
|
353
|
-
|
|
354
|
-
| Timestamp | Agent | Action | Detail |
|
|
355
|
-
|-----------|-------|--------|--------|
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
**Log these events** by appending a row:
|
|
359
|
-
- Lead spawns an agent → `| {ISO timestamp} | lead | spawned | {agent-name} for {purpose} |`
|
|
360
|
-
- Agent starts → `| {ISO timestamp} | {agent-name} | started | {task summary} |`
|
|
361
|
-
- Agent loads a skill → `| {ISO timestamp} | {agent-name} | skill-loaded | {skill-name} |`
|
|
362
|
-
- Agent completes → `| {ISO timestamp} | {agent-name} | completed | {files changed count} files |`
|
|
363
|
-
- Agent blocked → `| {ISO timestamp} | {agent-name} | blocked | {reason} |`
|
|
364
|
-
|
|
365
|
-
**Rules:**
|
|
366
|
-
- Append only, never overwrite previous entries
|
|
367
|
-
- One row per event, keep detail column short
|
|
368
|
-
- Use ISO 8601 timestamps
|
|
369
|
-
- The file is gitignored — never commit it
|
|
370
|
-
|
|
371
|
-
---
|
|
372
|
-
|
|
373
277
|
## RTK
|
|
374
278
|
|
|
375
279
|
Use `rtk` wrapper for ALL CLI commands. Never run git, az, gh, or openspec commands directly.
|
|
@@ -401,7 +305,7 @@ Agents CANNOT:
|
|
|
401
305
|
|
|
402
306
|
### Platform CLI
|
|
403
307
|
|
|
404
|
-
ALL platform interactions via CLI only. Browser MCP FORBIDDEN for any DevOps or GitHub operation.
|
|
308
|
+
ALL platform interactions via CLI only. Browser MCP and webfetch FORBIDDEN for any DevOps or GitHub operation, use `gh` or `az` CLI exclusively, never fall back to HTTP requests.
|
|
405
309
|
|
|
406
310
|
| Operation | Azure DevOps | GitHub |
|
|
407
311
|
|-----------|-------------|--------|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import chalk from 'chalk'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
3
4
|
import { checkEnv } from './steps/check-env.js'
|
|
4
5
|
import { checkPlatform } from './steps/check-platform.js'
|
|
5
6
|
import { checkRtk } from './steps/check-rtk.js'
|
|
@@ -13,22 +14,24 @@ import { installBrowser } from './steps/install-browser.js'
|
|
|
13
14
|
|
|
14
15
|
if (process.stdout.isTTY) console.clear()
|
|
15
16
|
console.log()
|
|
17
|
+
const require = createRequire(import.meta.url)
|
|
18
|
+
const { version } = require('../package.json')
|
|
16
19
|
const logo = chalk.hex('#fe3d57')
|
|
17
20
|
const bannerLines = [
|
|
18
|
-
logo('
|
|
21
|
+
logo(' '),
|
|
19
22
|
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒ '),
|
|
20
23
|
logo(' ▒▒▓ ▓▒▓ '),
|
|
21
24
|
logo(' ▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒ '),
|
|
22
25
|
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
23
26
|
logo(' ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓ '),
|
|
24
|
-
logo('
|
|
27
|
+
logo(' ▓▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▓▓ '),
|
|
25
28
|
logo(' ▓▓▓▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▓▓▓▓ '),
|
|
26
|
-
logo('
|
|
27
|
-
logo(' ▓▒▒▒▒▒▒░▓▒▒▓▒▓▒▒▒▒▒▒▒▒▒▓▓ '),
|
|
29
|
+
logo(' ▓▓▒▒▒▒▒▒░▒▒▒▒▒▒▒░▒▒▒▒▒▒▓▓ '),
|
|
28
30
|
logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
29
|
-
logo('
|
|
31
|
+
logo(' ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ '),
|
|
32
|
+
logo(' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ '),
|
|
30
33
|
'',
|
|
31
|
-
chalk.bold(' 🧰 opencode-onboard'),
|
|
34
|
+
chalk.bold(' 🧰 opencode-onboard') + chalk.dim(` v${version}`),
|
|
32
35
|
chalk.dim(' Prepare your codebase for AI agents'),
|
|
33
36
|
]
|
|
34
37
|
|
|
@@ -51,7 +54,7 @@ if (process.stdin.isTTY) {
|
|
|
51
54
|
})
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
try {
|
|
57
|
+
try {
|
|
55
58
|
// 1. Check Node + pnpm
|
|
56
59
|
await checkEnv()
|
|
57
60
|
|
|
@@ -132,7 +132,22 @@ export async function chooseModels() {
|
|
|
132
132
|
const config = await fse.readJson(opencodeJsonPath)
|
|
133
133
|
config.model = planModel
|
|
134
134
|
await fse.writeJson(opencodeJsonPath, config, { spaces: 2 })
|
|
135
|
-
success(`plan model
|
|
135
|
+
success(`plan model -> ${planModel} (written to .opencode/opencode.json)`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Write build and fast models to ensemble.json
|
|
139
|
+
const ensembleJsonPath = path.join(process.cwd(), '.opencode', 'ensemble.json')
|
|
140
|
+
if (await fse.pathExists(ensembleJsonPath)) {
|
|
141
|
+
const ensemble = await fse.readJson(ensembleJsonPath)
|
|
142
|
+
delete ensemble.defaultModel
|
|
143
|
+
ensemble.modelsByAgent = {
|
|
144
|
+
...ensemble.modelsByAgent,
|
|
145
|
+
build: buildModel,
|
|
146
|
+
explore: fastModel,
|
|
147
|
+
}
|
|
148
|
+
await fse.writeJson(ensembleJsonPath, ensemble, { spaces: 2 })
|
|
149
|
+
success(`build model -> ${buildModel} (written to .opencode/ensemble.json)`)
|
|
150
|
+
success(`fast model -> ${fastModel} (written to .opencode/ensemble.json)`)
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
console.log()
|
|
@@ -3,25 +3,45 @@ import path from 'path'
|
|
|
3
3
|
import { findAiFiles } from '../utils/copy.js'
|
|
4
4
|
import { header, info, prompt, success, warn } from '../utils/exec.js'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Enumerate immediate children of a directory, returning their absolute paths.
|
|
8
|
+
* Skips any entry named 'skills' at any level to preserve user-installed skills.
|
|
9
|
+
*/
|
|
10
|
+
async function childrenExcludingSkills(dir) {
|
|
11
|
+
const results = []
|
|
12
|
+
if (!await fse.pathExists(dir)) return results
|
|
13
|
+
const entries = await fse.readdir(dir)
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
if (entry === 'skills') continue
|
|
16
|
+
results.push(path.join(dir, entry))
|
|
17
|
+
}
|
|
18
|
+
return results
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
export async function cleanAiFiles() {
|
|
7
22
|
header('Step 2, Existing AI config files')
|
|
8
23
|
|
|
9
24
|
const cwd = process.cwd()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
25
|
+
|
|
26
|
+
// Flat AI config files (not directories)
|
|
27
|
+
const flatFiles = await findAiFiles(cwd)
|
|
28
|
+
|
|
29
|
+
// For directory targets (.opencode, .agents), enumerate children and skip skills/
|
|
30
|
+
const dirTargets = ['.opencode', '.agents']
|
|
31
|
+
const dirEntries = []
|
|
32
|
+
for (const dirName of dirTargets) {
|
|
33
|
+
const dirPath = path.join(cwd, dirName)
|
|
34
|
+
const children = await childrenExcludingSkills(dirPath)
|
|
35
|
+
dirEntries.push(...children)
|
|
22
36
|
}
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
// Remove the directory targets themselves from flat list (we handle them via children)
|
|
39
|
+
const filteredFlat = flatFiles.filter(f => {
|
|
40
|
+
const rel = path.relative(cwd, f)
|
|
41
|
+
return !dirTargets.includes(rel)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const allFiles = [...filteredFlat, ...dirEntries]
|
|
25
45
|
|
|
26
46
|
if (allFiles.length === 0) {
|
|
27
47
|
success('No existing AI config files found')
|
|
@@ -33,7 +53,7 @@ export async function cleanAiFiles() {
|
|
|
33
53
|
info(f.replace(cwd, '.'))
|
|
34
54
|
}
|
|
35
55
|
console.log()
|
|
36
|
-
prompt('Press Enter to remove them all (
|
|
56
|
+
prompt('Press Enter to remove them all (skills/ folders will be kept)')
|
|
37
57
|
console.log()
|
|
38
58
|
|
|
39
59
|
await new Promise(resolve => {
|
|
@@ -1,6 +1,114 @@
|
|
|
1
1
|
import { execa } from 'execa'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
2
4
|
import { error, header, success, warn } from '../utils/exec.js'
|
|
3
5
|
|
|
6
|
+
const ENSEMBLE_PATCH = `6. **Implement via ensemble team**
|
|
7
|
+
|
|
8
|
+
NEVER implement tasks directly. Always delegate to specialists via ensemble.
|
|
9
|
+
Do NOT touch any source files before the team is running, not even a single edit.
|
|
10
|
+
|
|
11
|
+
a. Create feature branch if not already on one: \`feature/{id}-{slug}\`
|
|
12
|
+
|
|
13
|
+
b. Clean up any stale team state, then create the team:
|
|
14
|
+
\`\`\`
|
|
15
|
+
team_cleanup force:true acknowledge_uncommitted:true (ignore "not in a team" errors)
|
|
16
|
+
team_create "<change-name>"
|
|
17
|
+
\`\`\`
|
|
18
|
+
Announce: "Team running. Monitor at http://localhost:4747/"
|
|
19
|
+
|
|
20
|
+
c. Add all tasks to the shared board so progress is visible in the dashboard.
|
|
21
|
+
Tasks take { content, priority } only, no assignee field:
|
|
22
|
+
\`\`\`
|
|
23
|
+
team_tasks_add tasks:[
|
|
24
|
+
{ content: "1.1 <task description>", priority: "high" },
|
|
25
|
+
{ content: "1.2 <task description>", priority: "high" },
|
|
26
|
+
...one entry per task from the tasks.md checklist...
|
|
27
|
+
]
|
|
28
|
+
\`\`\`
|
|
29
|
+
team_tasks_add returns the task IDs, save them to pass to each agent.
|
|
30
|
+
|
|
31
|
+
d. Spawn specialists one at a time (wait for each team_spawn result before calling the next,
|
|
32
|
+
this avoids git worktree contention). Include the FULL task list,
|
|
33
|
+
ALL context file paths, and the task IDs for their assigned tasks directly in each spawn prompt.
|
|
34
|
+
Agents must call team_claim on each task ID before starting it, then team_tasks_complete when done:
|
|
35
|
+
\`\`\`
|
|
36
|
+
team_spawn name:back agent:back-engineer prompt:"<full task list + context file paths + architecture notes + task IDs for your tasks + call team_claim before each task, team_tasks_complete after + report back when done or blocked>"
|
|
37
|
+
team_spawn name:front agent:front-engineer prompt:"<full task list + context file paths + architecture notes + task IDs for your tasks + call team_claim before each task, team_tasks_complete after + report back when done or blocked>"
|
|
38
|
+
team_spawn name:infra agent:infra-engineer prompt:"<full task list + context file paths + architecture notes + task IDs for your tasks + call team_claim before each task, team_tasks_complete after + report back when done or blocked>"
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
e. Wait for all → \`team_results\` → \`team_shutdown\` + \`team_merge\`
|
|
42
|
+
|
|
43
|
+
7. **Quality check**
|
|
44
|
+
|
|
45
|
+
\`\`\`
|
|
46
|
+
team_spawn name:quality agent:quality-engineer prompt:"<task list, context files, run tests + build + lint + verify acceptance criteria, call team_claim before each task, team_tasks_complete after, report back when done>"
|
|
47
|
+
\`\`\`
|
|
48
|
+
Wait → \`team_results\` → fix blockers → \`team_shutdown\`
|
|
49
|
+
|
|
50
|
+
8. **Mark tasks complete in openspec**
|
|
51
|
+
|
|
52
|
+
After specialists finish, update the tasks file: \`- [ ]\` → \`- [x]\` for each completed task.
|
|
53
|
+
Run \`openspec status --change "<name>" --json\` to confirm progress.
|
|
54
|
+
|
|
55
|
+
9. **On completion or pause, show status**
|
|
56
|
+
|
|
57
|
+
Display:
|
|
58
|
+
- Tasks completed this session
|
|
59
|
+
- Overall progress: "N/M tasks complete"
|
|
60
|
+
- If all done: suggest archive with \`/opsx-archive\`
|
|
61
|
+
- If paused: explain why and wait for guidance
|
|
62
|
+
|
|
63
|
+
Then run \`team_cleanup\`.
|
|
64
|
+
|
|
65
|
+
**Guardrails**
|
|
66
|
+
- NEVER implement tasks directly. Always use \`team_create\` + \`team_spawn\`, no exceptions
|
|
67
|
+
- NEVER touch source files before \`team_create\` is called, not even one edit
|
|
68
|
+
- ALWAYS run \`team_cleanup force:true\` before \`team_create\` to clear stale state from previous runs
|
|
69
|
+
- ALWAYS add tasks to the board with \`team_tasks_add tasks:[{content, priority}]\` before spawning. No assignee field exists.
|
|
70
|
+
- ALWAYS pass the task IDs returned by team_tasks_add to each agent in their spawn prompt so they can call team_claim
|
|
71
|
+
- NEVER claim or complete tasks yourself as lead. Only subagents call team_claim and team_tasks_complete
|
|
72
|
+
- Spawn teammates ONE AT A TIME, waiting for each team_spawn result before calling the next (avoids git worktree contention)
|
|
73
|
+
- "Small feature", "faster to do it directly", "environment issues", "teammates not responding" are NOT valid reasons to implement directly. If teammates are stuck, use \`team_message\` to resend tasks, then wait
|
|
74
|
+
- Always read context files before spawning (from the apply instructions output)
|
|
75
|
+
- Mark tasks complete in openspec AFTER specialists finish, not before
|
|
76
|
+
- If task is ambiguous, pause and ask before spawning
|
|
77
|
+
- If implementation reveals issues, pause and suggest artifact updates
|
|
78
|
+
- Pause on errors, blockers, or unclear requirements. Do not guess
|
|
79
|
+
- Use contextFiles from CLI output, do not assume specific file names
|
|
80
|
+
`
|
|
81
|
+
|
|
82
|
+
// Patterns that identify the solo implementation step in openspec-generated files
|
|
83
|
+
const SOLO_IMPL_PATTERNS = [
|
|
84
|
+
/^#{1,3}\s+\d+\..*(implement|loop until|make the code changes|for each (pending )?task)/im,
|
|
85
|
+
/\*\*Implement tasks.*loop until/im,
|
|
86
|
+
/^6\.\s+\*\*Implement tasks/im,
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
function patchOpensxApply(filePath) {
|
|
90
|
+
if (!fs.existsSync(filePath)) return false
|
|
91
|
+
|
|
92
|
+
const content = fs.readFileSync(filePath, 'utf8')
|
|
93
|
+
const lines = content.split('\n')
|
|
94
|
+
|
|
95
|
+
// Find the line index where the solo implementation step starts
|
|
96
|
+
let cutLine = -1
|
|
97
|
+
for (let i = 0; i < lines.length; i++) {
|
|
98
|
+
if (SOLO_IMPL_PATTERNS.some(p => p.test(lines[i]))) {
|
|
99
|
+
cutLine = i
|
|
100
|
+
break
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (cutLine === -1) return false // Pattern not found, skip
|
|
105
|
+
|
|
106
|
+
const preamble = lines.slice(0, cutLine).join('\n').trimEnd()
|
|
107
|
+
const patched = preamble + '\n\n' + ENSEMBLE_PATCH
|
|
108
|
+
fs.writeFileSync(filePath, patched, 'utf8')
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
|
|
4
112
|
export async function initOpenspec() {
|
|
5
113
|
header('Step 6, Initializing OpenSpec')
|
|
6
114
|
|
|
@@ -19,4 +127,20 @@ export async function initOpenspec() {
|
|
|
19
127
|
} catch (err) {
|
|
20
128
|
error(`Failed to run openspec init: ${err.message}`)
|
|
21
129
|
}
|
|
130
|
+
|
|
131
|
+
// Patch opsx-apply.md to use ensemble orchestration instead of solo implementation
|
|
132
|
+
const targets = [
|
|
133
|
+
path.join(process.cwd(), '.opencode', 'commands', 'opsx-apply.md'),
|
|
134
|
+
path.join(process.cwd(), '.opencode', 'skills', 'openspec-apply-change', 'SKILL.md'),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
for (const target of targets) {
|
|
138
|
+
try {
|
|
139
|
+
const patched = patchOpensxApply(target)
|
|
140
|
+
if (patched) success(`Patched ${path.relative(process.cwd(), target)} for ensemble`)
|
|
141
|
+
} catch (err) {
|
|
142
|
+
warn(`Could not patch ${path.relative(process.cwd(), target)}: ${err.message}`)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
22
145
|
}
|
|
146
|
+
|
package/src/utils/copy.js
CHANGED
|
@@ -2,7 +2,7 @@ import fse from 'fs-extra'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
|
|
4
4
|
// Folders never copied (skills handled separately by chooseSkillsProvider, .bootstrap is internal tooling)
|
|
5
|
-
const ALWAYS_EXCLUDE = ['.bootstrap', 'skills']
|
|
5
|
+
const ALWAYS_EXCLUDE = ['.bootstrap', 'skills', 'node_modules']
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Copy content/ directory to destination, excluding skills (handled separately by chooseSkillsProvider)
|
|
@@ -40,6 +40,8 @@ const AI_FILES = [
|
|
|
40
40
|
'copilot-instructions.md',
|
|
41
41
|
'.aider.conf.yml',
|
|
42
42
|
'.aider',
|
|
43
|
+
'.opencode',
|
|
44
|
+
'.agents'
|
|
43
45
|
]
|
|
44
46
|
|
|
45
47
|
export async function findAiFiles(dir) {
|