pluidr 0.7.1 → 0.7.3

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 CHANGED
@@ -12,11 +12,11 @@
12
12
 
13
13
  Pluidr installs **3 primary agents + 14 subagents** into OpenCode — each primary agent owns its exclusive subagents, scoped permissions, and enforced workflow rules. No shared subagents. No ad-hoc delegation.
14
14
 
15
- | Primary Agent | Model Tier | Purpose | Subagents |
16
- |---|---|---|---|
17
- | **Composer** | 🧠 Reasoning | Feature work — Explore → Plan → Build | Researcher, Plan-Writer, Plan-Checker, Coder, Tester, Reviewer, Compose-Reporter |
18
- | **Debugger** | 🧠 Reasoning | Bug investigation — Investigate → Fix → Report | Inspector, Fixer, Debug-Reporter |
19
- | **Prober** | 🧠 Reasoning | Security audit — Trace → Patch → Audit | Tracer, Patcher, Auditor, Probe-Reporter |
15
+ | Primary Agent | Purpose | Subagents |
16
+ |---|---|---|
17
+ | **Composer** | Feature work — Explore → Plan → Build | Researcher, Plan-Writer, Plan-Checker, Coder, Tester, Reviewer, Compose-Reporter |
18
+ | **Debugger** | Bug investigation — Investigate → Fix → Report | Inspector, Fixer, Debug-Reporter |
19
+ | **Prober** | Security audit — Trace → Patch → Audit | Tracer, Patcher, Auditor, Probe-Reporter |
20
20
 
21
21
  ---
22
22
 
@@ -35,21 +35,20 @@ You describe a feature / idea
35
35
  │ │
36
36
  │ Delegate: Researcher │
37
37
  │ → deep codebase + web fact-finding │
38
- │ → confirmed_facts / inferred_facts /
39
- │ unknowns / risks │
38
+ │ → confirmed-inferred facts / risks
40
39
  │ │
41
40
  │ Composer internally assesses: │
42
41
  │ Is this feature simple or complex? │
43
42
  └──────────┬──────────────────────────────┘
44
43
 
45
- ┌────────┴────────┐
46
- │ Simple feature │ Complex feature
47
-
48
- GUARDRAIL GATE 1a GUARDRAIL GATE 1b
49
- "Build directly?" "Write a PRD?"
50
-
51
- │ Yes │ Yes
52
-
44
+ ┌────────┴───────────────┐
45
+ │ Simple feature │ Complex feature
46
+
47
+ GUARDRAIL GATE 1a GUARDRAIL GATE 1b
48
+ "Build directly?" "Write a PRD?"
49
+
50
+ │ Yes │ Yes
51
+
53
52
  │ ┌──────────────────────────────┐
54
53
  │ │ PLAN PHASE │
55
54
  │ │ │
@@ -62,9 +61,9 @@ GUARDRAIL GATE 1a GUARDRAIL GATE 1b
62
61
  │ │ │
63
62
  │ │ PASS → GUARDRAIL GATE 2 │
64
63
  │ │ "Build from this PRD?" │
65
- └──────────┬───────────────────┘
66
- │ Yes
67
- └──────────┬──────────┘
64
+ └─────────────┬────────────────┘
65
+ │ Yes
66
+ └──────────┬─────────────┘
68
67
 
69
68
  ┌────────────────────────────────────────────┐
70
69
  │ BUILD PHASE │
@@ -293,12 +292,6 @@ All agents resolve conflicts using this priority order (defined in `hierarchy.tx
293
292
  npm install -g pluidr
294
293
  ```
295
294
 
296
- Or run directly without installing:
297
-
298
- ```sh
299
- npx pluidr init
300
- ```
301
-
302
295
  ## Usage
303
296
 
304
297
  ### `pluidr`
@@ -328,6 +321,7 @@ Checks installation health and reports ✓/✗ for each component:
328
321
  - `opencode.jsonc` exists
329
322
  - All 18 prompt files present
330
323
  - Both plugin files present
324
+ - `pluidr-contrast` theme file present
331
325
  - `package.json` with `@opencode-ai/plugin` dependency
332
326
  - squeeze binary available
333
327
  - Config is valid JSON
@@ -343,12 +337,13 @@ Checks the npm registry for a newer version of pluidr and prompts to install it.
343
337
  Restores your previous configuration:
344
338
 
345
339
  - Finds the latest timestamped backup and restores it to `opencode.jsonc`
346
- - Removes `prompts/`, `plugins/`, and `bin/` directories
340
+ - Restores `tui.json` from the latest backup (returning the TUI theme to its pre-Pluidr state)
341
+ - Removes `prompts/`, `plugins/`, `bin/`, and `themes/pluidr-contrast.json` directories and files
347
342
  - Preserves `opencode.jsonc` and `package.json`
348
343
 
349
344
  ---
350
345
 
351
- ## Bundled Plugins
346
+ ## Bundled Package
352
347
 
353
348
  ### `pluidr-flow`
354
349
 
@@ -363,3 +358,7 @@ Provides subagents with cross-session context access:
363
358
  Hooks into tool execution to rewrite bash commands through the `squeeze` binary, filtering verbose output and saving **60–90% of tokens** across all agents.
364
359
 
365
360
  Both plugins and their dependency declaration are installed automatically by `pluidr init` — no extra user action required. On OpenCode's first launch, the bundled Bun runtime installs `@opencode-ai/plugin` from the generated `package.json`.
361
+
362
+ ### Themes & Colors
363
+
364
+ Pluidr comes with a pre-configured OLED high-contrast dark theme called **`pluidr-contrast`** that is automatically applied to your OpenCode terminal interface.
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "name": "pluidr",
3
- "version": "0.7.1",
4
- "description": "Opinionated Engineering Workflow for OpenCode — Explore, Plan, Build, Debug, and Security-Audit.",
5
- "keywords": [
6
- "opencode",
7
- "workflow",
8
- "engineering",
9
- "pipeline",
10
- "ai-agents",
11
- "code-review",
12
- "debugging",
13
- "security-audit",
14
- "security",
15
- "planner",
16
- "builder",
17
- "cli"
18
- ],
19
- "license": "MIT",
20
- "type": "module",
21
- "scripts": {
22
- "test": "node --test"
23
- },
24
- "bin": {
25
- "pluidr": "bin/pluidr.js"
26
- },
27
- "repository": {
28
- "type": "git",
29
- "url": "git+https://github.com/funara/pluidr.git"
30
- },
31
- "homepage": "https://github.com/funara/pluidr#readme",
32
- "bugs": {
33
- "url": "https://github.com/funara/pluidr/issues"
34
- },
35
- "engines": {
36
- "node": ">=18"
37
- },
38
- "files": [
39
- "bin",
40
- "src"
41
- ],
42
- "dependencies": {
43
- "@clack/prompts": "^0.9"
44
- }
45
- }
1
+ {
2
+ "name": "pluidr",
3
+ "version": "0.7.3",
4
+ "description": "Opinionated Engineering Workflow for OpenCode — Plan, Build, Review.",
5
+ "keywords": [
6
+ "opencode",
7
+ "workflow",
8
+ "engineering",
9
+ "pipeline",
10
+ "ai-agents",
11
+ "code-review",
12
+ "debugging",
13
+ "security-audit",
14
+ "security",
15
+ "planner",
16
+ "builder",
17
+ "cli"
18
+ ],
19
+ "license": "MIT",
20
+ "type": "module",
21
+ "scripts": {
22
+ "test": "node --test"
23
+ },
24
+ "bin": {
25
+ "pluidr": "bin/pluidr.js"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/funara/pluidr.git"
30
+ },
31
+ "homepage": "https://github.com/funara/pluidr#readme",
32
+ "bugs": {
33
+ "url": "https://github.com/funara/pluidr/issues"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "files": [
39
+ "bin",
40
+ "src"
41
+ ],
42
+ "dependencies": {
43
+ "@clack/prompts": "^0.9"
44
+ }
45
+ }
@@ -1,11 +1,18 @@
1
1
  import { existsSync, readFileSync, readdirSync } from "node:fs"
2
2
  import { join } from "node:path"
3
- import { getConfigDir, getConfigPath, getPromptsDir } from "../../core/paths.js"
3
+ import { getConfigDir, getConfigPath, getPromptsDir, getThemesDir } from "../../core/paths.js"
4
4
  import { findSqueezePath } from "../../core/squeezeInstaller.js"
5
5
 
6
6
  const EXPECTED_PROMPT_COUNT = 18
7
7
  const PLUGIN_FILES = ["pluidr-flow.js", "pluidr-squeeze.js"]
8
8
 
9
+ export function cleanJsonc(content) {
10
+ return content.replace(
11
+ /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)|(,\s*(?=[\]}]))/g,
12
+ (m, g1, g2) => g1 ? "" : g2 ? "" : m
13
+ )
14
+ }
15
+
9
16
  export function collectChecks() {
10
17
  const configDir = getConfigDir()
11
18
  const configPath = getConfigPath()
@@ -13,6 +20,7 @@ export function collectChecks() {
13
20
  const pluginsDir = join(configDir, "plugins")
14
21
  const packageJsonPath = join(configDir, "package.json")
15
22
  const binDir = join(configDir, "bin")
23
+ const themesDir = getThemesDir()
16
24
 
17
25
  const checks = []
18
26
 
@@ -39,6 +47,10 @@ export function collectChecks() {
39
47
  }
40
48
  checks.push({ component: "plugin files", pass: pluginsFound === PLUGIN_FILES.length, detail: `${pluginsFound}/${PLUGIN_FILES.length}` })
41
49
 
50
+ // 3b. Custom theme exists
51
+ const themeExists = existsSync(join(themesDir, "pluidr-contrast.json"))
52
+ checks.push({ component: "pluidr-contrast theme file", pass: themeExists })
53
+
42
54
  // 4. package.json with @opencode-ai/plugin dependency
43
55
  let pkgValid = false
44
56
  if (existsSync(packageJsonPath)) {
@@ -67,7 +79,7 @@ export function collectChecks() {
67
79
  if (configExists) {
68
80
  try {
69
81
  const content = readFileSync(configPath, "utf-8")
70
- const cleaned = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m)
82
+ const cleaned = cleanJsonc(content)
71
83
  JSON.parse(cleaned)
72
84
  configValid = true
73
85
  } catch {
@@ -1,4 +1,4 @@
1
- import { readFileSync } from "node:fs"
1
+ import { readFileSync, writeFileSync } from "node:fs"
2
2
  import { resolve, dirname } from "node:path"
3
3
  import { fileURLToPath } from "node:url"
4
4
  import { confirm, isCancel } from "@clack/prompts"
@@ -8,8 +8,10 @@ import { buildConfig } from "../../core/configBuilder.js"
8
8
  import { writeConfig } from "../../core/configWriter.js"
9
9
  import { writeAgentPrompts } from "../../core/agentPromptWriter.js"
10
10
  import { writePluginBundle, writePluginPackageJson } from "../../core/pluginWriter.js"
11
+ import { writeThemes } from "../../core/themeWriter.js"
12
+ import { writeTuiConfig } from "../../core/tuiConfigWriter.js"
11
13
  import { installSqueeze } from "../../core/squeezeInstaller.js"
12
- import { getConfigPath } from "../../core/paths.js"
14
+ import { getConfigPath, getConfigDir } from "../../core/paths.js"
13
15
 
14
16
  const __dirname = dirname(fileURLToPath(import.meta.url))
15
17
  const TEMPLATES_DIR = resolve(__dirname, "../../templates")
@@ -32,6 +34,8 @@ export async function runInit() {
32
34
  const configObject = buildConfig(tierChoices, TEMPLATES_DIR)
33
35
  writeConfig(configObject)
34
36
  writeAgentPrompts(TEMPLATES_DIR)
37
+ writeThemes(TEMPLATES_DIR)
38
+ writeTuiConfig("pluidr-contrast")
35
39
  writePluginBundle()
36
40
  writePluginPackageJson()
37
41
  if (installSqueezePlugin) {
@@ -31,7 +31,7 @@ export async function runLaunch() {
31
31
  }
32
32
 
33
33
  // 3. Launch opencode
34
- console.log("Launching opencode...")
34
+ console.log("Launching pluidr...")
35
35
  const child = process.platform === "win32"
36
36
  ? spawn("opencode", { stdio: "inherit", shell: true })
37
37
  : spawn("opencode", [], { stdio: "inherit" })
@@ -16,6 +16,20 @@ function findLatestBackup() {
16
16
  return backups.length > 0 ? join(dir, backups[0]) : null
17
17
  }
18
18
 
19
+ function findLatestTuiBackup() {
20
+ const dir = getConfigDir()
21
+ let backups = []
22
+ try {
23
+ backups = readdirSync(dir)
24
+ .filter((f) => f.startsWith("tui.json.bak."))
25
+ .sort()
26
+ .reverse()
27
+ } catch {
28
+ return null
29
+ }
30
+ return backups.length > 0 ? join(dir, backups[0]) : null
31
+ }
32
+
19
33
  export async function runUninstall() {
20
34
  const configDir = getConfigDir()
21
35
  const configPath = getConfigPath()
@@ -32,6 +46,14 @@ export async function runUninstall() {
32
46
  summary.restored = latestBackup
33
47
  }
34
48
 
49
+ const latestTuiBackup = findLatestTuiBackup()
50
+ const tuiConfigPath = join(configDir, "tui.json")
51
+ if (latestTuiBackup) {
52
+ copyFileSync(latestTuiBackup, tuiConfigPath)
53
+ }
54
+
55
+ const themesDir = join(configDir, "themes")
56
+
35
57
  // Remove Pluidr-installed artifacts
36
58
  if (existsSync(promptsDir)) {
37
59
  rmSync(promptsDir, { recursive: true, force: true })
@@ -45,6 +67,11 @@ export async function runUninstall() {
45
67
  rmSync(binDir, { recursive: true, force: true })
46
68
  summary.removed.push("bin/")
47
69
  }
70
+ const customThemePath = join(themesDir, "pluidr-contrast.json")
71
+ if (existsSync(customThemePath)) {
72
+ rmSync(customThemePath, { force: true })
73
+ summary.removed.push("themes/pluidr-contrast.json")
74
+ }
48
75
 
49
76
  // Print summary
50
77
  console.log("Uninstall complete:")
@@ -1,6 +1,6 @@
1
1
  import { existsSync, copyFileSync, readdirSync, unlinkSync } from "node:fs"
2
2
  import { join, dirname } from "node:path"
3
- import { getConfigPath } from "./paths.js"
3
+ import { getConfigPath, getTuiConfigPath } from "./paths.js"
4
4
 
5
5
  const MAX_BACKUPS = 5
6
6
 
@@ -32,9 +32,15 @@ function rotateBackups(configPath) {
32
32
 
33
33
  export function backupExistingConfig(configPath) {
34
34
  const resolvedPath = configPath || getConfigPath()
35
- if (!existsSync(resolvedPath)) return
35
+ if (existsSync(resolvedPath)) {
36
+ rotateBackups(resolvedPath)
37
+ const backupPath = `${resolvedPath}.bak.${timestamp()}`
38
+ copyFileSync(resolvedPath, backupPath)
39
+ }
36
40
 
37
- rotateBackups(resolvedPath)
38
- const backupPath = `${resolvedPath}.bak.${timestamp()}`
39
- copyFileSync(resolvedPath, backupPath)
41
+ const tuiPath = getTuiConfigPath()
42
+ if (existsSync(tuiPath)) {
43
+ const backupTuiPath = `${tuiPath}.bak.${timestamp()}`
44
+ copyFileSync(tuiPath, backupTuiPath)
45
+ }
40
46
  }
package/src/core/paths.js CHANGED
@@ -5,5 +5,7 @@ const BASE = join(homedir(), ".config", "opencode")
5
5
 
6
6
  export const getConfigDir = () => BASE
7
7
  export const getConfigPath = () => join(BASE, "opencode.jsonc")
8
+ export const getTuiConfigPath = () => join(BASE, "tui.json")
8
9
  export const getPromptsDir = () => join(BASE, "prompts")
10
+ export const getThemesDir = () => join(BASE, "themes")
9
11
  // ponytail: getBackupPath removed (YAGNI)
@@ -2,7 +2,7 @@ import { describe, it } from "node:test"
2
2
  import assert from "node:assert"
3
3
  import { homedir } from "node:os"
4
4
  import { join } from "node:path"
5
- import { getConfigDir, getConfigPath, getPromptsDir } from "./paths.js"
5
+ import { getConfigDir, getConfigPath, getTuiConfigPath, getPromptsDir, getThemesDir } from "./paths.js"
6
6
 
7
7
  const BASE = join(homedir(), ".config", "opencode")
8
8
 
@@ -15,7 +15,15 @@ describe("paths", () => {
15
15
  assert.strictEqual(getConfigPath(), join(BASE, "opencode.jsonc"))
16
16
  })
17
17
 
18
+ it("getTuiConfigPath returns tui.json path", () => {
19
+ assert.strictEqual(getTuiConfigPath(), join(BASE, "tui.json"))
20
+ })
21
+
18
22
  it("getPromptsDir returns prompts path", () => {
19
23
  assert.strictEqual(getPromptsDir(), join(BASE, "prompts"))
20
24
  })
25
+
26
+ it("getThemesDir returns themes path", () => {
27
+ assert.strictEqual(getThemesDir(), join(BASE, "themes"))
28
+ })
21
29
  })
@@ -26,7 +26,7 @@ export function platformAsset() {
26
26
  }
27
27
 
28
28
  export function validatePath(path) {
29
- if (!/^[a-zA-Z0-9_\-\.\:\/\\]+$/.test(path)) {
29
+ if (!/^[a-zA-Z0-9_\-\.\:\/\\ ]+$/.test(path)) {
30
30
  throw new Error(`Invalid path: "${path}" contains unsafe characters`)
31
31
  }
32
32
  }
@@ -53,10 +53,7 @@ function extract(archivePath, destDir) {
53
53
  execFileSync("powershell", [
54
54
  "-NoProfile",
55
55
  "-Command",
56
- "Expand-Archive",
57
- "-LiteralPath", archivePath,
58
- "-DestinationPath", destDir,
59
- "-Force"
56
+ `Expand-Archive -LiteralPath '${archivePath}' -DestinationPath '${destDir}' -Force`
60
57
  ], { stdio: "ignore" })
61
58
  } else {
62
59
  execFileSync("tar", ["-xzf", archivePath, "-C", destDir], { stdio: "ignore" })
@@ -41,6 +41,10 @@ describe("validatePath", () => {
41
41
  assert.doesNotThrow(() => validatePath("C:\\Users\\test"))
42
42
  })
43
43
 
44
+ it("accepts paths with spaces", () => {
45
+ assert.doesNotThrow(() => validatePath("C:\\Users\\John Doe\\.config"))
46
+ })
47
+
44
48
  it("accepts paths with dots and dashes", () => {
45
49
  assert.doesNotThrow(() => validatePath("./dir/file-name.ext"))
46
50
  })
@@ -0,0 +1,12 @@
1
+ import { mkdirSync, copyFileSync } from "node:fs"
2
+ import { resolve } from "node:path"
3
+ import { getThemesDir } from "./paths.js"
4
+
5
+ export function writeThemes(templatesDir, destDir) {
6
+ const sourcePath = resolve(templatesDir, "themes", "pluidr-contrast.json")
7
+ const targetDir = destDir || getThemesDir()
8
+ const destPath = resolve(targetDir, "pluidr-contrast.json")
9
+
10
+ mkdirSync(targetDir, { recursive: true })
11
+ copyFileSync(sourcePath, destPath)
12
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, it, after } from "node:test"
2
+ import assert from "node:assert"
3
+ import { existsSync, mkdtempSync, readFileSync, rmSync, mkdirSync, writeFileSync } from "node:fs"
4
+ import { join } from "node:path"
5
+ import { tmpdir } from "node:os"
6
+ import { writeThemes } from "./themeWriter.js"
7
+
8
+ describe("themeWriter", () => {
9
+ const destDir = mkdtempSync(join(tmpdir(), "pluidr-theme-dest-"))
10
+
11
+ after(() => {
12
+ if (existsSync(destDir)) rmSync(destDir, { recursive: true })
13
+ })
14
+
15
+ it("copies theme file to destination directory", () => {
16
+ const tmpSrcDir = mkdtempSync(join(tmpdir(), "pluidr-theme-src-"))
17
+ const srcThemesDir = join(tmpSrcDir, "themes")
18
+ mkdirSync(srcThemesDir, { recursive: true })
19
+ writeFileSync(join(srcThemesDir, "pluidr-contrast.json"), "mock-theme-content", "utf-8")
20
+
21
+ writeThemes(tmpSrcDir, destDir)
22
+
23
+ const destPath = join(destDir, "pluidr-contrast.json")
24
+ assert.ok(existsSync(destPath), "theme file should be copied")
25
+ assert.strictEqual(readFileSync(destPath, "utf-8"), "mock-theme-content")
26
+
27
+ rmSync(tmpSrcDir, { recursive: true })
28
+ })
29
+ })
@@ -0,0 +1,22 @@
1
+ import { readFileSync, writeFileSync, existsSync } from "node:fs"
2
+ import { getTuiConfigPath } from "./paths.js"
3
+
4
+ export function writeTuiConfig(themeName, customPath) {
5
+ const path = customPath || getTuiConfigPath()
6
+ let config = {
7
+ "$schema": "https://opencode.ai/tui.json"
8
+ }
9
+
10
+ if (existsSync(path)) {
11
+ try {
12
+ const content = readFileSync(path, "utf-8")
13
+ config = JSON.parse(content)
14
+ } catch {
15
+ // If parsing fails, fall back to default structure
16
+ }
17
+ }
18
+
19
+ config.theme = themeName
20
+
21
+ writeFileSync(path, JSON.stringify(config, null, 2), "utf-8")
22
+ }
@@ -0,0 +1,38 @@
1
+ import { describe, it, after } from "node:test"
2
+ import assert from "node:assert"
3
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
4
+ import { join } from "node:path"
5
+ import { tmpdir } from "node:os"
6
+ import { writeTuiConfig } from "./tuiConfigWriter.js"
7
+
8
+ describe("tuiConfigWriter", () => {
9
+ const destDir = mkdtempSync(join(tmpdir(), "pluidr-tui-dest-"))
10
+ const tuiPath = join(destDir, "tui.json")
11
+
12
+ after(() => {
13
+ if (existsSync(destDir)) rmSync(destDir, { recursive: true })
14
+ })
15
+
16
+ it("writes new tui.json with theme name if it does not exist", () => {
17
+ writeTuiConfig("pluidr-contrast", tuiPath)
18
+
19
+ assert.ok(existsSync(tuiPath))
20
+ const parsed = JSON.parse(readFileSync(tuiPath, "utf-8"))
21
+ assert.strictEqual(parsed.theme, "pluidr-contrast")
22
+ assert.strictEqual(parsed["$schema"], "https://opencode.ai/tui.json")
23
+ })
24
+
25
+ it("updates existing tui.json theme name while preserving other fields", () => {
26
+ writeFileSync(tuiPath, JSON.stringify({
27
+ "$schema": "https://opencode.ai/tui.json",
28
+ "theme": "ayu-dark",
29
+ "font": "monolisa"
30
+ }, null, 2), "utf-8")
31
+
32
+ writeTuiConfig("pluidr-contrast", tuiPath)
33
+
34
+ const parsed = JSON.parse(readFileSync(tuiPath, "utf-8"))
35
+ assert.strictEqual(parsed.theme, "pluidr-contrast")
36
+ assert.strictEqual(parsed.font, "monolisa")
37
+ })
38
+ })
@@ -394,8 +394,7 @@ elsewhere. Do not accept a report that is not in `docs/reports/`.
394
394
  - **Regression Awareness** — When re-triggering coder after a FAIL, pass the
395
395
  gap list in full so coder doesn't fix one thing and break something
396
396
  already confirmed as PASS.
397
- - **Clarification** — If anything is ambiguous at any phase, ask the user
398
- using multiple-choice options (2-4 short choices per question).
397
+ - **Clarification** — If anything is ambiguous at any phase, avoid excessive questioning. For minor details, make a reasonable, safe engineering assumption based on existing codebase conventions and document it. Only prompt the user using multiple-choice options (2-3 short choices per question, and list your recommended option first prefixed with '(Recommended)') if there is a critical blocking issue that directly impacts the design direction or has high regression risk.
399
398
  - **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.
400
399
 
401
400
  ---
@@ -132,6 +132,7 @@ just a technical restriction.
132
132
  - **Separation of Investigation/Fix/Report** — Do not mix investigation,
133
133
  implementation, and reporting in the same delegate call. Each subagent
134
134
  has one job; the Debugger orchestrates the sequence.
135
+ - **Avoid Excessive Questioning** — Avoid excessive querying of the user for minor ambiguities. For small implementation details, make a reasonable, safe engineering assumption based on existing codebase conventions and proceed. Only prompt the user using multiple-choice options (2-3 short choices per question, and list your recommended option first prefixed with '(Recommended)') when a critical blocking issue directly prevents a safe diagnosis or fix.
135
136
 
136
137
  ## What you do NOT do
137
138
 
@@ -85,6 +85,10 @@ Do NOT remain in a completed state without offering next steps.
85
85
  - You do not patch without a GUARDRAIL GATE user confirmation after TRACE.
86
86
  - You do not loop more than 5 times on AUDIT before surfacing to the user.
87
87
 
88
+ ## Principles you apply
89
+
90
+ - **Avoid Excessive Questioning** — Avoid excessive querying of the user for minor ambiguities. For small implementation details, make a reasonable, safe engineering assumption based on existing codebase conventions and proceed. Only prompt the user using the `question` tool with multiple-choice options (2-3 short choices per question, and list your recommended option first prefixed with '(Recommended)') for critical blocking decisions (such as the Guardrail Gate or a complete audit block).
91
+
88
92
  ## Conflict Resolution
89
93
  Refer to `hierarchy.txt` (loaded globally) — you do not resolve principle
90
94
  conflicts by your own judgment outside that hierarchy.
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "$schema": "https://opencode.ai/config.json",
3
+ "theme": "pluidr-contrast",
3
4
  "instructions": ["{file:./prompts/hierarchy.txt}"],
4
5
  "permission": {
5
6
  "read": {
@@ -24,6 +25,7 @@
24
25
 
25
26
  "composer": {
26
27
  "mode": "primary",
28
+ "color": "#00b4cc",
27
29
  "temperature": 0.7,
28
30
  "description": "Full engineering workflow: Explore, Plan, Build — 3 phases, 7 subagents. Starts in Explore mode, transitions via guardrail gates.",
29
31
  "prompt": "{file:./prompts/composer.txt}",
@@ -54,6 +56,7 @@
54
56
 
55
57
  "debugger": {
56
58
  "mode": "primary",
59
+ "color": "#ff7039",
57
60
  "description": "Root-cause analysis via inspector/fixer/reporter. Standalone primary agent — does not depend on Composer. Pure orchestrator — all investigation, fixes, and reports delegated to subagents.",
58
61
  "prompt": "{file:./prompts/debugger.txt}",
59
62
  "permission": {
@@ -344,6 +347,7 @@
344
347
 
345
348
  "prober": {
346
349
  "mode": "primary",
350
+ "color": "#66ffbf",
347
351
  "temperature": 0.7,
348
352
  "description": "Standalone security and quality auditing agent. Recon and vuln trace, minimal patch application, validation gate.",
349
353
  "prompt": "{file:./prompts/prober.txt}",
@@ -0,0 +1,177 @@
1
+ {
2
+ "$schema": "https://opencode.ai/theme.json",
3
+ "theme": {
4
+ "primary": {
5
+ "dark": "#ff7039",
6
+ "light": "#ff7039"
7
+ },
8
+ "secondary": {
9
+ "dark": "#99f3ff",
10
+ "light": "#99f3ff"
11
+ },
12
+ "accent": {
13
+ "dark": "#ff7039",
14
+ "light": "#ff7039"
15
+ },
16
+ "error": {
17
+ "dark": "#fc5d7c",
18
+ "light": "#fc5d7c"
19
+ },
20
+ "warning": {
21
+ "dark": "#ff669c",
22
+ "light": "#ff669c"
23
+ },
24
+ "success": {
25
+ "dark": "#00cc76",
26
+ "light": "#00cc76"
27
+ },
28
+ "info": {
29
+ "dark": "#00b4cc",
30
+ "light": "#00b4cc"
31
+ },
32
+ "text": {
33
+ "dark": "#ffffff",
34
+ "light": "#ffffff"
35
+ },
36
+ "textMuted": {
37
+ "dark": "#888888",
38
+ "light": "#888888"
39
+ },
40
+ "background": {
41
+ "dark": "#000000",
42
+ "light": "#000000"
43
+ },
44
+ "backgroundPanel": {
45
+ "dark": "#000000",
46
+ "light": "#000000"
47
+ },
48
+ "backgroundElement": {
49
+ "dark": "#000000",
50
+ "light": "#000000"
51
+ },
52
+ "border": {
53
+ "dark": "#101520",
54
+ "light": "#101520"
55
+ },
56
+ "borderActive": {
57
+ "dark": "#ff7039",
58
+ "light": "#ff7039"
59
+ },
60
+ "borderSubtle": {
61
+ "dark": "#101520",
62
+ "light": "#101520"
63
+ },
64
+ "diffAdded": {
65
+ "dark": "#00cc76",
66
+ "light": "#00cc76"
67
+ },
68
+ "diffRemoved": {
69
+ "dark": "#fc5d7c",
70
+ "light": "#fc5d7c"
71
+ },
72
+ "diffContext": {
73
+ "dark": "#888888",
74
+ "light": "#888888"
75
+ },
76
+ "diffAddedBg": {
77
+ "dark": "#1a2618",
78
+ "light": "#1a2618"
79
+ },
80
+ "diffRemovedBg": {
81
+ "dark": "#2a1a1a",
82
+ "light": "#2a1a1a"
83
+ },
84
+ "diffContextBg": {
85
+ "dark": "#000000",
86
+ "light": "#000000"
87
+ },
88
+ "markdownText": {
89
+ "dark": "#ffffff",
90
+ "light": "#ffffff"
91
+ },
92
+ "markdownHeading": {
93
+ "dark": "#ff669c",
94
+ "light": "#ff669c"
95
+ },
96
+ "markdownLink": {
97
+ "dark": "#99f3ff",
98
+ "light": "#99f3ff"
99
+ },
100
+ "markdownLinkText": {
101
+ "dark": "#ff7039",
102
+ "light": "#ff7039"
103
+ },
104
+ "markdownCode": {
105
+ "dark": "#66ffbf",
106
+ "light": "#66ffbf"
107
+ },
108
+ "markdownBlockQuote": {
109
+ "dark": "#888888",
110
+ "light": "#888888"
111
+ },
112
+ "markdownStrong": {
113
+ "dark": "#ff7039",
114
+ "light": "#ff7039"
115
+ },
116
+ "syntaxComment": {
117
+ "dark": "#888888",
118
+ "light": "#888888"
119
+ },
120
+ "syntaxKeyword": {
121
+ "dark": "#ff669c",
122
+ "light": "#ff669c"
123
+ },
124
+ "syntaxFunction": {
125
+ "dark": "#00b4cc",
126
+ "light": "#00b4cc"
127
+ },
128
+ "syntaxVariable": {
129
+ "dark": "#ffb966",
130
+ "light": "#ffb966"
131
+ },
132
+ "syntaxString": {
133
+ "dark": "#66ffbf",
134
+ "light": "#66ffbf"
135
+ },
136
+ "syntaxNumber": {
137
+ "dark": "#a000ff",
138
+ "light": "#a000ff"
139
+ },
140
+ "syntaxType": {
141
+ "dark": "#99f3ff",
142
+ "light": "#99f3ff"
143
+ },
144
+ "syntaxOperator": {
145
+ "dark": "#ff669c",
146
+ "light": "#ff669c"
147
+ },
148
+ "syntaxPunctuation": {
149
+ "dark": "#ffffff",
150
+ "light": "#ffffff"
151
+ },
152
+ "inputBackground": {
153
+ "dark": "#000000",
154
+ "light": "#000000"
155
+ },
156
+ "InputBorder": {
157
+ "dark": "#101520",
158
+ "light": "#101520"
159
+ },
160
+ "InputBorderActive": {
161
+ "dark": "#ff7039",
162
+ "light": "#ff7039"
163
+ },
164
+ "inputPrompt": {
165
+ "dark": "#ff7039",
166
+ "light": "#ff7039"
167
+ },
168
+ "inputCursor": {
169
+ "dark": "#ff7039",
170
+ "light": "#ff7039"
171
+ },
172
+ "inputText": {
173
+ "dark": "#ffffff",
174
+ "light": "#ffffff"
175
+ }
176
+ }
177
+ }