agenthub-multiagent-mcp 1.44.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.
@@ -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;AA8B/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,CA0+EtC;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,CAg1ElB"}
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"}
@@ -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);