pluidr 0.3.1 → 0.4.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 +22 -2
- package/package.json +1 -1
- package/src/cli/commands/init.js +11 -4
- package/src/cli/index.js +11 -0
- package/src/core/agentPromptWriter.js +21 -3
- package/src/core/identityHeader.js +8 -0
- package/src/core/pluginWriter.js +25 -0
- package/src/plugins/README.md +54 -0
- package/src/plugins/parent-session.js +142 -0
- package/src/templates/agent-prompts/planner.txt +27 -13
- package/src/templates/model-defaults.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Pluidr sets up a **14-agent** pipeline in OpenCode organized under **4 primary a
|
|
|
14
14
|
|
|
15
15
|
**1. Explorer tab** (optional, research-only) — Brainstorms with you, scans the codebase and web for context, then produces actionable recommendations. No subagents, no file editing. Handoff to Planner when you're ready to formalize.
|
|
16
16
|
|
|
17
|
-
**2. Planner tab** — Turns your request into a verified PRD. It researches technical facts via Researcher, writes the spec via Plan-Writer, and validates it for completeness via Plan-Checker
|
|
17
|
+
**2. Planner tab** — Turns your request into a verified PRD. It researches technical facts via Researcher, writes the spec via Plan-Writer, and validates it for completeness via Plan-Checker. On FAIL, Planner surfaces gap remedies to you via the question tool (you pick the fix direction) — Planner never silently re-routes. After your input, Planner internally delegates to Researcher or Plan-Writer. Max 3 loops, then surfaces to you for direction. Planner never edits files directly.
|
|
18
18
|
|
|
19
19
|
**3. Builder tab** — Executes a confirmed PRD. It delegates implementation to Coder, tests via Tester, checks traceability with Reviewer, and produces a completion report via Writer. Builder never edits files or runs bash directly.
|
|
20
20
|
|
|
@@ -69,7 +69,8 @@ Each subagent belongs to exactly one primary agent and cannot be invoked by anyo
|
|
|
69
69
|
| **Researcher agents** | Researcher and Inspector output confirmed_facts/inferred_facts/unknowns/risks — no recommendations |
|
|
70
70
|
| **Writer agents** | Plan-Writer, Writer, and Reporter are stateless formatters — missing input = TBD, never invent content |
|
|
71
71
|
| **Build gate order** | Coder → Tester → Reviewer → Writer (Builder orchestrates in sequence) |
|
|
72
|
-
| **
|
|
72
|
+
| **Planner FAIL loop** | Plan-checker FAIL → Planner surfaces gap remedies to you via question tool (MC options) → you pick → Planner internally routes to Researcher or Plan-Writer. Max 3 loops, then surfaces to you for direction |
|
|
73
|
+
| **Builder FAIL loop** | 3 consecutive FAILs from Tester or Reviewer → Builder surfaces to you |
|
|
73
74
|
| **No shared subagents** | Each subagent belongs to exactly one primary — no cross-primary delegation |
|
|
74
75
|
| **Debugger independence** | Debugger is standalone — does not flow through Builder, triggered directly by you |
|
|
75
76
|
|
|
@@ -114,3 +115,22 @@ Prompts you to select models for two agent tiers, then:
|
|
|
114
115
|
- Backs up any existing config at `~/.config/opencode/opencode.jsonc` to `opencode.jsonc.bak`
|
|
115
116
|
- Writes the new config to `~/.config/opencode/opencode.jsonc`
|
|
116
117
|
- Copies agent system-prompt files into `~/.config/opencode/prompts/`
|
|
118
|
+
- Copies the bundled `parent-session` plugin into `~/.config/opencode/plugins/`
|
|
119
|
+
- Writes a `package.json` into `~/.config/opencode/` declaring `@opencode-ai/plugin` as a dependency (OpenCode installs it automatically on first launch via its bundled Bun runtime)
|
|
120
|
+
|
|
121
|
+
On completion, prints:
|
|
122
|
+
```
|
|
123
|
+
Pluidr setup complete!
|
|
124
|
+
You can update your agent model settings later in opencode.jsonc
|
|
125
|
+
```
|
|
126
|
+
The filename `opencode.jsonc` (second line) is a clickable terminal hyperlink — Ctrl+click to open the config in your default editor.
|
|
127
|
+
|
|
128
|
+
## Bundled plugins
|
|
129
|
+
|
|
130
|
+
Pluidr ships with one plugin: [`parent-session`](src/plugins/README.md), which gives subagents three tools for cross-session context access:
|
|
131
|
+
|
|
132
|
+
- `parent_session_messages` — read the parent session's transcript
|
|
133
|
+
- `session_messages(sessionId)` — read any session by ID
|
|
134
|
+
- `session_messages_batch(sessionIds)` — read multiple sessions in one call
|
|
135
|
+
|
|
136
|
+
`pluidr init` installs the plugin and its dependency declaration automatically — no extra user action. On OpenCode's first launch, the bundled Bun runtime installs `@opencode-ai/plugin` from the generated `package.json`, then the plugin's tools become available to all subagents.
|
package/package.json
CHANGED
package/src/cli/commands/init.js
CHANGED
|
@@ -6,11 +6,18 @@ import { backupExistingConfig } from "../../core/backup.js"
|
|
|
6
6
|
import { buildConfig } from "../../core/configBuilder.js"
|
|
7
7
|
import { writeConfig } from "../../core/configWriter.js"
|
|
8
8
|
import { writeAgentPrompts } from "../../core/agentPromptWriter.js"
|
|
9
|
-
import {
|
|
9
|
+
import { writePluginBundle, writePluginPackageJson } from "../../core/pluginWriter.js"
|
|
10
|
+
import { getConfigPath } from "../../core/paths.js"
|
|
10
11
|
|
|
11
12
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
13
|
const TEMPLATES_DIR = resolve(__dirname, "../../templates")
|
|
13
14
|
|
|
15
|
+
function makeFileHyperlink(absolutePath, displayText) {
|
|
16
|
+
const normalized = absolutePath.replace(/\\/g, "/").replace(/^\//, "")
|
|
17
|
+
const fileUrl = `file:///${normalized}`
|
|
18
|
+
return `\x1b]8;;${fileUrl}\x1b\\${displayText}\x1b]8;;\x1b\\`
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
export async function runInit() {
|
|
15
22
|
const defaultsPath = resolve(TEMPLATES_DIR, "model-defaults.json")
|
|
16
23
|
const modelDefaults = JSON.parse(readFileSync(defaultsPath, "utf-8"))
|
|
@@ -22,8 +29,8 @@ export async function runInit() {
|
|
|
22
29
|
const configObject = buildConfig(tierChoices, TEMPLATES_DIR)
|
|
23
30
|
writeConfig(configObject)
|
|
24
31
|
writeAgentPrompts(TEMPLATES_DIR)
|
|
32
|
+
writePluginBundle()
|
|
33
|
+
writePluginPackageJson()
|
|
25
34
|
|
|
26
|
-
console.log(
|
|
27
|
-
console.log(` Config : ${getConfigPath()}`)
|
|
28
|
-
console.log(` Prompts : ${getPromptsDir()}`)
|
|
35
|
+
console.log(`Pluidr setup complete!\nYou can update your agent model settings later in ${makeFileHyperlink(getConfigPath(), "opencode.jsonc")}`)
|
|
29
36
|
}
|
package/src/cli/index.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import { resolve, dirname } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
1
4
|
import { runInit } from "./commands/init.js"
|
|
2
5
|
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, "../../package.json"), "utf-8"))
|
|
8
|
+
|
|
3
9
|
export function run(argv) {
|
|
4
10
|
const cmd = argv[2]
|
|
5
11
|
|
|
12
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
13
|
+
console.log(pkg.version)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
if (cmd === "init") {
|
|
7
18
|
return runInit()
|
|
8
19
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { mkdirSync, readdirSync,
|
|
2
|
-
import { resolve } from "node:path"
|
|
1
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { basename, extname, resolve } from "node:path"
|
|
3
3
|
import { getPromptsDir } from "./paths.js"
|
|
4
|
+
import IDENTITY_HEADER from "./identityHeader.js"
|
|
5
|
+
|
|
6
|
+
// hierarchy.txt is a global reference document (referenced from
|
|
7
|
+
// opencode.config.json's `instructions`), not an agent prompt. It must be
|
|
8
|
+
// copied verbatim — no agent-identity header.
|
|
9
|
+
const REFERENCE_FILES = new Set(["hierarchy"])
|
|
4
10
|
|
|
5
11
|
export function writeAgentPrompts(templatesDir) {
|
|
6
12
|
const sourceDir = resolve(templatesDir, "agent-prompts")
|
|
@@ -9,6 +15,18 @@ export function writeAgentPrompts(templatesDir) {
|
|
|
9
15
|
mkdirSync(destDir, { recursive: true })
|
|
10
16
|
|
|
11
17
|
for (const file of readdirSync(sourceDir)) {
|
|
12
|
-
|
|
18
|
+
if (extname(file) !== ".txt") continue
|
|
19
|
+
|
|
20
|
+
const sourcePath = resolve(sourceDir, file)
|
|
21
|
+
const destPath = resolve(destDir, file)
|
|
22
|
+
const content = readFileSync(sourcePath, "utf-8")
|
|
23
|
+
|
|
24
|
+
const agentName = basename(file, ".txt")
|
|
25
|
+
if (REFERENCE_FILES.has(agentName)) {
|
|
26
|
+
writeFileSync(destPath, content, "utf-8")
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
writeFileSync(destPath, IDENTITY_HEADER(agentName) + content, "utf-8")
|
|
13
31
|
}
|
|
14
32
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Single source of truth for the agent identity prefix prepended to every
|
|
2
|
+
// generated prompt file. Mirrors the system identity concept from the
|
|
3
|
+
// dropped hook-based AgentSelfIdentityPlugin, but as a static prepend
|
|
4
|
+
// (KISS: agent names are known at install time).
|
|
5
|
+
|
|
6
|
+
export default function IDENTITY_HEADER(agentName) {
|
|
7
|
+
return `You are the "${agentName}" agent.\n\n`
|
|
8
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { dirname, join, resolve } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { getConfigDir } from "./paths.js"
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const PLUGIN_NAME = "parent-session.js"
|
|
8
|
+
const PACKAGE_JSON = {
|
|
9
|
+
dependencies: { "@opencode-ai/plugin": "^1.17.9" },
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function writePluginBundle() {
|
|
13
|
+
const pluginsDir = join(getConfigDir(), "plugins")
|
|
14
|
+
mkdirSync(pluginsDir, { recursive: true })
|
|
15
|
+
|
|
16
|
+
const sourcePath = resolve(__dirname, "../plugins", PLUGIN_NAME)
|
|
17
|
+
copyFileSync(sourcePath, join(pluginsDir, PLUGIN_NAME))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function writePluginPackageJson() {
|
|
21
|
+
const packageJsonPath = join(getConfigDir(), "package.json")
|
|
22
|
+
if (existsSync(packageJsonPath)) return
|
|
23
|
+
|
|
24
|
+
writeFileSync(packageJsonPath, JSON.stringify(PACKAGE_JSON, null, 2), "utf-8")
|
|
25
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Bundled plugins
|
|
2
|
+
|
|
3
|
+
This folder holds plugin source files that `pluidr init` copies into the
|
|
4
|
+
user's OpenCode plugin directory (`~/.config/opencode/plugins/`), giving
|
|
5
|
+
subagents cross-session context tools out of the box.
|
|
6
|
+
|
|
7
|
+
## What is `parent-session.js`?
|
|
8
|
+
|
|
9
|
+
It is the implementation of the `ParentSessionPlugin`, registered by
|
|
10
|
+
OpenCode at startup. It exposes three tools that subagents use to read
|
|
11
|
+
the conversation history of related sessions (parent, sibling, or batch):
|
|
12
|
+
|
|
13
|
+
- `parent_session_messages` — read the parent session's full transcript
|
|
14
|
+
- `session_messages(sessionId)` — read any session by ID
|
|
15
|
+
- `session_messages_batch(sessionIds)` — read multiple sessions in one call
|
|
16
|
+
|
|
17
|
+
The output is a structured text dump with numbered messages, agent
|
|
18
|
+
attribution, and one-line tool-invocation summaries, separated by
|
|
19
|
+
`\n\n---\n\n`.
|
|
20
|
+
|
|
21
|
+
## Why `parent-session` was extracted
|
|
22
|
+
|
|
23
|
+
Subagents (Coder, Tester, Reviewer, Inspector, Fixer, etc.) run in fresh
|
|
24
|
+
child sessions with no access to the parent orchestrator's conversation
|
|
25
|
+
history. When a parent agent dispatches a subagent via the `Task` tool, the
|
|
26
|
+
subagent cannot see what was discussed in the parent — which means it has
|
|
27
|
+
to ask the user to re-paste context, or guess. `parent_session_messages`
|
|
28
|
+
bridges that gap: the subagent can fetch the parent transcript on demand
|
|
29
|
+
and pick up where the parent left off.
|
|
30
|
+
|
|
31
|
+
## Why the identity plugin was dropped
|
|
32
|
+
|
|
33
|
+
The original `AgentSelfIdentityPlugin` injected agent identity via
|
|
34
|
+
`experimental.chat.*` hooks. Pluidr's 14 agent names are known at install
|
|
35
|
+
time, so we prepend a static identity header (`You are the "<name>" agent.`)
|
|
36
|
+
to every generated prompt file. This is KISS: no hook evaluation per
|
|
37
|
+
request, no dependency on undocumented experimental APIs, single source
|
|
38
|
+
of truth in `src/core/identityHeader.js`.
|
|
39
|
+
|
|
40
|
+
## Why the attribution tool was dropped
|
|
41
|
+
|
|
42
|
+
The original `AgentAttributionToolPlugin` was designed for a Retrospective
|
|
43
|
+
agent that captures post-session observations. Pluidr has no Retrospective
|
|
44
|
+
agent in its pipeline, so the tool would have no caller — YAGNI.
|
|
45
|
+
|
|
46
|
+
## How it is loaded
|
|
47
|
+
|
|
48
|
+
`pluidr init` copies `parent-session.js` into
|
|
49
|
+
`~/.config/opencode/plugins/` and writes a `package.json` next to it
|
|
50
|
+
declaring `@opencode-ai/plugin: ^1.17.9` as a dependency. On OpenCode's
|
|
51
|
+
first launch, the bundled Bun runtime detects the `package.json` and runs
|
|
52
|
+
`bun install` automatically — no user action required. Once installed,
|
|
53
|
+
the plugin's three tools are available to every agent that has the
|
|
54
|
+
appropriate permissions.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Parent-session plugin: provides tools for cross-session context access in
|
|
2
|
+
// subagent workflows. Translated from the original TypeScript implementation
|
|
3
|
+
// in addition/opencode-session-context/src/parent-session.ts to plain ESM JS
|
|
4
|
+
// (no build step required). Behavior, formatting, and tool names are unchanged.
|
|
5
|
+
|
|
6
|
+
import { tool } from "@opencode-ai/plugin"
|
|
7
|
+
|
|
8
|
+
function formatInput(input) {
|
|
9
|
+
if (
|
|
10
|
+
input == null ||
|
|
11
|
+
(typeof input === "object" &&
|
|
12
|
+
!Array.isArray(input) &&
|
|
13
|
+
Object.keys(input).length === 0)
|
|
14
|
+
) {
|
|
15
|
+
return ""
|
|
16
|
+
}
|
|
17
|
+
return `\n input: ${JSON.stringify(input)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatTool(part) {
|
|
21
|
+
const state = part.state
|
|
22
|
+
const status = state.status
|
|
23
|
+
const title = state.title ?? part.tool
|
|
24
|
+
const input = formatInput(state.input)
|
|
25
|
+
if (status === "error") {
|
|
26
|
+
return ` [tool] ${title} → error: ${state.error}${input}`
|
|
27
|
+
}
|
|
28
|
+
return ` [tool] ${title} → ${status}${input}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatParts(parts) {
|
|
32
|
+
const lines = []
|
|
33
|
+
for (const part of parts) {
|
|
34
|
+
if (part.type === "text") {
|
|
35
|
+
lines.push(part.text)
|
|
36
|
+
} else if (part.type === "tool") {
|
|
37
|
+
lines.push(formatTool(part))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return lines.join("\n")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function formatModel(info) {
|
|
44
|
+
const base = `${info.providerID}/${info.modelID}`
|
|
45
|
+
return info.variant ? `${base} (${info.variant})` : base
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatMessage(msg, index) {
|
|
49
|
+
const num = index + 1
|
|
50
|
+
if (msg.info.role === "assistant") {
|
|
51
|
+
const info = msg.info
|
|
52
|
+
const agent = info.agent ?? "unknown"
|
|
53
|
+
const header = `${num}. assistant (${agent}) [${formatModel(info)}]`
|
|
54
|
+
return `${header}\n${formatParts(msg.parts)}`
|
|
55
|
+
}
|
|
56
|
+
return `${num}. ${msg.info.role}\n${formatParts(msg.parts)}`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const ParentSessionPlugin = async ({ client }) => {
|
|
60
|
+
async function formatSessionMessages(sessionID, emptyMessage) {
|
|
61
|
+
const response = await client.session.messages({
|
|
62
|
+
path: { id: sessionID },
|
|
63
|
+
})
|
|
64
|
+
const messages = response.data ?? []
|
|
65
|
+
if (messages.length === 0) {
|
|
66
|
+
return emptyMessage
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return messages.map(formatMessage).join("\n\n---\n\n")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function formatSessionMessagesBatch(sessionIDs) {
|
|
73
|
+
const sections = await Promise.all(
|
|
74
|
+
sessionIDs.map(async (sessionID) => {
|
|
75
|
+
const emptyMessage = "(No messages found or session not accessible)"
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const output = await formatSessionMessages(sessionID, emptyMessage)
|
|
79
|
+
return `=== Session: ${sessionID} ===\n${output}`
|
|
80
|
+
} catch {
|
|
81
|
+
return `=== Session: ${sessionID} ===\n${emptyMessage}`
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return sections.join("\n\n")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
tool: {
|
|
91
|
+
parent_session_messages: tool({
|
|
92
|
+
description:
|
|
93
|
+
"Fetch all messages from the parent session. " +
|
|
94
|
+
"Returns the full conversation with agent attribution " +
|
|
95
|
+
"and message content. Only works from subagent sessions " +
|
|
96
|
+
"(sessions with a parentID).",
|
|
97
|
+
args: {},
|
|
98
|
+
async execute(_args, context) {
|
|
99
|
+
const session = await client.session.get({
|
|
100
|
+
path: { id: context.sessionID },
|
|
101
|
+
})
|
|
102
|
+
const parent = session.data?.parentID
|
|
103
|
+
if (!parent) {
|
|
104
|
+
return `Error: Session ${context.sessionID} has no parent. This tool only works from subagent sessions.`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return formatSessionMessages(
|
|
108
|
+
parent,
|
|
109
|
+
`Session ${parent} has no messages.`,
|
|
110
|
+
)
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
113
|
+
session_messages: tool({
|
|
114
|
+
description:
|
|
115
|
+
"Fetch all messages from a session by ID. " +
|
|
116
|
+
"Returns the full conversation with agent attribution " +
|
|
117
|
+
"and message content.",
|
|
118
|
+
args: {
|
|
119
|
+
sessionId: tool.schema.string().describe("The session ID to read"),
|
|
120
|
+
},
|
|
121
|
+
async execute(args) {
|
|
122
|
+
return formatSessionMessages(
|
|
123
|
+
args.sessionId,
|
|
124
|
+
`Session ${args.sessionId} has no messages.`,
|
|
125
|
+
)
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
session_messages_batch: tool({
|
|
129
|
+
description:
|
|
130
|
+
"Fetch all messages from multiple sessions by ID. Returns the full conversations concatenated with session delimiters. Useful for reading multiple related sessions (e.g., TDD phase sessions from a dispatch log) in a single tool call.",
|
|
131
|
+
args: {
|
|
132
|
+
sessionIds: tool.schema
|
|
133
|
+
.array(tool.schema.string())
|
|
134
|
+
.describe("Array of session IDs to fetch messages from."),
|
|
135
|
+
},
|
|
136
|
+
async execute(args) {
|
|
137
|
+
return formatSessionMessagesBatch(args.sessionIds)
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -25,19 +25,33 @@ edit code or files — this is a hard constraint, not a guideline.
|
|
|
25
25
|
of the plan-writer's PRD output — Planner does not write or edit the
|
|
26
26
|
file directly. Then present the PRD to the user, ask for confirmation
|
|
27
27
|
to proceed to Builder.
|
|
28
|
-
- FAIL →
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
user
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
- FAIL → Surface gap list to user with remedy options:
|
|
29
|
+
* The Planner does NOT decide the gap remedies — the user does.
|
|
30
|
+
* The Planner DOES decide the delegation route (researcher vs.
|
|
31
|
+
plan-writer) — that is a process decision, not a domain decision.
|
|
32
|
+
* On plan-checker FAIL, the Planner MUST surface the gap list to the
|
|
33
|
+
user with gap-remedy options using the question tool. This is the
|
|
34
|
+
ONLY permitted mechanism for gathering user input on gaps. The
|
|
35
|
+
Planner MUST NOT invent remedies, assume the user's choice, or use
|
|
36
|
+
silent interpretation, auto-pick, or prose-only responses.
|
|
37
|
+
* Present each gap via the question tool with 2-4 multiple-choice
|
|
38
|
+
remedy options per gap. The question is GAP-RELATED (what remedy
|
|
39
|
+
should be applied), not ROUTE-RELATED (do not ask the user to pick
|
|
40
|
+
researcher vs. plan-writer).
|
|
41
|
+
* After the user submits their gap-remedy answers, the Planner
|
|
42
|
+
internally decides the delegation route:
|
|
43
|
+
- Knowledge/research gaps → delegate to researcher.
|
|
44
|
+
- Revision/content gaps → delegate to plan-writer (PRD mode).
|
|
45
|
+
* The Planner does NOT ask the user for the route choice.
|
|
46
|
+
* After delegation, loop back to step 5 (plan-checker).
|
|
47
|
+
* If plan-checker returns PASS → done, loop counter resets.
|
|
48
|
+
* If plan-checker returns FAIL again → increment loop counter by 1,
|
|
49
|
+
repeat from step 1 (surface gaps to user again).
|
|
50
|
+
* After 3 consecutive FAIL→user→delegate→plan-checker loops without
|
|
51
|
+
PASS, the Planner MUST surface the accumulated gap list with the
|
|
52
|
+
loop count to the user, and ask for direction (without auto-delegating
|
|
53
|
+
further). The user decides whether to continue, change approach, or
|
|
54
|
+
cancel.
|
|
41
55
|
|
|
42
56
|
## Delegation rules
|
|
43
57
|
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"reasoningHeavy": {
|
|
3
3
|
"provider": "opencode",
|
|
4
4
|
"model": "big-pickle",
|
|
5
|
-
"agents": ["planner", "debugger", "explorer", "researcher", "plan-
|
|
5
|
+
"agents": ["planner", "debugger", "explorer", "researcher", "plan-checker", "inspector", "builder"]
|
|
6
6
|
},
|
|
7
7
|
"fast": {
|
|
8
8
|
"provider": "opencode",
|
|
9
9
|
"model": "deepseek-v4-flash-free",
|
|
10
|
-
"agents": ["
|
|
10
|
+
"agents": ["coder", "tester", "reviewer", "writer", "fixer", "plan-writer", "reporter"]
|
|
11
11
|
}
|
|
12
12
|
}
|