opencode-thrum-pro 0.1.15 → 0.1.17
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/assets/commands/thrum-compact-extended.md +14 -12
- package/assets/commands/thrum-compact.md +17 -15
- package/assets/commands/thrum-prime.md +1 -0
- package/assets/skills/configure-roles/SKILL.md +0 -1
- package/assets/skills/coordinator-hotpath-merge-gate/SKILL.md +15 -6
- package/assets/skills/coordinator-maintaining-memory/SKILL.md +2 -0
- package/assets/skills/coordinator-merging-code/SKILL.md +25 -10
- package/assets/skills/coordinator-merging-code/resources/overbuild-check-prompt.md +60 -0
- package/assets/skills/coordinator-philosophy-merge-gate/SKILL.md +5 -4
- package/assets/skills/implementer-maintaining-memory/SKILL.md +2 -0
- package/assets/skills/memory-curate/SKILL.md +2 -0
- package/assets/skills/memory-current-rule/SKILL.md +121 -0
- package/assets/skills/memory-maintenance/SKILL.md +2 -0
- package/assets/skills/memory-write-discipline/SKILL.md +2 -0
- package/assets/skills/persistent-watcher-archetype/SKILL.md +142 -60
- package/assets/skills/project-hotpath-gate/SKILL.md +8 -0
- package/assets/skills/researcher-maintaining-memory/SKILL.md +2 -0
- package/assets/skills/thrum/resources/CLI_REFERENCE.md +14 -6
- package/assets/skills/thrum/resources/TMUX_SESSIONS.md +16 -8
- package/assets/skills/thrum-memory/SKILL.md +2 -0
- package/assets/skills/using-the-queue/SKILL.md +6 -7
- package/assets/skills/using-thrum-state/SKILL.md +2 -0
- package/assets/skills/using-tmux-ssh/resources/tmux-ssh +23 -2
- package/assets/skills/watcher-archetype/SKILL.md +13 -5
- package/dist/installer-assets.test.js +5 -2
- package/dist/installer.d.ts +5 -1
- package/dist/installer.js +38 -7
- package/dist/installer.test.js +53 -0
- package/package.json +4 -1
- package/dist/permission-fixture.integration.test.js +0 -506
- /package/dist/{permission-fixture.integration.test.d.ts → installer.test.d.ts} +0 -0
package/dist/installer.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
+
export declare function sanitizeSkillsSubdir(sub: string | undefined): string;
|
|
3
|
+
export declare function getSkillsSubdir(): string;
|
|
2
4
|
export declare function getConfigDir(): string;
|
|
3
|
-
export declare function installAssets(ctx: PluginInput
|
|
5
|
+
export declare function installAssets(ctx: PluginInput, opts?: {
|
|
6
|
+
skillsSubdir?: string;
|
|
7
|
+
}): Promise<void>;
|
|
4
8
|
interface AllowlistSource {
|
|
5
9
|
version: number;
|
|
6
10
|
command_patterns: string[];
|
package/dist/installer.js
CHANGED
|
@@ -5,16 +5,32 @@ import { fileURLToPath } from "url";
|
|
|
5
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
const ASSETS_DIR = path.resolve(__dirname, "..", "assets");
|
|
7
7
|
const VERSION_FILE = ".thrum-plugin-version";
|
|
8
|
-
|
|
8
|
+
// Written next to the version file so a later release can tell pro from
|
|
9
|
+
// open-core installs and clean up safely.
|
|
10
|
+
const FLAVOR_FILE = ".thrum-plugin-flavor";
|
|
11
|
+
const DEFAULT_SKILLS_SUBDIR = "thrum";
|
|
12
|
+
function readPackageJson() {
|
|
9
13
|
const pkgPath = path.resolve(__dirname, "..", "package.json");
|
|
10
14
|
try {
|
|
11
|
-
|
|
12
|
-
return pkg.version ?? "0.0.0";
|
|
15
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
13
16
|
}
|
|
14
17
|
catch {
|
|
15
|
-
return
|
|
18
|
+
return {};
|
|
16
19
|
}
|
|
17
20
|
}
|
|
21
|
+
function getPluginVersion() {
|
|
22
|
+
return readPackageJson().version ?? "0.0.0";
|
|
23
|
+
}
|
|
24
|
+
// The pro build sets thrum.skillsSubdir in the packaged package.json to
|
|
25
|
+
// "thrum-pro" so it never shares a directory with the open-core package.
|
|
26
|
+
export function sanitizeSkillsSubdir(sub) {
|
|
27
|
+
return sub && /^[A-Za-z0-9._-]+$/.test(sub) && sub !== "." && sub !== ".."
|
|
28
|
+
? sub
|
|
29
|
+
: DEFAULT_SKILLS_SUBDIR;
|
|
30
|
+
}
|
|
31
|
+
export function getSkillsSubdir() {
|
|
32
|
+
return sanitizeSkillsSubdir(readPackageJson().thrum?.skillsSubdir);
|
|
33
|
+
}
|
|
18
34
|
export function getConfigDir() {
|
|
19
35
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
20
36
|
if (xdg)
|
|
@@ -34,11 +50,15 @@ function copyDirRecursive(src, dest) {
|
|
|
34
50
|
}
|
|
35
51
|
}
|
|
36
52
|
}
|
|
37
|
-
export async function installAssets(ctx) {
|
|
53
|
+
export async function installAssets(ctx, opts = {}) {
|
|
38
54
|
const configDir = getConfigDir();
|
|
39
55
|
const skillsDir = path.join(configDir, "skills");
|
|
40
56
|
const commandsDir = path.join(configDir, "commands");
|
|
41
|
-
const
|
|
57
|
+
const subdir = opts.skillsSubdir !== undefined
|
|
58
|
+
? sanitizeSkillsSubdir(opts.skillsSubdir)
|
|
59
|
+
: getSkillsSubdir();
|
|
60
|
+
const installRoot = path.join(skillsDir, subdir);
|
|
61
|
+
const versionPath = path.join(installRoot, VERSION_FILE);
|
|
42
62
|
const currentVersion = getPluginVersion();
|
|
43
63
|
// Check if already installed at this version
|
|
44
64
|
try {
|
|
@@ -52,6 +72,14 @@ export async function installAssets(ctx) {
|
|
|
52
72
|
const log = (msg) => ctx.client.app.log({
|
|
53
73
|
body: { service: "opencode-thrum", level: "info", message: msg },
|
|
54
74
|
});
|
|
75
|
+
if (subdir !== DEFAULT_SKILLS_SUBDIR) {
|
|
76
|
+
// Pro build: a pre-existing skills/thrum copy cannot be attributed to pro
|
|
77
|
+
// vs open-core (same marker file, no flavor field), so never delete it.
|
|
78
|
+
const legacy = path.join(skillsDir, DEFAULT_SKILLS_SUBDIR, VERSION_FILE);
|
|
79
|
+
if (fs.existsSync(legacy)) {
|
|
80
|
+
await log(`Legacy flat install found at ${path.join(skillsDir, DEFAULT_SKILLS_SUBDIR)}; not removed (cannot tell pro from open-core). If it is an old pro install, delete it and its sibling skill dirs manually.`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
55
83
|
// Copy each skill subdirectory
|
|
56
84
|
const assetsSkillsDir = path.join(ASSETS_DIR, "skills");
|
|
57
85
|
if (fs.existsSync(assetsSkillsDir)) {
|
|
@@ -61,7 +89,9 @@ export async function installAssets(ctx) {
|
|
|
61
89
|
if (!entry.isDirectory())
|
|
62
90
|
continue;
|
|
63
91
|
const src = path.join(assetsSkillsDir, entry.name);
|
|
64
|
-
|
|
92
|
+
// Open-core keeps its historical flat layout (skills/<name>); only a
|
|
93
|
+
// non-default subdir (pro) nests skills under skills/<subdir>/.
|
|
94
|
+
const dest = path.join(subdir === DEFAULT_SKILLS_SUBDIR ? skillsDir : installRoot, entry.name);
|
|
65
95
|
copyDirRecursive(src, dest);
|
|
66
96
|
await log(`Installed skill: ${entry.name}`);
|
|
67
97
|
}
|
|
@@ -82,6 +112,7 @@ export async function installAssets(ctx) {
|
|
|
82
112
|
// Write version marker
|
|
83
113
|
fs.mkdirSync(path.dirname(versionPath), { recursive: true });
|
|
84
114
|
fs.writeFileSync(versionPath, currentVersion);
|
|
115
|
+
fs.writeFileSync(path.join(installRoot, FLAVOR_FILE), subdir === DEFAULT_SKILLS_SUBDIR ? "open-core" : "pro");
|
|
85
116
|
await log(`opencode-thrum v${currentVersion} assets installed`);
|
|
86
117
|
}
|
|
87
118
|
const ALLOWLIST_ASSET_PATH = path.join(ASSETS_DIR, "thrum_allowlist.json");
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { installAssets, sanitizeSkillsSubdir } from "./installer.js";
|
|
7
|
+
const ctx = { client: { app: { log: async () => { } } } };
|
|
8
|
+
function withConfig(fn) {
|
|
9
|
+
return async () => {
|
|
10
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "oc-inst-"));
|
|
11
|
+
const prev = process.env.XDG_CONFIG_HOME;
|
|
12
|
+
process.env.XDG_CONFIG_HOME = dir;
|
|
13
|
+
try {
|
|
14
|
+
await fn(path.join(dir, "opencode"));
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
if (prev === undefined)
|
|
18
|
+
delete process.env.XDG_CONFIG_HOME;
|
|
19
|
+
else
|
|
20
|
+
process.env.XDG_CONFIG_HOME = prev;
|
|
21
|
+
fs.rmSync(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const pkgVersion = JSON.parse(fs.readFileSync(path.resolve(import.meta.dirname, "..", "package.json"), "utf8")).version;
|
|
26
|
+
test("open-core: flat skills/<name>, marker in skills/thrum", withConfig(async (cfg) => {
|
|
27
|
+
await installAssets(ctx, { skillsSubdir: "thrum" });
|
|
28
|
+
assert.ok(fs.existsSync(path.join(cfg, "skills", "thrum", ".thrum-plugin-version")));
|
|
29
|
+
assert.ok(fs.existsSync(path.join(cfg, "skills", "configure-roles")));
|
|
30
|
+
assert.ok(!fs.existsSync(path.join(cfg, "skills", "thrum-pro")));
|
|
31
|
+
assert.equal(fs.readFileSync(path.join(cfg, "skills", "thrum", ".thrum-plugin-flavor"), "utf8"), "open-core");
|
|
32
|
+
}));
|
|
33
|
+
test("pro: skills nested under skills/thrum-pro, marker there", withConfig(async (cfg) => {
|
|
34
|
+
await installAssets(ctx, { skillsSubdir: "thrum-pro" });
|
|
35
|
+
const v = fs.readFileSync(path.join(cfg, "skills", "thrum-pro", ".thrum-plugin-version"), "utf8");
|
|
36
|
+
assert.equal(v, pkgVersion);
|
|
37
|
+
assert.ok(fs.existsSync(path.join(cfg, "skills", "thrum-pro", "configure-roles")));
|
|
38
|
+
assert.ok(!fs.existsSync(path.join(cfg, "skills", "configure-roles")));
|
|
39
|
+
assert.equal(fs.readFileSync(path.join(cfg, "skills", "thrum-pro", ".thrum-plugin-flavor"), "utf8"), "pro");
|
|
40
|
+
}));
|
|
41
|
+
test("pro: leaves an existing skills/thrum install untouched", withConfig(async (cfg) => {
|
|
42
|
+
const legacy = path.join(cfg, "skills", "thrum");
|
|
43
|
+
fs.mkdirSync(legacy, { recursive: true });
|
|
44
|
+
fs.writeFileSync(path.join(legacy, ".thrum-plugin-version"), "9.9.9");
|
|
45
|
+
await installAssets(ctx, { skillsSubdir: "thrum-pro" });
|
|
46
|
+
assert.equal(fs.readFileSync(path.join(legacy, ".thrum-plugin-version"), "utf8"), "9.9.9");
|
|
47
|
+
}));
|
|
48
|
+
test("invalid subdir falls back to default", () => {
|
|
49
|
+
for (const bad of ["../evil", "a/b", "", "..", ".", undefined]) {
|
|
50
|
+
assert.equal(sanitizeSkillsSubdir(bad), "thrum");
|
|
51
|
+
}
|
|
52
|
+
assert.equal(sanitizeSkillsSubdir("thrum-pro"), "thrum-pro");
|
|
53
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-thrum-pro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Thrum multi-agent coordination plugin for Open Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -46,5 +46,8 @@
|
|
|
46
46
|
"@opencode-ai/plugin": "^1.4.0",
|
|
47
47
|
"@types/node": "^25.5.2",
|
|
48
48
|
"typescript": "^5.3.0"
|
|
49
|
+
},
|
|
50
|
+
"thrum": {
|
|
51
|
+
"skillsSubdir": "thrum-pro"
|
|
49
52
|
}
|
|
50
53
|
}
|