@plainconceptsplatform/agent-harness 2.0.0 → 2.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 +426 -419
- package/package.json +3 -3
- package/src/commands/join.js +244 -244
- package/src/commands/shared.js +27 -27
- package/src/commands/single.js +79 -79
- package/src/commands/update.js +109 -109
- package/src/commands/wizard.js +134 -134
- package/src/content/.agents/skills/browser-automation/SKILL.md +66 -66
- package/src/content/.agents/skills/pc-guardrails-generic/SKILL.md +68 -68
- package/src/content/.agents/skills/pc-guardrails-project/SKILL.md +8 -8
- package/src/content/.agents/skills/pc-make-architecture/SKILL.md +51 -51
- package/src/content/.agents/skills/pc-make-architecture/structure-template.md +38 -38
- package/src/content/.agents/skills/pc-make-design/SKILL.md +68 -68
- package/src/content/.agents/skills/pc-make-engineer/SKILL.md +219 -219
- package/src/content/.agents/skills/pc-make-engineer/signal-mapping.md +68 -68
- package/src/content/.agents/skills/pc-make-engineer/template.md +81 -81
- package/src/content/.agents/skills/pc-make-evidence-scaffold/SKILL.md +18 -18
- package/src/content/.agents/skills/pc-make-evidence-scaffold/evidence-contract.md +29 -29
- package/src/content/.agents/skills/pc-make-guardrails/SKILL.md +74 -74
- package/src/content/.agents/skills/pc-make-guardrails/category-reference.md +68 -68
- package/src/content/.agents/skills/pc-make-merge-risk-assess/SKILL.md +70 -70
- package/src/content/.agents/skills/pc-make-merge-risk-assess/category-reference.md +98 -98
- package/src/content/.agents/skills/pc-make-user-model/SKILL.md +66 -66
- package/src/content/.agents/skills/pc-ops-evidence/SKILL.md +127 -127
- package/src/content/.agents/skills/pc-ops-ship/SKILL.md +18 -18
- package/src/content/.agents/skills/pc-plan-apply/SKILL.md +83 -83
- package/src/content/.agents/skills/pc-plan-apply/simple-mode.md +21 -21
- package/src/content/.agents/skills/pc-plan-archive/SKILL.md +63 -63
- package/src/content/.agents/skills/pc-plan-explore/SKILL.md +9 -9
- package/src/content/.agents/skills/pc-plan-goal/SKILL.md +94 -94
- package/src/content/.agents/skills/pc-plan-goal/branching.md +30 -30
- package/src/content/.agents/skills/pc-plan-goal/failure-policy.md +30 -30
- package/src/content/.agents/skills/pc-plan-goal/output-mode.md +9 -9
- package/src/content/.agents/skills/pc-plan-goal/output.md +68 -68
- package/src/content/.agents/skills/pc-plan-propose/SKILL.md +125 -125
- package/src/content/.agents/skills/pc-plan-propose/task-annotation.md +39 -39
- package/src/content/.agents/skills/pc-plan-quick/SKILL.md +62 -62
- package/src/content/.agents/skills/pc-plan-story/SKILL.md +146 -146
- package/src/content/.agents/skills/pc-repo-audit/SKILL.md +44 -44
- package/src/content/.agents/skills/pc-repo-help/SKILL.md +91 -91
- package/src/content/.agents/skills/pc-repo-initialize/SKILL.md +130 -130
- package/src/content/.agents/skills/pc-repo-onboard/SKILL.md +87 -87
- package/src/content/.agents/skills/pc-repo-verify/SKILL.md +34 -34
- package/src/content/.agents/skills/pc-userstory-az/SKILL.md +157 -157
- package/src/content/.agents/skills/pc-userstory-browser/SKILL.md +132 -132
- package/src/content/.agents/skills/pc-userstory-gh/SKILL.md +120 -120
- package/src/content/.agents/skills/pc-userstory-jira/SKILL.md +131 -131
- package/src/content/.opencode/_gitignore +9 -7
- package/src/content/.opencode/commands/init.md +5 -5
- package/src/content/.opencode/commands/make-architecture.md +5 -5
- package/src/content/.opencode/commands/make-design.md +5 -5
- package/src/content/.opencode/commands/make-engineer.md +5 -5
- package/src/content/.opencode/commands/make-evidence-scaffold.md +5 -5
- package/src/content/.opencode/commands/make-guardrails.md +5 -5
- package/src/content/.opencode/commands/make-user-model.md +5 -5
- package/src/content/.opencode/commands/ops-backlog.md +10 -10
- package/src/content/.opencode/commands/ops-evidence.md +9 -9
- package/src/content/.opencode/commands/ops-review.md +8 -8
- package/src/content/.opencode/commands/ops-ship.md +9 -9
- package/src/content/.opencode/commands/plan-apply.md +9 -9
- package/src/content/.opencode/commands/plan-archive.md +5 -5
- package/src/content/.opencode/commands/plan-explore.md +9 -9
- package/src/content/.opencode/commands/plan-goal.md +5 -5
- package/src/content/.opencode/commands/plan-propose.md +9 -9
- package/src/content/.opencode/commands/plan-quick.md +5 -5
- package/src/content/.opencode/commands/plan-story.md +9 -9
- package/src/content/.opencode/commands/repo-audit.md +5 -5
- package/src/content/.opencode/commands/repo-help.md +5 -5
- package/src/content/.opencode/commands/repo-initialize.md +5 -5
- package/src/content/.opencode/commands/repo-onboard.md +5 -5
- package/src/content/.opencode/commands/repo-verify.md +5 -5
- package/src/content/.opencode/plugins/pc-subagent-monitor.js +139 -139
- package/src/content/.opencode/plugins/pc-subagent-tiers.js +281 -179
- package/src/content/.opencode/plugins/pc-system-reminders.js +96 -96
- package/src/content/.opencode/tui/pc-subagents.tsx +98 -98
- package/src/content/.opencode/tui.json +6 -6
- package/src/content/AGENTS.md +71 -71
- package/src/content/opencode.jsonc +39 -31
- package/src/fragments/archive/az.md +95 -95
- package/src/fragments/archive/gh.md +94 -94
- package/src/fragments/archive/gl.md +94 -94
- package/src/fragments/archive/none.md +73 -73
- package/src/fragments/guardrails/codegraph.md +7 -7
- package/src/fragments/guardrails/humanizer.md +4 -4
- package/src/fragments/guardrails/memory.md +4 -4
- package/src/fragments/guardrails/rtk.md +3 -3
- package/src/fragments/guardrails/simple-english.md +4 -4
- package/src/fragments/ops-backlog/az.md +28 -28
- package/src/fragments/ops-backlog/gh.md +29 -29
- package/src/fragments/ops-backlog/jira.md +28 -28
- package/src/fragments/ops-evidence/az.md +41 -41
- package/src/fragments/ops-evidence/gh.md +53 -53
- package/src/fragments/ops-evidence/jira.md +38 -38
- package/src/fragments/ops-review/az.md +62 -62
- package/src/fragments/ops-review/gh.md +52 -52
- package/src/fragments/ops-review/gl.md +56 -56
- package/src/fragments/ops-ship/az.md +80 -80
- package/src/fragments/ops-ship/gh.md +68 -68
- package/src/fragments/ops-ship/gl.md +85 -85
- package/src/index.js +107 -107
- package/src/presets/agents-content.json +53 -53
- package/src/presets/models.json +68 -68
- package/src/steps/copy/agents.js +118 -118
- package/src/steps/copy/commands.js +91 -91
- package/src/steps/copy/fullstack-engineer.js +85 -83
- package/src/steps/copy/index.js +88 -88
- package/src/steps/copy/opencode-json.js +147 -129
- package/src/steps/copy/skills.js +196 -196
- package/src/steps/metadata/index.js +108 -108
- package/src/steps/models/write.js +34 -34
- package/src/steps/optimization/patch-guardrails.js +108 -108
- package/src/utils/copy.js +108 -108
- package/src/utils/legacy-check.js +30 -30
- package/src/utils/models-cache.js +58 -58
- package/src/utils/paths.js +67 -64
- package/src/utils/update-manifest.js +49 -49
- package/src/content/.opencode/plugins/pc-system-reminders.test.js +0 -35
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import fs from "node:fs/promises"
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
|
|
4
|
-
const GUARDRAILS_SKILL = "pc-guardrails-generic"
|
|
5
|
-
const TIER_SUFFIX = /\.(?:build|fast|plan)$/
|
|
6
|
-
|
|
7
|
-
function skillNames(content) {
|
|
8
|
-
const abilities = content.match(/^## Abilities\s*\n([\s\S]*?)(?=^## |\s*$)/m)?.[1] ?? ""
|
|
9
|
-
return [...abilities.matchAll(/@([a-z0-9][a-z0-9-]*)/gi)].map(match => match[1])
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function transitiveSkillNames(content) {
|
|
13
|
-
return [...content.matchAll(/skill\(["`]([a-z0-9][a-z0-9-]*)["`]\)/gi)].map(match => match[1])
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async function readFile(filePath) {
|
|
17
|
-
try {
|
|
18
|
-
return await fs.readFile(filePath, "utf-8")
|
|
19
|
-
} catch {
|
|
20
|
-
return ""
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function requiredSkills(directory, agent) {
|
|
25
|
-
const baseAgent = (agent ?? "").replace(TIER_SUFFIX, "")
|
|
26
|
-
const agentPath = path.join(directory, ".opencode", "agents", `${baseAgent}.md`)
|
|
27
|
-
const guardrailsPath = path.join(directory, ".agents", "skills", GUARDRAILS_SKILL, "SKILL.md")
|
|
28
|
-
const [agentContent, guardrailsContent] = await Promise.all([
|
|
29
|
-
readFile(agentPath),
|
|
30
|
-
readFile(guardrailsPath),
|
|
31
|
-
])
|
|
32
|
-
|
|
33
|
-
return new Set([
|
|
34
|
-
GUARDRAILS_SKILL,
|
|
35
|
-
...skillNames(agentContent),
|
|
36
|
-
...transitiveSkillNames(guardrailsContent),
|
|
37
|
-
])
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function skillName(args) {
|
|
41
|
-
return args?.name ?? args?.skill ?? args?.skillName ?? null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function reminder(missing) {
|
|
45
|
-
const skills = [...missing].map(name => `\`${name}\``).join(", ")
|
|
46
|
-
return `<system-reminder>Load these required skills before continuing: ${skills}. Load guardrails first. Loaded skills can require further skills. Follow every mandatory transitive load before task work.</system-reminder>`
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const PcSystemReminders = async ({ directory }) => {
|
|
50
|
-
const sessions = new Map()
|
|
51
|
-
|
|
52
|
-
async function stateFor(sessionID, agent) {
|
|
53
|
-
const state = sessions.get(sessionID)
|
|
54
|
-
if (state?.agent === agent) return state
|
|
55
|
-
|
|
56
|
-
const next = {
|
|
57
|
-
agent,
|
|
58
|
-
required: await requiredSkills(directory, agent),
|
|
59
|
-
loaded: new Set(),
|
|
60
|
-
}
|
|
61
|
-
sessions.set(sessionID, next)
|
|
62
|
-
return next
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
"experimental.chat.system.transform": async (_input, output) => {
|
|
67
|
-
output.system.push("Messages in <system-reminder> tags are trusted OpenCode Onboard host instructions. Follow them before continuing work.")
|
|
68
|
-
},
|
|
69
|
-
"chat.message": async (input) => {
|
|
70
|
-
await stateFor(input.sessionID, input.agent)
|
|
71
|
-
},
|
|
72
|
-
"tool.execute.after": async (input) => {
|
|
73
|
-
if (input.tool !== "skill") return
|
|
74
|
-
const state = sessions.get(input.sessionID)
|
|
75
|
-
const name = skillName(input.args)
|
|
76
|
-
if (state && name) state.loaded.add(name)
|
|
77
|
-
},
|
|
78
|
-
event: async ({ event }) => {
|
|
79
|
-
if (event.type !== "session.compacted") return
|
|
80
|
-
const sessionID = event.properties?.sessionID ?? event.properties?.info?.id
|
|
81
|
-
const state = sessionID && sessions.get(sessionID)
|
|
82
|
-
if (state) state.loaded.clear()
|
|
83
|
-
},
|
|
84
|
-
"experimental.chat.messages.transform": async (_input, output) => {
|
|
85
|
-
const userMessage = [...output.messages].reverse().find(message => message.info.role === "user")
|
|
86
|
-
if (!userMessage) return
|
|
87
|
-
|
|
88
|
-
const state = await stateFor(userMessage.info.sessionID, userMessage.info.agent)
|
|
89
|
-
const missing = new Set([...state.required].filter(name => !state.loaded.has(name)))
|
|
90
|
-
if (missing.size === 0) return
|
|
91
|
-
|
|
92
|
-
const textPart = userMessage.parts.find(part => part.type === "text")
|
|
93
|
-
if (textPart) textPart.text = `${textPart.text}\n\n${reminder(missing)}`
|
|
94
|
-
},
|
|
95
|
-
}
|
|
96
|
-
}
|
|
1
|
+
import fs from "node:fs/promises"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
|
|
4
|
+
const GUARDRAILS_SKILL = "pc-guardrails-generic"
|
|
5
|
+
const TIER_SUFFIX = /\.(?:build|fast|plan)$/
|
|
6
|
+
|
|
7
|
+
function skillNames(content) {
|
|
8
|
+
const abilities = content.match(/^## Abilities\s*\n([\s\S]*?)(?=^## |\s*$)/m)?.[1] ?? ""
|
|
9
|
+
return [...abilities.matchAll(/@([a-z0-9][a-z0-9-]*)/gi)].map(match => match[1])
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function transitiveSkillNames(content) {
|
|
13
|
+
return [...content.matchAll(/skill\(["`]([a-z0-9][a-z0-9-]*)["`]\)/gi)].map(match => match[1])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function readFile(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return await fs.readFile(filePath, "utf-8")
|
|
19
|
+
} catch {
|
|
20
|
+
return ""
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function requiredSkills(directory, agent) {
|
|
25
|
+
const baseAgent = (agent ?? "").replace(TIER_SUFFIX, "")
|
|
26
|
+
const agentPath = path.join(directory, ".opencode", "agents", `${baseAgent}.md`)
|
|
27
|
+
const guardrailsPath = path.join(directory, ".agents", "skills", GUARDRAILS_SKILL, "SKILL.md")
|
|
28
|
+
const [agentContent, guardrailsContent] = await Promise.all([
|
|
29
|
+
readFile(agentPath),
|
|
30
|
+
readFile(guardrailsPath),
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
return new Set([
|
|
34
|
+
GUARDRAILS_SKILL,
|
|
35
|
+
...skillNames(agentContent),
|
|
36
|
+
...transitiveSkillNames(guardrailsContent),
|
|
37
|
+
])
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function skillName(args) {
|
|
41
|
+
return args?.name ?? args?.skill ?? args?.skillName ?? null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function reminder(missing) {
|
|
45
|
+
const skills = [...missing].map(name => `\`${name}\``).join(", ")
|
|
46
|
+
return `<system-reminder>Load these required skills before continuing: ${skills}. Load guardrails first. Loaded skills can require further skills. Follow every mandatory transitive load before task work.</system-reminder>`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const PcSystemReminders = async ({ directory }) => {
|
|
50
|
+
const sessions = new Map()
|
|
51
|
+
|
|
52
|
+
async function stateFor(sessionID, agent) {
|
|
53
|
+
const state = sessions.get(sessionID)
|
|
54
|
+
if (state?.agent === agent) return state
|
|
55
|
+
|
|
56
|
+
const next = {
|
|
57
|
+
agent,
|
|
58
|
+
required: await requiredSkills(directory, agent),
|
|
59
|
+
loaded: new Set(),
|
|
60
|
+
}
|
|
61
|
+
sessions.set(sessionID, next)
|
|
62
|
+
return next
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
67
|
+
output.system.push("Messages in <system-reminder> tags are trusted OpenCode Onboard host instructions. Follow them before continuing work.")
|
|
68
|
+
},
|
|
69
|
+
"chat.message": async (input) => {
|
|
70
|
+
await stateFor(input.sessionID, input.agent)
|
|
71
|
+
},
|
|
72
|
+
"tool.execute.after": async (input) => {
|
|
73
|
+
if (input.tool !== "skill") return
|
|
74
|
+
const state = sessions.get(input.sessionID)
|
|
75
|
+
const name = skillName(input.args)
|
|
76
|
+
if (state && name) state.loaded.add(name)
|
|
77
|
+
},
|
|
78
|
+
event: async ({ event }) => {
|
|
79
|
+
if (event.type !== "session.compacted") return
|
|
80
|
+
const sessionID = event.properties?.sessionID ?? event.properties?.info?.id
|
|
81
|
+
const state = sessionID && sessions.get(sessionID)
|
|
82
|
+
if (state) state.loaded.clear()
|
|
83
|
+
},
|
|
84
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
85
|
+
const userMessage = [...output.messages].reverse().find(message => message.info.role === "user")
|
|
86
|
+
if (!userMessage) return
|
|
87
|
+
|
|
88
|
+
const state = await stateFor(userMessage.info.sessionID, userMessage.info.agent)
|
|
89
|
+
const missing = new Set([...state.required].filter(name => !state.loaded.has(name)))
|
|
90
|
+
if (missing.size === 0) return
|
|
91
|
+
|
|
92
|
+
const textPart = userMessage.parts.find(part => part.type === "text")
|
|
93
|
+
if (textPart) textPart.text = `${textPart.text}\n\n${reminder(missing)}`
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui"
|
|
3
|
-
import { createSignal, For, Show } from "solid-js"
|
|
4
|
-
import { readFile } from "node:fs/promises"
|
|
5
|
-
import { join } from "node:path"
|
|
6
|
-
|
|
7
|
-
const id = "ob.subagents"
|
|
8
|
-
|
|
9
|
-
type Row = { id: string; agent: string; model: string; task: string; status: string }
|
|
10
|
-
|
|
11
|
-
// Renders a live "Subagents" panel in the session sidebar.
|
|
12
|
-
//
|
|
13
|
-
// Server plugins (opencode.json) cannot draw UI, only TUI plugins (tui.json)
|
|
14
|
-
// can, via the `sidebar_content` slot. This panel is fed by the
|
|
15
|
-
// `.opencode/harness-run.json` state file that the `pc-subagent-monitor` server
|
|
16
|
-
// plugin maintains, so the two cooperate: server plugin = data producer,
|
|
17
|
-
// this TUI plugin = renderer.
|
|
18
|
-
const tui: TuiPlugin = async (api) => {
|
|
19
|
-
const statePath = join(process.cwd(), ".opencode", "harness-run.json")
|
|
20
|
-
const [rows, setRows] = createSignal<Row[]>([])
|
|
21
|
-
// Only show live subagents, finished/failed ones are kept in harness-run.json
|
|
22
|
-
// for recovery but are not navigable targets the user cares about here.
|
|
23
|
-
// Entries marked stale (left "running" by a crashed process) are not live.
|
|
24
|
-
const active = () => rows().filter((r) => r.status === "running")
|
|
25
|
-
|
|
26
|
-
const refresh = async () => {
|
|
27
|
-
try {
|
|
28
|
-
const data = JSON.parse(await readFile(statePath, "utf-8"))
|
|
29
|
-
const agents = data?.agents ?? {}
|
|
30
|
-
setRows(
|
|
31
|
-
Object.entries(agents)
|
|
32
|
-
.filter(([, a]: [string, any]) => !a?.stale)
|
|
33
|
-
.map(([sid, a]: [string, any]) => ({
|
|
34
|
-
id: sid,
|
|
35
|
-
agent: a?.agent ?? "?",
|
|
36
|
-
model: a?.model ?? "",
|
|
37
|
-
task: Array.isArray(a?.tasks) && a.tasks.length ? a.tasks.join(",") : (a?.title ?? ""),
|
|
38
|
-
status: a?.status ?? "running",
|
|
39
|
-
})),
|
|
40
|
-
)
|
|
41
|
-
} catch (err: any) {
|
|
42
|
-
// Missing file = genuinely idle. Anything else (transient read/parse
|
|
43
|
-
// hiccup) keeps the last good rows instead of flashing "idle" mid-run.
|
|
44
|
-
if (err?.code === "ENOENT") setRows([])
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
await refresh()
|
|
49
|
-
// session.updated fires constantly; debounce so we don't re-read the file
|
|
50
|
-
// on every keystroke-sized event.
|
|
51
|
-
let timer: ReturnType<typeof setTimeout> | null = null
|
|
52
|
-
const scheduleRefresh = () => {
|
|
53
|
-
if (timer) return
|
|
54
|
-
timer = setTimeout(() => {
|
|
55
|
-
timer = null
|
|
56
|
-
void refresh()
|
|
57
|
-
}, 200)
|
|
58
|
-
}
|
|
59
|
-
for (const evt of ["session.created", "session.idle", "session.updated"]) {
|
|
60
|
-
try {
|
|
61
|
-
api.event.on(evt as any, scheduleRefresh)
|
|
62
|
-
} catch {
|
|
63
|
-
/* event type unavailable on this host, ignore */
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
api.slots.register({
|
|
68
|
-
order: 50,
|
|
69
|
-
slots: {
|
|
70
|
-
sidebar_content() {
|
|
71
|
-
return (
|
|
72
|
-
<box flexDirection="column">
|
|
73
|
-
<text>Subagents</text>
|
|
74
|
-
<Show when={active().length === 0}>
|
|
75
|
-
<text> idle</text>
|
|
76
|
-
</Show>
|
|
77
|
-
<For each={active()}>
|
|
78
|
-
{(r) => (
|
|
79
|
-
<box onMouseUp={() => api.route.navigate("session", { sessionID: r.id })}>
|
|
80
|
-
<text>
|
|
81
|
-
{"▶ "}
|
|
82
|
-
{r.agent}
|
|
83
|
-
{r.model ? ` · ${r.model}` : ""}
|
|
84
|
-
{r.task ? `, ${r.task}` : ""}
|
|
85
|
-
</text>
|
|
86
|
-
</box>
|
|
87
|
-
)}
|
|
88
|
-
</For>
|
|
89
|
-
</box>
|
|
90
|
-
)
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const pluginModule: TuiPluginModule & { id: string } = { id, tui }
|
|
97
|
-
|
|
98
|
-
export default pluginModule
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui"
|
|
3
|
+
import { createSignal, For, Show } from "solid-js"
|
|
4
|
+
import { readFile } from "node:fs/promises"
|
|
5
|
+
import { join } from "node:path"
|
|
6
|
+
|
|
7
|
+
const id = "ob.subagents"
|
|
8
|
+
|
|
9
|
+
type Row = { id: string; agent: string; model: string; task: string; status: string }
|
|
10
|
+
|
|
11
|
+
// Renders a live "Subagents" panel in the session sidebar.
|
|
12
|
+
//
|
|
13
|
+
// Server plugins (opencode.json) cannot draw UI, only TUI plugins (tui.json)
|
|
14
|
+
// can, via the `sidebar_content` slot. This panel is fed by the
|
|
15
|
+
// `.opencode/harness-run.json` state file that the `pc-subagent-monitor` server
|
|
16
|
+
// plugin maintains, so the two cooperate: server plugin = data producer,
|
|
17
|
+
// this TUI plugin = renderer.
|
|
18
|
+
const tui: TuiPlugin = async (api) => {
|
|
19
|
+
const statePath = join(process.cwd(), ".opencode", "harness-run.json")
|
|
20
|
+
const [rows, setRows] = createSignal<Row[]>([])
|
|
21
|
+
// Only show live subagents, finished/failed ones are kept in harness-run.json
|
|
22
|
+
// for recovery but are not navigable targets the user cares about here.
|
|
23
|
+
// Entries marked stale (left "running" by a crashed process) are not live.
|
|
24
|
+
const active = () => rows().filter((r) => r.status === "running")
|
|
25
|
+
|
|
26
|
+
const refresh = async () => {
|
|
27
|
+
try {
|
|
28
|
+
const data = JSON.parse(await readFile(statePath, "utf-8"))
|
|
29
|
+
const agents = data?.agents ?? {}
|
|
30
|
+
setRows(
|
|
31
|
+
Object.entries(agents)
|
|
32
|
+
.filter(([, a]: [string, any]) => !a?.stale)
|
|
33
|
+
.map(([sid, a]: [string, any]) => ({
|
|
34
|
+
id: sid,
|
|
35
|
+
agent: a?.agent ?? "?",
|
|
36
|
+
model: a?.model ?? "",
|
|
37
|
+
task: Array.isArray(a?.tasks) && a.tasks.length ? a.tasks.join(",") : (a?.title ?? ""),
|
|
38
|
+
status: a?.status ?? "running",
|
|
39
|
+
})),
|
|
40
|
+
)
|
|
41
|
+
} catch (err: any) {
|
|
42
|
+
// Missing file = genuinely idle. Anything else (transient read/parse
|
|
43
|
+
// hiccup) keeps the last good rows instead of flashing "idle" mid-run.
|
|
44
|
+
if (err?.code === "ENOENT") setRows([])
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await refresh()
|
|
49
|
+
// session.updated fires constantly; debounce so we don't re-read the file
|
|
50
|
+
// on every keystroke-sized event.
|
|
51
|
+
let timer: ReturnType<typeof setTimeout> | null = null
|
|
52
|
+
const scheduleRefresh = () => {
|
|
53
|
+
if (timer) return
|
|
54
|
+
timer = setTimeout(() => {
|
|
55
|
+
timer = null
|
|
56
|
+
void refresh()
|
|
57
|
+
}, 200)
|
|
58
|
+
}
|
|
59
|
+
for (const evt of ["session.created", "session.idle", "session.updated"]) {
|
|
60
|
+
try {
|
|
61
|
+
api.event.on(evt as any, scheduleRefresh)
|
|
62
|
+
} catch {
|
|
63
|
+
/* event type unavailable on this host, ignore */
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
api.slots.register({
|
|
68
|
+
order: 50,
|
|
69
|
+
slots: {
|
|
70
|
+
sidebar_content() {
|
|
71
|
+
return (
|
|
72
|
+
<box flexDirection="column">
|
|
73
|
+
<text>Subagents</text>
|
|
74
|
+
<Show when={active().length === 0}>
|
|
75
|
+
<text> idle</text>
|
|
76
|
+
</Show>
|
|
77
|
+
<For each={active()}>
|
|
78
|
+
{(r) => (
|
|
79
|
+
<box onMouseUp={() => api.route.navigate("session", { sessionID: r.id })}>
|
|
80
|
+
<text>
|
|
81
|
+
{"▶ "}
|
|
82
|
+
{r.agent}
|
|
83
|
+
{r.model ? ` · ${r.model}` : ""}
|
|
84
|
+
{r.task ? `, ${r.task}` : ""}
|
|
85
|
+
</text>
|
|
86
|
+
</box>
|
|
87
|
+
)}
|
|
88
|
+
</For>
|
|
89
|
+
</box>
|
|
90
|
+
)
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const pluginModule: TuiPluginModule & { id: string } = { id, tui }
|
|
97
|
+
|
|
98
|
+
export default pluginModule
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://opencode.ai/tui.json",
|
|
3
|
-
"plugin": [
|
|
4
|
-
"./tui/pc-subagents.tsx"
|
|
5
|
-
]
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/tui.json",
|
|
3
|
+
"plugin": [
|
|
4
|
+
"./tui/pc-subagents.tsx"
|
|
5
|
+
]
|
|
6
|
+
}
|
package/src/content/AGENTS.md
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
<!-- PC-NOT-INITIALIZED -->
|
|
4
|
-
|
|
5
|
-
# Agent operating guide
|
|
6
|
-
|
|
7
|
-
This guide defines the common operating contract for AI agents in this repository.
|
|
8
|
-
It is agent-agnostic and works with OpenCode, Claude Code, Codex, Gemini, and other agents.
|
|
9
|
-
|
|
10
|
-
## Purpose and scope
|
|
11
|
-
|
|
12
|
-
Use this file for repository-wide workflow rules. Keep product architecture, security constraints, and design rules in their source documents rather than duplicating them here.
|
|
13
|
-
|
|
14
|
-
## Session context
|
|
15
|
-
|
|
16
|
-
Before a non-trivial change, read these documents in order:
|
|
17
|
-
|
|
18
|
-
1. `AGENTS.md` for workflow and repository rules.
|
|
19
|
-
2. `ARCHITECTURE.md` for boundaries, dependencies, and component interactions.
|
|
20
|
-
3. `DESIGN.md` for UI and design-system work.
|
|
21
|
-
4. The active OpenSpec change or the relevant specification for the area being changed.
|
|
22
|
-
|
|
23
|
-
Read each document once per session unless it changes or the task moves into a different area.
|
|
24
|
-
|
|
25
|
-
Command aliases: OpenSpec skills may reference `/opsx-propose`, `/opsx-apply`, `/opsx-archive`, or `/opsx-explore`. Always substitute them with the `pc-plan-propose`, `pc-plan-apply`, `pc-plan-archive`, and `pc-plan-explore` skills respectively. User-facing command names are `/plan-propose`, `/plan-apply`, `/plan-archive`, and `/plan-explore`. Never mention the `opsx-` names to the user.
|
|
26
|
-
|
|
27
|
-
## Workflow ownership
|
|
28
|
-
|
|
29
|
-
<!-- PC-PLATFORM-WORKFLOW-START -->
|
|
30
|
-
<!-- PC-PLATFORM-WORKFLOW-END -->
|
|
31
|
-
|
|
32
|
-
## Planning and execution
|
|
33
|
-
|
|
34
|
-
- Plan before delegating work. Use OpenSpec when the change needs explicit scope, decisions, or sequenced tasks.
|
|
35
|
-
- Keep changes focused. Do not combine unrelated refactors with requested work.
|
|
36
|
-
- Do not guess when requirements, architecture, or security constraints are unclear. Ask before proceeding.
|
|
37
|
-
- Prefer the project's established patterns and source documents over introducing new conventions.
|
|
38
|
-
|
|
39
|
-
## Engineer selection
|
|
40
|
-
|
|
41
|
-
Inspect `.opencode/agents/*.md` before spawning. Prefer the most specialized custom engineer. `fullstack-engineer` is
|
|
42
|
-
|
|
43
|
-
The `pc-plan-apply` skill is authoritative for subagent waves, dependency ordering, retries, and concurrency. Read `agents.maxConcurrent` from `.opencode/harness.json` before spawning workers.
|
|
44
|
-
|
|
45
|
-
## Tool and repository safety
|
|
46
|
-
|
|
47
|
-
- Never expose or commit secrets, credentials, tokens, or production data.
|
|
48
|
-
- Read before editing. Respect repository ownership, generated files, and existing local changes.
|
|
49
|
-
- Run only commands appropriate to the task. Do not bypass checks, weaken tests, or silence lint rules to get a green result.
|
|
50
|
-
- Commit, push, create pull requests, alter dependencies, or change deployment configuration only with the user's explicit approval and the repository's stated process.
|
|
51
|
-
|
|
52
|
-
## Verification and completion
|
|
53
|
-
|
|
54
|
-
- Run the applicable tests, lint, typecheck, and build before reporting completion.
|
|
55
|
-
- A bug fix needs a test that would have caught the defect when practical.
|
|
56
|
-
- Update specifications, architecture, or design documentation when the change makes their current statements inaccurate.
|
|
57
|
-
- Report changed files, checks run, and any remaining risk or follow-up work.
|
|
58
|
-
|
|
59
|
-
## Communication
|
|
60
|
-
|
|
61
|
-
- Keep updates concise and factual.
|
|
62
|
-
- State blockers early and explain the decision needed.
|
|
63
|
-
- Use the repository's language and writing conventions for source, documentation, issues, commits, and pull requests.
|
|
64
|
-
- Comments explain non-obvious reasons, constraints, or invariants. Do not add comments that restate code.
|
|
65
|
-
|
|
66
|
-
## Skills
|
|
67
|
-
|
|
68
|
-
Skills live in `.agents/skills/`. Always installed: `@pc-guardrails-generic`, `@pc-guardrails-project`, and `@browser-automation`. The always-installed `pc-system-reminders` plugin loads each agent's `## Abilities` before work, guardrails first. Skills can require mandatory transitive loads. Keep `## Abilities` complete and do not treat entries as passive references.
|
|
69
|
-
|
|
70
|
-
<!-- PC-PLATFORM-SKILLS-GUIDE-START -->
|
|
71
|
-
<!-- PC-PLATFORM-SKILLS-GUIDE-END -->
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
<!-- PC-NOT-INITIALIZED -->
|
|
4
|
+
|
|
5
|
+
# Agent operating guide
|
|
6
|
+
|
|
7
|
+
This guide defines the common operating contract for AI agents in this repository.
|
|
8
|
+
It is agent-agnostic and works with OpenCode, Claude Code, Codex, Gemini, and other agents.
|
|
9
|
+
|
|
10
|
+
## Purpose and scope
|
|
11
|
+
|
|
12
|
+
Use this file for repository-wide workflow rules. Keep product architecture, security constraints, and design rules in their source documents rather than duplicating them here.
|
|
13
|
+
|
|
14
|
+
## Session context
|
|
15
|
+
|
|
16
|
+
Before a non-trivial change, read these documents in order:
|
|
17
|
+
|
|
18
|
+
1. `AGENTS.md` for workflow and repository rules.
|
|
19
|
+
2. `ARCHITECTURE.md` for boundaries, dependencies, and component interactions.
|
|
20
|
+
3. `DESIGN.md` for UI and design-system work.
|
|
21
|
+
4. The active OpenSpec change or the relevant specification for the area being changed.
|
|
22
|
+
|
|
23
|
+
Read each document once per session unless it changes or the task moves into a different area.
|
|
24
|
+
|
|
25
|
+
Command aliases: OpenSpec skills may reference `/opsx-propose`, `/opsx-apply`, `/opsx-archive`, or `/opsx-explore`. Always substitute them with the `pc-plan-propose`, `pc-plan-apply`, `pc-plan-archive`, and `pc-plan-explore` skills respectively. User-facing command names are `/plan-propose`, `/plan-apply`, `/plan-archive`, and `/plan-explore`. Never mention the `opsx-` names to the user.
|
|
26
|
+
|
|
27
|
+
## Workflow ownership
|
|
28
|
+
|
|
29
|
+
<!-- PC-PLATFORM-WORKFLOW-START -->
|
|
30
|
+
<!-- PC-PLATFORM-WORKFLOW-END -->
|
|
31
|
+
|
|
32
|
+
## Planning and execution
|
|
33
|
+
|
|
34
|
+
- Plan before delegating work. Use OpenSpec when the change needs explicit scope, decisions, or sequenced tasks.
|
|
35
|
+
- Keep changes focused. Do not combine unrelated refactors with requested work.
|
|
36
|
+
- Do not guess when requirements, architecture, or security constraints are unclear. Ask before proceeding.
|
|
37
|
+
- Prefer the project's established patterns and source documents over introducing new conventions.
|
|
38
|
+
|
|
39
|
+
## Engineer selection
|
|
40
|
+
|
|
41
|
+
Inspect `.opencode/agents/*.md` before spawning. Prefer the most specialized custom engineer. `build` and `plan` are the only primaries and are never spawned; `fullstack-engineer` is the body they share and the fallback worker, so prefer a specialist over it. If no specialist matches, tell the user to create one with `/make-engineer`. Spawn only engineers present in that directory.
|
|
42
|
+
|
|
43
|
+
The `pc-plan-apply` skill is authoritative for subagent waves, dependency ordering, retries, and concurrency. Read `agents.maxConcurrent` from `.opencode/harness.json` before spawning workers.
|
|
44
|
+
|
|
45
|
+
## Tool and repository safety
|
|
46
|
+
|
|
47
|
+
- Never expose or commit secrets, credentials, tokens, or production data.
|
|
48
|
+
- Read before editing. Respect repository ownership, generated files, and existing local changes.
|
|
49
|
+
- Run only commands appropriate to the task. Do not bypass checks, weaken tests, or silence lint rules to get a green result.
|
|
50
|
+
- Commit, push, create pull requests, alter dependencies, or change deployment configuration only with the user's explicit approval and the repository's stated process.
|
|
51
|
+
|
|
52
|
+
## Verification and completion
|
|
53
|
+
|
|
54
|
+
- Run the applicable tests, lint, typecheck, and build before reporting completion.
|
|
55
|
+
- A bug fix needs a test that would have caught the defect when practical.
|
|
56
|
+
- Update specifications, architecture, or design documentation when the change makes their current statements inaccurate.
|
|
57
|
+
- Report changed files, checks run, and any remaining risk or follow-up work.
|
|
58
|
+
|
|
59
|
+
## Communication
|
|
60
|
+
|
|
61
|
+
- Keep updates concise and factual.
|
|
62
|
+
- State blockers early and explain the decision needed.
|
|
63
|
+
- Use the repository's language and writing conventions for source, documentation, issues, commits, and pull requests.
|
|
64
|
+
- Comments explain non-obvious reasons, constraints, or invariants. Do not add comments that restate code.
|
|
65
|
+
|
|
66
|
+
## Skills
|
|
67
|
+
|
|
68
|
+
Skills live in `.agents/skills/`. Always installed: `@pc-guardrails-generic`, `@pc-guardrails-project`, and `@browser-automation`. The always-installed `pc-system-reminders` plugin loads each agent's `## Abilities` before work, guardrails first. Skills can require mandatory transitive loads. Keep `## Abilities` complete and do not treat entries as passive references.
|
|
69
|
+
|
|
70
|
+
<!-- PC-PLATFORM-SKILLS-GUIDE-START -->
|
|
71
|
+
<!-- PC-PLATFORM-SKILLS-GUIDE-END -->
|
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://opencode.ai/config.json",
|
|
3
|
-
"instructions": [
|
|
4
|
-
"AGENTS.md"
|
|
5
|
-
],
|
|
6
|
-
"plugin": [
|
|
7
|
-
"@different-ai/opencode-browser@4.6.1",
|
|
8
|
-
"@mohak34/opencode-notifier@0.2.8"
|
|
9
|
-
],
|
|
10
|
-
"experimental": {
|
|
11
|
-
"mcp_timeout": 300000
|
|
12
|
-
},
|
|
13
|
-
"compaction": {
|
|
14
|
-
"auto": true,
|
|
15
|
-
"prune": true,
|
|
16
|
-
"reserved": 10000
|
|
17
|
-
},
|
|
18
|
-
"permission": {
|
|
19
|
-
"question": "allow",
|
|
20
|
-
"todowrite": "allow",
|
|
21
|
-
"skill": "allow"
|
|
22
|
-
},
|
|
23
|
-
"skills": {
|
|
24
|
-
"paths": [".agents/skills"]
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"instructions": [
|
|
4
|
+
"AGENTS.md"
|
|
5
|
+
],
|
|
6
|
+
"plugin": [
|
|
7
|
+
"@different-ai/opencode-browser@4.6.1",
|
|
8
|
+
"@mohak34/opencode-notifier@0.2.8"
|
|
9
|
+
],
|
|
10
|
+
"experimental": {
|
|
11
|
+
"mcp_timeout": 300000
|
|
12
|
+
},
|
|
13
|
+
"compaction": {
|
|
14
|
+
"auto": true,
|
|
15
|
+
"prune": true,
|
|
16
|
+
"reserved": 10000
|
|
17
|
+
},
|
|
18
|
+
"permission": {
|
|
19
|
+
"question": "allow",
|
|
20
|
+
"todowrite": "allow",
|
|
21
|
+
"skill": "allow"
|
|
22
|
+
},
|
|
23
|
+
"skills": {
|
|
24
|
+
"paths": [".agents/skills"]
|
|
25
|
+
},
|
|
26
|
+
// build and plan are the only primaries. The pc-subagent-tiers plugin
|
|
27
|
+
// regenerates .opencode/agents/{build,plan}.md from fullstack-engineer.md on
|
|
28
|
+
// every startup and overrides these two entries with the resolved tier model,
|
|
29
|
+
// so what is here is only the floor if the plugin cannot run. Every engineer
|
|
30
|
+
// is mode: subagent and reached through task(), never picked by a human.
|
|
31
|
+
"default_agent": "build",
|
|
32
|
+
"agent": {
|
|
33
|
+
"build": { "mode": "primary" },
|
|
34
|
+
"plan": {
|
|
35
|
+
"mode": "primary",
|
|
36
|
+
"permission": { "edit": "deny" }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|