@plainconceptsplatform/agent-harness 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +423 -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 +7 -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 +179 -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/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 +83 -83
- package/src/steps/copy/index.js +88 -88
- package/src/steps/copy/opencode-json.js +129 -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 +64 -64
- package/src/utils/update-manifest.js +49 -49
- package/src/content/.opencode/plugins/pc-system-reminders.test.js +0 -35
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Create a pull request for the current feature branch with screenshots if UI changed.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-ops-ship` skill and execute it.
|
|
6
|
-
|
|
7
|
-
Input:
|
|
8
|
-
|
|
9
|
-
$ARGUMENTS
|
|
1
|
+
---
|
|
2
|
+
description: Create a pull request for the current feature branch with screenshots if UI changed.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-ops-ship` skill and execute it.
|
|
6
|
+
|
|
7
|
+
Input:
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Implement tasks from a plan: works with OpenSpec proposals and in-conversation plans.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-apply` skill and execute it in **interactive mode** with `start_from: branch` (the full protocol, including branch creation).
|
|
6
|
-
|
|
7
|
-
Input:
|
|
8
|
-
|
|
9
|
-
$ARGUMENTS
|
|
1
|
+
---
|
|
2
|
+
description: Implement tasks from a plan: works with OpenSpec proposals and in-conversation plans.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-apply` skill and execute it in **interactive mode** with `start_from: branch` (the full protocol, including branch creation).
|
|
6
|
+
|
|
7
|
+
Input:
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Archive the oldest merged unarchived OpenSpec change and update documentation.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-archive` skill and execute it in **interactive mode**. No input required.
|
|
1
|
+
---
|
|
2
|
+
description: Archive the oldest merged unarchived OpenSpec change and update documentation.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-archive` skill and execute it in **interactive mode**. No input required.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Think through an idea, investigate a problem, or clarify requirements before creating a change.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-explore` skill and execute it in **interactive mode**.
|
|
6
|
-
|
|
7
|
-
Input:
|
|
8
|
-
|
|
9
|
-
$ARGUMENTS
|
|
1
|
+
---
|
|
2
|
+
description: Think through an idea, investigate a problem, or clarify requirements before creating a change.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-explore` skill and execute it in **interactive mode**.
|
|
6
|
+
|
|
7
|
+
Input:
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Autonomous pipeline: explore, propose, apply, archive, then merge/PR/push. For loop-engineering.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-goal` skill and follow every step defined in it.
|
|
1
|
+
---
|
|
2
|
+
description: Autonomous pipeline: explore, propose, apply, archive, then merge/PR/push. For loop-engineering.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-goal` skill and follow every step defined in it.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Parse a work item or idea and propose a change plan with enriched task assignments.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-propose` skill and execute it in **interactive mode**.
|
|
6
|
-
|
|
7
|
-
Input:
|
|
8
|
-
|
|
9
|
-
$ARGUMENTS
|
|
1
|
+
---
|
|
2
|
+
description: Parse a work item or idea and propose a change plan with enriched task assignments.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-propose` skill and execute it in **interactive mode**.
|
|
6
|
+
|
|
7
|
+
Input:
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Quick plan: analyze the codebase and create a task checklist using the Todo pane. No files, no OpenSpec.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-quick` skill and follow every step defined in it.
|
|
1
|
+
---
|
|
2
|
+
description: Quick plan: analyze the codebase and create a task checklist using the Todo pane. No files, no OpenSpec.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-quick` skill and follow every step defined in it.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Write a detailed, repo-aware user story from a feature idea or need.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-plan-story` skill and follow every step defined in it.
|
|
6
|
-
|
|
7
|
-
Input:
|
|
8
|
-
|
|
9
|
-
$ARGUMENTS
|
|
1
|
+
---
|
|
2
|
+
description: Write a detailed, repo-aware user story from a feature idea or need.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-plan-story` skill and follow every step defined in it.
|
|
6
|
+
|
|
7
|
+
Input:
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Audit every configured source root against the fullstack engineer's guardrails and abilities. Read-only.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-repo-audit` skill and follow every step defined in it.
|
|
1
|
+
---
|
|
2
|
+
description: Audit every configured source root against the fullstack engineer's guardrails and abilities. Read-only.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-repo-audit` skill and follow every step defined in it.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Show all available commands and when to use each one.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-repo-help` skill and display its reference to the user exactly as written. Do not summarize.
|
|
1
|
+
---
|
|
2
|
+
description: Show all available commands and when to use each one.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-repo-help` skill and display its reference to the user exactly as written. Do not summarize.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Initialize the project. Presents a single form with all setup questions, then executes selected steps.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-repo-initialize` skill and follow every step defined in it.
|
|
1
|
+
---
|
|
2
|
+
description: Initialize the project. Presents a single form with all setup questions, then executes selected steps.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-repo-initialize` skill and follow every step defined in it.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Walk the user through the project and its agentic infrastructure. Explains what exists, how agents work, and how to use the system.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-repo-onboard` skill and follow every step defined in it.
|
|
1
|
+
---
|
|
2
|
+
description: Walk the user through the project and its agentic infrastructure. Explains what exists, how agents work, and how to use the system.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-repo-onboard` skill and follow every step defined in it.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Verify current-branch changes, plus immutable dependency installs, builds, and tests for every discovered project.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Load the `pc-repo-verify` skill and follow every step defined in it.
|
|
1
|
+
---
|
|
2
|
+
description: Verify current-branch changes, plus immutable dependency installs, builds, and tests for every discovered project.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Load the `pc-repo-verify` skill and follow every step defined in it.
|
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
// pc-subagent-monitor: tracks spawned subagents in .opencode/harness-run.json for
|
|
2
|
-
// live TUI display and crash recovery. Never throws: monitor failures must
|
|
3
|
-
// not break a session.
|
|
4
|
-
|
|
5
|
-
import fs from "node:fs/promises"
|
|
6
|
-
import path from "node:path"
|
|
7
|
-
|
|
8
|
-
export const PcSubagentMonitor = async ({ directory, client }) => {
|
|
9
|
-
const root = directory || process.cwd()
|
|
10
|
-
const statePath = path.join(root, ".opencode", "harness-run.json")
|
|
11
|
-
const state = { updatedAt: null, agents: {} }
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const prev = JSON.parse(await fs.readFile(statePath, "utf-8"))
|
|
15
|
-
if (prev?.agents && typeof prev.agents === "object") {
|
|
16
|
-
for (const [id, entry] of Object.entries(prev.agents)) {
|
|
17
|
-
if (entry?.status === "running") entry.stale = true
|
|
18
|
-
state.agents[id] = entry
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
} catch {
|
|
22
|
-
// fresh start
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let _modelsCache = null
|
|
26
|
-
async function loadModels() {
|
|
27
|
-
if (_modelsCache) return _modelsCache
|
|
28
|
-
const result = { build: null, fast: null, plan: null }
|
|
29
|
-
for (const file of ["harness.user.json", "harness.json"]) {
|
|
30
|
-
try {
|
|
31
|
-
const raw = await fs.readFile(path.join(root, ".opencode", file), "utf-8")
|
|
32
|
-
const { models = {} } = JSON.parse(raw)
|
|
33
|
-
for (const tier of ["build", "fast", "plan"]) {
|
|
34
|
-
if (!result[tier] && models[tier]) result[tier] = models[tier]
|
|
35
|
-
}
|
|
36
|
-
} catch {
|
|
37
|
-
continue
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
_modelsCache = result
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function modelForAgent(agent) {
|
|
45
|
-
if (!agent) return null
|
|
46
|
-
const dotIdx = agent.lastIndexOf(".")
|
|
47
|
-
const tier = dotIdx !== -1 ? agent.slice(dotIdx + 1) : null
|
|
48
|
-
if (tier && ["build", "fast", "plan"].includes(tier)) {
|
|
49
|
-
const models = await loadModels()
|
|
50
|
-
return models[tier] ?? null
|
|
51
|
-
}
|
|
52
|
-
return null
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function parseTasks(title) {
|
|
56
|
-
if (!title) return []
|
|
57
|
-
const m = /^\s*([\d]+(?:\.[\d]+)*(?:\s*,\s*[\d]+(?:\.[\d]+)*)*)/.exec(title)
|
|
58
|
-
return m ? m[1].split(",").map(s => s.trim()) : []
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Debounced persist: coalesce rapid writes when multiple subagents spawn
|
|
62
|
-
// in the same microtask batch (e.g. a wave of 5 task() calls).
|
|
63
|
-
let _persistScheduled = false
|
|
64
|
-
async function persist() {
|
|
65
|
-
if (_persistScheduled) return
|
|
66
|
-
_persistScheduled = true
|
|
67
|
-
queueMicrotask(async () => {
|
|
68
|
-
_persistScheduled = false
|
|
69
|
-
state.updatedAt = new Date().toISOString()
|
|
70
|
-
try {
|
|
71
|
-
await fs.mkdir(path.dirname(statePath), { recursive: true })
|
|
72
|
-
const tmpPath = `${statePath}.tmp`
|
|
73
|
-
await fs.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8")
|
|
74
|
-
await fs.rename(tmpPath, statePath)
|
|
75
|
-
} catch {
|
|
76
|
-
// best-effort
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function sessionInfo(props) {
|
|
82
|
-
const info = props?.info ?? props ?? {}
|
|
83
|
-
return {
|
|
84
|
-
id: info.id ?? info.sessionID ?? props?.sessionID,
|
|
85
|
-
parentID: info.parentID ?? info.parentId,
|
|
86
|
-
agent: info.agent,
|
|
87
|
-
title: info.title,
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function pruneStale() {
|
|
92
|
-
for (const [id, entry] of Object.entries(state.agents)) {
|
|
93
|
-
if (entry?.stale) delete state.agents[id]
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
event: async ({ event }) => {
|
|
99
|
-
try {
|
|
100
|
-
if (!event?.type?.startsWith("session.")) return
|
|
101
|
-
const info = sessionInfo(event.properties)
|
|
102
|
-
if (!info.id) return
|
|
103
|
-
|
|
104
|
-
if (event.type === "session.created" && info.parentID) {
|
|
105
|
-
pruneStale()
|
|
106
|
-
|
|
107
|
-
state.agents[info.id] = {
|
|
108
|
-
agent: info.agent ?? null,
|
|
109
|
-
model: await modelForAgent(info.agent),
|
|
110
|
-
tasks: parseTasks(info.title),
|
|
111
|
-
title: info.title ?? null,
|
|
112
|
-
status: "running",
|
|
113
|
-
startedAt: new Date().toISOString(),
|
|
114
|
-
endedAt: null,
|
|
115
|
-
}
|
|
116
|
-
await persist()
|
|
117
|
-
return
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const entry = state.agents[info.id]
|
|
121
|
-
if (!entry) return
|
|
122
|
-
|
|
123
|
-
if (event.type === "session.idle" && entry.status === "running") {
|
|
124
|
-
entry.status = "done"
|
|
125
|
-
entry.endedAt = new Date().toISOString()
|
|
126
|
-
await persist()
|
|
127
|
-
client?.tui?.showToast?.({
|
|
128
|
-
body: {
|
|
129
|
-
message: `subagent done: ${entry.title ?? info.id}`,
|
|
130
|
-
variant: "success",
|
|
131
|
-
},
|
|
132
|
-
})
|
|
133
|
-
}
|
|
134
|
-
} catch {
|
|
135
|
-
// monitoring must never disrupt the run
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
}
|
|
139
|
-
}
|
|
1
|
+
// pc-subagent-monitor: tracks spawned subagents in .opencode/harness-run.json for
|
|
2
|
+
// live TUI display and crash recovery. Never throws: monitor failures must
|
|
3
|
+
// not break a session.
|
|
4
|
+
|
|
5
|
+
import fs from "node:fs/promises"
|
|
6
|
+
import path from "node:path"
|
|
7
|
+
|
|
8
|
+
export const PcSubagentMonitor = async ({ directory, client }) => {
|
|
9
|
+
const root = directory || process.cwd()
|
|
10
|
+
const statePath = path.join(root, ".opencode", "harness-run.json")
|
|
11
|
+
const state = { updatedAt: null, agents: {} }
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const prev = JSON.parse(await fs.readFile(statePath, "utf-8"))
|
|
15
|
+
if (prev?.agents && typeof prev.agents === "object") {
|
|
16
|
+
for (const [id, entry] of Object.entries(prev.agents)) {
|
|
17
|
+
if (entry?.status === "running") entry.stale = true
|
|
18
|
+
state.agents[id] = entry
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
// fresh start
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let _modelsCache = null
|
|
26
|
+
async function loadModels() {
|
|
27
|
+
if (_modelsCache) return _modelsCache
|
|
28
|
+
const result = { build: null, fast: null, plan: null }
|
|
29
|
+
for (const file of ["harness.user.json", "harness.json"]) {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await fs.readFile(path.join(root, ".opencode", file), "utf-8")
|
|
32
|
+
const { models = {} } = JSON.parse(raw)
|
|
33
|
+
for (const tier of ["build", "fast", "plan"]) {
|
|
34
|
+
if (!result[tier] && models[tier]) result[tier] = models[tier]
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
_modelsCache = result
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function modelForAgent(agent) {
|
|
45
|
+
if (!agent) return null
|
|
46
|
+
const dotIdx = agent.lastIndexOf(".")
|
|
47
|
+
const tier = dotIdx !== -1 ? agent.slice(dotIdx + 1) : null
|
|
48
|
+
if (tier && ["build", "fast", "plan"].includes(tier)) {
|
|
49
|
+
const models = await loadModels()
|
|
50
|
+
return models[tier] ?? null
|
|
51
|
+
}
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseTasks(title) {
|
|
56
|
+
if (!title) return []
|
|
57
|
+
const m = /^\s*([\d]+(?:\.[\d]+)*(?:\s*,\s*[\d]+(?:\.[\d]+)*)*)/.exec(title)
|
|
58
|
+
return m ? m[1].split(",").map(s => s.trim()) : []
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Debounced persist: coalesce rapid writes when multiple subagents spawn
|
|
62
|
+
// in the same microtask batch (e.g. a wave of 5 task() calls).
|
|
63
|
+
let _persistScheduled = false
|
|
64
|
+
async function persist() {
|
|
65
|
+
if (_persistScheduled) return
|
|
66
|
+
_persistScheduled = true
|
|
67
|
+
queueMicrotask(async () => {
|
|
68
|
+
_persistScheduled = false
|
|
69
|
+
state.updatedAt = new Date().toISOString()
|
|
70
|
+
try {
|
|
71
|
+
await fs.mkdir(path.dirname(statePath), { recursive: true })
|
|
72
|
+
const tmpPath = `${statePath}.tmp`
|
|
73
|
+
await fs.writeFile(tmpPath, JSON.stringify(state, null, 2), "utf-8")
|
|
74
|
+
await fs.rename(tmpPath, statePath)
|
|
75
|
+
} catch {
|
|
76
|
+
// best-effort
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function sessionInfo(props) {
|
|
82
|
+
const info = props?.info ?? props ?? {}
|
|
83
|
+
return {
|
|
84
|
+
id: info.id ?? info.sessionID ?? props?.sessionID,
|
|
85
|
+
parentID: info.parentID ?? info.parentId,
|
|
86
|
+
agent: info.agent,
|
|
87
|
+
title: info.title,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function pruneStale() {
|
|
92
|
+
for (const [id, entry] of Object.entries(state.agents)) {
|
|
93
|
+
if (entry?.stale) delete state.agents[id]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
event: async ({ event }) => {
|
|
99
|
+
try {
|
|
100
|
+
if (!event?.type?.startsWith("session.")) return
|
|
101
|
+
const info = sessionInfo(event.properties)
|
|
102
|
+
if (!info.id) return
|
|
103
|
+
|
|
104
|
+
if (event.type === "session.created" && info.parentID) {
|
|
105
|
+
pruneStale()
|
|
106
|
+
|
|
107
|
+
state.agents[info.id] = {
|
|
108
|
+
agent: info.agent ?? null,
|
|
109
|
+
model: await modelForAgent(info.agent),
|
|
110
|
+
tasks: parseTasks(info.title),
|
|
111
|
+
title: info.title ?? null,
|
|
112
|
+
status: "running",
|
|
113
|
+
startedAt: new Date().toISOString(),
|
|
114
|
+
endedAt: null,
|
|
115
|
+
}
|
|
116
|
+
await persist()
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const entry = state.agents[info.id]
|
|
121
|
+
if (!entry) return
|
|
122
|
+
|
|
123
|
+
if (event.type === "session.idle" && entry.status === "running") {
|
|
124
|
+
entry.status = "done"
|
|
125
|
+
entry.endedAt = new Date().toISOString()
|
|
126
|
+
await persist()
|
|
127
|
+
client?.tui?.showToast?.({
|
|
128
|
+
body: {
|
|
129
|
+
message: `subagent done: ${entry.title ?? info.id}`,
|
|
130
|
+
variant: "success",
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
// monitoring must never disrupt the run
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
}
|