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
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import { describe, it } from "node:test"
|
|
2
|
-
import assert from "node:assert"
|
|
3
|
-
import { mkdtempSync, writeFileSync, rmSync } from "node:fs"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import { tmpdir } from "node:os"
|
|
6
|
-
import { buildConfig } from "./configBuilder.js"
|
|
7
|
-
|
|
8
|
-
describe("configBuilder", () => {
|
|
9
|
-
it("injects models into agent config per tier", () => {
|
|
10
|
-
const tmpDir = mkdtempSync(join(tmpdir(), "configbuilder-test-"))
|
|
11
|
-
|
|
12
|
-
const configJson = {
|
|
13
|
-
agent: {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
},
|
|
18
|
-
}
|
|
19
|
-
const defaultsJson = {
|
|
20
|
-
reasoning: {
|
|
21
|
-
provider: "test",
|
|
22
|
-
model: "big-model",
|
|
23
|
-
agents: ["
|
|
24
|
-
},
|
|
25
|
-
precision: {
|
|
26
|
-
provider: "test",
|
|
27
|
-
model: "mid-model",
|
|
28
|
-
agents: ["
|
|
29
|
-
},
|
|
30
|
-
fast: {
|
|
31
|
-
provider: "test",
|
|
32
|
-
model: "small-model",
|
|
33
|
-
agents: ["
|
|
34
|
-
},
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
writeFileSync(join(tmpDir, "opencode.config.json"), JSON.stringify(configJson), "utf-8")
|
|
38
|
-
writeFileSync(join(tmpDir, "model-defaults.json"), JSON.stringify(defaultsJson), "utf-8")
|
|
39
|
-
|
|
40
|
-
const result = buildConfig(
|
|
41
|
-
{
|
|
42
|
-
reasoning: "test/big-model",
|
|
43
|
-
precision: "test/mid-model",
|
|
44
|
-
fast: "test/small-model",
|
|
45
|
-
},
|
|
46
|
-
tmpDir,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
assert.strictEqual(result.agent.
|
|
50
|
-
assert.strictEqual(result.agent.
|
|
51
|
-
assert.strictEqual(result.agent.
|
|
52
|
-
|
|
53
|
-
rmSync(tmpDir, { recursive: true })
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it("throws an error if a defaulted agent is missing from config template", () => {
|
|
57
|
-
const tmpDir = mkdtempSync(join(tmpdir(), "configbuilder-test-err-"))
|
|
58
|
-
|
|
59
|
-
const configJson = {
|
|
60
|
-
agent: {
|
|
61
|
-
// missing '
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
}
|
|
65
|
-
const defaultsJson = {
|
|
66
|
-
reasoning: {
|
|
67
|
-
provider: "test",
|
|
68
|
-
model: "big-model",
|
|
69
|
-
agents: ["
|
|
70
|
-
},
|
|
71
|
-
fast: {
|
|
72
|
-
provider: "test",
|
|
73
|
-
model: "small-model",
|
|
74
|
-
agents: ["
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
writeFileSync(join(tmpDir, "opencode.config.json"), JSON.stringify(configJson), "utf-8")
|
|
79
|
-
writeFileSync(join(tmpDir, "model-defaults.json"), JSON.stringify(defaultsJson), "utf-8")
|
|
80
|
-
|
|
81
|
-
assert.throws(() => {
|
|
82
|
-
buildConfig(
|
|
83
|
-
{
|
|
84
|
-
reasoning: "test/big-model",
|
|
85
|
-
fast: "test/small-model",
|
|
86
|
-
},
|
|
87
|
-
tmpDir,
|
|
88
|
-
)
|
|
89
|
-
}, /Template validation failed: agent\(s\) not found/)
|
|
90
|
-
|
|
91
|
-
rmSync(tmpDir, { recursive: true })
|
|
92
|
-
})
|
|
93
|
-
})
|
|
1
|
+
import { describe, it } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { tmpdir } from "node:os"
|
|
6
|
+
import { buildConfig } from "./configBuilder.js"
|
|
7
|
+
|
|
8
|
+
describe("configBuilder", () => {
|
|
9
|
+
it("injects models into agent config per tier", () => {
|
|
10
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "configbuilder-test-"))
|
|
11
|
+
|
|
12
|
+
const configJson = {
|
|
13
|
+
agent: {
|
|
14
|
+
compose: {},
|
|
15
|
+
verifier: {},
|
|
16
|
+
builder: {},
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
const defaultsJson = {
|
|
20
|
+
reasoning: {
|
|
21
|
+
provider: "test",
|
|
22
|
+
model: "big-model",
|
|
23
|
+
agents: ["compose"],
|
|
24
|
+
},
|
|
25
|
+
precision: {
|
|
26
|
+
provider: "test",
|
|
27
|
+
model: "mid-model",
|
|
28
|
+
agents: ["verifier"],
|
|
29
|
+
},
|
|
30
|
+
fast: {
|
|
31
|
+
provider: "test",
|
|
32
|
+
model: "small-model",
|
|
33
|
+
agents: ["builder"],
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
writeFileSync(join(tmpDir, "opencode.config.json"), JSON.stringify(configJson), "utf-8")
|
|
38
|
+
writeFileSync(join(tmpDir, "model-defaults.json"), JSON.stringify(defaultsJson), "utf-8")
|
|
39
|
+
|
|
40
|
+
const result = buildConfig(
|
|
41
|
+
{
|
|
42
|
+
reasoning: "test/big-model",
|
|
43
|
+
precision: "test/mid-model",
|
|
44
|
+
fast: "test/small-model",
|
|
45
|
+
},
|
|
46
|
+
tmpDir,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
assert.strictEqual(result.agent.compose.model, "test/big-model")
|
|
50
|
+
assert.strictEqual(result.agent.verifier.model, "test/mid-model")
|
|
51
|
+
assert.strictEqual(result.agent.builder.model, "test/small-model")
|
|
52
|
+
|
|
53
|
+
rmSync(tmpDir, { recursive: true })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("throws an error if a defaulted agent is missing from config template", () => {
|
|
57
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "configbuilder-test-err-"))
|
|
58
|
+
|
|
59
|
+
const configJson = {
|
|
60
|
+
agent: {
|
|
61
|
+
// missing 'compose'
|
|
62
|
+
builder: {},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
const defaultsJson = {
|
|
66
|
+
reasoning: {
|
|
67
|
+
provider: "test",
|
|
68
|
+
model: "big-model",
|
|
69
|
+
agents: ["compose"],
|
|
70
|
+
},
|
|
71
|
+
fast: {
|
|
72
|
+
provider: "test",
|
|
73
|
+
model: "small-model",
|
|
74
|
+
agents: ["builder"],
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
writeFileSync(join(tmpDir, "opencode.config.json"), JSON.stringify(configJson), "utf-8")
|
|
79
|
+
writeFileSync(join(tmpDir, "model-defaults.json"), JSON.stringify(defaultsJson), "utf-8")
|
|
80
|
+
|
|
81
|
+
assert.throws(() => {
|
|
82
|
+
buildConfig(
|
|
83
|
+
{
|
|
84
|
+
reasoning: "test/big-model",
|
|
85
|
+
fast: "test/small-model",
|
|
86
|
+
},
|
|
87
|
+
tmpDir,
|
|
88
|
+
)
|
|
89
|
+
}, /Template validation failed: agent\(s\) not found/)
|
|
90
|
+
|
|
91
|
+
rmSync(tmpDir, { recursive: true })
|
|
92
|
+
})
|
|
93
|
+
})
|
package/src/core/configWriter.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from "node:fs"
|
|
2
|
-
import { join } from "node:path"
|
|
3
|
-
import { getConfigDir } from "./paths.js"
|
|
4
|
-
|
|
5
|
-
export function writeConfig(configObject, targetDir) {
|
|
6
|
-
const baseDir = targetDir || getConfigDir()
|
|
7
|
-
const configPath = join(baseDir, "opencode.jsonc")
|
|
8
|
-
mkdirSync(baseDir, { recursive: true })
|
|
9
|
-
writeFileSync(configPath, JSON.stringify(configObject, null, 2), "utf-8")
|
|
10
|
-
}
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import { getConfigDir } from "./paths.js"
|
|
4
|
+
|
|
5
|
+
export function writeConfig(configObject, targetDir) {
|
|
6
|
+
const baseDir = targetDir || getConfigDir()
|
|
7
|
+
const configPath = join(baseDir, "opencode.jsonc")
|
|
8
|
+
mkdirSync(baseDir, { recursive: true })
|
|
9
|
+
writeFileSync(configPath, JSON.stringify(configObject, null, 2), "utf-8")
|
|
10
|
+
}
|
|
@@ -13,7 +13,7 @@ describe("configWriter", () => {
|
|
|
13
13
|
})
|
|
14
14
|
|
|
15
15
|
it("writes config as formatted JSON to the correct path", () => {
|
|
16
|
-
const configObj = { agent: {
|
|
16
|
+
const configObj = { agent: { compose: { model: "test/model" } } }
|
|
17
17
|
const expected = JSON.stringify(configObj, null, 2)
|
|
18
18
|
|
|
19
19
|
writeConfig(configObj, tempDir)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// Single source of truth for the agent identity prefix prepended to every
|
|
2
|
-
// generated prompt file. Mirrors the system identity concept from the
|
|
3
|
-
// dropped hook-based AgentSelfIdentityPlugin, but as a static prepend
|
|
4
|
-
// (KISS: agent names are known at install time).
|
|
5
|
-
|
|
6
|
-
export default function IDENTITY_HEADER(agentName) {
|
|
7
|
-
return `You are the "${agentName}" agent.\n\n`
|
|
8
|
-
}
|
|
1
|
+
// Single source of truth for the agent identity prefix prepended to every
|
|
2
|
+
// generated prompt file. Mirrors the system identity concept from the
|
|
3
|
+
// dropped hook-based AgentSelfIdentityPlugin, but as a static prepend
|
|
4
|
+
// (KISS: agent names are known at install time).
|
|
5
|
+
|
|
6
|
+
export default function IDENTITY_HEADER(agentName) {
|
|
7
|
+
return `You are the "${agentName}" agent.\n\n`
|
|
8
|
+
}
|
package/src/core/paths.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { homedir } from "node:os"
|
|
2
|
-
import { join } from "node:path"
|
|
3
|
-
|
|
4
|
-
const BASE = join(homedir(), ".config", "opencode")
|
|
5
|
-
|
|
6
|
-
export const getConfigDir = () => BASE
|
|
7
|
-
export const getConfigPath = () => join(BASE, "opencode.jsonc")
|
|
8
|
-
export const getTuiConfigPath = () => join(BASE, "tui.json")
|
|
9
|
-
export const getPromptsDir = () => join(BASE, "prompts")
|
|
10
|
-
export const getThemesDir = () => join(BASE, "themes")
|
|
11
|
-
|
|
1
|
+
import { homedir } from "node:os"
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
|
|
4
|
+
const BASE = join(homedir(), ".config", "opencode")
|
|
5
|
+
|
|
6
|
+
export const getConfigDir = () => BASE
|
|
7
|
+
export const getConfigPath = () => join(BASE, "opencode.jsonc")
|
|
8
|
+
export const getTuiConfigPath = () => join(BASE, "tui.json")
|
|
9
|
+
export const getPromptsDir = () => join(BASE, "prompts")
|
|
10
|
+
export const getThemesDir = () => join(BASE, "themes")
|
|
11
|
+
export const getSkillsDir = () => join(BASE, "skills")
|
|
12
|
+
export const getCommandsDir = () => join(BASE, "commands")
|
|
13
|
+
// ponytail: getBackupPath removed (YAGNI)
|
package/src/core/paths.test.js
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import { describe, it } from "node:test"
|
|
2
|
-
import assert from "node:assert"
|
|
3
|
-
import { homedir } from "node:os"
|
|
4
|
-
import { join } from "node:path"
|
|
5
|
-
import { getConfigDir, getConfigPath, getTuiConfigPath, getPromptsDir, getThemesDir } from "./paths.js"
|
|
6
|
-
|
|
7
|
-
const BASE = join(homedir(), ".config", "opencode")
|
|
8
|
-
|
|
9
|
-
describe("paths", () => {
|
|
10
|
-
it("getConfigDir returns ~/.config/opencode", () => {
|
|
11
|
-
assert.strictEqual(getConfigDir(), BASE)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it("getConfigPath returns opencode.jsonc path", () => {
|
|
15
|
-
assert.strictEqual(getConfigPath(), join(BASE, "opencode.jsonc"))
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it("getTuiConfigPath returns tui.json path", () => {
|
|
19
|
-
assert.strictEqual(getTuiConfigPath(), join(BASE, "tui.json"))
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it("getPromptsDir returns prompts path", () => {
|
|
23
|
-
assert.strictEqual(getPromptsDir(), join(BASE, "prompts"))
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it("getThemesDir returns themes path", () => {
|
|
27
|
-
assert.strictEqual(getThemesDir(), join(BASE, "themes"))
|
|
28
|
-
})
|
|
29
|
-
|
|
1
|
+
import { describe, it } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { homedir } from "node:os"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { getConfigDir, getConfigPath, getTuiConfigPath, getPromptsDir, getThemesDir, getSkillsDir } from "./paths.js"
|
|
6
|
+
|
|
7
|
+
const BASE = join(homedir(), ".config", "opencode")
|
|
8
|
+
|
|
9
|
+
describe("paths", () => {
|
|
10
|
+
it("getConfigDir returns ~/.config/opencode", () => {
|
|
11
|
+
assert.strictEqual(getConfigDir(), BASE)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("getConfigPath returns opencode.jsonc path", () => {
|
|
15
|
+
assert.strictEqual(getConfigPath(), join(BASE, "opencode.jsonc"))
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it("getTuiConfigPath returns tui.json path", () => {
|
|
19
|
+
assert.strictEqual(getTuiConfigPath(), join(BASE, "tui.json"))
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it("getPromptsDir returns prompts path", () => {
|
|
23
|
+
assert.strictEqual(getPromptsDir(), join(BASE, "prompts"))
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it("getThemesDir returns themes path", () => {
|
|
27
|
+
assert.strictEqual(getThemesDir(), join(BASE, "themes"))
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it("getSkillsDir returns skills path", () => {
|
|
31
|
+
assert.strictEqual(getSkillsDir(), join(BASE, "skills"))
|
|
32
|
+
})
|
|
33
|
+
})
|
package/src/core/pluginWriter.js
CHANGED
|
@@ -1,29 +1,43 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs"
|
|
2
|
-
import { dirname, join, resolve } from "node:path"
|
|
3
|
-
import { fileURLToPath } from "node:url"
|
|
4
|
-
import { getConfigDir } from "./paths.js"
|
|
5
|
-
|
|
6
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
-
const PLUGINS = ["pluidr-flow.js", "pluidr-squeeze.js"]
|
|
8
|
-
const PACKAGE_JSON = {
|
|
9
|
-
type: "module",
|
|
10
|
-
dependencies: { "@opencode-ai/plugin": "^1.17.9" },
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function writePluginBundle(targetDir) {
|
|
14
|
-
const baseDir = targetDir || getConfigDir()
|
|
15
|
-
const pluginsDir = join(baseDir, "plugins")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync } from "node:fs"
|
|
2
|
+
import { dirname, join, resolve } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { getConfigDir } from "./paths.js"
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const PLUGINS = ["pluidr-flow.js", "pluidr-squeeze.js"]
|
|
8
|
+
const PACKAGE_JSON = {
|
|
9
|
+
type: "module",
|
|
10
|
+
dependencies: { "@opencode-ai/plugin": "^1.17.9" },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function writePluginBundle(targetDir) {
|
|
14
|
+
const baseDir = targetDir || getConfigDir()
|
|
15
|
+
const pluginsDir = join(baseDir, "plugins")
|
|
16
|
+
|
|
17
|
+
// Clear any existing .js files in pluginsDir to prevent stale plugins (like wevr-*)
|
|
18
|
+
if (existsSync(pluginsDir)) {
|
|
19
|
+
try {
|
|
20
|
+
for (const file of readdirSync(pluginsDir)) {
|
|
21
|
+
if (file.endsWith(".js")) {
|
|
22
|
+
unlinkSync(join(pluginsDir, file))
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
// Ignore
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
mkdirSync(pluginsDir, { recursive: true })
|
|
31
|
+
|
|
32
|
+
for (const name of PLUGINS) {
|
|
33
|
+
const sourcePath = resolve(__dirname, "../plugins", name)
|
|
34
|
+
copyFileSync(sourcePath, join(pluginsDir, name))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function writePluginPackageJson(targetDir) {
|
|
39
|
+
const baseDir = targetDir || getConfigDir()
|
|
40
|
+
const packageJsonPath = join(baseDir, "package.json")
|
|
41
|
+
|
|
42
|
+
writeFileSync(packageJsonPath, JSON.stringify(PACKAGE_JSON, null, 2), "utf-8")
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { mkdirSync, cpSync, existsSync, rmSync } from "node:fs"
|
|
2
|
+
import { resolve } from "node:path"
|
|
3
|
+
import { getSkillsDir } from "./paths.js"
|
|
4
|
+
|
|
5
|
+
export function writeSkills(templatesDir, destDir) {
|
|
6
|
+
const sourcePath = resolve(templatesDir, "skills")
|
|
7
|
+
const targetDir = destDir || getSkillsDir()
|
|
8
|
+
|
|
9
|
+
if (existsSync(targetDir)) {
|
|
10
|
+
try {
|
|
11
|
+
rmSync(targetDir, { recursive: true, force: true })
|
|
12
|
+
} catch {
|
|
13
|
+
// Ignore
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
mkdirSync(targetDir, { recursive: true })
|
|
18
|
+
if (existsSync(sourcePath)) {
|
|
19
|
+
cpSync(sourcePath, targetDir, { recursive: true })
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, after } from "node:test"
|
|
2
|
+
import assert from "node:assert"
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, mkdirSync, writeFileSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { tmpdir } from "node:os"
|
|
6
|
+
import { writeSkills } from "./skillsWriter.js"
|
|
7
|
+
|
|
8
|
+
describe("skillsWriter", () => {
|
|
9
|
+
const destDir = mkdtempSync(join(tmpdir(), "pluidr-skills-dest-"))
|
|
10
|
+
|
|
11
|
+
after(() => {
|
|
12
|
+
if (existsSync(destDir)) rmSync(destDir, { recursive: true })
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it("copies skills directories recursively to destination directory", () => {
|
|
16
|
+
const tmpSrcDir = mkdtempSync(join(tmpdir(), "pluidr-skills-src-"))
|
|
17
|
+
const srcSkillsDir = join(tmpSrcDir, "skills")
|
|
18
|
+
const testSkillDir = join(srcSkillsDir, "test-skill")
|
|
19
|
+
mkdirSync(testSkillDir, { recursive: true })
|
|
20
|
+
writeFileSync(join(testSkillDir, "SKILL.md"), "mock-skill-content", "utf-8")
|
|
21
|
+
|
|
22
|
+
writeSkills(tmpSrcDir, destDir)
|
|
23
|
+
|
|
24
|
+
const destPath = join(destDir, "test-skill", "SKILL.md")
|
|
25
|
+
assert.ok(existsSync(destPath), "SKILL.md should be copied recursively")
|
|
26
|
+
assert.strictEqual(readFileSync(destPath, "utf-8"), "mock-skill-content")
|
|
27
|
+
|
|
28
|
+
rmSync(tmpSrcDir, { recursive: true })
|
|
29
|
+
})
|
|
30
|
+
})
|