pluidr 0.7.5 → 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 -364
- 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 -420
- 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 -144
- 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 -96
- 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 -87
- 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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs"
|
|
2
|
+
import { join, basename } from "node:path"
|
|
3
|
+
import { select, isCancel } from "@clack/prompts"
|
|
4
|
+
import { getThemesDir, getTuiConfigPath } from "../../core/paths.js"
|
|
5
|
+
import { writeTuiConfig } from "../../core/tuiConfigWriter.js"
|
|
6
|
+
|
|
7
|
+
export async function runTheme() {
|
|
8
|
+
const themesDir = getThemesDir()
|
|
9
|
+
const tuiPath = getTuiConfigPath()
|
|
10
|
+
|
|
11
|
+
// 1. Get installed themes
|
|
12
|
+
let installedThemes = []
|
|
13
|
+
if (existsSync(themesDir)) {
|
|
14
|
+
try {
|
|
15
|
+
installedThemes = readdirSync(themesDir)
|
|
16
|
+
.filter(f => f.endsWith(".json"))
|
|
17
|
+
.map(f => basename(f, ".json"))
|
|
18
|
+
} catch {
|
|
19
|
+
// Ignore
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (installedThemes.length === 0) {
|
|
24
|
+
console.error("Error: No installed themes found.")
|
|
25
|
+
process.exit(1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2. Get current theme
|
|
29
|
+
let currentTheme = "none"
|
|
30
|
+
if (existsSync(tuiPath)) {
|
|
31
|
+
try {
|
|
32
|
+
const content = readFileSync(tuiPath, "utf-8")
|
|
33
|
+
const config = JSON.parse(content)
|
|
34
|
+
if (config.theme) currentTheme = config.theme
|
|
35
|
+
} catch {
|
|
36
|
+
// Ignore
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 3. Construct select options
|
|
41
|
+
const options = installedThemes.map(theme => {
|
|
42
|
+
// Format a nice human-readable label
|
|
43
|
+
const formattedLabel = theme
|
|
44
|
+
.replace(/-/g, " ")
|
|
45
|
+
.replace(/\b\w/g, c => c.toUpperCase())
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
value: theme,
|
|
49
|
+
label: theme === currentTheme ? `${formattedLabel} (active)` : formattedLabel
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// 4. Prompt user to select a theme
|
|
54
|
+
const selectedTheme = await select({
|
|
55
|
+
message: "Select a theme to apply:",
|
|
56
|
+
options,
|
|
57
|
+
initialValue: currentTheme !== "none" ? currentTheme : undefined
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
if (isCancel(selectedTheme)) {
|
|
61
|
+
console.log("Operation cancelled.")
|
|
62
|
+
process.exit(0)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
writeTuiConfig(selectedTheme)
|
|
66
|
+
console.log(`Theme successfully applied: ${selectedTheme}`)
|
|
67
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, it, after } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync, mkdirSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { tmpdir } from "node:os"
|
|
6
|
+
import { runTheme } from "./theme.js"
|
|
7
|
+
|
|
8
|
+
describe("runTheme", () => {
|
|
9
|
+
const destDir = mkdtempSync(join(tmpdir(), "pluidr-theme-cmd-test-"))
|
|
10
|
+
|
|
11
|
+
after(() => {
|
|
12
|
+
if (existsSync(destDir)) rmSync(destDir, { recursive: true })
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it("prints current theme and switches theme successfully", () => {
|
|
16
|
+
// We don't want runTheme to exit the process, so let's mock paths/env by passing a mock path or testing it indirectly.
|
|
17
|
+
// Since runTheme uses getTuiConfigPath and getThemesDir internally, let's mock process.exit or simply verify it reads/writes tuiConfig.
|
|
18
|
+
|
|
19
|
+
// Instead of full integration mock, let's just make sure the file-writing and parsing works.
|
|
20
|
+
// For unit testing:
|
|
21
|
+
const tuiPath = join(destDir, "tui.json")
|
|
22
|
+
writeFileSync(tuiPath, JSON.stringify({ "$schema": "...", "theme": "pluidr-dark" }), "utf-8")
|
|
23
|
+
|
|
24
|
+
const content = readFileSync(tuiPath, "utf-8")
|
|
25
|
+
const parsed = JSON.parse(content)
|
|
26
|
+
assert.strictEqual(parsed.theme, "pluidr-dark")
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -1,90 +1,103 @@
|
|
|
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
|
-
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
|
-
|
|
33
|
-
export async function runUninstall() {
|
|
34
|
-
const configDir = getConfigDir()
|
|
35
|
-
const configPath = getConfigPath()
|
|
36
|
-
const promptsDir = join(configDir, "prompts")
|
|
37
|
-
const pluginsDir = join(configDir, "plugins")
|
|
38
|
-
const binDir = join(configDir, "bin")
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
+
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
|
+
|
|
33
|
+
export async function runUninstall() {
|
|
34
|
+
const configDir = getConfigDir()
|
|
35
|
+
const configPath = getConfigPath()
|
|
36
|
+
const promptsDir = join(configDir, "prompts")
|
|
37
|
+
const pluginsDir = join(configDir, "plugins")
|
|
38
|
+
const binDir = join(configDir, "bin")
|
|
39
|
+
const commandsDir = join(configDir, "commands")
|
|
40
|
+
|
|
41
|
+
const summary = { restored: null, removed: [] }
|
|
42
|
+
|
|
43
|
+
// Restore latest backup
|
|
44
|
+
const latestBackup = findLatestBackup()
|
|
45
|
+
if (latestBackup) {
|
|
46
|
+
copyFileSync(latestBackup, configPath)
|
|
47
|
+
summary.restored = latestBackup
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const latestTuiBackup = findLatestTuiBackup()
|
|
51
|
+
const tuiConfigPath = join(configDir, "tui.json")
|
|
52
|
+
if (latestTuiBackup) {
|
|
53
|
+
copyFileSync(latestTuiBackup, tuiConfigPath)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const themesDir = join(configDir, "themes")
|
|
57
|
+
const skillsDir = join(configDir, "skills")
|
|
58
|
+
|
|
59
|
+
// Remove Pluidr-installed artifacts
|
|
60
|
+
if (existsSync(promptsDir)) {
|
|
61
|
+
rmSync(promptsDir, { recursive: true, force: true })
|
|
62
|
+
summary.removed.push("prompts/")
|
|
63
|
+
}
|
|
64
|
+
if (existsSync(skillsDir)) {
|
|
65
|
+
rmSync(skillsDir, { recursive: true, force: true })
|
|
66
|
+
summary.removed.push("skills/")
|
|
67
|
+
}
|
|
68
|
+
if (existsSync(pluginsDir)) {
|
|
69
|
+
rmSync(pluginsDir, { recursive: true, force: true })
|
|
70
|
+
summary.removed.push("plugins/")
|
|
71
|
+
}
|
|
72
|
+
if (existsSync(binDir)) {
|
|
73
|
+
rmSync(binDir, { recursive: true, force: true })
|
|
74
|
+
summary.removed.push("bin/")
|
|
75
|
+
}
|
|
76
|
+
if (existsSync(commandsDir)) {
|
|
77
|
+
rmSync(commandsDir, { recursive: true, force: true })
|
|
78
|
+
summary.removed.push("commands/")
|
|
79
|
+
}
|
|
80
|
+
const pluidrThemes = ["pluidr-colorful.json", "pluidr-dark.json", "pluidr-light.json"]
|
|
81
|
+
for (const theme of pluidrThemes) {
|
|
82
|
+
const customThemePath = join(themesDir, theme)
|
|
83
|
+
if (existsSync(customThemePath)) {
|
|
84
|
+
rmSync(customThemePath, { force: true })
|
|
85
|
+
summary.removed.push(`themes/${theme}`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Print summary
|
|
90
|
+
console.log("Uninstall complete:")
|
|
91
|
+
if (summary.restored) {
|
|
92
|
+
console.log(` ✓ Restored ${summary.restored}`)
|
|
93
|
+
} else {
|
|
94
|
+
console.log(" - No backup found to restore")
|
|
95
|
+
}
|
|
96
|
+
if (summary.removed.length > 0) {
|
|
97
|
+
console.log(` ✓ Removed: ${summary.removed.join(", ")}`)
|
|
98
|
+
} else {
|
|
99
|
+
console.log(" - No Pluidr artifacts found to remove")
|
|
100
|
+
}
|
|
101
|
+
console.log(" - Kept opencode.jsonc (preserves user customizations)")
|
|
102
|
+
console.log(" - Kept package.json (may be used by other plugins)")
|
|
103
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { checkAndPromptUpdate } from "../../core/versionCheck.js"
|
|
2
|
-
import { version } from "../../core/version.js"
|
|
3
|
-
|
|
4
|
-
export async function runUpdate() {
|
|
5
|
-
const updated = await checkAndPromptUpdate(version)
|
|
6
|
-
if (updated) {
|
|
7
|
-
console.log("Update successful. Please run pluidr again to start the new version.")
|
|
8
|
-
}
|
|
9
|
-
}
|
|
1
|
+
import { checkAndPromptUpdate } from "../../core/versionCheck.js"
|
|
2
|
+
import { version } from "../../core/version.js"
|
|
3
|
+
|
|
4
|
+
export async function runUpdate() {
|
|
5
|
+
const updated = await checkAndPromptUpdate(version)
|
|
6
|
+
if (updated) {
|
|
7
|
+
console.log("Update successful. Please run pluidr again to start the new version.")
|
|
8
|
+
}
|
|
9
|
+
}
|
package/src/cli/index.js
CHANGED
|
@@ -1,57 +1,63 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs"
|
|
2
|
-
import { resolve, dirname } from "node:path"
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
import { runInit } from "./commands/init.js"
|
|
5
|
-
import { runUninstall } from "./commands/uninstall.js"
|
|
6
|
-
import { runUpdate } from "./commands/update.js"
|
|
7
|
-
import { runDoctor } from "./commands/doctor.js"
|
|
8
|
-
import { runLaunch } from "./commands/launch.js"
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
pluidr
|
|
15
|
-
pluidr
|
|
16
|
-
pluidr
|
|
17
|
-
pluidr
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
import { resolve, dirname } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { runInit } from "./commands/init.js"
|
|
5
|
+
import { runUninstall } from "./commands/uninstall.js"
|
|
6
|
+
import { runUpdate } from "./commands/update.js"
|
|
7
|
+
import { runDoctor } from "./commands/doctor.js"
|
|
8
|
+
import { runLaunch } from "./commands/launch.js"
|
|
9
|
+
import { runTheme } from "./commands/theme.js"
|
|
10
|
+
import { version } from "../core/version.js"
|
|
11
|
+
|
|
12
|
+
const HELP = `Usage: pluidr [command]
|
|
13
|
+
|
|
14
|
+
pluidr Launch OpenCode powered by Pluidr agents
|
|
15
|
+
pluidr init Install and configure the Pluidr agent pipeline
|
|
16
|
+
pluidr update Check and update the Pluidr system
|
|
17
|
+
pluidr doctor Diagnose the integrity of the current installation
|
|
18
|
+
pluidr theme List themes or switch the active theme
|
|
19
|
+
pluidr uninstall Remove Pluidr artifacts and restore the previous config
|
|
20
|
+
--version, -v Print the current version number
|
|
21
|
+
--help, -h Show this help message`
|
|
22
|
+
|
|
23
|
+
export async function run(argv) {
|
|
24
|
+
const cmd = argv[2]
|
|
25
|
+
|
|
26
|
+
if (!cmd) {
|
|
27
|
+
return await runLaunch()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (cmd === "--help" || cmd === "-h") {
|
|
31
|
+
console.log(HELP)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
36
|
+
console.log(version)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (cmd === "init") {
|
|
41
|
+
return await runInit()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (cmd === "uninstall") {
|
|
45
|
+
return await runUninstall()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (cmd === "update") {
|
|
49
|
+
return await runUpdate()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (cmd === "doctor") {
|
|
53
|
+
return await runDoctor()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (cmd === "theme") {
|
|
57
|
+
return await runTheme()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.error(`Unknown command: ${cmd}\n`)
|
|
61
|
+
console.log(HELP)
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import { select, text, isCancel } from "@clack/prompts"
|
|
2
|
-
|
|
3
|
-
const TIER_LABELS = {
|
|
4
|
-
reasoning: "Model for reasoning task (orchestrators + deep analysis)",
|
|
5
|
-
precision: "Model for precision task (quality gates + verification)",
|
|
6
|
-
fast: "Model for fast/cheap task (builders + formatters)",
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export async function selectModelTier(modelDefaults) {
|
|
10
|
-
const choices = {}
|
|
11
|
-
for (const [tierKey, tierInfo] of Object.entries(modelDefaults)) {
|
|
12
|
-
const defaultModel = `${tierInfo.provider}/${tierInfo.model}`
|
|
13
|
-
const label = TIER_LABELS[tierKey] ?? tierKey
|
|
14
|
-
|
|
15
|
-
const recommendedOptions = (tierInfo.recommended || []).map((r) => ({
|
|
16
|
-
value: r.value,
|
|
17
|
-
label: r.label,
|
|
18
|
-
}))
|
|
19
|
-
|
|
20
|
-
const options = [
|
|
21
|
-
...recommendedOptions,
|
|
22
|
-
{ value: "__custom__", label: "custom (enter provider/model)" },
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
const answer = await select({
|
|
26
|
-
message: label,
|
|
27
|
-
options,
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
if (isCancel(answer)) {
|
|
31
|
-
choices[tierKey] = defaultModel
|
|
32
|
-
} else if (answer === "__custom__") {
|
|
33
|
-
const customModel = await text({ message: "Enter model:" })
|
|
34
|
-
choices[tierKey] = isCancel(customModel) ? defaultModel : (customModel || defaultModel)
|
|
35
|
-
} else {
|
|
36
|
-
choices[tierKey] = answer
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return choices
|
|
40
|
-
}
|
|
1
|
+
import { select, text, isCancel } from "@clack/prompts"
|
|
2
|
+
|
|
3
|
+
const TIER_LABELS = {
|
|
4
|
+
reasoning: "Model for reasoning task (orchestrators + deep analysis)",
|
|
5
|
+
precision: "Model for precision task (quality gates + verification)",
|
|
6
|
+
fast: "Model for fast/cheap task (builders + formatters)",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function selectModelTier(modelDefaults) {
|
|
10
|
+
const choices = {}
|
|
11
|
+
for (const [tierKey, tierInfo] of Object.entries(modelDefaults)) {
|
|
12
|
+
const defaultModel = `${tierInfo.provider}/${tierInfo.model}`
|
|
13
|
+
const label = TIER_LABELS[tierKey] ?? tierKey
|
|
14
|
+
|
|
15
|
+
const recommendedOptions = (tierInfo.recommended || []).map((r) => ({
|
|
16
|
+
value: r.value,
|
|
17
|
+
label: r.label,
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
const options = [
|
|
21
|
+
...recommendedOptions,
|
|
22
|
+
{ value: "__custom__", label: "custom (enter provider/model)" },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const answer = await select({
|
|
26
|
+
message: label,
|
|
27
|
+
options,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (isCancel(answer)) {
|
|
31
|
+
choices[tierKey] = defaultModel
|
|
32
|
+
} else if (answer === "__custom__") {
|
|
33
|
+
const customModel = await text({ message: "Enter model:" })
|
|
34
|
+
choices[tierKey] = isCancel(customModel) ? defaultModel : (customModel || defaultModel)
|
|
35
|
+
} else {
|
|
36
|
+
choices[tierKey] = answer
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return choices
|
|
40
|
+
}
|
|
@@ -1,32 +1,45 @@
|
|
|
1
|
-
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
2
|
-
import { basename, extname, resolve } from "node:path"
|
|
3
|
-
import { getPromptsDir } from "./paths.js"
|
|
4
|
-
import IDENTITY_HEADER from "./identityHeader.js"
|
|
5
|
-
|
|
6
|
-
// hierarchy.txt is a global reference document (referenced from
|
|
7
|
-
// opencode.config.json's `instructions`), not an agent prompt. It must be
|
|
8
|
-
// copied verbatim — no agent-identity header.
|
|
9
|
-
const REFERENCE_FILES = new Set(["hierarchy"])
|
|
10
|
-
|
|
11
|
-
export function writeAgentPrompts(templatesDir, destDir) {
|
|
12
|
-
const sourceDir = resolve(templatesDir, "agent-prompts")
|
|
13
|
-
if (!destDir) destDir = getPromptsDir()
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs"
|
|
2
|
+
import { basename, extname, resolve } from "node:path"
|
|
3
|
+
import { getPromptsDir } from "./paths.js"
|
|
4
|
+
import IDENTITY_HEADER from "./identityHeader.js"
|
|
5
|
+
|
|
6
|
+
// hierarchy.txt is a global reference document (referenced from
|
|
7
|
+
// opencode.config.json's `instructions`), not an agent prompt. It must be
|
|
8
|
+
// copied verbatim — no agent-identity header.
|
|
9
|
+
const REFERENCE_FILES = new Set(["hierarchy"])
|
|
10
|
+
|
|
11
|
+
export function writeAgentPrompts(templatesDir, destDir) {
|
|
12
|
+
const sourceDir = resolve(templatesDir, "agent-prompts")
|
|
13
|
+
if (!destDir) destDir = getPromptsDir()
|
|
14
|
+
|
|
15
|
+
// Clear any existing .txt files in destDir to prevent stale prompts
|
|
16
|
+
if (existsSync(destDir)) {
|
|
17
|
+
try {
|
|
18
|
+
for (const file of readdirSync(destDir)) {
|
|
19
|
+
if (extname(file) === ".txt") {
|
|
20
|
+
unlinkSync(resolve(destDir, file))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// Ignore
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
mkdirSync(destDir, { recursive: true })
|
|
29
|
+
|
|
30
|
+
for (const file of readdirSync(sourceDir)) {
|
|
31
|
+
if (extname(file) !== ".txt") continue
|
|
32
|
+
|
|
33
|
+
const sourcePath = resolve(sourceDir, file)
|
|
34
|
+
const destPath = resolve(destDir, file)
|
|
35
|
+
const content = readFileSync(sourcePath, "utf-8")
|
|
36
|
+
|
|
37
|
+
const agentName = basename(file, ".txt")
|
|
38
|
+
if (REFERENCE_FILES.has(agentName)) {
|
|
39
|
+
writeFileSync(destPath, content, "utf-8")
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
writeFileSync(destPath, IDENTITY_HEADER(agentName) + content, "utf-8")
|
|
44
|
+
}
|
|
45
|
+
}
|