pluidr 0.7.0 → 0.7.2

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.
Files changed (53) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +365 -365
  3. package/bin/pluidr.js +3 -3
  4. package/package.json +45 -45
  5. package/src/cli/commands/doctor.js +113 -101
  6. package/src/cli/commands/init.js +47 -43
  7. package/src/cli/commands/launch.js +46 -44
  8. package/src/cli/commands/uninstall.js +90 -63
  9. package/src/cli/commands/update.js +6 -6
  10. package/src/cli/index.js +57 -57
  11. package/src/cli/wizard/selectModelTier.js +40 -40
  12. package/src/core/agentPromptWriter.js +32 -32
  13. package/src/core/agentPromptWriter.test.js +56 -56
  14. package/src/core/backup.js +46 -40
  15. package/src/core/configBuilder.js +32 -32
  16. package/src/core/configBuilder.test.js +93 -93
  17. package/src/core/configWriter.js +10 -10
  18. package/src/core/identityHeader.js +8 -8
  19. package/src/core/paths.js +11 -9
  20. package/src/core/paths.test.js +29 -21
  21. package/src/core/pluginWriter.js +29 -29
  22. package/src/core/squeezeInstaller.js +158 -161
  23. package/src/core/squeezeInstaller.test.js +79 -75
  24. package/src/core/themeWriter.js +12 -0
  25. package/src/core/themeWriter.test.js +29 -0
  26. package/src/core/tuiConfigWriter.js +22 -0
  27. package/src/core/tuiConfigWriter.test.js +38 -0
  28. package/src/core/version.js +8 -8
  29. package/src/core/versionCheck.js +44 -35
  30. package/src/core/versionCheck.test.js +12 -2
  31. package/src/plugins/README.md +68 -68
  32. package/src/plugins/pluidr-squeeze.js +77 -77
  33. package/src/templates/agent-prompts/auditor.txt +20 -20
  34. package/src/templates/agent-prompts/coder.txt +87 -87
  35. package/src/templates/agent-prompts/compose-reporter.txt +55 -55
  36. package/src/templates/agent-prompts/composer.txt +429 -430
  37. package/src/templates/agent-prompts/debug-reporter.txt +65 -65
  38. package/src/templates/agent-prompts/debugger.txt +152 -151
  39. package/src/templates/agent-prompts/fixer.txt +66 -66
  40. package/src/templates/agent-prompts/hierarchy.txt +96 -96
  41. package/src/templates/agent-prompts/inspector.txt +79 -79
  42. package/src/templates/agent-prompts/patcher.txt +20 -20
  43. package/src/templates/agent-prompts/plan-checker.txt +45 -45
  44. package/src/templates/agent-prompts/plan-writer.txt +57 -57
  45. package/src/templates/agent-prompts/probe-reporter.txt +62 -62
  46. package/src/templates/agent-prompts/prober.txt +94 -90
  47. package/src/templates/agent-prompts/researcher.txt +48 -48
  48. package/src/templates/agent-prompts/reviewer.txt +57 -57
  49. package/src/templates/agent-prompts/tester.txt +66 -66
  50. package/src/templates/agent-prompts/tracer.txt +33 -33
  51. package/src/templates/model-defaults.json +73 -73
  52. package/src/templates/opencode.config.json +482 -481
  53. package/src/templates/themes/pluidr-contrast.json +177 -0
package/bin/pluidr.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { run } from "../src/cli/index.js"
3
-
4
- run(process.argv)
2
+ import { run } from "../src/cli/index.js"
3
+
4
+ run(process.argv)
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "name": "pluidr",
3
- "version": "0.7.0",
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.2",
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,101 +1,113 @@
1
- import { existsSync, readFileSync, readdirSync } from "node:fs"
2
- import { join } from "node:path"
3
- import { getConfigDir, getConfigPath, getPromptsDir } from "../../core/paths.js"
4
- import { findSqueezePath } from "../../core/squeezeInstaller.js"
5
-
6
- const EXPECTED_PROMPT_COUNT = 18
7
- const PLUGIN_FILES = ["pluidr-flow.js", "pluidr-squeeze.js"]
8
-
9
- export function collectChecks() {
10
- const configDir = getConfigDir()
11
- const configPath = getConfigPath()
12
- const promptsDir = getPromptsDir()
13
- const pluginsDir = join(configDir, "plugins")
14
- const packageJsonPath = join(configDir, "package.json")
15
- const binDir = join(configDir, "bin")
16
-
17
- const checks = []
18
-
19
- // 1. opencode.jsonc exists
20
- const configExists = existsSync(configPath)
21
- checks.push({ component: "opencode.jsonc", pass: configExists })
22
-
23
- // 2. All 18 prompt files exist
24
- let promptCount = 0
25
- if (existsSync(promptsDir)) {
26
- try {
27
- const files = readdirSync(promptsDir).filter((f) => f.endsWith(".txt"))
28
- promptCount = files.length
29
- } catch {
30
- promptCount = 0
31
- }
32
- }
33
- checks.push({ component: "18 prompt files", pass: promptCount === EXPECTED_PROMPT_COUNT, detail: `${promptCount}/${EXPECTED_PROMPT_COUNT}` })
34
-
35
- // 3. Both plugin files exist
36
- let pluginsFound = 0
37
- for (const f of PLUGIN_FILES) {
38
- if (existsSync(join(pluginsDir, f))) pluginsFound++
39
- }
40
- checks.push({ component: "plugin files", pass: pluginsFound === PLUGIN_FILES.length, detail: `${pluginsFound}/${PLUGIN_FILES.length}` })
41
-
42
- // 4. package.json with @opencode-ai/plugin dependency
43
- let pkgValid = false
44
- if (existsSync(packageJsonPath)) {
45
- try {
46
- const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"))
47
- pkgValid = !!(pkg.dependencies && pkg.dependencies["@opencode-ai/plugin"])
48
- } catch {
49
- pkgValid = false
50
- }
51
- }
52
- checks.push({ component: "package.json + plugin dep", pass: pkgValid })
53
-
54
- // 5. Squeeze binary exists
55
- let squeezeFound = false
56
- const squeezeOnPath = findSqueezePath()
57
- if (squeezeOnPath) {
58
- squeezeFound = true
59
- } else {
60
- const binName = process.platform === "win32" ? "squeeze.exe" : "squeeze"
61
- squeezeFound = existsSync(join(binDir, binName))
62
- }
63
- checks.push({ component: "squeeze binary tested", pass: squeezeFound })
64
-
65
- // 6. Config is valid JSON/JSONC
66
- let configValid = false
67
- if (configExists) {
68
- try {
69
- const content = readFileSync(configPath, "utf-8")
70
- const cleaned = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m)
71
- JSON.parse(cleaned)
72
- configValid = true
73
- } catch {
74
- configValid = false
75
- }
76
- }
77
- checks.push({ component: "config JSON validity", pass: configValid })
78
-
79
- return checks
80
- }
81
-
82
- export async function runDoctor() {
83
- const checks = collectChecks()
84
- const allPass = checks.every((c) => c.pass)
85
-
86
- console.log("\nPluidr Doctor — Installation Health Check\n")
87
- for (const c of checks) {
88
- const icon = c.pass ? "\u2713" : "\u2717"
89
- const detail = c.detail ? ` (${c.detail})` : ""
90
- console.log(` ${icon} ${c.component}${detail}`)
91
- }
92
- console.log("")
93
-
94
- if (allPass) {
95
- console.log("All checks passed.")
96
- } else {
97
- console.log("Some checks failed. Re-run `pluidr init` to repair.")
98
- }
99
-
100
- process.exit(allPass ? 0 : 1)
101
- }
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import { getConfigDir, getConfigPath, getPromptsDir, getThemesDir } from "../../core/paths.js"
4
+ import { findSqueezePath } from "../../core/squeezeInstaller.js"
5
+
6
+ const EXPECTED_PROMPT_COUNT = 18
7
+ const PLUGIN_FILES = ["pluidr-flow.js", "pluidr-squeeze.js"]
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
+
16
+ export function collectChecks() {
17
+ const configDir = getConfigDir()
18
+ const configPath = getConfigPath()
19
+ const promptsDir = getPromptsDir()
20
+ const pluginsDir = join(configDir, "plugins")
21
+ const packageJsonPath = join(configDir, "package.json")
22
+ const binDir = join(configDir, "bin")
23
+ const themesDir = getThemesDir()
24
+
25
+ const checks = []
26
+
27
+ // 1. opencode.jsonc exists
28
+ const configExists = existsSync(configPath)
29
+ checks.push({ component: "opencode.jsonc", pass: configExists })
30
+
31
+ // 2. All 18 prompt files exist
32
+ let promptCount = 0
33
+ if (existsSync(promptsDir)) {
34
+ try {
35
+ const files = readdirSync(promptsDir).filter((f) => f.endsWith(".txt"))
36
+ promptCount = files.length
37
+ } catch {
38
+ promptCount = 0
39
+ }
40
+ }
41
+ checks.push({ component: "18 prompt files", pass: promptCount === EXPECTED_PROMPT_COUNT, detail: `${promptCount}/${EXPECTED_PROMPT_COUNT}` })
42
+
43
+ // 3. Both plugin files exist
44
+ let pluginsFound = 0
45
+ for (const f of PLUGIN_FILES) {
46
+ if (existsSync(join(pluginsDir, f))) pluginsFound++
47
+ }
48
+ checks.push({ component: "plugin files", pass: pluginsFound === PLUGIN_FILES.length, detail: `${pluginsFound}/${PLUGIN_FILES.length}` })
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
+
54
+ // 4. package.json with @opencode-ai/plugin dependency
55
+ let pkgValid = false
56
+ if (existsSync(packageJsonPath)) {
57
+ try {
58
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"))
59
+ pkgValid = !!(pkg.dependencies && pkg.dependencies["@opencode-ai/plugin"])
60
+ } catch {
61
+ pkgValid = false
62
+ }
63
+ }
64
+ checks.push({ component: "package.json + plugin dep", pass: pkgValid })
65
+
66
+ // 5. Squeeze binary exists
67
+ let squeezeFound = false
68
+ const squeezeOnPath = findSqueezePath()
69
+ if (squeezeOnPath) {
70
+ squeezeFound = true
71
+ } else {
72
+ const binName = process.platform === "win32" ? "squeeze.exe" : "squeeze"
73
+ squeezeFound = existsSync(join(binDir, binName))
74
+ }
75
+ checks.push({ component: "squeeze binary tested", pass: squeezeFound })
76
+
77
+ // 6. Config is valid JSON/JSONC
78
+ let configValid = false
79
+ if (configExists) {
80
+ try {
81
+ const content = readFileSync(configPath, "utf-8")
82
+ const cleaned = cleanJsonc(content)
83
+ JSON.parse(cleaned)
84
+ configValid = true
85
+ } catch {
86
+ configValid = false
87
+ }
88
+ }
89
+ checks.push({ component: "config JSON validity", pass: configValid })
90
+
91
+ return checks
92
+ }
93
+
94
+ export async function runDoctor() {
95
+ const checks = collectChecks()
96
+ const allPass = checks.every((c) => c.pass)
97
+
98
+ console.log("\nPluidr Doctor — Installation Health Check\n")
99
+ for (const c of checks) {
100
+ const icon = c.pass ? "\u2713" : "\u2717"
101
+ const detail = c.detail ? ` (${c.detail})` : ""
102
+ console.log(` ${icon} ${c.component}${detail}`)
103
+ }
104
+ console.log("")
105
+
106
+ if (allPass) {
107
+ console.log("All checks passed.")
108
+ } else {
109
+ console.log("Some checks failed. Re-run `pluidr init` to repair.")
110
+ }
111
+
112
+ process.exit(allPass ? 0 : 1)
113
+ }
@@ -1,43 +1,47 @@
1
- import { readFileSync } from "node:fs"
2
- import { resolve, dirname } from "node:path"
3
- import { fileURLToPath } from "node:url"
4
- import { confirm, isCancel } from "@clack/prompts"
5
- import { selectModelTier } from "../wizard/selectModelTier.js"
6
- import { backupExistingConfig } from "../../core/backup.js"
7
- import { buildConfig } from "../../core/configBuilder.js"
8
- import { writeConfig } from "../../core/configWriter.js"
9
- import { writeAgentPrompts } from "../../core/agentPromptWriter.js"
10
- import { writePluginBundle, writePluginPackageJson } from "../../core/pluginWriter.js"
11
- import { installSqueeze } from "../../core/squeezeInstaller.js"
12
- import { getConfigPath } from "../../core/paths.js"
13
-
14
- const __dirname = dirname(fileURLToPath(import.meta.url))
15
- const TEMPLATES_DIR = resolve(__dirname, "../../templates")
16
-
17
- export async function runInit() {
18
- const defaultsPath = resolve(TEMPLATES_DIR, "model-defaults.json")
19
- const modelDefaults = JSON.parse(readFileSync(defaultsPath, "utf-8"))
20
-
21
- const tierChoices = await selectModelTier(modelDefaults)
22
-
23
- const squeezeAnswer = await confirm({
24
- message: "Install pluidr-squeeze plugin? Reduces bash output tokens by 60-90%.",
25
- initialValue: true,
26
- })
27
-
28
- const installSqueezePlugin = isCancel(squeezeAnswer) ? false : squeezeAnswer
29
-
30
- backupExistingConfig()
31
-
32
- const configObject = buildConfig(tierChoices, TEMPLATES_DIR)
33
- writeConfig(configObject)
34
- writeAgentPrompts(TEMPLATES_DIR)
35
- writePluginBundle()
36
- writePluginPackageJson()
37
- if (installSqueezePlugin) {
38
- await installSqueeze()
39
- }
40
-
41
- const path = getConfigPath().replace(/\\/g, "/").replace(/^\//, "")
42
- console.log(`Pluidr setup complete!\nYou can update your agent model settings later in \x1b]8;;file:///${path}\x1b\\opencode.jsonc\x1b]8;;\x1b\\`)
43
- }
1
+ import { readFileSync, writeFileSync } from "node:fs"
2
+ import { resolve, dirname } from "node:path"
3
+ import { fileURLToPath } from "node:url"
4
+ import { confirm, isCancel } from "@clack/prompts"
5
+ import { selectModelTier } from "../wizard/selectModelTier.js"
6
+ import { backupExistingConfig } from "../../core/backup.js"
7
+ import { buildConfig } from "../../core/configBuilder.js"
8
+ import { writeConfig } from "../../core/configWriter.js"
9
+ import { writeAgentPrompts } from "../../core/agentPromptWriter.js"
10
+ import { writePluginBundle, writePluginPackageJson } from "../../core/pluginWriter.js"
11
+ import { writeThemes } from "../../core/themeWriter.js"
12
+ import { writeTuiConfig } from "../../core/tuiConfigWriter.js"
13
+ import { installSqueeze } from "../../core/squeezeInstaller.js"
14
+ import { getConfigPath, getConfigDir } from "../../core/paths.js"
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url))
17
+ const TEMPLATES_DIR = resolve(__dirname, "../../templates")
18
+
19
+ export async function runInit() {
20
+ const defaultsPath = resolve(TEMPLATES_DIR, "model-defaults.json")
21
+ const modelDefaults = JSON.parse(readFileSync(defaultsPath, "utf-8"))
22
+
23
+ const tierChoices = await selectModelTier(modelDefaults)
24
+
25
+ const squeezeAnswer = await confirm({
26
+ message: "Install pluidr-squeeze plugin? Reduces bash output tokens by 60-90%.",
27
+ initialValue: true,
28
+ })
29
+
30
+ const installSqueezePlugin = isCancel(squeezeAnswer) ? false : squeezeAnswer
31
+
32
+ backupExistingConfig()
33
+
34
+ const configObject = buildConfig(tierChoices, TEMPLATES_DIR)
35
+ writeConfig(configObject)
36
+ writeAgentPrompts(TEMPLATES_DIR)
37
+ writeThemes(TEMPLATES_DIR)
38
+ writeTuiConfig("pluidr-contrast")
39
+ writePluginBundle()
40
+ writePluginPackageJson()
41
+ if (installSqueezePlugin) {
42
+ await installSqueeze()
43
+ }
44
+
45
+ const path = getConfigPath().replace(/\\/g, "/").replace(/^\//, "")
46
+ console.log(`Pluidr setup complete!\nYou can update your agent model settings later in \x1b]8;;file:///${path}\x1b\\opencode.jsonc\x1b]8;;\x1b\\`)
47
+ }
@@ -1,44 +1,46 @@
1
- import { spawn } from "node:child_process"
2
- import { confirm, isCancel } from "@clack/prompts"
3
- import { checkAndPromptUpdate } from "../../core/versionCheck.js"
4
- import { version } from "../../core/version.js"
5
- import { collectChecks } from "./doctor.js"
6
- import { runInit } from "./init.js"
7
-
8
- export async function runLaunch() {
9
- // 1. Update check
10
- await checkAndPromptUpdate(version)
11
-
12
- // 2. Doctor check
13
- process.stdout.write("Running doctor... ")
14
- const checks = collectChecks()
15
- const allPass = checks.every((c) => c.pass)
16
-
17
- if (allPass) {
18
- console.log("✓ All checks passed")
19
- } else {
20
- const failed = checks.filter((c) => !c.pass)
21
- console.log(`✗ ${failed.length} check${failed.length > 1 ? "s" : ""} failed`)
22
- for (const c of failed) {
23
- const detail = c.detail ? ` (${c.detail})` : ""
24
- console.log(` ✗ ${c.component}${detail}`)
25
- }
26
-
27
- const answer = await confirm({ message: "Repair with pluidr init?", initialValue: true })
28
- if (!isCancel(answer) && answer) {
29
- await runInit()
30
- }
31
- }
32
-
33
- // 3. Launch opencode
34
- console.log("Launching opencode...")
35
- const child = spawn("opencode", [], { stdio: "inherit" })
36
- child.on("error", (err) => {
37
- if (err.code === "ENOENT") {
38
- console.error("Error: opencode not found on PATH. Install it from https://opencode.ai")
39
- } else {
40
- console.error(`Error launching opencode: ${err.message}`)
41
- }
42
- process.exit(1)
43
- })
44
- }
1
+ import { spawn } from "node:child_process"
2
+ import { confirm, isCancel } from "@clack/prompts"
3
+ import { checkAndPromptUpdate } from "../../core/versionCheck.js"
4
+ import { version } from "../../core/version.js"
5
+ import { collectChecks } from "./doctor.js"
6
+ import { runInit } from "./init.js"
7
+
8
+ export async function runLaunch() {
9
+ // 1. Update check
10
+ await checkAndPromptUpdate(version)
11
+
12
+ // 2. Doctor check
13
+ process.stdout.write("Running doctor... ")
14
+ const checks = collectChecks()
15
+ const allPass = checks.every((c) => c.pass)
16
+
17
+ if (allPass) {
18
+ console.log("✓ All checks passed")
19
+ } else {
20
+ const failed = checks.filter((c) => !c.pass)
21
+ console.log(`✗ ${failed.length} check${failed.length > 1 ? "s" : ""} failed`)
22
+ for (const c of failed) {
23
+ const detail = c.detail ? ` (${c.detail})` : ""
24
+ console.log(` ✗ ${c.component}${detail}`)
25
+ }
26
+
27
+ const answer = await confirm({ message: "Repair with pluidr init?", initialValue: true })
28
+ if (!isCancel(answer) && answer) {
29
+ await runInit()
30
+ }
31
+ }
32
+
33
+ // 3. Launch opencode
34
+ console.log("Launching pluidr...")
35
+ const child = process.platform === "win32"
36
+ ? spawn("opencode", { stdio: "inherit", shell: true })
37
+ : spawn("opencode", [], { stdio: "inherit" })
38
+ child.on("error", (err) => {
39
+ if (err.code === "ENOENT") {
40
+ console.error("Error: opencode not found on PATH. Install it from https://opencode.ai")
41
+ } else {
42
+ console.error(`Error launching opencode: ${err.message}`)
43
+ }
44
+ process.exit(1)
45
+ })
46
+ }