pluidr 0.6.0 → 0.6.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/package.json +1 -1
- package/src/cli/commands/doctor.js +9 -9
- package/src/core/squeezeInstaller.js +28 -8
- package/src/core/squeezeInstaller.test.js +4 -6
- package/src/plugins/README.md +11 -11
- package/src/plugins/pluidr-squeeze.js +27 -6
- package/src/templates/agent-prompts/composer.txt +11 -9
- package/src/templates/agent-prompts/researcher.txt +1 -0
- package/src/templates/opencode.config.json +108 -80
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import { join, dirname } from "node:path"
|
|
|
3
3
|
import { fileURLToPath } from "node:url"
|
|
4
4
|
import { execFileSync } from "node:child_process"
|
|
5
5
|
import { getConfigDir, getConfigPath, getPromptsDir } from "../../core/paths.js"
|
|
6
|
-
import {
|
|
6
|
+
import { findSqueezePath } from "../../core/squeezeInstaller.js"
|
|
7
7
|
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
9
|
const EXPECTED_PROMPT_COUNT = 18
|
|
@@ -54,16 +54,16 @@ export async function runDoctor() {
|
|
|
54
54
|
}
|
|
55
55
|
checks.push({ component: "package.json + plugin dep", pass: pkgValid })
|
|
56
56
|
|
|
57
|
-
// 5.
|
|
58
|
-
let
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
57
|
+
// 5. Squeeze binary exists
|
|
58
|
+
let squeezeFound = false
|
|
59
|
+
const squeezeOnPath = findSqueezePath()
|
|
60
|
+
if (squeezeOnPath) {
|
|
61
|
+
squeezeFound = true
|
|
62
62
|
} else {
|
|
63
|
-
const binName = process.platform === "win32" ? "
|
|
64
|
-
|
|
63
|
+
const binName = process.platform === "win32" ? "squeeze.exe" : "squeeze"
|
|
64
|
+
squeezeFound = existsSync(join(binDir, binName))
|
|
65
65
|
}
|
|
66
|
-
checks.push({ component: "squeeze binary", pass:
|
|
66
|
+
checks.push({ component: "squeeze binary tested", pass: squeezeFound })
|
|
67
67
|
|
|
68
68
|
// 6. Config is valid JSON/JSONC
|
|
69
69
|
let configValid = false
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process"
|
|
2
|
-
import { chmodSync, mkdirSync, existsSync, writeFileSync, unlinkSync, readFileSync } from "node:fs"
|
|
2
|
+
import { chmodSync, mkdirSync, existsSync, writeFileSync, unlinkSync, readFileSync, renameSync } from "node:fs"
|
|
3
3
|
import { extname, join, resolve, dirname } from "node:path"
|
|
4
4
|
import { fileURLToPath } from "node:url"
|
|
5
5
|
import { createHash } from "node:crypto"
|
|
@@ -7,7 +7,10 @@ import { getConfigDir } from "./paths.js"
|
|
|
7
7
|
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// Local binary name — friendlier than the upstream "rtk" name
|
|
11
|
+
const BIN_NAME = process.platform === "win32" ? "squeeze.exe" : "squeeze"
|
|
12
|
+
// Name of the binary inside the downloaded archive (upstream uses "rtk")
|
|
13
|
+
const RTK_BIN_NAME = process.platform === "win32" ? "rtk.exe" : "rtk"
|
|
11
14
|
export const ASSETS = {
|
|
12
15
|
"darwin-arm64": "rtk-aarch64-apple-darwin.tar.gz",
|
|
13
16
|
"darwin-x64": "rtk-x86_64-apple-darwin.tar.gz",
|
|
@@ -28,10 +31,10 @@ export function validatePath(path) {
|
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
export function
|
|
34
|
+
export function findSqueezePath() {
|
|
32
35
|
try {
|
|
33
36
|
const [cmd, ...args] =
|
|
34
|
-
process.platform === "win32" ? ["where", "
|
|
37
|
+
process.platform === "win32" ? ["where", "squeeze"] : ["which", "squeeze"]
|
|
35
38
|
return execFileSync(cmd, args, { stdio: "pipe" })
|
|
36
39
|
.toString()
|
|
37
40
|
.trim()
|
|
@@ -85,13 +88,13 @@ function verifyChecksum(buffer, assetName) {
|
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
export async function installSqueeze() {
|
|
88
|
-
let rtkPath =
|
|
91
|
+
let rtkPath = findSqueezePath()
|
|
89
92
|
|
|
90
93
|
if (!rtkPath) {
|
|
91
94
|
// Not on PATH — download it to the managed location
|
|
92
95
|
const asset = platformAsset()
|
|
93
96
|
if (!asset) {
|
|
94
|
-
console.warn("⚠ Squeeze: unsupported platform —
|
|
97
|
+
console.warn("⚠ Squeeze: unsupported platform — squeeze binary unavailable")
|
|
95
98
|
return
|
|
96
99
|
}
|
|
97
100
|
|
|
@@ -127,14 +130,31 @@ export async function installSqueeze() {
|
|
|
127
130
|
|
|
128
131
|
try { unlinkSync(archivePath) } catch {}
|
|
129
132
|
|
|
133
|
+
// Archive contains "rtk"/"rtk.exe" — rename to local BIN_NAME ("squeeze")
|
|
134
|
+
const extracted = join(binDir, RTK_BIN_NAME)
|
|
130
135
|
rtkPath = join(binDir, BIN_NAME)
|
|
136
|
+
if (existsSync(extracted) && extracted !== rtkPath) {
|
|
137
|
+
renameSync(extracted, rtkPath)
|
|
138
|
+
}
|
|
131
139
|
|
|
132
140
|
if (process.platform !== "win32" && existsSync(rtkPath)) {
|
|
133
141
|
chmodSync(rtkPath, 0o755)
|
|
134
142
|
}
|
|
135
|
-
|
|
143
|
+
|
|
144
|
+
// Verify that the downloaded binary is executable and runs on this platform
|
|
145
|
+
try {
|
|
146
|
+
execFileSync(rtkPath, ["--help"], { stdio: "ignore" })
|
|
147
|
+
} catch (err) {
|
|
148
|
+
// If it throws because of execution format or permissions (e.g. ENOEXEC, EACCES, ENOENT)
|
|
149
|
+
if (err.code === "ENOEXEC" || err.code === "EACCES" || err.code === "ENOENT") {
|
|
150
|
+
try { unlinkSync(rtkPath) } catch {}
|
|
151
|
+
throw new Error(`Downloaded binary is not executable on this platform: ${err.message}`)
|
|
152
|
+
}
|
|
153
|
+
// If it ran but exited with non-zero (like returning exit code 1 for --help), that's fine, it means it is executable!
|
|
154
|
+
}
|
|
155
|
+
} catch (err) {
|
|
136
156
|
try { unlinkSync(archivePath) } catch {}
|
|
137
|
-
console.warn(
|
|
157
|
+
console.warn(`⚠ Squeeze: installation failed (${err.message}) — squeeze plugin disabled`)
|
|
138
158
|
return
|
|
139
159
|
}
|
|
140
160
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "node:test"
|
|
2
2
|
import assert from "node:assert"
|
|
3
|
-
import { platformAsset, validatePath,
|
|
3
|
+
import { platformAsset, validatePath, findSqueezePath, ASSETS } from "./squeezeInstaller.js"
|
|
4
4
|
|
|
5
5
|
describe("platformAsset", () => {
|
|
6
6
|
it("returns a string or null for current platform", () => {
|
|
@@ -66,11 +66,9 @@ describe("validatePath", () => {
|
|
|
66
66
|
})
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
describe("
|
|
70
|
-
it("returns null when
|
|
71
|
-
|
|
72
|
-
// string back — but the test doesn't crash either way.
|
|
73
|
-
const result = findRtkPath()
|
|
69
|
+
describe("findSqueezePath", () => {
|
|
70
|
+
it("returns null when squeeze is not available on PATH", () => {
|
|
71
|
+
const result = findSqueezePath()
|
|
74
72
|
// Either null (not on PATH) or a non-empty string (found)
|
|
75
73
|
assert.ok(result === null || typeof result === "string")
|
|
76
74
|
})
|
package/src/plugins/README.md
CHANGED
|
@@ -21,16 +21,16 @@ attribution, and one-line tool-invocation summaries, separated by
|
|
|
21
21
|
## `pluidr-squeeze.js` — command output filtering
|
|
22
22
|
|
|
23
23
|
Implementation of the `PluidrSqueezePlugin`. Hooks into `tool.execute.before`
|
|
24
|
-
to rewrite bash commands through the `
|
|
25
|
-
groups, truncates, and deduplicates command
|
|
26
|
-
tokens across all agents.
|
|
24
|
+
to rewrite bash commands through the local `squeeze` binary (built on the `rtk`
|
|
25
|
+
engine). The binary filters, groups, truncates, and deduplicates command
|
|
26
|
+
output — saving 60-90% of tokens across all agents.
|
|
27
27
|
|
|
28
|
-
The plugin is a thin delegator: all rewrite logic lives in the
|
|
28
|
+
The plugin is a thin delegator: all rewrite logic lives in the engine binary.
|
|
29
29
|
It checks `PATH` first, then falls back to Pluidr's managed location at
|
|
30
|
-
`~/.config/opencode/bin/
|
|
30
|
+
`~/.config/opencode/bin/squeeze`. If the binary is not found, the plugin
|
|
31
31
|
gracefully disables itself with a warning.
|
|
32
32
|
|
|
33
|
-
`pluidr init` downloads the `
|
|
33
|
+
`pluidr init` downloads the `squeeze` binary automatically during setup.
|
|
34
34
|
|
|
35
35
|
## Why `pluidr-flow` was extracted
|
|
36
36
|
|
|
@@ -61,8 +61,8 @@ agent in its pipeline, so the tool would have no caller — YAGNI.
|
|
|
61
61
|
|
|
62
62
|
`pluidr init` copies both plugins into `~/.config/opencode/plugins/` and
|
|
63
63
|
writes a `package.json` declaring `@opencode-ai/plugin: ^1.17.9` as a
|
|
64
|
-
dependency. For `pluidr-squeeze`, it also downloads the `
|
|
65
|
-
to `~/.config/opencode/bin/`. On OpenCode's first launch, the
|
|
66
|
-
runtime detects the `package.json` and runs `bun install`
|
|
67
|
-
no user action required. Once installed, both plugins are
|
|
68
|
-
every agent that has the appropriate permissions.
|
|
64
|
+
dependency. For `pluidr-squeeze`, it also downloads and extracts the `squeeze`
|
|
65
|
+
engine binary to `~/.config/opencode/bin/`. On OpenCode's first launch, the
|
|
66
|
+
bundled Bun runtime detects the `package.json` and runs `bun install`
|
|
67
|
+
automatically — no user action required. Once installed, both plugins are
|
|
68
|
+
available to every agent that has the appropriate permissions.
|
|
@@ -2,21 +2,42 @@ import { existsSync } from "node:fs"
|
|
|
2
2
|
import { homedir } from "node:os"
|
|
3
3
|
import { join } from "node:path"
|
|
4
4
|
|
|
5
|
-
const BIN_NAME = process.platform === "win32" ? "
|
|
5
|
+
const BIN_NAME = process.platform === "win32" ? "squeeze.exe" : "squeeze"
|
|
6
6
|
const SQUEEZE_BIN = join(homedir(), ".config", "opencode", "bin", BIN_NAME)
|
|
7
7
|
|
|
8
8
|
function normalise(p) {
|
|
9
9
|
return p.replace(/\\/g, "/")
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// Quote a path if it contains spaces so the shell treats it as one token.
|
|
13
|
+
export function shellQuote(p) {
|
|
14
|
+
return p.includes(" ") ? `"${p}"` : p
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Exported for testing. When binPath is the managed full path (not "squeeze"
|
|
18
|
+
// on PATH), the binary may output "rtk <cmd>" or "squeeze <cmd>" depending on
|
|
19
|
+
// how it resolves its own name. Replace the leading token with the actual
|
|
20
|
+
// binPath (quoted if it contains spaces) so the shell can find it. When
|
|
21
|
+
// squeeze is on PATH, normalise any "rtk" prefix to "squeeze".
|
|
22
|
+
export function resolveRewritten(rewritten, binPath) {
|
|
23
|
+
const m = rewritten.match(/^(rtk|squeeze)(\s|$)/)
|
|
24
|
+
if (!m) return rewritten
|
|
25
|
+
if (binPath === "squeeze") {
|
|
26
|
+
// squeeze is on PATH — normalise any leading binary name to "squeeze"
|
|
27
|
+
return "squeeze" + rewritten.slice(m[1].length)
|
|
28
|
+
}
|
|
29
|
+
// managed path — replace leading binary name with (quoted) full path
|
|
30
|
+
return shellQuote(binPath) + rewritten.slice(m[1].length)
|
|
31
|
+
}
|
|
32
|
+
|
|
12
33
|
async function findBinary($) {
|
|
13
34
|
try {
|
|
14
35
|
if (process.platform === "win32") {
|
|
15
|
-
await $`where
|
|
36
|
+
await $`where squeeze`.quiet()
|
|
16
37
|
} else {
|
|
17
|
-
await $`which
|
|
38
|
+
await $`which squeeze`.quiet()
|
|
18
39
|
}
|
|
19
|
-
return "
|
|
40
|
+
return "squeeze"
|
|
20
41
|
} catch {
|
|
21
42
|
if (existsSync(SQUEEZE_BIN)) {
|
|
22
43
|
return normalise(SQUEEZE_BIN)
|
|
@@ -28,7 +49,7 @@ async function findBinary($) {
|
|
|
28
49
|
export const PluidrSqueezePlugin = async ({ $ }) => {
|
|
29
50
|
const binPath = await findBinary($)
|
|
30
51
|
if (!binPath) {
|
|
31
|
-
console.warn("[squeeze]
|
|
52
|
+
console.warn("[squeeze] squeeze binary not found — plugin disabled")
|
|
32
53
|
return {}
|
|
33
54
|
}
|
|
34
55
|
|
|
@@ -46,7 +67,7 @@ export const PluidrSqueezePlugin = async ({ $ }) => {
|
|
|
46
67
|
const result = await $`${binPath} rewrite ${command}`.quiet().nothrow()
|
|
47
68
|
const rewritten = String(result.stdout).trim()
|
|
48
69
|
if (rewritten && rewritten !== command) {
|
|
49
|
-
args.command = rewritten
|
|
70
|
+
args.command = resolveRewritten(rewritten, binPath)
|
|
50
71
|
}
|
|
51
72
|
} catch {
|
|
52
73
|
// rewrite failed — pass through unchanged
|
|
@@ -142,17 +142,18 @@ only). That's it — you have no `read`, `glob`, `grep`, `webfetch`,
|
|
|
142
142
|
**Guardrail Gate 1 — EXPLORE to PLAN or BUILD transition**:
|
|
143
143
|
After you have gathered sufficient context and produced recommendations,
|
|
144
144
|
you MUST internally assess whether the feature is simple enough to skip
|
|
145
|
-
the PRD phase.
|
|
145
|
+
the PRD phase.
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
- The approach is well-understood (no research unknowns)
|
|
149
|
-
- Few files to change (1-3 files)
|
|
150
|
-
- Low risk of regressions or side effects
|
|
151
|
-
- Clear, unambiguous requirements
|
|
152
|
-
- No complex dependencies or integration concerns
|
|
147
|
+
**Bias heavily toward the PLAN phase.** Writing a PRD is the safest default. You must default to the PLAN (PRD) phase unless the feature is extremely simple.
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
A feature is **simple** ONLY when ALL of these apply:
|
|
150
|
+
- It is an extremely trivial change (e.g., fixing a typo, updating a single configuration value, changing a single line of text).
|
|
151
|
+
- It changes only 1 single file.
|
|
152
|
+
- The approach is completely obvious and well-understood (no research unknowns).
|
|
153
|
+
- Zero risk of regressions or side effects.
|
|
154
|
+
- No new files or new tests are created.
|
|
155
|
+
|
|
156
|
+
If the change involves writing new logic, adding new functions, creating a new file, or modifying multiple files, it is **complex** by definition — you MUST proceed to the PLAN phase. Do not offer a direct build shortcut.
|
|
156
157
|
|
|
157
158
|
Based on your assessment:
|
|
158
159
|
|
|
@@ -395,6 +396,7 @@ elsewhere. Do not accept a report that is not in `docs/reports/`.
|
|
|
395
396
|
already confirmed as PASS.
|
|
396
397
|
- **Clarification** — If anything is ambiguous at any phase, ask the user
|
|
397
398
|
using multiple-choice options (2-4 short choices per question).
|
|
399
|
+
- **Front-End / UI Mockups** — If the user asks for a design, mockup, or anything related to the Front-End (FE), you must provide a detailed ASCII mockup of the UI structure in your output.
|
|
398
400
|
|
|
399
401
|
---
|
|
400
402
|
|
|
@@ -14,6 +14,7 @@ You have no `task` permission — you cannot invoke any other agent or subagent.
|
|
|
14
14
|
- **Principle of Least Astonishment** — Prioritize patterns that already exist in the codebase over introducing new ones. The most predictable solution is the one the codebase already uses.
|
|
15
15
|
- **DRY** — Before researching a solution, check if a precedent already exists in the codebase. Do not research from scratch if prior art exists.
|
|
16
16
|
- **Source Verification** — Explicitly distinguish between "confirmed from codebase/docs" (with exact source citation) and "inferred/recommended" (with rationale). Never blend the two.
|
|
17
|
+
- **Front-End / UI Mockups** — If the research request or context involves UI components, mockups, or Front-End (FE) design elements, include a detailed ASCII mockup representing the visual layout in your output.
|
|
17
18
|
|
|
18
19
|
## Mandatory Output Schema
|
|
19
20
|
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
"*.env": "deny",
|
|
7
7
|
"*.env.*": "deny",
|
|
8
8
|
"secrets/**": "deny"
|
|
9
|
+
},
|
|
10
|
+
"edit": {
|
|
11
|
+
"*.env": "deny",
|
|
12
|
+
"*.env.*": "deny",
|
|
13
|
+
"secrets/**": "deny"
|
|
14
|
+
},
|
|
15
|
+
"write": {
|
|
16
|
+
"*.env": "deny",
|
|
17
|
+
"*.env.*": "deny",
|
|
18
|
+
"secrets/**": "deny"
|
|
9
19
|
}
|
|
10
20
|
},
|
|
11
21
|
"agent": {
|
|
@@ -75,17 +85,8 @@
|
|
|
75
85
|
"read": "allow",
|
|
76
86
|
"glob": "allow",
|
|
77
87
|
"grep": "allow",
|
|
78
|
-
"edit":
|
|
79
|
-
|
|
80
|
-
"src/**": "allow",
|
|
81
|
-
"*.{json,md,txt}": "allow",
|
|
82
|
-
"package.json": "allow"
|
|
83
|
-
},
|
|
84
|
-
"write": {
|
|
85
|
-
"*": "deny",
|
|
86
|
-
"src/**": "allow",
|
|
87
|
-
"*.{json,md,txt}": "allow"
|
|
88
|
-
},
|
|
88
|
+
"edit": "allow",
|
|
89
|
+
"write": "allow",
|
|
89
90
|
"todowrite": "allow",
|
|
90
91
|
"bash": {
|
|
91
92
|
"*": "deny",
|
|
@@ -99,17 +100,10 @@
|
|
|
99
100
|
"mv *": "allow",
|
|
100
101
|
"cat *": "allow",
|
|
101
102
|
"ls *": "allow",
|
|
102
|
-
"pwd": "allow",
|
|
103
|
-
"echo *": "allow",
|
|
104
103
|
"rg *": "allow",
|
|
105
104
|
"grep *": "allow",
|
|
106
105
|
"find *": "allow",
|
|
107
|
-
"
|
|
108
|
-
"where *": "allow",
|
|
109
|
-
"dir *": "allow",
|
|
110
|
-
"type *": "allow",
|
|
111
|
-
"node --test": "allow",
|
|
112
|
-
"cd *": "allow"
|
|
106
|
+
"node --test": "allow"
|
|
113
107
|
},
|
|
114
108
|
"task": "deny"
|
|
115
109
|
}
|
|
@@ -117,7 +111,7 @@
|
|
|
117
111
|
|
|
118
112
|
"tester": {
|
|
119
113
|
"mode": "subagent",
|
|
120
|
-
"description": "Runs tests on implemented code and reports results (PASS/FAIL/BLOCKED) with coverage gaps. Cannot fix code,
|
|
114
|
+
"description": "Runs tests on implemented code and reports results (PASS/FAIL/BLOCKED) with coverage gaps. Cannot fix code, install deps, or decide next steps.",
|
|
121
115
|
"prompt": "{file:./prompts/tester.txt}",
|
|
122
116
|
"permission": {
|
|
123
117
|
"read": "allow",
|
|
@@ -126,7 +120,19 @@
|
|
|
126
120
|
"edit": "deny",
|
|
127
121
|
"write": "deny",
|
|
128
122
|
"todowrite": "deny",
|
|
129
|
-
"bash":
|
|
123
|
+
"bash": {
|
|
124
|
+
"*": "deny",
|
|
125
|
+
"node --test": "allow",
|
|
126
|
+
"npm test": "allow",
|
|
127
|
+
"npx *": "allow",
|
|
128
|
+
"rg *": "allow",
|
|
129
|
+
"grep *": "allow",
|
|
130
|
+
"find *": "allow",
|
|
131
|
+
"cat *": "allow",
|
|
132
|
+
"ls *": "allow",
|
|
133
|
+
"git diff*": "allow",
|
|
134
|
+
"git log*": "allow"
|
|
135
|
+
},
|
|
130
136
|
"task": "deny"
|
|
131
137
|
}
|
|
132
138
|
},
|
|
@@ -156,14 +162,23 @@
|
|
|
156
162
|
|
|
157
163
|
"compose-reporter": {
|
|
158
164
|
"mode": "subagent",
|
|
159
|
-
"description": "Text-file executor for Composer. Writes and edits .md/.txt files
|
|
165
|
+
"description": "Text-file executor for Composer. Writes and edits .md/.txt files under docs/ — stateless formatter for documents only. No bash.",
|
|
160
166
|
"prompt": "{file:./prompts/compose-reporter.txt}",
|
|
161
167
|
"permission": {
|
|
162
|
-
"read":
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
"
|
|
168
|
+
"read": {
|
|
169
|
+
"*": "deny",
|
|
170
|
+
"docs/**": "allow"
|
|
171
|
+
},
|
|
172
|
+
"glob": "deny",
|
|
173
|
+
"grep": "deny",
|
|
174
|
+
"edit": {
|
|
175
|
+
"*": "deny",
|
|
176
|
+
"docs/**": "allow"
|
|
177
|
+
},
|
|
178
|
+
"write": {
|
|
179
|
+
"*": "deny",
|
|
180
|
+
"docs/**": "allow"
|
|
181
|
+
},
|
|
167
182
|
"bash": "deny",
|
|
168
183
|
"todowrite": "deny",
|
|
169
184
|
"task": "deny"
|
|
@@ -181,7 +196,16 @@
|
|
|
181
196
|
"grep": "allow",
|
|
182
197
|
"edit": "deny",
|
|
183
198
|
"write": "deny",
|
|
184
|
-
"bash":
|
|
199
|
+
"bash": {
|
|
200
|
+
"*": "deny",
|
|
201
|
+
"rg *": "allow",
|
|
202
|
+
"grep *": "allow",
|
|
203
|
+
"find *": "allow",
|
|
204
|
+
"cat *": "allow",
|
|
205
|
+
"ls *": "allow",
|
|
206
|
+
"node --version": "allow",
|
|
207
|
+
"npm --version": "allow"
|
|
208
|
+
},
|
|
185
209
|
"webfetch": "allow",
|
|
186
210
|
"websearch": "allow",
|
|
187
211
|
"task": "deny"
|
|
@@ -193,9 +217,12 @@
|
|
|
193
217
|
"description": "Stateless formatter for Composer. Produces PRD documents from structured input — no inference, no decisions. PRD mode only.",
|
|
194
218
|
"prompt": "{file:./prompts/plan-writer.txt}",
|
|
195
219
|
"permission": {
|
|
196
|
-
"read":
|
|
197
|
-
|
|
198
|
-
|
|
220
|
+
"read": {
|
|
221
|
+
"*": "deny",
|
|
222
|
+
"docs/plans/**": "allow"
|
|
223
|
+
},
|
|
224
|
+
"glob": "deny",
|
|
225
|
+
"grep": "deny",
|
|
199
226
|
"edit": {
|
|
200
227
|
"*": "deny",
|
|
201
228
|
"docs/plans/**": "allow"
|
|
@@ -264,41 +291,28 @@
|
|
|
264
291
|
"read": "allow",
|
|
265
292
|
"glob": "allow",
|
|
266
293
|
"grep": "allow",
|
|
267
|
-
"edit":
|
|
268
|
-
|
|
269
|
-
"src/**": "allow",
|
|
270
|
-
"*.{json,md,txt}": "allow",
|
|
271
|
-
"package.json": "allow"
|
|
272
|
-
},
|
|
273
|
-
"write": {
|
|
274
|
-
"*": "deny",
|
|
275
|
-
"src/**": "allow",
|
|
276
|
-
"*.{json,md,txt}": "allow"
|
|
277
|
-
},
|
|
294
|
+
"edit": "allow",
|
|
295
|
+
"write": "allow",
|
|
278
296
|
"todowrite": "allow",
|
|
279
297
|
"bash": {
|
|
280
298
|
"*": "deny",
|
|
299
|
+
"node --test": "allow",
|
|
300
|
+
"npm test": "allow",
|
|
281
301
|
"npm *": "allow",
|
|
282
302
|
"npx *": "allow",
|
|
283
303
|
"node *": "allow",
|
|
284
|
-
"git *": "allow",
|
|
304
|
+
"git diff*": "allow",
|
|
305
|
+
"git log*": "allow",
|
|
306
|
+
"git show*": "allow",
|
|
285
307
|
"mkdir *": "allow",
|
|
286
308
|
"cp *": "allow",
|
|
287
309
|
"rm *": "allow",
|
|
288
310
|
"mv *": "allow",
|
|
289
311
|
"cat *": "allow",
|
|
290
312
|
"ls *": "allow",
|
|
291
|
-
"pwd": "allow",
|
|
292
|
-
"echo *": "allow",
|
|
293
313
|
"rg *": "allow",
|
|
294
314
|
"grep *": "allow",
|
|
295
|
-
"find *": "allow"
|
|
296
|
-
"which *": "allow",
|
|
297
|
-
"where *": "allow",
|
|
298
|
-
"dir *": "allow",
|
|
299
|
-
"type *": "allow",
|
|
300
|
-
"node --test": "allow",
|
|
301
|
-
"cd *": "allow"
|
|
315
|
+
"find *": "allow"
|
|
302
316
|
},
|
|
303
317
|
"task": "deny"
|
|
304
318
|
}
|
|
@@ -309,9 +323,12 @@
|
|
|
309
323
|
"description": "Stateless formatter for Debugger. Produces diagnosis + remedy reports from structured input — no inference, no decisions. Summary mode only.",
|
|
310
324
|
"prompt": "{file:./prompts/debug-reporter.txt}",
|
|
311
325
|
"permission": {
|
|
312
|
-
"read":
|
|
313
|
-
|
|
314
|
-
|
|
326
|
+
"read": {
|
|
327
|
+
"*": "deny",
|
|
328
|
+
"docs/reports/**": "allow"
|
|
329
|
+
},
|
|
330
|
+
"glob": "deny",
|
|
331
|
+
"grep": "deny",
|
|
315
332
|
"edit": {
|
|
316
333
|
"*": "deny",
|
|
317
334
|
"docs/reports/**": "allow"
|
|
@@ -361,7 +378,22 @@
|
|
|
361
378
|
"grep": "allow",
|
|
362
379
|
"edit": "deny",
|
|
363
380
|
"write": "deny",
|
|
364
|
-
"bash":
|
|
381
|
+
"bash": {
|
|
382
|
+
"*": "deny",
|
|
383
|
+
"rg *": "allow",
|
|
384
|
+
"grep *": "allow",
|
|
385
|
+
"find *": "allow",
|
|
386
|
+
"cat *": "allow",
|
|
387
|
+
"ls *": "allow",
|
|
388
|
+
"git log*": "allow",
|
|
389
|
+
"git diff*": "allow",
|
|
390
|
+
"git show*": "allow",
|
|
391
|
+
"git blame*": "allow",
|
|
392
|
+
"node --version": "allow",
|
|
393
|
+
"npm --version": "allow",
|
|
394
|
+
"which *": "allow",
|
|
395
|
+
"where *": "allow"
|
|
396
|
+
},
|
|
365
397
|
"task": "deny"
|
|
366
398
|
}
|
|
367
399
|
},
|
|
@@ -374,18 +406,8 @@
|
|
|
374
406
|
"read": "allow",
|
|
375
407
|
"glob": "allow",
|
|
376
408
|
"grep": "allow",
|
|
377
|
-
"edit":
|
|
378
|
-
|
|
379
|
-
"src/**": "allow",
|
|
380
|
-
"*.{json,md,txt}": "allow",
|
|
381
|
-
"package.json": "allow"
|
|
382
|
-
},
|
|
383
|
-
"write": {
|
|
384
|
-
"*": "deny",
|
|
385
|
-
"src/**": "allow",
|
|
386
|
-
"*.{json,md,txt}": "allow"
|
|
387
|
-
},
|
|
388
|
-
"todowrite": "allow",
|
|
409
|
+
"edit": "allow",
|
|
410
|
+
"write": "allow",
|
|
389
411
|
"bash": {
|
|
390
412
|
"*": "deny",
|
|
391
413
|
"npm *": "allow",
|
|
@@ -398,17 +420,10 @@
|
|
|
398
420
|
"mv *": "allow",
|
|
399
421
|
"cat *": "allow",
|
|
400
422
|
"ls *": "allow",
|
|
401
|
-
"pwd": "allow",
|
|
402
|
-
"echo *": "allow",
|
|
403
423
|
"rg *": "allow",
|
|
404
424
|
"grep *": "allow",
|
|
405
425
|
"find *": "allow",
|
|
406
|
-
"
|
|
407
|
-
"where *": "allow",
|
|
408
|
-
"dir *": "allow",
|
|
409
|
-
"type *": "allow",
|
|
410
|
-
"node --test": "allow",
|
|
411
|
-
"cd *": "allow"
|
|
426
|
+
"node --test": "allow"
|
|
412
427
|
},
|
|
413
428
|
"task": "deny"
|
|
414
429
|
}
|
|
@@ -424,7 +439,17 @@
|
|
|
424
439
|
"grep": "allow",
|
|
425
440
|
"edit": "deny",
|
|
426
441
|
"write": "deny",
|
|
427
|
-
"bash":
|
|
442
|
+
"bash": {
|
|
443
|
+
"*": "deny",
|
|
444
|
+
"rg *": "allow",
|
|
445
|
+
"grep *": "allow",
|
|
446
|
+
"find *": "allow",
|
|
447
|
+
"cat *": "allow",
|
|
448
|
+
"git diff*": "allow",
|
|
449
|
+
"git log*": "allow",
|
|
450
|
+
"node --test": "allow",
|
|
451
|
+
"npm test": "allow"
|
|
452
|
+
},
|
|
428
453
|
"task": "deny"
|
|
429
454
|
}
|
|
430
455
|
},
|
|
@@ -434,9 +459,12 @@
|
|
|
434
459
|
"description": "Stateless formatter for Prober. Produces security audit reports under docs/reports/.",
|
|
435
460
|
"prompt": "{file:./prompts/probe-reporter.txt}",
|
|
436
461
|
"permission": {
|
|
437
|
-
"read":
|
|
438
|
-
|
|
439
|
-
|
|
462
|
+
"read": {
|
|
463
|
+
"*": "deny",
|
|
464
|
+
"docs/reports/**": "allow"
|
|
465
|
+
},
|
|
466
|
+
"glob": "deny",
|
|
467
|
+
"grep": "deny",
|
|
440
468
|
"edit": {
|
|
441
469
|
"*": "deny",
|
|
442
470
|
"docs/reports/**": "allow"
|