pluidr 0.7.6 → 0.8.0
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/LICENSE +20 -20
- package/README.md +408 -399
- package/bin/pluidr.js +3 -3
- package/package.json +1 -1
- package/src/cli/commands/doctor.js +137 -134
- package/src/cli/commands/init.js +134 -47
- package/src/cli/commands/launch.js +101 -50
- package/src/cli/commands/theme.js +67 -0
- package/src/cli/commands/theme.test.js +28 -0
- package/src/cli/commands/uninstall.js +103 -90
- package/src/cli/commands/update.js +9 -9
- package/src/cli/index.js +63 -57
- package/src/cli/wizard/selectModelTier.js +40 -40
- package/src/core/agentPromptWriter.js +45 -32
- package/src/core/agentPromptWriter.test.js +56 -56
- package/src/core/animation.js +83 -0
- package/src/core/animation.test.js +118 -0
- package/src/core/backup.js +46 -46
- package/src/core/commandsWriter.js +26 -0
- package/src/core/commandsWriter.test.js +29 -0
- package/src/core/configBuilder.js +32 -32
- package/src/core/configBuilder.test.js +93 -93
- package/src/core/configWriter.js +10 -10
- package/src/core/configWriter.test.js +1 -1
- package/src/core/identityHeader.js +8 -8
- package/src/core/paths.js +13 -11
- package/src/core/paths.test.js +33 -29
- package/src/core/pluginWriter.js +43 -29
- package/src/core/skillsWriter.js +21 -0
- package/src/core/skillsWriter.test.js +30 -0
- package/src/core/squeezeInstaller.js +158 -158
- package/src/core/squeezeInstaller.test.js +79 -79
- package/src/core/themeWriter.js +18 -4
- package/src/core/themeWriter.test.js +2 -2
- package/src/core/tuiConfigWriter.js +3 -2
- package/src/core/tuiConfigWriter.test.js +4 -4
- package/src/core/version.js +8 -8
- package/src/core/versionCheck.js +44 -44
- package/src/plugins/README.md +57 -68
- package/src/plugins/pluidr-squeeze.js +77 -77
- package/src/templates/agent-prompts/analyze.txt +49 -0
- package/src/templates/agent-prompts/auditor.txt +20 -20
- package/src/templates/agent-prompts/builder.txt +10 -0
- package/src/templates/agent-prompts/coder.txt +87 -87
- package/src/templates/agent-prompts/compose-reporter.txt +55 -55
- package/src/templates/agent-prompts/compose.txt +50 -0
- package/src/templates/agent-prompts/composer.txt +414 -414
- package/src/templates/agent-prompts/debug-reporter.txt +65 -65
- package/src/templates/agent-prompts/debug.txt +49 -0
- package/src/templates/agent-prompts/debugger.txt +149 -149
- package/src/templates/agent-prompts/explorer.txt +10 -0
- package/src/templates/agent-prompts/fixer.txt +66 -66
- package/src/templates/agent-prompts/hierarchy.txt +101 -95
- package/src/templates/agent-prompts/inspector.txt +79 -79
- package/src/templates/agent-prompts/patcher.txt +20 -20
- package/src/templates/agent-prompts/plan-checker.txt +45 -45
- package/src/templates/agent-prompts/plan-writer.txt +57 -57
- package/src/templates/agent-prompts/probe-reporter.txt +62 -62
- package/src/templates/agent-prompts/prober.txt +93 -93
- package/src/templates/agent-prompts/reporter.txt +10 -0
- package/src/templates/agent-prompts/researcher.txt +48 -48
- package/src/templates/agent-prompts/reviewer.txt +57 -57
- package/src/templates/agent-prompts/tester.txt +66 -66
- package/src/templates/agent-prompts/tracer.txt +33 -33
- package/src/templates/agent-prompts/verifier.txt +10 -0
- package/src/templates/commands/squeeze-dashboard.md +5 -0
- package/src/templates/commands/squeeze-health.md +10 -0
- package/src/templates/commands/squeeze-quick.md +10 -0
- package/src/templates/model-defaults.json +59 -73
- package/src/templates/opencode.config.json +243 -572
- package/src/templates/skills/brooks-lint-rca/SKILL.md +48 -0
- package/src/templates/skills/codebase-fact-finding/SKILL.md +39 -0
- package/src/templates/skills/diff-review/SKILL.md +42 -0
- package/src/templates/skills/general-coding/SKILL.md +44 -0
- package/src/templates/skills/minimal-fixing/SKILL.md +25 -0
- package/src/templates/skills/plan-checking/SKILL.md +33 -0
- package/src/templates/skills/ponytail-patching/SKILL.md +20 -0
- package/src/templates/skills/prd-formatting/SKILL.md +45 -0
- package/src/templates/skills/refactoring-patterns/SKILL.md +37 -0
- package/src/templates/skills/security-auditing/SKILL.md +35 -0
- package/src/templates/skills/security-remediation/SKILL.md +37 -0
- package/src/templates/skills/summary-reporting/SKILL.md +83 -0
- package/src/templates/skills/test-assurance/SKILL.md +48 -0
- package/src/templates/skills/test-mocking-strategy/SKILL.md +18 -0
- package/src/templates/skills/ui-design-audit/SKILL.md +23 -0
- package/src/templates/skills/ui-design-system/SKILL.md +37 -0
- package/src/templates/skills/wstg-recon/SKILL.md +33 -0
- package/src/templates/themes/pluidr-colorful.json +241 -0
- package/src/templates/themes/pluidr-dark.json +177 -0
- package/src/templates/themes/pluidr-light.json +241 -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,134 +1,137 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
2
|
-
import { join } from "node:path"
|
|
3
|
-
import { getConfigDir, getConfigPath, getPromptsDir, getThemesDir, getTuiConfigPath } from "../../core/paths.js"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
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
|
|
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: "
|
|
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
|
-
//
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { getConfigDir, getConfigPath, getPromptsDir, getThemesDir, getTuiConfigPath, getCommandsDir } from "../../core/paths.js"
|
|
4
|
+
|
|
5
|
+
const EXPECTED_PROMPT_COUNT = 8
|
|
6
|
+
const PLUGIN_FILES = ["pluidr-flow.js", "pluidr-squeeze.js"]
|
|
7
|
+
const EXPECTED_COMMAND_FILES = ["squeeze-quick.md", "squeeze-health.md", "squeeze-dashboard.md"]
|
|
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 8 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: "8 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
|
+
// 3a. Slash command files exist
|
|
51
|
+
let commandsFound = 0
|
|
52
|
+
const commandsDir = getCommandsDir()
|
|
53
|
+
for (const f of EXPECTED_COMMAND_FILES) {
|
|
54
|
+
if (existsSync(join(commandsDir, f))) commandsFound++
|
|
55
|
+
}
|
|
56
|
+
checks.push({ component: "slash command files", pass: commandsFound === EXPECTED_COMMAND_FILES.length, detail: `${commandsFound}/${EXPECTED_COMMAND_FILES.length}` })
|
|
57
|
+
|
|
58
|
+
// 3b. Theme configuration verification (accepts any valid pluidr theme)
|
|
59
|
+
const ALLOWED_THEMES = new Set(["pluidr-dark", "pluidr-light", "pluidr-colorful"])
|
|
60
|
+
const tuiConfigPath = getTuiConfigPath()
|
|
61
|
+
let activeTheme = "pluidr-dark" // default fallback
|
|
62
|
+
let tuiConfigValid = false
|
|
63
|
+
let tuiDetail = "missing mapping"
|
|
64
|
+
|
|
65
|
+
if (existsSync(tuiConfigPath)) {
|
|
66
|
+
try {
|
|
67
|
+
const content = readFileSync(tuiConfigPath, "utf-8")
|
|
68
|
+
const parsed = JSON.parse(content)
|
|
69
|
+
if (parsed.theme && ALLOWED_THEMES.has(parsed.theme)) {
|
|
70
|
+
activeTheme = parsed.theme
|
|
71
|
+
tuiConfigValid = true
|
|
72
|
+
tuiDetail = ""
|
|
73
|
+
} else {
|
|
74
|
+
tuiDetail = `theme: ${parsed.theme || "none"}`
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
tuiDetail = "corrupted tui.json"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const themeExists = existsSync(join(themesDir, `${activeTheme}.json`))
|
|
82
|
+
checks.push({
|
|
83
|
+
component: themeExists && tuiConfigValid ? `${activeTheme} theme configured` : "theme not configured",
|
|
84
|
+
pass: themeExists && tuiConfigValid,
|
|
85
|
+
detail: !themeExists ? `missing theme file ${activeTheme}.json` : tuiDetail
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// 4. package.json with @opencode-ai/plugin dependency
|
|
89
|
+
let pkgValid = false
|
|
90
|
+
if (existsSync(packageJsonPath)) {
|
|
91
|
+
try {
|
|
92
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"))
|
|
93
|
+
pkgValid = !!(pkg.dependencies && pkg.dependencies["@opencode-ai/plugin"])
|
|
94
|
+
} catch {
|
|
95
|
+
pkgValid = false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
checks.push({ component: "package.json + plugin dep", pass: pkgValid })
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
// 6. Config is valid JSON/JSONC
|
|
102
|
+
let configValid = false
|
|
103
|
+
if (configExists) {
|
|
104
|
+
try {
|
|
105
|
+
const content = readFileSync(configPath, "utf-8")
|
|
106
|
+
const cleaned = cleanJsonc(content)
|
|
107
|
+
JSON.parse(cleaned)
|
|
108
|
+
configValid = true
|
|
109
|
+
} catch {
|
|
110
|
+
configValid = false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
checks.push({ component: "config JSON validity", pass: configValid })
|
|
114
|
+
|
|
115
|
+
return checks
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function runDoctor() {
|
|
119
|
+
const checks = collectChecks()
|
|
120
|
+
const allPass = checks.every((c) => c.pass)
|
|
121
|
+
|
|
122
|
+
console.log("\nPluidr Doctor — Installation Health Check\n")
|
|
123
|
+
for (const c of checks) {
|
|
124
|
+
const icon = c.pass ? "\u2713" : "\u2717"
|
|
125
|
+
const detail = c.detail ? ` (${c.detail})` : ""
|
|
126
|
+
console.log(` ${icon} ${c.component}${detail}`)
|
|
127
|
+
}
|
|
128
|
+
console.log("")
|
|
129
|
+
|
|
130
|
+
if (allPass) {
|
|
131
|
+
console.log("All checks passed.")
|
|
132
|
+
} else {
|
|
133
|
+
console.log("Some checks failed. Re-run `pluidr init` to repair.")
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
process.exit(allPass ? 0 : 1)
|
|
137
|
+
}
|
package/src/cli/commands/init.js
CHANGED
|
@@ -1,47 +1,134 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "node:fs"
|
|
2
|
-
import { resolve, dirname } from "node:path"
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
import {
|
|
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 {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { resolve, dirname } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { select, 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 { writeSkills } from "../../core/skillsWriter.js"
|
|
14
|
+
import { writeCommands } from "../../core/commandsWriter.js"
|
|
15
|
+
import { version } from "../../core/version.js"
|
|
16
|
+
import { getConfigPath } from "../../core/paths.js"
|
|
17
|
+
import { getAnimationType, getLogoLine, ASCII_LOGO_LINES } from "../../core/animation.js"
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
20
|
+
const TEMPLATES_DIR = resolve(__dirname, "../../templates")
|
|
21
|
+
|
|
22
|
+
export async function runInit() {
|
|
23
|
+
console.log("\x1b[1;36m┌ pluidr init\x1b[0m")
|
|
24
|
+
|
|
25
|
+
// Print initial blank lines so the animation loop can safely clear them
|
|
26
|
+
for (let i = 0; i < 9; i++) console.log("│")
|
|
27
|
+
|
|
28
|
+
const animType = getAnimationType()
|
|
29
|
+
let frame = 0
|
|
30
|
+
const animInterval = setInterval(() => {
|
|
31
|
+
// Clear logo lines (9 rows)
|
|
32
|
+
process.stdout.write("\x1b[9A\x1b[0J")
|
|
33
|
+
|
|
34
|
+
for (let r = 0; r < 5; r++) {
|
|
35
|
+
const { text, color } = getLogoLine(ASCII_LOGO_LINES[r], r, frame, animType)
|
|
36
|
+
console.log("│ " + color + text + "\x1b[0m")
|
|
37
|
+
}
|
|
38
|
+
console.log("│");
|
|
39
|
+
console.log(`│ Spex-driven development, made fluid. • v${version} (Stable)`);
|
|
40
|
+
console.log("│ \x1b[90mPress any key to begin setup...\x1b[0m");
|
|
41
|
+
console.log("│");
|
|
42
|
+
|
|
43
|
+
frame++
|
|
44
|
+
}, 100)
|
|
45
|
+
|
|
46
|
+
// Loop until user presses any key (or resolve immediately if not running in TTY)
|
|
47
|
+
if (process.stdin.isTTY) {
|
|
48
|
+
await new Promise((resolve) => {
|
|
49
|
+
const onData = (key) => {
|
|
50
|
+
if (key === "\u0003") {
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
clearInterval(animInterval);
|
|
54
|
+
process.stdin.setRawMode(false);
|
|
55
|
+
process.stdin.pause();
|
|
56
|
+
process.stdin.off("data", onData);
|
|
57
|
+
resolve();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
process.stdin.setRawMode(true);
|
|
61
|
+
process.stdin.resume();
|
|
62
|
+
process.stdin.setEncoding("utf8");
|
|
63
|
+
process.stdin.on("data", onData);
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
clearInterval(animInterval);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Overwrite "Press any key to begin setup..." with "Starting setup wizard..." in place
|
|
70
|
+
process.stdout.write("\x1b[2A\r│ \x1b[32m✓\x1b[0m Starting setup wizard...\x1b[0K\n│\n\n");
|
|
71
|
+
|
|
72
|
+
const defaultsPath = resolve(TEMPLATES_DIR, "model-defaults.json")
|
|
73
|
+
const modelDefaults = JSON.parse(readFileSync(defaultsPath, "utf-8"))
|
|
74
|
+
|
|
75
|
+
// 1. Model Tier Selection Prompts (Directly separated by reasoning, precision, fast)
|
|
76
|
+
const tierChoices = await selectModelTier(modelDefaults)
|
|
77
|
+
|
|
78
|
+
// 2. Theme Selection Prompt
|
|
79
|
+
const selectedTheme = await select({
|
|
80
|
+
message: "Select a Default Theme:",
|
|
81
|
+
options: [
|
|
82
|
+
{ value: "pluidr-dark", label: "pluidr-dark - Dark High Contrast (Recommended)" },
|
|
83
|
+
{ value: "pluidr-light", label: "pluidr-light - Light High Contrast" },
|
|
84
|
+
{ value: "pluidr-colorful", label: "pluidr-colorful - Pastel Palette" }
|
|
85
|
+
]
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
if (isCancel(selectedTheme)) {
|
|
89
|
+
console.log("Operation cancelled.")
|
|
90
|
+
process.exit(0)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 3. Execution steps
|
|
94
|
+
backupExistingConfig()
|
|
95
|
+
|
|
96
|
+
const configObject = buildConfig(tierChoices, TEMPLATES_DIR)
|
|
97
|
+
writeConfig(configObject)
|
|
98
|
+
writeAgentPrompts(TEMPLATES_DIR)
|
|
99
|
+
console.log("│ Writing agent prompts... \x1b[32m✓\x1b[0m Wrote 8 files to ~/.config/opencode/prompts/")
|
|
100
|
+
|
|
101
|
+
writeThemes(TEMPLATES_DIR)
|
|
102
|
+
console.log("│ Writing themes... \x1b[32m✓\x1b[0m Installed 3 themes")
|
|
103
|
+
|
|
104
|
+
writeSkills(TEMPLATES_DIR)
|
|
105
|
+
console.log("│ Writing skills... \x1b[32m✓\x1b[0m Installed 17 developer skill playbooks")
|
|
106
|
+
|
|
107
|
+
writeCommands(TEMPLATES_DIR)
|
|
108
|
+
console.log("│ Writing commands... \x1b[32m✓\x1b[0m Installed 3 custom slash commands")
|
|
109
|
+
|
|
110
|
+
writeTuiConfig(selectedTheme, undefined, "wave")
|
|
111
|
+
console.log("│ Writing configuration... \x1b[32m✓\x1b[0m Configured opencode.jsonc and tui.json")
|
|
112
|
+
|
|
113
|
+
writePluginBundle()
|
|
114
|
+
writePluginPackageJson()
|
|
115
|
+
console.log("│ Installing plugins... \x1b[32m✓\x1b[0m Installed bundled plugins (pluidr-flow, pluidr-squeeze)");
|
|
116
|
+
|
|
117
|
+
const path = getConfigPath().replace(/\\/g, "/").replace(/^\//, "")
|
|
118
|
+
console.log(`
|
|
119
|
+
======================================================
|
|
120
|
+
🚀 Pluidr Setup Complete!
|
|
121
|
+
======================================================
|
|
122
|
+
|
|
123
|
+
You can view the full list of available commands using:
|
|
124
|
+
$ pluidr --help
|
|
125
|
+
|
|
126
|
+
To customize your agent model settings, open your config:
|
|
127
|
+
👉 \x1b]8;;file:///${path}\x1b\\opencode.jsonc\x1b]8;;\x1b\\
|
|
128
|
+
|
|
129
|
+
To launch OpenCode with your Pluidr pipeline, simply run:
|
|
130
|
+
$ pluidr
|
|
131
|
+
======================================================
|
|
132
|
+
`)
|
|
133
|
+
console.log("\x1b[1;36m└ Setup finished!\x1b[0m")
|
|
134
|
+
}
|
|
@@ -1,50 +1,101 @@
|
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
import { getAnimationType, getLogoLine, ASCII_LOGO_LINES } from "../../core/animation.js"
|
|
9
|
+
|
|
10
|
+
export async function runLaunch() {
|
|
11
|
+
// 1. Start loading animation
|
|
12
|
+
for (let i = 0; i < 9; i++) console.log("");
|
|
13
|
+
|
|
14
|
+
const animType = getAnimationType();
|
|
15
|
+
let frame = 0;
|
|
16
|
+
let statusMessage = "Loading Pluidr...";
|
|
17
|
+
|
|
18
|
+
const animInterval = setInterval(() => {
|
|
19
|
+
// Clear logo lines (9 rows)
|
|
20
|
+
process.stdout.write("\x1b[9A\x1b[0J");
|
|
21
|
+
|
|
22
|
+
for (let r = 0; r < 5; r++) {
|
|
23
|
+
const { text, color } = getLogoLine(ASCII_LOGO_LINES[r], r, frame, animType);
|
|
24
|
+
console.log(color + text + "\x1b[0m");
|
|
25
|
+
}
|
|
26
|
+
console.log("");
|
|
27
|
+
console.log(` Spex-driven development, made fluid. • v${version} (Stable)`);
|
|
28
|
+
console.log("");
|
|
29
|
+
|
|
30
|
+
// Draw spinning wave indicator
|
|
31
|
+
const spinner = ["∿", "≋", "≁", "≋"][frame % 4];
|
|
32
|
+
console.log(` ${spinner} [ \x1b[36m🌊\x1b[0m ] ${statusMessage}\r`);
|
|
33
|
+
|
|
34
|
+
frame++;
|
|
35
|
+
}, 100);
|
|
36
|
+
|
|
37
|
+
// 2. Perform async checks (enforce minimum 1.5s animation duration for visual quality)
|
|
38
|
+
const startTime = Date.now();
|
|
39
|
+
|
|
40
|
+
statusMessage = "Checking environment and version...";
|
|
41
|
+
const updated = await checkAndPromptUpdate(version);
|
|
42
|
+
if (updated) {
|
|
43
|
+
clearInterval(animInterval);
|
|
44
|
+
console.log("Update successful. Please run pluidr again to start the new version.");
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
statusMessage = "Diagnosing installation integrity...";
|
|
49
|
+
const checks = collectChecks();
|
|
50
|
+
const allPass = checks.every((c) => c.pass);
|
|
51
|
+
|
|
52
|
+
// Wait for remainder of minimum duration
|
|
53
|
+
const elapsed = Date.now() - startTime;
|
|
54
|
+
if (elapsed < 1500) {
|
|
55
|
+
await new Promise(resolve => setTimeout(resolve, 1500 - elapsed));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
clearInterval(animInterval);
|
|
59
|
+
|
|
60
|
+
// 3. Handle checks outcomes
|
|
61
|
+
if (allPass) {
|
|
62
|
+
// Overwrite the spinner line with the success checkmark in place
|
|
63
|
+
process.stdout.write("\x1b[1A\r \x1b[32m✓\x1b[0m All checks passed! Spawning OpenCode...\x1b[0K\n\n");
|
|
64
|
+
} else {
|
|
65
|
+
// Overwrite the spinner line with the failure message in place
|
|
66
|
+
process.stdout.write("\x1b[1A\r \x1b[31m✗\x1b[0m Diagnostics check failed.\x1b[0K\n\n");
|
|
67
|
+
const failed = checks.filter((c) => !c.pass);
|
|
68
|
+
console.log(`✗ Pluidr diagnostics found ${failed.length} failure${failed.length > 1 ? "s" : ""}:`);
|
|
69
|
+
for (const c of failed) {
|
|
70
|
+
const detail = c.detail ? ` (${c.detail})` : "";
|
|
71
|
+
console.log(` ✗ ${c.component}${detail}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log("\nIt looks like Pluidr is not fully initialized yet.");
|
|
75
|
+
console.log("We highly recommend running the initialization command to set up templates, themes, and configs:");
|
|
76
|
+
console.log(" $ pluidr init");
|
|
77
|
+
console.log("\nYou can view all available commands using:");
|
|
78
|
+
console.log(" $ pluidr --help\n");
|
|
79
|
+
|
|
80
|
+
const answer = await confirm({ message: "Run pluidr init now to complete setup?", initialValue: true });
|
|
81
|
+
if (!isCancel(answer) && answer) {
|
|
82
|
+
await runInit();
|
|
83
|
+
} else {
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 4. Launch opencode
|
|
89
|
+
console.log("Launching pluidr...");
|
|
90
|
+
const child = process.platform === "win32"
|
|
91
|
+
? spawn("opencode", { stdio: "inherit", shell: true })
|
|
92
|
+
: spawn("opencode", [], { stdio: "inherit" })
|
|
93
|
+
child.on("error", (err) => {
|
|
94
|
+
if (err.code === "ENOENT") {
|
|
95
|
+
console.error("Error: opencode not found on PATH. Install it from https://opencode.ai");
|
|
96
|
+
} else {
|
|
97
|
+
console.error(`Error launching opencode: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
process.exit(1);
|
|
100
|
+
})
|
|
101
|
+
}
|