pluidr 0.8.0 → 0.8.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 CHANGED
@@ -371,12 +371,13 @@ Restores your previous configuration:
371
371
 
372
372
  - Finds the latest timestamped backup and restores it to `opencode.jsonc`
373
373
  - Restores `tui.json` from the latest backup (returning the TUI theme to its pre-Pluidr state)
374
- - Removes `prompts/`, `plugins/`, `bin/`, `skills/`, `commands/` directories, and the installed `pluidr` themes
374
+ - Removes `prompts/`, `plugins/`, `bin/`, `skills/`, `commands/` directories, `pluidr.json`, and the installed `pluidr` themes
375
375
  - Preserves `opencode.jsonc` and `package.json`
376
376
 
377
377
  ### Logo Animation
378
378
 
379
- Both `pluidr` and `pluidr init` feature an animated ASCII banner. The animation style can be configured via CLI flag (e.g. `--animation <type>`), environment variable (`PLUIDR_ANIMATION`), or in `~/.config/opencode/tui.json` (as the `"animation"` key).
379
+ Both `pluidr` and `pluidr init` feature an animated ASCII banner. The animation style can be configured via CLI flag (e.g. `--animation <type>`), environment variable (`PLUIDR_ANIMATION`), or in `~/.config/opencode/pluidr.json` (as the `"animation"` key).
380
+
380
381
 
381
382
  Available animation styles:
382
383
 
@@ -405,4 +406,4 @@ Both plugins and their dependency declaration are installed automatically by `pl
405
406
 
406
407
  ### Themes & Colors
407
408
 
408
- Pluidr comes with a pre-configured OLED high-contrast dark theme called **`pluidr-contrast`** that is automatically applied to your OpenCode terminal interface.
409
+ Pluidr comes with themes called that you can choose to your OpenCode terminal interface.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pluidr",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Opinionated Engineering Workflow for OpenCode — Plan, Build, Review.",
5
5
  "keywords": [
6
6
  "opencode",
@@ -94,7 +94,19 @@ export async function runInit() {
94
94
  backupExistingConfig()
95
95
 
96
96
  const configObject = buildConfig(tierChoices, TEMPLATES_DIR)
97
- writeConfig(configObject)
97
+ const orderedConfig = {}
98
+ if ("$schema" in configObject) {
99
+ orderedConfig["$schema"] = configObject["$schema"]
100
+ }
101
+ if (selectedTheme) {
102
+ orderedConfig.theme = selectedTheme
103
+ }
104
+ for (const [k, v] of Object.entries(configObject)) {
105
+ if (k !== "$schema" && k !== "theme") {
106
+ orderedConfig[k] = v
107
+ }
108
+ }
109
+ writeConfig(orderedConfig)
98
110
  writeAgentPrompts(TEMPLATES_DIR)
99
111
  console.log("│ Writing agent prompts... \x1b[32m✓\x1b[0m Wrote 8 files to ~/.config/opencode/prompts/")
100
112
 
@@ -1,7 +1,7 @@
1
- import { existsSync, readdirSync, readFileSync } from "node:fs"
1
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"
2
2
  import { join, basename } from "node:path"
3
3
  import { select, isCancel } from "@clack/prompts"
4
- import { getThemesDir, getTuiConfigPath } from "../../core/paths.js"
4
+ import { getThemesDir, getTuiConfigPath, getConfigPath } from "../../core/paths.js"
5
5
  import { writeTuiConfig } from "../../core/tuiConfigWriter.js"
6
6
 
7
7
  export async function runTheme() {
@@ -63,5 +63,29 @@ export async function runTheme() {
63
63
  }
64
64
 
65
65
  writeTuiConfig(selectedTheme)
66
+
67
+ // Also write theme configuration to opencode.jsonc
68
+ const configPath = getConfigPath()
69
+ if (existsSync(configPath)) {
70
+ try {
71
+ const configContent = readFileSync(configPath, "utf-8")
72
+ const config = JSON.parse(configContent)
73
+
74
+ const orderedConfig = {}
75
+ if ("$schema" in config) {
76
+ orderedConfig["$schema"] = config["$schema"]
77
+ }
78
+ orderedConfig.theme = selectedTheme
79
+ for (const [k, v] of Object.entries(config)) {
80
+ if (k !== "$schema" && k !== "theme") {
81
+ orderedConfig[k] = v
82
+ }
83
+ }
84
+ writeFileSync(configPath, JSON.stringify(orderedConfig, null, 2), "utf-8")
85
+ } catch {
86
+ // Ignore
87
+ }
88
+ }
89
+
66
90
  console.log(`Theme successfully applied: ${selectedTheme}`)
67
91
  }
@@ -86,6 +86,12 @@ export async function runUninstall() {
86
86
  }
87
87
  }
88
88
 
89
+ const pluidrConfigPath = join(configDir, "pluidr.json")
90
+ if (existsSync(pluidrConfigPath)) {
91
+ rmSync(pluidrConfigPath, { force: true })
92
+ summary.removed.push("pluidr.json")
93
+ }
94
+
89
95
  // Print summary
90
96
  console.log("Uninstall complete:")
91
97
  if (summary.restored) {
@@ -17,11 +17,15 @@ export function writeAgentPrompts(templatesDir, destDir) {
17
17
  try {
18
18
  for (const file of readdirSync(destDir)) {
19
19
  if (extname(file) === ".txt") {
20
- unlinkSync(resolve(destDir, file))
20
+ try {
21
+ unlinkSync(resolve(destDir, file))
22
+ } catch {
23
+ // Ignore individual file errors and continue
24
+ }
21
25
  }
22
26
  }
23
27
  } catch {
24
- // Ignore
28
+ // Ignore directory-level errors
25
29
  }
26
30
  }
27
31
 
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs"
2
- import { getTuiConfigPath } from "./paths.js"
2
+ import { join } from "node:path"
3
+ import { getConfigDir } from "./paths.js"
3
4
 
4
5
  export const COLORS = [
5
6
  "\x1b[38;2;0;225;255m", // Bright Cyan
@@ -36,8 +37,8 @@ export function getAnimationType() {
36
37
  }
37
38
  }
38
39
 
39
- // 3. Check tui.json
40
- const path = getTuiConfigPath()
40
+ // 3. Check pluidr.json
41
+ const path = join(getConfigDir(), "pluidr.json")
41
42
  if (fs.existsSync(path)) {
42
43
  try {
43
44
  const config = JSON.parse(fs.readFileSync(path, "utf-8"))
@@ -1,5 +1,6 @@
1
1
  import { readFileSync, writeFileSync, existsSync } from "node:fs"
2
- import { getTuiConfigPath } from "./paths.js"
2
+ import { join } from "node:path"
3
+ import { getTuiConfigPath, getConfigDir } from "./paths.js"
3
4
 
4
5
  export function writeTuiConfig(themeName, customPath, animationName) {
5
6
  const path = customPath || getTuiConfigPath()
@@ -17,7 +18,24 @@ export function writeTuiConfig(themeName, customPath, animationName) {
17
18
  }
18
19
 
19
20
  if (themeName) config.theme = themeName
20
- if (animationName) config.animation = animationName
21
+ // Ensure animation is never in tui.json to prevent OpenCode validation failure
22
+ delete config.animation
21
23
 
22
24
  writeFileSync(path, JSON.stringify(config, null, 2), "utf-8")
25
+
26
+ // Write animation to pluidr.json instead
27
+ if (animationName) {
28
+ const configDir = customPath ? join(customPath, "..") : getConfigDir()
29
+ const pluidrPath = join(configDir, "pluidr.json")
30
+ let pluidrConfig = {}
31
+ if (existsSync(pluidrPath)) {
32
+ try {
33
+ pluidrConfig = JSON.parse(readFileSync(pluidrPath, "utf-8"))
34
+ } catch {
35
+ // Ignore
36
+ }
37
+ }
38
+ pluidrConfig.animation = animationName
39
+ writeFileSync(pluidrPath, JSON.stringify(pluidrConfig, null, 2), "utf-8")
40
+ }
23
41
  }
@@ -13,13 +13,19 @@ describe("tuiConfigWriter", () => {
13
13
  if (existsSync(destDir)) rmSync(destDir, { recursive: true })
14
14
  })
15
15
 
16
- it("writes new tui.json with theme name if it does not exist", () => {
17
- writeTuiConfig("pluidr-dark", tuiPath)
16
+ it("writes new tui.json with theme name if it does not exist, and animation to pluidr.json", () => {
17
+ writeTuiConfig("pluidr-dark", tuiPath, "wave")
18
18
 
19
19
  assert.ok(existsSync(tuiPath))
20
20
  const parsed = JSON.parse(readFileSync(tuiPath, "utf-8"))
21
21
  assert.strictEqual(parsed.theme, "pluidr-dark")
22
+ assert.strictEqual(parsed.animation, undefined) // should not exist in tui.json
22
23
  assert.strictEqual(parsed["$schema"], "https://opencode.ai/tui.json")
24
+
25
+ const pluidrPath = join(destDir, "pluidr.json")
26
+ assert.ok(existsSync(pluidrPath))
27
+ const pluidrConfig = JSON.parse(readFileSync(pluidrPath, "utf-8"))
28
+ assert.strictEqual(pluidrConfig.animation, "wave")
23
29
  })
24
30
 
25
31
  it("updates existing tui.json theme name while preserving other fields", () => {
@@ -1,20 +0,0 @@
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,88 +0,0 @@
1
- # Role: Coder Subagent
2
-
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.
4
-
5
- Refer to `hierarchy.txt` (loaded globally) for conflict resolution.
6
-
7
- ## Principles
8
-
9
- **KISS**
10
- - Choose the simplest implementation that satisfies the requirement.
11
- - If a more complex approach is chosen, explain why in a comment.
12
-
13
- **DRY**
14
- - Before writing new logic, check if equivalent logic already exists.
15
- - If duplication is unavoidable in this pass, flag it.
16
-
17
- **Fail Fast / Defensive Programming**
18
- - Validate all inputs at function/API boundaries.
19
- - Never assume upstream data is valid -- raise explicit errors.
20
-
21
- **SOLID -- Dependency Inversion**
22
- - Depend on abstractions (interfaces, protocols) not concrete implementations,
23
- especially for external services (DB, API clients).
24
-
25
- **Clean Code**
26
- - Meaningful names -- no abbreviations unless domain-standard (e.g., `id`, `db`).
27
- - Small functions -- one function does one thing.
28
- - No hidden side effects.
29
- - Minimize comments -- code should be self-explanatory; comment only "why", not "what".
30
-
31
- **Single Level of Abstraction**
32
- - A function should not mix high-level orchestration with low-level details
33
- (e.g., business logic + raw SQL in the same function).
34
-
35
- ## Delegation rules
36
-
37
- You have no `task` permission -- you cannot invoke any other agent or
38
- subagent. If implementation reveals a need for research (e.g., unclear
39
- library behavior) or a requirement question, stop and report back to
40
- Composer rather than guessing or trying to research it yourself outside
41
- your given tools.
42
-
43
- ## Task tracking
44
-
45
- - Use `todowrite` to break the assigned task(s) into a working checklist
46
- before starting implementation.
47
- - Update status as you progress. This is for session visibility, not a
48
- deliverable -- do not write it to a file.
49
-
50
- ## What you do NOT do
51
-
52
- - You do not change the requirement -- if the PRD task is ambiguous or
53
- infeasible as written, stop and report back to Composer rather than
54
- reinterpreting it.
55
- - You do not produce documentation -- that's Compose-Reporter's job.
56
-
57
- ## Output
58
-
59
- - Code changes, with a brief note on any deviation from the assigned task (if any).
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,55 +0,0 @@
1
- # Role: Compose-Reporter Subagent
2
-
3
- You are the **Compose-Reporter** subagent, the text-file executor for the **Composer**
4
- agent. You write and edit `.md` and `.txt` files across the project -- like
5
- Coder but for documents only. You do not run bash commands.
6
-
7
- ## Mode: Document Creation / Editing
8
-
9
- You create and modify documentation files (.md, .txt) as instructed by
10
- Composer. You may also be invoked in Summary mode to produce completion
11
- reports from structured input.
12
-
13
- ## Delegation rules
14
-
15
- You have no `task` permission -- you cannot invoke any other agent or
16
- subagent. If Composer's instructions are insufficient to produce the
17
- required document, mark missing sections as `TBD` -- do not infer, research,
18
- or invent content.
19
-
20
- ## Principles
21
-
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
- - **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
- - **KISS** -- Prefer simple, flat structure over nested hierarchies. A reader should grasp the outcome from the first paragraph.
25
-
26
- ## Compose-Reporter MUST NOT
27
-
28
- - Make inferences about what the user "probably means."
29
- - Make decisions (e.g., which approach is better).
30
- - Add analysis, recommendations, or opinions.
31
- - Fill missing information with assumptions -- mark as `TBD` instead.
32
- - Run bash commands -- you have no `bash` permission.
33
-
34
- ## Compose-Reporter MAY ONLY
35
-
36
- - Write and edit `.md` and `.txt` files as instructed.
37
- - Reformat / restructure given input into the target document type.
38
- - Flag missing required fields explicitly (`TBD`).
39
-
40
- ## Output Format (Summary/Report mode)
41
-
42
- ```markdown
43
- # Report: <subject>
44
-
45
- ## For: <audience, as specified by caller>
46
-
47
- ## Outcome
48
- <PASS/FAIL or completion status, as given>
49
-
50
- ## Details
51
- <compressed from input, no added interpretation>
52
- ```
53
-
54
- If the input to any section doesn't give you enough to fill it, write `TBD`
55
- and move on. Never write "I think..." or "this probably means...".