pluidr 0.4.1 → 0.6.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 +294 -73
- package/package.json +7 -2
- package/src/cli/commands/doctor.js +99 -0
- package/src/cli/commands/init.js +14 -7
- package/src/cli/commands/uninstall.js +67 -0
- package/src/cli/commands/update.js +22 -0
- package/src/cli/index.js +31 -1
- package/src/cli/wizard/selectModelTier.js +22 -31
- package/src/core/agentPromptWriter.js +2 -2
- package/src/core/agentPromptWriter.test.js +56 -0
- package/src/core/backup.js +35 -5
- package/src/core/backup.test.js +51 -0
- package/src/core/configBuilder.js +13 -0
- package/src/core/configBuilder.test.js +47 -0
- package/src/core/configWriter.js +7 -4
- package/src/core/configWriter.test.js +26 -0
- package/src/core/identityHeader.test.js +15 -0
- package/src/core/paths.js +4 -15
- package/src/core/paths.test.js +21 -0
- package/src/core/pluginWriter.js +12 -8
- package/src/core/pluginWriter.test.js +41 -0
- package/src/core/squeezeInstaller.js +141 -0
- package/src/core/squeezeInstaller.test.js +77 -0
- package/src/plugins/README.md +29 -15
- package/src/plugins/{parent-session.js → pluidr-flow.js} +1 -6
- package/src/plugins/pluidr-squeeze.js +56 -0
- package/src/templates/agent-prompts/auditor.txt +20 -0
- package/src/templates/agent-prompts/coder.txt +33 -7
- package/src/templates/agent-prompts/{writer.txt → compose-reporter.txt} +7 -7
- package/src/templates/agent-prompts/composer.txt +428 -0
- package/src/templates/agent-prompts/{reporter.txt → debug-reporter.txt} +3 -7
- package/src/templates/agent-prompts/debugger.txt +64 -18
- package/src/templates/agent-prompts/fixer.txt +7 -0
- package/src/templates/agent-prompts/hierarchy.txt +29 -15
- package/src/templates/agent-prompts/patcher.txt +20 -0
- package/src/templates/agent-prompts/plan-checker.txt +5 -5
- package/src/templates/agent-prompts/plan-writer.txt +3 -3
- package/src/templates/agent-prompts/probe-reporter.txt +62 -0
- package/src/templates/agent-prompts/prober.txt +90 -0
- package/src/templates/agent-prompts/researcher.txt +3 -3
- package/src/templates/agent-prompts/reviewer.txt +16 -4
- package/src/templates/agent-prompts/tester.txt +10 -1
- package/src/templates/agent-prompts/tracer.txt +33 -0
- package/src/templates/model-defaults.json +45 -2
- package/src/templates/opencode.config.json +227 -73
- package/src/templates/rtk-checksums.json +7 -0
- package/src/templates/agent-prompts/builder.txt +0 -107
- package/src/templates/agent-prompts/explorer.txt +0 -53
- package/src/templates/agent-prompts/planner.txt +0 -126
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process"
|
|
2
|
+
import { chmodSync, mkdirSync, existsSync, writeFileSync, unlinkSync, readFileSync } from "node:fs"
|
|
3
|
+
import { extname, join, resolve, dirname } from "node:path"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
import { createHash } from "node:crypto"
|
|
6
|
+
import { getConfigDir } from "./paths.js"
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
|
|
10
|
+
const BIN_NAME = process.platform === "win32" ? "rtk.exe" : "rtk"
|
|
11
|
+
export const ASSETS = {
|
|
12
|
+
"darwin-arm64": "rtk-aarch64-apple-darwin.tar.gz",
|
|
13
|
+
"darwin-x64": "rtk-x86_64-apple-darwin.tar.gz",
|
|
14
|
+
"linux-x64": "rtk-x86_64-unknown-linux-musl.tar.gz",
|
|
15
|
+
"linux-arm64": "rtk-aarch64-unknown-linux-gnu.tar.gz",
|
|
16
|
+
"win32-x64": "rtk-x86_64-pc-windows-msvc.zip",
|
|
17
|
+
}
|
|
18
|
+
const RTK_RELEASE_BASE = "https://github.com/rtk-ai/rtk/releases/latest/download"
|
|
19
|
+
|
|
20
|
+
export function platformAsset() {
|
|
21
|
+
const key = `${process.platform}-${process.arch}`
|
|
22
|
+
return ASSETS[key] ?? null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function validatePath(path) {
|
|
26
|
+
if (!/^[a-zA-Z0-9_\-\.\:\/\\]+$/.test(path)) {
|
|
27
|
+
throw new Error(`Invalid path: "${path}" contains unsafe characters`)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function findRtkPath() {
|
|
32
|
+
try {
|
|
33
|
+
const [cmd, ...args] =
|
|
34
|
+
process.platform === "win32" ? ["where", "rtk"] : ["which", "rtk"]
|
|
35
|
+
return execFileSync(cmd, args, { stdio: "pipe" })
|
|
36
|
+
.toString()
|
|
37
|
+
.trim()
|
|
38
|
+
.split(/\r?\n/)[0]
|
|
39
|
+
} catch {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function extract(archivePath, destDir) {
|
|
45
|
+
validatePath(archivePath)
|
|
46
|
+
validatePath(destDir)
|
|
47
|
+
const isZip = extname(archivePath) === ".zip"
|
|
48
|
+
|
|
49
|
+
if (isZip && process.platform === "win32") {
|
|
50
|
+
execFileSync("powershell", [
|
|
51
|
+
"-NoProfile",
|
|
52
|
+
"-Command",
|
|
53
|
+
"Expand-Archive",
|
|
54
|
+
"-LiteralPath", archivePath,
|
|
55
|
+
"-DestinationPath", destDir,
|
|
56
|
+
"-Force"
|
|
57
|
+
], { stdio: "ignore" })
|
|
58
|
+
} else {
|
|
59
|
+
execFileSync("tar", ["-xzf", archivePath, "-C", destDir], { stdio: "ignore" })
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function verifyChecksum(buffer, assetName) {
|
|
64
|
+
const checksumsPath = resolve(__dirname, "../templates/rtk-checksums.json")
|
|
65
|
+
let checksums
|
|
66
|
+
try {
|
|
67
|
+
checksums = JSON.parse(readFileSync(checksumsPath, "utf-8"))
|
|
68
|
+
} catch {
|
|
69
|
+
// No checksums file — skip verification
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const expectedHash = checksums[assetName]
|
|
74
|
+
if (expectedHash === null || expectedHash === undefined) {
|
|
75
|
+
// Hash not yet populated for this platform — silently skip
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const computed = createHash("sha256").update(buffer).digest("hex")
|
|
80
|
+
if (computed !== expectedHash) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`SHA256 mismatch for ${assetName}\n expected: ${expectedHash}\n computed: ${computed}`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function installSqueeze() {
|
|
88
|
+
let rtkPath = findRtkPath()
|
|
89
|
+
|
|
90
|
+
if (!rtkPath) {
|
|
91
|
+
// Not on PATH — download it to the managed location
|
|
92
|
+
const asset = platformAsset()
|
|
93
|
+
if (!asset) {
|
|
94
|
+
console.warn("⚠ Squeeze: unsupported platform — install rtk manually")
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const binDir = join(getConfigDir(), "bin")
|
|
99
|
+
mkdirSync(binDir, { recursive: true })
|
|
100
|
+
|
|
101
|
+
const url = `${RTK_RELEASE_BASE}/${asset}`
|
|
102
|
+
const archivePath = join(binDir, asset)
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
let response
|
|
106
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
107
|
+
try {
|
|
108
|
+
response = await fetch(url)
|
|
109
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
|
110
|
+
break
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (attempt < 3) {
|
|
113
|
+
// Retry on any error (network, HTTP, etc.) with exponential backoff
|
|
114
|
+
const delay = Math.pow(2, attempt - 1) * 1000
|
|
115
|
+
await new Promise(r => setTimeout(r, delay))
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
throw err
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const buffer = Buffer.from(await response.arrayBuffer())
|
|
123
|
+
verifyChecksum(buffer, asset)
|
|
124
|
+
writeFileSync(archivePath, buffer)
|
|
125
|
+
|
|
126
|
+
extract(archivePath, binDir)
|
|
127
|
+
|
|
128
|
+
try { unlinkSync(archivePath) } catch {}
|
|
129
|
+
|
|
130
|
+
rtkPath = join(binDir, BIN_NAME)
|
|
131
|
+
|
|
132
|
+
if (process.platform !== "win32" && existsSync(rtkPath)) {
|
|
133
|
+
chmodSync(rtkPath, 0o755)
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
try { unlinkSync(archivePath) } catch {}
|
|
137
|
+
console.warn("⚠ Squeeze: download failed after 3 attempts — install rtk manually")
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { platformAsset, validatePath, findRtkPath, ASSETS } from "./squeezeInstaller.js"
|
|
4
|
+
|
|
5
|
+
describe("platformAsset", () => {
|
|
6
|
+
it("returns a string or null for current platform", () => {
|
|
7
|
+
const result = platformAsset()
|
|
8
|
+
// On a supported platform: string; on unsupported: null
|
|
9
|
+
assert.ok(result === null || typeof result === "string")
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it("returns known asset names for mapped platforms", () => {
|
|
13
|
+
// Verify the ASSETS map has entries for all known platform combos
|
|
14
|
+
const knownCombos = [
|
|
15
|
+
{ plat: "darwin", arch: "arm64" },
|
|
16
|
+
{ plat: "darwin", arch: "x64" },
|
|
17
|
+
{ plat: "linux", arch: "x64" },
|
|
18
|
+
{ plat: "linux", arch: "arm64" },
|
|
19
|
+
{ plat: "win32", arch: "x64" },
|
|
20
|
+
]
|
|
21
|
+
for (const { plat, arch } of knownCombos) {
|
|
22
|
+
const key = `${plat}-${arch}`
|
|
23
|
+
const asset = ASSETS[key]
|
|
24
|
+
assert.ok(asset, `ASSETS map should have entry for ${key}`)
|
|
25
|
+
assert.ok(asset.endsWith(".tar.gz") || asset.endsWith(".zip"),
|
|
26
|
+
`Asset "${asset}" should end with .tar.gz or .zip`)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe("validatePath", () => {
|
|
32
|
+
it("accepts simple relative paths", () => {
|
|
33
|
+
assert.doesNotThrow(() => validatePath("foo/bar"))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it("accepts absolute POSIX paths", () => {
|
|
37
|
+
assert.doesNotThrow(() => validatePath("/home/user/.config"))
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("accepts absolute Windows paths", () => {
|
|
41
|
+
assert.doesNotThrow(() => validatePath("C:\\Users\\test"))
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("accepts paths with dots and dashes", () => {
|
|
45
|
+
assert.doesNotThrow(() => validatePath("./dir/file-name.ext"))
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("allows .. in paths (shell metacharacter check only, not path traversal)", () => {
|
|
49
|
+
assert.doesNotThrow(() => validatePath("../evil"))
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("rejects paths with semicolons", () => {
|
|
53
|
+
assert.throws(() => validatePath("foo;rm -rf /"), /unsafe characters/)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("rejects paths with backticks", () => {
|
|
57
|
+
assert.throws(() => validatePath("foo`id`"), /unsafe characters/)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("rejects paths with dollar signs", () => {
|
|
61
|
+
assert.throws(() => validatePath("foo$PATH"), /unsafe characters/)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it("rejects paths with pipe characters", () => {
|
|
65
|
+
assert.throws(() => validatePath("foo|bar"), /unsafe characters/)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe("findRtkPath", () => {
|
|
70
|
+
it("returns null when rtk is not available on PATH", () => {
|
|
71
|
+
// This test is safe because if rtk IS on PATH, we still get a
|
|
72
|
+
// string back — but the test doesn't crash either way.
|
|
73
|
+
const result = findRtkPath()
|
|
74
|
+
// Either null (not on PATH) or a non-empty string (found)
|
|
75
|
+
assert.ok(result === null || typeof result === "string")
|
|
76
|
+
})
|
|
77
|
+
})
|
package/src/plugins/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This folder holds plugin source files that `pluidr init` copies into the
|
|
4
4
|
user's OpenCode plugin directory (`~/.config/opencode/plugins/`), giving
|
|
5
|
-
subagents cross-session context tools out of
|
|
5
|
+
subagents cross-session context tools and command output filtering out of
|
|
6
|
+
the box.
|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## `pluidr-flow.js` — cross-session context
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
the conversation history of related sessions (parent, sibling, or batch):
|
|
10
|
+
Implementation of the `SessionFlowPlugin`. Exposes three tools for reading
|
|
11
|
+
conversation history across related sessions (parent, sibling, or batch):
|
|
12
12
|
|
|
13
13
|
- `parent_session_messages` — read the parent session's full transcript
|
|
14
14
|
- `session_messages(sessionId)` — read any session by ID
|
|
@@ -18,7 +18,21 @@ The output is a structured text dump with numbered messages, agent
|
|
|
18
18
|
attribution, and one-line tool-invocation summaries, separated by
|
|
19
19
|
`\n\n---\n\n`.
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## `pluidr-squeeze.js` — command output filtering
|
|
22
|
+
|
|
23
|
+
Implementation of the `PluidrSqueezePlugin`. Hooks into `tool.execute.before`
|
|
24
|
+
to rewrite bash commands through the `rtk` binary. The binary filters,
|
|
25
|
+
groups, truncates, and deduplicates command output — saving 60-90% of
|
|
26
|
+
tokens across all agents.
|
|
27
|
+
|
|
28
|
+
The plugin is a thin delegator: all rewrite logic lives in the `rtk` binary.
|
|
29
|
+
It checks `PATH` first, then falls back to Pluidr's managed location at
|
|
30
|
+
`~/.config/opencode/bin/rtk`. If the binary is not found, the plugin
|
|
31
|
+
gracefully disables itself with a warning.
|
|
32
|
+
|
|
33
|
+
`pluidr init` downloads the `rtk` binary automatically during setup.
|
|
34
|
+
|
|
35
|
+
## Why `pluidr-flow` was extracted
|
|
22
36
|
|
|
23
37
|
Subagents (Coder, Tester, Reviewer, Inspector, Fixer, etc.) run in fresh
|
|
24
38
|
child sessions with no access to the parent orchestrator's conversation
|
|
@@ -31,7 +45,7 @@ and pick up where the parent left off.
|
|
|
31
45
|
## Why the identity plugin was dropped
|
|
32
46
|
|
|
33
47
|
The original `AgentSelfIdentityPlugin` injected agent identity via
|
|
34
|
-
`experimental.chat.*` hooks. Pluidr's
|
|
48
|
+
`experimental.chat.*` hooks. Pluidr's 12 agent names are known at install
|
|
35
49
|
time, so we prepend a static identity header (`You are the "<name>" agent.`)
|
|
36
50
|
to every generated prompt file. This is KISS: no hook evaluation per
|
|
37
51
|
request, no dependency on undocumented experimental APIs, single source
|
|
@@ -43,12 +57,12 @@ The original `AgentAttributionToolPlugin` was designed for a Retrospective
|
|
|
43
57
|
agent that captures post-session observations. Pluidr has no Retrospective
|
|
44
58
|
agent in its pipeline, so the tool would have no caller — YAGNI.
|
|
45
59
|
|
|
46
|
-
## How
|
|
60
|
+
## How they are loaded
|
|
47
61
|
|
|
48
|
-
`pluidr init` copies
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
first launch, the bundled Bun
|
|
52
|
-
`bun install` automatically —
|
|
53
|
-
|
|
54
|
-
appropriate permissions.
|
|
62
|
+
`pluidr init` copies both plugins into `~/.config/opencode/plugins/` and
|
|
63
|
+
writes a `package.json` declaring `@opencode-ai/plugin: ^1.17.9` as a
|
|
64
|
+
dependency. For `pluidr-squeeze`, it also downloads the `rtk` engine binary
|
|
65
|
+
to `~/.config/opencode/bin/`. On OpenCode's first launch, the bundled Bun
|
|
66
|
+
runtime detects the `package.json` and runs `bun install` automatically —
|
|
67
|
+
no user action required. Once installed, both plugins are available to
|
|
68
|
+
every agent that has the appropriate permissions.
|
|
@@ -1,8 +1,3 @@
|
|
|
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
1
|
import { tool } from "@opencode-ai/plugin"
|
|
7
2
|
|
|
8
3
|
function formatInput(input) {
|
|
@@ -56,7 +51,7 @@ function formatMessage(msg, index) {
|
|
|
56
51
|
return `${num}. ${msg.info.role}\n${formatParts(msg.parts)}`
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
export const
|
|
54
|
+
export const SessionFlowPlugin = async ({ client }) => {
|
|
60
55
|
async function formatSessionMessages(sessionID, emptyMessage) {
|
|
61
56
|
const response = await client.session.messages({
|
|
62
57
|
path: { id: sessionID },
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { homedir } from "node:os"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
|
|
5
|
+
const BIN_NAME = process.platform === "win32" ? "rtk.exe" : "rtk"
|
|
6
|
+
const SQUEEZE_BIN = join(homedir(), ".config", "opencode", "bin", BIN_NAME)
|
|
7
|
+
|
|
8
|
+
function normalise(p) {
|
|
9
|
+
return p.replace(/\\/g, "/")
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function findBinary($) {
|
|
13
|
+
try {
|
|
14
|
+
if (process.platform === "win32") {
|
|
15
|
+
await $`where rtk`.quiet()
|
|
16
|
+
} else {
|
|
17
|
+
await $`which rtk`.quiet()
|
|
18
|
+
}
|
|
19
|
+
return "rtk"
|
|
20
|
+
} catch {
|
|
21
|
+
if (existsSync(SQUEEZE_BIN)) {
|
|
22
|
+
return normalise(SQUEEZE_BIN)
|
|
23
|
+
}
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const PluidrSqueezePlugin = async ({ $ }) => {
|
|
29
|
+
const binPath = await findBinary($)
|
|
30
|
+
if (!binPath) {
|
|
31
|
+
console.warn("[squeeze] rtk binary not found — plugin disabled")
|
|
32
|
+
return {}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
"tool.execute.before": async (input, output) => {
|
|
37
|
+
const tool = String(input?.tool ?? "").toLowerCase()
|
|
38
|
+
if (tool !== "bash" && tool !== "shell") return
|
|
39
|
+
const args = output?.args
|
|
40
|
+
if (!args || typeof args !== "object") return
|
|
41
|
+
|
|
42
|
+
const command = args.command
|
|
43
|
+
if (typeof command !== "string" || !command) return
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const result = await $`${binPath} rewrite ${command}`.quiet().nothrow()
|
|
47
|
+
const rewritten = String(result.stdout).trim()
|
|
48
|
+
if (rewritten && rewritten !== command) {
|
|
49
|
+
args.command = rewritten
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
// rewrite failed — pass through unchanged
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Role: Auditor Subagent
|
|
2
|
+
|
|
3
|
+
You are the **Auditor** subagent for the **Prober** agent. You act as a strict validation gate checking if security patches have resolved the vulnerabilities and ensuring no over-engineering was introduced.
|
|
4
|
+
|
|
5
|
+
## Auditor MUST NOT
|
|
6
|
+
- Propose remedies, suggest code adjustments, or edit code.
|
|
7
|
+
|
|
8
|
+
## Auditor MAY ONLY
|
|
9
|
+
- Inspect modified codebase files and run test commands to verify vulnerability elimination.
|
|
10
|
+
- Perform a security regression review: ensure the applied patches did not introduce new vulnerability pathways (e.g., input bypasses or insecure error handling).
|
|
11
|
+
- Perform an over-engineering review (Ponytail audit lens).
|
|
12
|
+
|
|
13
|
+
## Ponytail Audit Lens (Bloat Analysis)
|
|
14
|
+
Analyze changes for cognitive overload, accidental complexity, or redundant logic. Compile a list of unrequested abstractions, boilerplate, or excessive lines.
|
|
15
|
+
|
|
16
|
+
## Output Format
|
|
17
|
+
Your output must consist ONLY of:
|
|
18
|
+
- **Verdict**: PASS or FAIL
|
|
19
|
+
- **Gap List**: Specific files/lines where vulnerabilities are still active or regression was detected (FAIL cases only).
|
|
20
|
+
- **BLOAT List**: List of over-engineered constructs found in the patches.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# Role: Coder Subagent
|
|
2
2
|
|
|
3
|
-
You implement tasks assigned by
|
|
4
|
-
manage your own task tracking internally via `todowrite` — this is not a
|
|
5
|
-
persisted document, it's your working checklist for the current session.
|
|
3
|
+
You implement tasks assigned by Composer. If a PRD is provided, follow it exactly. If no PRD is provided (e.g., during a direct-build phase for simple tasks), follow the user's original specification and the Explore findings provided in the task payload verbatim. You manage your own task tracking internally via `todowrite` — this is not a persisted document, it's your working checklist for the current session.
|
|
6
4
|
|
|
7
5
|
Refer to `hierarchy.txt` (loaded globally) for conflict resolution.
|
|
8
6
|
|
|
@@ -39,7 +37,7 @@ Refer to `hierarchy.txt` (loaded globally) for conflict resolution.
|
|
|
39
37
|
You have no `task` permission — you cannot invoke any other agent or
|
|
40
38
|
subagent. If implementation reveals a need for research (e.g., unclear
|
|
41
39
|
library behavior) or a requirement question, stop and report back to
|
|
42
|
-
|
|
40
|
+
Composer rather than guessing or trying to research it yourself outside
|
|
43
41
|
your given tools.
|
|
44
42
|
|
|
45
43
|
## Task tracking
|
|
@@ -52,11 +50,39 @@ your given tools.
|
|
|
52
50
|
## What you do NOT do
|
|
53
51
|
|
|
54
52
|
- You do not change the requirement — if the PRD task is ambiguous or
|
|
55
|
-
infeasible as written, stop and report back to
|
|
53
|
+
infeasible as written, stop and report back to Composer rather than
|
|
56
54
|
reinterpreting it.
|
|
57
|
-
- You do not produce documentation — that's
|
|
55
|
+
- You do not produce documentation — that's Compose-Reporter's job.
|
|
58
56
|
|
|
59
57
|
## Output
|
|
60
58
|
|
|
61
59
|
- Code changes, with a brief note on any deviation from the assigned task (if any).
|
|
62
|
-
- Flag any duplicated logic found during implementation.
|
|
60
|
+
- Flag any duplicated logic found during implementation.
|
|
61
|
+
|
|
62
|
+
## Efficiency Constraints
|
|
63
|
+
|
|
64
|
+
**Decision Ladder**
|
|
65
|
+
Before writing any code, stop at the first rung that holds:
|
|
66
|
+
1. Does this need to be built at all? (YAGNI)
|
|
67
|
+
2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
|
|
68
|
+
3. Does the standard library already do this? Use it.
|
|
69
|
+
4. Does a native platform feature cover it? Use it.
|
|
70
|
+
5. Does an already-installed dependency solve it? Use it.
|
|
71
|
+
6. Can this be one line? Make it one line.
|
|
72
|
+
7. Only then: write the minimum code that works.
|
|
73
|
+
|
|
74
|
+
The ladder runs after you understand the problem: read the task and the code it touches, trace the real flow end to end, then climb.
|
|
75
|
+
|
|
76
|
+
**Never-Simplify Rules (Not Lazy About)**
|
|
77
|
+
- Understanding the problem: Trace the real flow before coding.
|
|
78
|
+
- Input validation at trust boundaries.
|
|
79
|
+
- Error handling that prevents data loss.
|
|
80
|
+
- Security and accessibility.
|
|
81
|
+
- Anything explicitly requested.
|
|
82
|
+
- No abstractions that weren't explicitly requested.
|
|
83
|
+
- No new dependency if it can be avoided.
|
|
84
|
+
- No boilerplate nobody asked for.
|
|
85
|
+
- Deletion over addition. Boring over clever. Fewest files possible.
|
|
86
|
+
|
|
87
|
+
**Simplification Comment Convention**
|
|
88
|
+
- Mark intentional simplifications with a `simplification:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment must name the ceiling and the upgrade path.
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
# Role:
|
|
1
|
+
# Role: Compose-Reporter Subagent
|
|
2
2
|
|
|
3
|
-
You are the **
|
|
3
|
+
You are the **Compose-Reporter** subagent, the text-file executor for the **Composer**
|
|
4
4
|
agent. You write and edit `.md` and `.txt` files across the project — like
|
|
5
5
|
Coder but for documents only. You do not run bash commands.
|
|
6
6
|
|
|
7
7
|
## Mode: Document Creation / Editing
|
|
8
8
|
|
|
9
9
|
You create and modify documentation files (.md, .txt) as instructed by
|
|
10
|
-
|
|
10
|
+
Composer. You may also be invoked in Summary mode to produce completion
|
|
11
11
|
reports from structured input.
|
|
12
12
|
|
|
13
13
|
## Delegation rules
|
|
14
14
|
|
|
15
15
|
You have no `task` permission — you cannot invoke any other agent or
|
|
16
|
-
subagent. If
|
|
16
|
+
subagent. If Composer's instructions are insufficient to produce the
|
|
17
17
|
required document, mark missing sections as `TBD` — do not infer, research,
|
|
18
18
|
or invent content.
|
|
19
19
|
|
|
20
20
|
## Principles
|
|
21
21
|
|
|
22
|
-
- **Audience-First** — Structure the document for its reader. If
|
|
22
|
+
- **Audience-First** — Structure the document for its reader. If Composer specifies an audience (technical lead, maintainer, end-user), tailor detail level and vocabulary accordingly. If no audience is specified, default to technical summary.
|
|
23
23
|
- **DRY for Docs** — Each finding or verdict appears exactly once with cross-references. Do not repeat the same information across sections — it creates maintenance burden and risks inconsistency.
|
|
24
24
|
- **KISS** — Prefer simple, flat structure over nested hierarchies. A reader should grasp the outcome from the first paragraph.
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Compose-Reporter MUST NOT
|
|
27
27
|
|
|
28
28
|
- Make inferences about what the user "probably means."
|
|
29
29
|
- Make decisions (e.g., which approach is better).
|
|
@@ -31,7 +31,7 @@ or invent content.
|
|
|
31
31
|
- Fill missing information with assumptions — mark as `TBD` instead.
|
|
32
32
|
- Run bash commands — you have no `bash` permission.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Compose-Reporter MAY ONLY
|
|
35
35
|
|
|
36
36
|
- Write and edit `.md` and `.txt` files as instructed.
|
|
37
37
|
- Reformat / restructure given input into the target document type.
|