agenthub-multiagent-mcp 1.43.0 → 1.45.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/dist/client.d.ts +40 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +43 -0
- package/dist/client.js.map +1 -1
- package/dist/daemon.js +5 -14
- package/dist/daemon.js.map +1 -1
- package/dist/daemonPolicy.d.ts +14 -0
- package/dist/daemonPolicy.d.ts.map +1 -1
- package/dist/daemonPolicy.js +25 -0
- package/dist/daemonPolicy.js.map +1 -1
- package/dist/daemonPolicy.test.js +31 -1
- package/dist/daemonPolicy.test.js.map +1 -1
- package/dist/skillPackInstaller.d.ts +28 -0
- package/dist/skillPackInstaller.d.ts.map +1 -0
- package/dist/skillPackInstaller.js +211 -0
- package/dist/skillPackInstaller.js.map +1 -0
- package/dist/skillPackInstaller.test.d.ts +2 -0
- package/dist/skillPackInstaller.test.d.ts.map +1 -0
- package/dist/skillPackInstaller.test.js +167 -0
- package/dist/skillPackInstaller.test.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +100 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +6 -3
- package/dist/tools/tools.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { employeeModel, shouldAutoRespond, LATEST_SONNET, maxTurnsFor, conversationalReply, } from "./daemonPolicy.js";
|
|
2
|
+
import { employeeModel, shouldAutoRespond, LATEST_SONNET, maxTurnsFor, conversationalReply, buildShiftEnv, } from "./daemonPolicy.js";
|
|
3
3
|
// fix-employee-daemon-loop-and-sonnet — the loop guard and the Sonnet-only
|
|
4
4
|
// model policy for AI employees.
|
|
5
5
|
describe("shouldAutoRespond (agent-ping-pong loop guard)", () => {
|
|
@@ -70,4 +70,34 @@ describe("conversationalReply (no silent drops)", () => {
|
|
|
70
70
|
expect(r.text.length).toBe(5000);
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
|
+
// add-employee-dm-picker §3 — daemon shifts run with auto-inbox disabled so the
|
|
74
|
+
// SessionStart hook can't narrate the inbox on top of the real answer.
|
|
75
|
+
describe("buildShiftEnv (daemon shift environment)", () => {
|
|
76
|
+
it("disables the auto-inbox hook inside the shift", () => {
|
|
77
|
+
const env = buildShiftEnv({ PATH: "/usr/bin" }, "expert");
|
|
78
|
+
expect(env.AGENTHUB_AUTO_INBOX).toBe("false");
|
|
79
|
+
});
|
|
80
|
+
it("pins the agent id and disables the worker websocket", () => {
|
|
81
|
+
const env = buildShiftEnv({}, "hr");
|
|
82
|
+
expect(env.AGENTHUB_AGENT_ID).toBe("hr");
|
|
83
|
+
expect(env.AGENTHUB_DISABLE_WEBSOCKET).toBe("true");
|
|
84
|
+
});
|
|
85
|
+
it("keeps the OAuth token but strips other CLAUDE* session vars", () => {
|
|
86
|
+
const env = buildShiftEnv({
|
|
87
|
+
CLAUDE_CODE_OAUTH_TOKEN: "tok",
|
|
88
|
+
CLAUDECODE: "1",
|
|
89
|
+
CLAUDE_CODE_SSE_PORT: "9999",
|
|
90
|
+
PATH: "/usr/bin",
|
|
91
|
+
}, "expert");
|
|
92
|
+
expect(env.CLAUDE_CODE_OAUTH_TOKEN).toBe("tok");
|
|
93
|
+
expect(env.CLAUDECODE).toBeUndefined();
|
|
94
|
+
expect(env.CLAUDE_CODE_SSE_PORT).toBeUndefined();
|
|
95
|
+
expect(env.PATH).toBe("/usr/bin");
|
|
96
|
+
});
|
|
97
|
+
it("skips undefined env values", () => {
|
|
98
|
+
const env = buildShiftEnv({ FOO: undefined, BAR: "x" }, "expert");
|
|
99
|
+
expect("FOO" in env).toBe(false);
|
|
100
|
+
expect(env.BAR).toBe("x");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
73
103
|
//# sourceMappingURL=daemonPolicy.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemonPolicy.test.js","sourceRoot":"","sources":["../src/daemonPolicy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,mBAAmB,
|
|
1
|
+
{"version":3,"file":"daemonPolicy.test.js","sourceRoot":"","sources":["../src/daemonPolicy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SkillPackManifestFile } from "./client.js";
|
|
2
|
+
export type PackFileActionKind = "installed" | "updated" | "skipped_current" | "skipped_user_modified" | "overwritten_force" | "would_install" | "would_update" | "would_overwrite" | "error";
|
|
3
|
+
export interface PackFileAction {
|
|
4
|
+
path: string;
|
|
5
|
+
action: PackFileActionKind;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PackInstallReport {
|
|
9
|
+
pack: string;
|
|
10
|
+
version: string;
|
|
11
|
+
actions: PackFileAction[];
|
|
12
|
+
counts: Record<PackFileActionKind, number>;
|
|
13
|
+
}
|
|
14
|
+
export interface InstallPackOptions {
|
|
15
|
+
pack: string;
|
|
16
|
+
version: string;
|
|
17
|
+
manifest: SkillPackManifestFile[];
|
|
18
|
+
archive: Buffer;
|
|
19
|
+
force?: boolean;
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
targetHome?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* installPackFromArchive applies a downloaded pack to ~/.claude/skills/.
|
|
25
|
+
* Hash-safe and idempotent. Returns a per-file action report.
|
|
26
|
+
*/
|
|
27
|
+
export declare function installPackFromArchive(opts: InstallPackOptions): Promise<PackInstallReport>;
|
|
28
|
+
//# sourceMappingURL=skillPackInstaller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillPackInstaller.d.ts","sourceRoot":"","sources":["../src/skillPackInstaller.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,SAAS,GACT,iBAAiB,GACjB,uBAAuB,GACvB,mBAAmB,GACnB,eAAe,GACf,cAAc,GACd,iBAAiB,GACjB,OAAO,CAAC;AAEZ,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAoGD;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CAqG5B"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// Installs an opt-in skill pack (add-skill-packs) into ~/.claude/skills/.
|
|
2
|
+
//
|
|
3
|
+
// Unlike the npm-bundled team skills (skillInstaller.ts), packs are NOT
|
|
4
|
+
// force-installed on startup — only on an explicit skill_pack_install call. The
|
|
5
|
+
// pack arrives as a gzip-tar archive from the AgentHub server; we extract it in
|
|
6
|
+
// memory and apply the same hash-safe semantics per file:
|
|
7
|
+
// - missing locally -> install
|
|
8
|
+
// - matches the pack's current hash -> no-op (skipped_current)
|
|
9
|
+
// - matches a hash we previously installed -> update (our prior version)
|
|
10
|
+
// - matches nothing (user edited it) -> skip, unless force=true
|
|
11
|
+
//
|
|
12
|
+
// "What we previously installed" is tracked in a per-pack record under
|
|
13
|
+
// ~/.claude/skills/.packs/<pack>.json so version upgrades don't require force
|
|
14
|
+
// while still never clobbering a file the user hand-edited.
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { gunzipSync } from "node:zlib";
|
|
17
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
function sha256(buf) {
|
|
21
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
22
|
+
}
|
|
23
|
+
// parseUstar decodes a (already gunzipped) USTAR tar buffer into its regular
|
|
24
|
+
// files. The AgentHub server writes plain USTAR headers (Go archive/tar) and
|
|
25
|
+
// every pack path is < 100 bytes, so no GNU/PAX long-name records appear. If
|
|
26
|
+
// one ever does, fail loud rather than silently mis-parse.
|
|
27
|
+
function parseUstar(buf) {
|
|
28
|
+
const entries = [];
|
|
29
|
+
let off = 0;
|
|
30
|
+
while (off + 512 <= buf.length) {
|
|
31
|
+
const block = buf.subarray(off, off + 512);
|
|
32
|
+
// Two consecutive zero blocks mark the end; a single zero block is enough
|
|
33
|
+
// to stop in practice.
|
|
34
|
+
if (block.every((b) => b === 0))
|
|
35
|
+
break;
|
|
36
|
+
const name = readCString(block, 0, 100);
|
|
37
|
+
const sizeStr = readCString(block, 124, 12).trim();
|
|
38
|
+
const size = sizeStr === "" ? 0 : parseInt(sizeStr, 8);
|
|
39
|
+
if (Number.isNaN(size) || size < 0) {
|
|
40
|
+
throw new Error(`skill pack: corrupt tar size field for "${name}"`);
|
|
41
|
+
}
|
|
42
|
+
const typeflag = String.fromCharCode(block[156]);
|
|
43
|
+
if (typeflag === "x" || typeflag === "g" || typeflag === "L" || typeflag === "K") {
|
|
44
|
+
throw new Error(`skill pack: archive uses extended (PAX/GNU) headers which are not supported`);
|
|
45
|
+
}
|
|
46
|
+
off += 512;
|
|
47
|
+
const content = Buffer.from(buf.subarray(off, off + size));
|
|
48
|
+
off += Math.ceil(size / 512) * 512;
|
|
49
|
+
// typeflag "0" or NUL = regular file; "5" = directory; others ignored.
|
|
50
|
+
if (typeflag === "0" || typeflag === "\0" || typeflag === "") {
|
|
51
|
+
entries.push({ name, content });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return entries;
|
|
55
|
+
}
|
|
56
|
+
function readCString(block, start, len) {
|
|
57
|
+
let end = start;
|
|
58
|
+
const limit = start + len;
|
|
59
|
+
while (end < limit && block[end] !== 0)
|
|
60
|
+
end++;
|
|
61
|
+
return block.toString("utf8", start, end);
|
|
62
|
+
}
|
|
63
|
+
// isSafePackPath rejects anything that could escape ~/.claude/. Pack files must
|
|
64
|
+
// live under the "skills/" install root.
|
|
65
|
+
function isSafePackPath(p) {
|
|
66
|
+
if (p.startsWith("/") || p.includes("..") || p.includes("\0"))
|
|
67
|
+
return false;
|
|
68
|
+
if (p.includes("\\"))
|
|
69
|
+
return false; // archive paths are forward-slash only
|
|
70
|
+
return p.startsWith("skills/");
|
|
71
|
+
}
|
|
72
|
+
function recordPath(claudeDir, pack) {
|
|
73
|
+
return join(claudeDir, "skills", ".packs", `${pack}.json`);
|
|
74
|
+
}
|
|
75
|
+
async function readRecord(claudeDir, pack) {
|
|
76
|
+
try {
|
|
77
|
+
const raw = await readFile(recordPath(claudeDir, pack), "utf8");
|
|
78
|
+
const parsed = JSON.parse(raw);
|
|
79
|
+
if (parsed && typeof parsed === "object" && parsed.files)
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// missing/corrupt → treat as no prior install
|
|
84
|
+
}
|
|
85
|
+
return { version: "", files: {} };
|
|
86
|
+
}
|
|
87
|
+
function emptyCounts() {
|
|
88
|
+
return {
|
|
89
|
+
installed: 0,
|
|
90
|
+
updated: 0,
|
|
91
|
+
skipped_current: 0,
|
|
92
|
+
skipped_user_modified: 0,
|
|
93
|
+
overwritten_force: 0,
|
|
94
|
+
would_install: 0,
|
|
95
|
+
would_update: 0,
|
|
96
|
+
would_overwrite: 0,
|
|
97
|
+
error: 0,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* installPackFromArchive applies a downloaded pack to ~/.claude/skills/.
|
|
102
|
+
* Hash-safe and idempotent. Returns a per-file action report.
|
|
103
|
+
*/
|
|
104
|
+
export async function installPackFromArchive(opts) {
|
|
105
|
+
const force = opts.force ?? false;
|
|
106
|
+
const dryRun = opts.dryRun ?? false;
|
|
107
|
+
const home = opts.targetHome ?? homedir();
|
|
108
|
+
const claudeDir = join(home, ".claude");
|
|
109
|
+
const report = {
|
|
110
|
+
pack: opts.pack,
|
|
111
|
+
version: opts.version,
|
|
112
|
+
actions: [],
|
|
113
|
+
counts: emptyCounts(),
|
|
114
|
+
};
|
|
115
|
+
const push = (a) => {
|
|
116
|
+
report.actions.push(a);
|
|
117
|
+
report.counts[a.action]++;
|
|
118
|
+
};
|
|
119
|
+
// Decompress + index the archive by path.
|
|
120
|
+
let files;
|
|
121
|
+
try {
|
|
122
|
+
const tar = gunzipSync(opts.archive);
|
|
123
|
+
const entries = parseUstar(tar);
|
|
124
|
+
files = new Map(entries.map((e) => [e.name, e.content]));
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
throw new Error(`skill pack ${opts.pack}: ${err.message}`);
|
|
128
|
+
}
|
|
129
|
+
const prior = await readRecord(claudeDir, opts.pack);
|
|
130
|
+
const newRecord = { version: opts.version, files: {} };
|
|
131
|
+
for (const mf of opts.manifest) {
|
|
132
|
+
const path = mf.path;
|
|
133
|
+
if (!isSafePackPath(path)) {
|
|
134
|
+
push({ path, action: "error", message: "unsafe path rejected" });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const content = files.get(path);
|
|
138
|
+
if (!content) {
|
|
139
|
+
push({ path, action: "error", message: "file missing from archive" });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const bundleHash = sha256(content);
|
|
143
|
+
if (bundleHash !== mf.sha256) {
|
|
144
|
+
push({ path, action: "error", message: "archive hash mismatch (integrity)" });
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const priorHashes = prior.files[path] ?? [];
|
|
148
|
+
// Record the current hash for next time regardless of the action taken.
|
|
149
|
+
newRecord.files[path] = dedupeAppend(priorHashes, bundleHash);
|
|
150
|
+
const target = join(claudeDir, path);
|
|
151
|
+
let userBuf = null;
|
|
152
|
+
try {
|
|
153
|
+
userBuf = await readFile(target);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
userBuf = null;
|
|
157
|
+
}
|
|
158
|
+
if (userBuf === null) {
|
|
159
|
+
if (dryRun) {
|
|
160
|
+
push({ path, action: "would_install" });
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
await mkdir(dirname(target), { recursive: true });
|
|
164
|
+
await writeFile(target, content);
|
|
165
|
+
push({ path, action: "installed" });
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const userHash = sha256(userBuf);
|
|
170
|
+
if (userHash === bundleHash) {
|
|
171
|
+
push({ path, action: "skipped_current" });
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (priorHashes.includes(userHash)) {
|
|
175
|
+
if (dryRun) {
|
|
176
|
+
push({ path, action: "would_update" });
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
await writeFile(target, content);
|
|
180
|
+
push({ path, action: "updated" });
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (force) {
|
|
185
|
+
if (dryRun) {
|
|
186
|
+
push({ path, action: "would_overwrite" });
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
await writeFile(target, content);
|
|
190
|
+
push({ path, action: "overwritten_force" });
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
push({ path, action: "skipped_user_modified" });
|
|
195
|
+
}
|
|
196
|
+
if (!dryRun) {
|
|
197
|
+
await persistRecord(claudeDir, opts.pack, newRecord);
|
|
198
|
+
}
|
|
199
|
+
return report;
|
|
200
|
+
}
|
|
201
|
+
function dedupeAppend(hashes, hash) {
|
|
202
|
+
if (hashes.includes(hash))
|
|
203
|
+
return hashes;
|
|
204
|
+
return [...hashes, hash];
|
|
205
|
+
}
|
|
206
|
+
async function persistRecord(claudeDir, pack, rec) {
|
|
207
|
+
const p = recordPath(claudeDir, pack);
|
|
208
|
+
await mkdir(dirname(p), { recursive: true });
|
|
209
|
+
await writeFile(p, JSON.stringify(rec, null, 2));
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=skillPackInstaller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillPackInstaller.js","sourceRoot":"","sources":["../src/skillPackInstaller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillPackInstaller.test.d.ts","sourceRoot":"","sources":["../src/skillPackInstaller.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { gzipSync } from "node:zlib";
|
|
7
|
+
import { installPackFromArchive } from "./skillPackInstaller.js";
|
|
8
|
+
function sha256(s) {
|
|
9
|
+
return createHash("sha256").update(s).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
// Build a plain USTAR header for one regular file.
|
|
12
|
+
function tarHeader(name, size) {
|
|
13
|
+
const h = Buffer.alloc(512);
|
|
14
|
+
h.write(name, 0, "utf8");
|
|
15
|
+
h.write("0000644\0", 100);
|
|
16
|
+
h.write("0000000\0", 108);
|
|
17
|
+
h.write("0000000\0", 116);
|
|
18
|
+
h.write(size.toString(8).padStart(11, "0") + "\0", 124);
|
|
19
|
+
h.write("00000000000\0", 136); // mtime
|
|
20
|
+
h.write("ustar\0", 257);
|
|
21
|
+
h.write("00", 263);
|
|
22
|
+
h[156] = "0".charCodeAt(0); // typeflag = regular file
|
|
23
|
+
// checksum: spaces in the field, sum bytes, write 6 octal digits + NUL + space
|
|
24
|
+
for (let i = 148; i < 156; i++)
|
|
25
|
+
h[i] = 0x20;
|
|
26
|
+
let sum = 0;
|
|
27
|
+
for (let i = 0; i < 512; i++)
|
|
28
|
+
sum += h[i];
|
|
29
|
+
h.write(sum.toString(8).padStart(6, "0") + "\0 ", 148);
|
|
30
|
+
return h;
|
|
31
|
+
}
|
|
32
|
+
function makeTarGz(files) {
|
|
33
|
+
const blocks = [];
|
|
34
|
+
for (const f of files) {
|
|
35
|
+
const content = Buffer.from(f.content, "utf8");
|
|
36
|
+
blocks.push(tarHeader(f.name, content.length));
|
|
37
|
+
const padded = Buffer.alloc(Math.ceil(content.length / 512) * 512);
|
|
38
|
+
content.copy(padded);
|
|
39
|
+
blocks.push(padded);
|
|
40
|
+
}
|
|
41
|
+
blocks.push(Buffer.alloc(512));
|
|
42
|
+
blocks.push(Buffer.alloc(512));
|
|
43
|
+
return gzipSync(Buffer.concat(blocks));
|
|
44
|
+
}
|
|
45
|
+
function manifestFor(files) {
|
|
46
|
+
return files.map((f) => ({
|
|
47
|
+
path: f.name,
|
|
48
|
+
sha256: sha256(f.content),
|
|
49
|
+
size: Buffer.byteLength(f.content),
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
describe("installPackFromArchive", () => {
|
|
53
|
+
let home;
|
|
54
|
+
const PATH_A = "skills/demo/SKILL.md";
|
|
55
|
+
const PATH_B = "skills/demo/references/r.md";
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
home = mkdtempSync(join(tmpdir(), "packinstall-"));
|
|
58
|
+
});
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
rmSync(home, { recursive: true, force: true });
|
|
61
|
+
});
|
|
62
|
+
function targetOf(path) {
|
|
63
|
+
return join(home, ".claude", path);
|
|
64
|
+
}
|
|
65
|
+
it("installs all files on a pristine machine", async () => {
|
|
66
|
+
const files = [
|
|
67
|
+
{ name: PATH_A, content: "# A v1" },
|
|
68
|
+
{ name: PATH_B, content: "ref v1" },
|
|
69
|
+
];
|
|
70
|
+
const report = await installPackFromArchive({
|
|
71
|
+
pack: "demo",
|
|
72
|
+
version: "1.0.0",
|
|
73
|
+
manifest: manifestFor(files),
|
|
74
|
+
archive: makeTarGz(files),
|
|
75
|
+
targetHome: home,
|
|
76
|
+
});
|
|
77
|
+
expect(report.counts.installed).toBe(2);
|
|
78
|
+
expect(readFileSync(targetOf(PATH_A), "utf8")).toBe("# A v1");
|
|
79
|
+
expect(readFileSync(targetOf(PATH_B), "utf8")).toBe("ref v1");
|
|
80
|
+
// Record written.
|
|
81
|
+
expect(existsSync(join(home, ".claude", "skills", ".packs", "demo.json"))).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
it("is a no-op on re-install of the same version", async () => {
|
|
84
|
+
const files = [{ name: PATH_A, content: "# A v1" }];
|
|
85
|
+
const opts = {
|
|
86
|
+
pack: "demo",
|
|
87
|
+
version: "1.0.0",
|
|
88
|
+
manifest: manifestFor(files),
|
|
89
|
+
archive: makeTarGz(files),
|
|
90
|
+
targetHome: home,
|
|
91
|
+
};
|
|
92
|
+
await installPackFromArchive(opts);
|
|
93
|
+
const second = await installPackFromArchive(opts);
|
|
94
|
+
expect(second.counts.skipped_current).toBe(1);
|
|
95
|
+
expect(second.counts.installed).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
it("updates a file we previously installed when the version changes", async () => {
|
|
98
|
+
const v1 = [{ name: PATH_A, content: "# A v1" }];
|
|
99
|
+
await installPackFromArchive({
|
|
100
|
+
pack: "demo", version: "1.0.0",
|
|
101
|
+
manifest: manifestFor(v1), archive: makeTarGz(v1), targetHome: home,
|
|
102
|
+
});
|
|
103
|
+
const v2 = [{ name: PATH_A, content: "# A v2" }];
|
|
104
|
+
const report = await installPackFromArchive({
|
|
105
|
+
pack: "demo", version: "2.0.0",
|
|
106
|
+
manifest: manifestFor(v2), archive: makeTarGz(v2), targetHome: home,
|
|
107
|
+
});
|
|
108
|
+
expect(report.counts.updated).toBe(1);
|
|
109
|
+
expect(readFileSync(targetOf(PATH_A), "utf8")).toBe("# A v2");
|
|
110
|
+
});
|
|
111
|
+
it("never clobbers a user-modified file unless forced", async () => {
|
|
112
|
+
const v1 = [{ name: PATH_A, content: "# A v1" }];
|
|
113
|
+
await installPackFromArchive({
|
|
114
|
+
pack: "demo", version: "1.0.0",
|
|
115
|
+
manifest: manifestFor(v1), archive: makeTarGz(v1), targetHome: home,
|
|
116
|
+
});
|
|
117
|
+
// User edits the file.
|
|
118
|
+
writeFileSync(targetOf(PATH_A), "# A user-edited");
|
|
119
|
+
const v2 = [{ name: PATH_A, content: "# A v2" }];
|
|
120
|
+
const skip = await installPackFromArchive({
|
|
121
|
+
pack: "demo", version: "2.0.0",
|
|
122
|
+
manifest: manifestFor(v2), archive: makeTarGz(v2), targetHome: home,
|
|
123
|
+
});
|
|
124
|
+
expect(skip.counts.skipped_user_modified).toBe(1);
|
|
125
|
+
expect(readFileSync(targetOf(PATH_A), "utf8")).toBe("# A user-edited");
|
|
126
|
+
const forced = await installPackFromArchive({
|
|
127
|
+
pack: "demo", version: "2.0.0", force: true,
|
|
128
|
+
manifest: manifestFor(v2), archive: makeTarGz(v2), targetHome: home,
|
|
129
|
+
});
|
|
130
|
+
expect(forced.counts.overwritten_force).toBe(1);
|
|
131
|
+
expect(readFileSync(targetOf(PATH_A), "utf8")).toBe("# A v2");
|
|
132
|
+
});
|
|
133
|
+
it("rejects a file whose archive bytes don't match the manifest hash", async () => {
|
|
134
|
+
const files = [{ name: PATH_A, content: "real content" }];
|
|
135
|
+
const manifest = manifestFor(files);
|
|
136
|
+
manifest[0].sha256 = sha256("tampered"); // mismatch
|
|
137
|
+
const report = await installPackFromArchive({
|
|
138
|
+
pack: "demo", version: "1.0.0",
|
|
139
|
+
manifest, archive: makeTarGz(files), targetHome: home,
|
|
140
|
+
});
|
|
141
|
+
expect(report.counts.error).toBe(1);
|
|
142
|
+
expect(report.actions[0].message).toContain("integrity");
|
|
143
|
+
expect(existsSync(targetOf(PATH_A))).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
it("rejects an unsafe (path-traversal) manifest entry", async () => {
|
|
146
|
+
const files = [{ name: PATH_A, content: "ok" }];
|
|
147
|
+
const manifest = manifestFor(files);
|
|
148
|
+
manifest.push({ path: "../../evil.sh", sha256: sha256("x"), size: 1 });
|
|
149
|
+
const report = await installPackFromArchive({
|
|
150
|
+
pack: "demo", version: "1.0.0",
|
|
151
|
+
manifest, archive: makeTarGz(files), targetHome: home,
|
|
152
|
+
});
|
|
153
|
+
expect(report.counts.error).toBe(1);
|
|
154
|
+
expect(report.actions.find((a) => a.path === "../../evil.sh")?.message).toContain("unsafe");
|
|
155
|
+
});
|
|
156
|
+
it("dry-run writes nothing", async () => {
|
|
157
|
+
const files = [{ name: PATH_A, content: "# A" }];
|
|
158
|
+
const report = await installPackFromArchive({
|
|
159
|
+
pack: "demo", version: "1.0.0", dryRun: true,
|
|
160
|
+
manifest: manifestFor(files), archive: makeTarGz(files), targetHome: home,
|
|
161
|
+
});
|
|
162
|
+
expect(report.counts.would_install).toBe(1);
|
|
163
|
+
expect(existsSync(targetOf(PATH_A))).toBe(false);
|
|
164
|
+
expect(existsSync(join(home, ".claude", "skills", ".packs", "demo.json"))).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=skillPackInstaller.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skillPackInstaller.test.js","sourceRoot":"","sources":["../src/skillPackInstaller.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAqD,MAAM,cAAc,CAAC;AACrG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAqD,MAAM,cAAc,CAAC;AACrG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AA+B/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,cAAc,EAAE,MAAM,WAAW,CAAC;CACnC;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAwJD,wBAAgB,aAAa,IAAI,IAAI,EAAE,CAmgFtC;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CAi6ElB"}
|
package/dist/tools/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import * as brainCapture from "../hooks/brainCapture.js";
|
|
|
8
8
|
import { getBrain } from "../brain/backend.js";
|
|
9
9
|
import open from "open";
|
|
10
10
|
import { registerOpenSpecVizTools, handleOpenSpecVizToolCall } from "./openspec-viz/index.js";
|
|
11
|
+
import { installPackFromArchive } from "../skillPackInstaller.js";
|
|
11
12
|
import { getServerTime, useTimeInjection } from "../lib/server-time.js";
|
|
12
13
|
import { ENG_IC_CHARTER } from "../employee.js";
|
|
13
14
|
/**
|
|
@@ -843,6 +844,29 @@ export function registerTools() {
|
|
|
843
844
|
},
|
|
844
845
|
},
|
|
845
846
|
},
|
|
847
|
+
{
|
|
848
|
+
name: "skill_pack_list",
|
|
849
|
+
description: "List the opt-in skill packs the AgentHub server offers (e.g. cybersecurity, brag) with this agent's install status and whether install is permitted. Skill packs are NOT installed automatically — use skill_pack_install to opt in. Destructive packs require the matching skill-pack:<name> capability.",
|
|
850
|
+
inputSchema: {
|
|
851
|
+
type: "object",
|
|
852
|
+
properties: {},
|
|
853
|
+
},
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
name: "skill_pack_install",
|
|
857
|
+
description: "Install an opt-in skill pack into ~/.claude/skills/. Hash-safe (never overwrites files you've edited unless force=true). Destructive packs (e.g. cybersecurity, which ships offensive PoC scripts) require the skill-pack:<name> capability attached to this agent. Returns a per-file install report.",
|
|
858
|
+
inputSchema: {
|
|
859
|
+
type: "object",
|
|
860
|
+
properties: {
|
|
861
|
+
pack: { type: "string", description: "Pack name, e.g. \"cybersecurity\" or \"brag\" (see skill_pack_list)" },
|
|
862
|
+
force: {
|
|
863
|
+
type: "boolean",
|
|
864
|
+
description: "Overwrite locally-modified pack files (default false)",
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
required: ["pack"],
|
|
868
|
+
},
|
|
869
|
+
},
|
|
846
870
|
{
|
|
847
871
|
name: "api_search",
|
|
848
872
|
description: "Search the API knowledge graph — endpoints (REST/MCP/GraphQL/gRPC) extracted from indexed repos across the org. Use this BEFORE building a new endpoint to check if it already exists or to match a sibling project's contract. Org-wide read.",
|
|
@@ -3800,6 +3824,82 @@ export async function handleToolCall(name, args, client, context) {
|
|
|
3800
3824
|
message: `Filed bug ${result.key} (${result.severity}/${result.bug_state}): ${args.title}`,
|
|
3801
3825
|
};
|
|
3802
3826
|
}
|
|
3827
|
+
case "skill_pack_list": {
|
|
3828
|
+
try {
|
|
3829
|
+
const { packs } = await client.listSkillPacks();
|
|
3830
|
+
const lines = packs.map((p) => {
|
|
3831
|
+
const gate = p.requires_capability
|
|
3832
|
+
? p.permitted
|
|
3833
|
+
? "permitted"
|
|
3834
|
+
: `needs capability ${p.capability}`
|
|
3835
|
+
: "ungated";
|
|
3836
|
+
return `${p.name} v${p.version} (${p.tier}, ${p.skill_count} skills) — ${gate}`;
|
|
3837
|
+
});
|
|
3838
|
+
return {
|
|
3839
|
+
ok: true,
|
|
3840
|
+
packs,
|
|
3841
|
+
message: packs.length === 0
|
|
3842
|
+
? "No skill packs available."
|
|
3843
|
+
: `Available skill packs:\n${lines.join("\n")}`,
|
|
3844
|
+
};
|
|
3845
|
+
}
|
|
3846
|
+
catch (err) {
|
|
3847
|
+
const e = err;
|
|
3848
|
+
// Most likely the master switch is off (503 skill_packs_disabled).
|
|
3849
|
+
return {
|
|
3850
|
+
ok: false,
|
|
3851
|
+
error: "skill_packs_unavailable",
|
|
3852
|
+
message: e.message,
|
|
3853
|
+
};
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
case "skill_pack_install": {
|
|
3857
|
+
const pack = args.pack?.trim();
|
|
3858
|
+
if (!pack) {
|
|
3859
|
+
return { ok: false, error: "invalid_args", message: "pack is required" };
|
|
3860
|
+
}
|
|
3861
|
+
const force = args.force === true;
|
|
3862
|
+
// Download the archive first — the server enforces the master switch and
|
|
3863
|
+
// (for destructive packs) the capability gate here, returning a coded
|
|
3864
|
+
// error before sending any bytes.
|
|
3865
|
+
let archive;
|
|
3866
|
+
try {
|
|
3867
|
+
archive = await client.fetchSkillPackArchive(pack);
|
|
3868
|
+
}
|
|
3869
|
+
catch (err) {
|
|
3870
|
+
const e = err;
|
|
3871
|
+
if (e.code === "skill_pack_capability_required") {
|
|
3872
|
+
return {
|
|
3873
|
+
ok: false,
|
|
3874
|
+
error: e.code,
|
|
3875
|
+
message: `Installing "${pack}" requires the skill-pack:${pack} capability. Request it via capability_request, then retry.`,
|
|
3876
|
+
};
|
|
3877
|
+
}
|
|
3878
|
+
if (e.code === "skill_packs_disabled" || e.code === "skill_pack_not_found") {
|
|
3879
|
+
return { ok: false, error: e.code, message: e.message };
|
|
3880
|
+
}
|
|
3881
|
+
throw err;
|
|
3882
|
+
}
|
|
3883
|
+
const manifest = await client.getSkillPackManifest(pack);
|
|
3884
|
+
const report = await installPackFromArchive({
|
|
3885
|
+
pack,
|
|
3886
|
+
version: manifest.version,
|
|
3887
|
+
manifest: manifest.files,
|
|
3888
|
+
archive,
|
|
3889
|
+
force,
|
|
3890
|
+
});
|
|
3891
|
+
const c = report.counts;
|
|
3892
|
+
const summary = `${pack} v${manifest.version}: ${c.installed} installed, ${c.updated} updated, ` +
|
|
3893
|
+
`${c.skipped_current} unchanged, ${c.skipped_user_modified} skipped (user-modified), ` +
|
|
3894
|
+
`${c.overwritten_force} overwritten, ${c.error} errors.`;
|
|
3895
|
+
return {
|
|
3896
|
+
ok: c.error === 0,
|
|
3897
|
+
pack,
|
|
3898
|
+
version: manifest.version,
|
|
3899
|
+
counts: c,
|
|
3900
|
+
message: summary,
|
|
3901
|
+
};
|
|
3902
|
+
}
|
|
3803
3903
|
case "ticket_get": {
|
|
3804
3904
|
const workingDir = context.getWorkingDir();
|
|
3805
3905
|
const agentState = state.loadState(workingDir);
|