pluidr 0.6.1 → 0.7.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.
Files changed (48) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +365 -357
  3. package/bin/pluidr.js +3 -3
  4. package/package.json +45 -45
  5. package/src/cli/commands/doctor.js +101 -99
  6. package/src/cli/commands/init.js +43 -43
  7. package/src/cli/commands/launch.js +46 -0
  8. package/src/cli/commands/uninstall.js +63 -67
  9. package/src/cli/commands/update.js +6 -22
  10. package/src/cli/index.js +57 -53
  11. package/src/cli/wizard/selectModelTier.js +40 -39
  12. package/src/core/agentPromptWriter.js +32 -32
  13. package/src/core/agentPromptWriter.test.js +56 -56
  14. package/src/core/backup.js +40 -38
  15. package/src/core/configBuilder.js +32 -32
  16. package/src/core/configBuilder.test.js +93 -47
  17. package/src/core/configWriter.js +10 -10
  18. package/src/core/identityHeader.js +8 -8
  19. package/src/core/paths.js +9 -9
  20. package/src/core/paths.test.js +21 -21
  21. package/src/core/pluginWriter.js +29 -29
  22. package/src/core/squeezeInstaller.js +161 -161
  23. package/src/core/squeezeInstaller.test.js +75 -75
  24. package/src/core/version.js +8 -0
  25. package/src/core/versionCheck.js +44 -0
  26. package/src/core/versionCheck.test.js +34 -0
  27. package/src/plugins/README.md +68 -68
  28. package/src/plugins/pluidr-squeeze.js +77 -77
  29. package/src/templates/agent-prompts/auditor.txt +20 -20
  30. package/src/templates/agent-prompts/coder.txt +87 -87
  31. package/src/templates/agent-prompts/compose-reporter.txt +55 -55
  32. package/src/templates/agent-prompts/composer.txt +430 -430
  33. package/src/templates/agent-prompts/debug-reporter.txt +65 -65
  34. package/src/templates/agent-prompts/debugger.txt +151 -151
  35. package/src/templates/agent-prompts/fixer.txt +66 -66
  36. package/src/templates/agent-prompts/hierarchy.txt +96 -96
  37. package/src/templates/agent-prompts/inspector.txt +79 -79
  38. package/src/templates/agent-prompts/patcher.txt +20 -20
  39. package/src/templates/agent-prompts/plan-checker.txt +45 -45
  40. package/src/templates/agent-prompts/plan-writer.txt +57 -57
  41. package/src/templates/agent-prompts/probe-reporter.txt +62 -62
  42. package/src/templates/agent-prompts/prober.txt +90 -90
  43. package/src/templates/agent-prompts/researcher.txt +48 -48
  44. package/src/templates/agent-prompts/reviewer.txt +57 -57
  45. package/src/templates/agent-prompts/tester.txt +66 -66
  46. package/src/templates/agent-prompts/tracer.txt +33 -33
  47. package/src/templates/model-defaults.json +73 -55
  48. package/src/templates/opencode.config.json +481 -481
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.6.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.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,99 +1,101 @@
1
- import { existsSync, readFileSync, readdirSync } from "node:fs"
2
- import { join, dirname } from "node:path"
3
- import { fileURLToPath } from "node:url"
4
- import { execFileSync } from "node:child_process"
5
- import { getConfigDir, getConfigPath, getPromptsDir } from "../../core/paths.js"
6
- import { findSqueezePath } from "../../core/squeezeInstaller.js"
7
-
8
- const __dirname = dirname(fileURLToPath(import.meta.url))
9
- const EXPECTED_PROMPT_COUNT = 18
10
- const PLUGIN_FILES = ["pluidr-flow.js", "pluidr-squeeze.js"]
11
-
12
- export async function runDoctor() {
13
- const configDir = getConfigDir()
14
- const configPath = getConfigPath()
15
- const promptsDir = getPromptsDir()
16
- const pluginsDir = join(configDir, "plugins")
17
- const packageJsonPath = join(configDir, "package.json")
18
- const binDir = join(configDir, "bin")
19
-
20
- const checks = []
21
-
22
- // 1. opencode.jsonc exists
23
- const configExists = existsSync(configPath)
24
- checks.push({ component: "opencode.jsonc", pass: configExists })
25
-
26
- // 2. All 18 prompt files exist
27
- let promptCount = 0
28
- if (existsSync(promptsDir)) {
29
- try {
30
- const files = readdirSync(promptsDir).filter((f) => f.endsWith(".txt"))
31
- promptCount = files.length
32
- } catch {
33
- promptCount = 0
34
- }
35
- }
36
- checks.push({ component: "18 prompt files", pass: promptCount === EXPECTED_PROMPT_COUNT, detail: `${promptCount}/${EXPECTED_PROMPT_COUNT}` })
37
-
38
- // 3. Both plugin files exist
39
- let pluginsFound = 0
40
- for (const f of PLUGIN_FILES) {
41
- if (existsSync(join(pluginsDir, f))) pluginsFound++
42
- }
43
- checks.push({ component: "plugin files", pass: pluginsFound === PLUGIN_FILES.length, detail: `${pluginsFound}/${PLUGIN_FILES.length}` })
44
-
45
- // 4. package.json with @opencode-ai/plugin dependency
46
- let pkgValid = false
47
- if (existsSync(packageJsonPath)) {
48
- try {
49
- const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"))
50
- pkgValid = !!(pkg.dependencies && pkg.dependencies["@opencode-ai/plugin"])
51
- } catch {
52
- pkgValid = false
53
- }
54
- }
55
- checks.push({ component: "package.json + plugin dep", pass: pkgValid })
56
-
57
- // 5. Squeeze binary exists
58
- let squeezeFound = false
59
- const squeezeOnPath = findSqueezePath()
60
- if (squeezeOnPath) {
61
- squeezeFound = true
62
- } else {
63
- const binName = process.platform === "win32" ? "squeeze.exe" : "squeeze"
64
- squeezeFound = existsSync(join(binDir, binName))
65
- }
66
- checks.push({ component: "squeeze binary tested", pass: squeezeFound })
67
-
68
- // 6. Config is valid JSON/JSONC
69
- let configValid = false
70
- if (configExists) {
71
- try {
72
- const content = readFileSync(configPath, "utf-8")
73
- const cleaned = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m)
74
- JSON.parse(cleaned)
75
- configValid = true
76
- } catch {
77
- configValid = false
78
- }
79
- }
80
- checks.push({ component: "config JSON validity", pass: configValid })
81
-
82
- // Print summary table
83
- const allPass = checks.every((c) => c.pass)
84
- console.log("\nPluidr Doctor Installation Health Check\n")
85
- for (const c of checks) {
86
- const icon = c.pass ? "\u2713" : "\u2717"
87
- const detail = c.detail ? ` (${c.detail})` : ""
88
- console.log(` ${icon} ${c.component}${detail}`)
89
- }
90
- console.log("")
91
-
92
- if (allPass) {
93
- console.log("All checks passed.")
94
- } else {
95
- console.log("Some checks failed. Re-run `pluidr init` to repair.")
96
- }
97
-
98
- process.exit(allPass ? 0 : 1)
99
- }
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,43 +1,43 @@
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) ? true : 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 } 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
+ }
@@ -0,0 +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 = 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
+ }
@@ -1,67 +1,63 @@
1
- import { existsSync, copyFileSync, rmSync, readdirSync } from "node:fs"
2
- import { join, dirname } from "node:path"
3
- import { getConfigDir, getConfigPath } from "../../core/paths.js"
4
-
5
- function findLatestBackup() {
6
- const dir = getConfigDir()
7
- let backups = []
8
- try {
9
- backups = readdirSync(dir)
10
- .filter((f) => f.startsWith("opencode.jsonc.bak."))
11
- .sort()
12
- .reverse()
13
- } catch {
14
- return null
15
- }
16
- return backups.length > 0 ? join(dir, backups[0]) : null
17
- }
18
-
19
- export async function runUninstall() {
20
- const configDir = getConfigDir()
21
- const configPath = getConfigPath()
22
- const promptsDir = join(configDir, "prompts")
23
- const pluginsDir = join(configDir, "plugins")
24
- const binDir = join(configDir, "bin")
25
-
26
- const summary = { restored: null, removed: [] }
27
-
28
- // Restore latest backup
29
- const latestBackup = findLatestBackup()
30
- if (latestBackup && existsSync(configPath)) {
31
- copyFileSync(latestBackup, configPath)
32
- summary.restored = latestBackup
33
- } else if (latestBackup) {
34
- // Config doesn't exist but backup does — restore anyway
35
- copyFileSync(latestBackup, configPath)
36
- summary.restored = latestBackup
37
- }
38
-
39
- // Remove Pluidr-installed artifacts
40
- if (existsSync(promptsDir)) {
41
- rmSync(promptsDir, { recursive: true, force: true })
42
- summary.removed.push("prompts/")
43
- }
44
- if (existsSync(pluginsDir)) {
45
- rmSync(pluginsDir, { recursive: true, force: true })
46
- summary.removed.push("plugins/")
47
- }
48
- if (existsSync(binDir)) {
49
- rmSync(binDir, { recursive: true, force: true })
50
- summary.removed.push("bin/")
51
- }
52
-
53
- // Print summary
54
- console.log("Uninstall complete:")
55
- if (summary.restored) {
56
- console.log(` Restored ${summary.restored}`)
57
- } else {
58
- console.log(" - No backup found to restore")
59
- }
60
- if (summary.removed.length > 0) {
61
- console.log(` Removed: ${summary.removed.join(", ")}`)
62
- } else {
63
- console.log(" - No Pluidr artifacts found to remove")
64
- }
65
- console.log(" - Kept opencode.jsonc (preserves user customizations)")
66
- console.log(" - Kept package.json (may be used by other plugins)")
67
- }
1
+ import { existsSync, copyFileSync, rmSync, readdirSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import { getConfigDir, getConfigPath } from "../../core/paths.js"
4
+
5
+ function findLatestBackup() {
6
+ const dir = getConfigDir()
7
+ let backups = []
8
+ try {
9
+ backups = readdirSync(dir)
10
+ .filter((f) => f.startsWith("opencode.jsonc.bak."))
11
+ .sort()
12
+ .reverse()
13
+ } catch {
14
+ return null
15
+ }
16
+ return backups.length > 0 ? join(dir, backups[0]) : null
17
+ }
18
+
19
+ export async function runUninstall() {
20
+ const configDir = getConfigDir()
21
+ const configPath = getConfigPath()
22
+ const promptsDir = join(configDir, "prompts")
23
+ const pluginsDir = join(configDir, "plugins")
24
+ const binDir = join(configDir, "bin")
25
+
26
+ const summary = { restored: null, removed: [] }
27
+
28
+ // Restore latest backup
29
+ const latestBackup = findLatestBackup()
30
+ if (latestBackup) {
31
+ copyFileSync(latestBackup, configPath)
32
+ summary.restored = latestBackup
33
+ }
34
+
35
+ // Remove Pluidr-installed artifacts
36
+ if (existsSync(promptsDir)) {
37
+ rmSync(promptsDir, { recursive: true, force: true })
38
+ summary.removed.push("prompts/")
39
+ }
40
+ if (existsSync(pluginsDir)) {
41
+ rmSync(pluginsDir, { recursive: true, force: true })
42
+ summary.removed.push("plugins/")
43
+ }
44
+ if (existsSync(binDir)) {
45
+ rmSync(binDir, { recursive: true, force: true })
46
+ summary.removed.push("bin/")
47
+ }
48
+
49
+ // Print summary
50
+ console.log("Uninstall complete:")
51
+ if (summary.restored) {
52
+ console.log(` ✓ Restored ${summary.restored}`)
53
+ } else {
54
+ console.log(" - No backup found to restore")
55
+ }
56
+ if (summary.removed.length > 0) {
57
+ console.log(` Removed: ${summary.removed.join(", ")}`)
58
+ } else {
59
+ console.log(" - No Pluidr artifacts found to remove")
60
+ }
61
+ console.log(" - Kept opencode.jsonc (preserves user customizations)")
62
+ console.log(" - Kept package.json (may be used by other plugins)")
63
+ }
@@ -1,22 +1,6 @@
1
- import { existsSync } from "node:fs"
2
- import { confirm, isCancel } from "@clack/prompts"
3
- import { getConfigPath } from "../../core/paths.js"
4
- import { runInit } from "./init.js"
5
-
6
- export async function runUpdate() {
7
- const configPath = getConfigPath()
8
-
9
- if (existsSync(configPath)) {
10
- const answer = await confirm({
11
- message: "Existing Pluidr config found. Overwrite? (Y/n)",
12
- initialValue: true,
13
- })
14
-
15
- if (isCancel(answer) || !answer) {
16
- console.log("Update cancelled.")
17
- return
18
- }
19
- }
20
-
21
- await runInit()
22
- }
1
+ import { checkAndPromptUpdate } from "../../core/versionCheck.js"
2
+ import { version } from "../../core/version.js"
3
+
4
+ export async function runUpdate() {
5
+ await checkAndPromptUpdate(version)
6
+ }