agenthub-multiagent-mcp 1.60.0 → 1.64.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/README.md +3 -3
- package/dist/brain/memvidBrain.d.ts +22 -0
- package/dist/brain/memvidBrain.d.ts.map +1 -1
- package/dist/brain/memvidBrain.js +41 -2
- package/dist/brain/memvidBrain.js.map +1 -1
- package/dist/brain/memvidBrain.test.js +90 -0
- package/dist/brain/memvidBrain.test.js.map +1 -1
- package/dist/brain/migrate.d.ts +43 -0
- package/dist/brain/migrate.d.ts.map +1 -1
- package/dist/brain/migrate.js +148 -23
- package/dist/brain/migrate.js.map +1 -1
- package/dist/brain/migrate.test.js +194 -1
- package/dist/brain/migrate.test.js.map +1 -1
- package/dist/channel.js +48 -6
- package/dist/channel.js.map +1 -1
- package/dist/channelDiagnostics.d.ts +53 -0
- package/dist/channelDiagnostics.d.ts.map +1 -1
- package/dist/channelDiagnostics.js +91 -0
- package/dist/channelDiagnostics.js.map +1 -1
- package/dist/channelDiagnostics.test.js +109 -1
- package/dist/channelDiagnostics.test.js.map +1 -1
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +32 -0
- package/dist/client.js.map +1 -1
- package/dist/daemon.js +16 -22
- package/dist/daemon.js.map +1 -1
- package/dist/index.js +36 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/pending-items.d.ts +63 -0
- package/dist/lib/pending-items.d.ts.map +1 -0
- package/dist/lib/pending-items.js +96 -0
- package/dist/lib/pending-items.js.map +1 -0
- package/dist/pinSweep.d.ts +83 -0
- package/dist/pinSweep.d.ts.map +1 -0
- package/dist/pinSweep.js +105 -0
- package/dist/pinSweep.js.map +1 -0
- package/dist/pinSweep.test.d.ts +12 -0
- package/dist/pinSweep.test.d.ts.map +1 -0
- package/dist/pinSweep.test.js +110 -0
- package/dist/pinSweep.test.js.map +1 -0
- package/dist/pinSweepCli.d.ts +20 -0
- package/dist/pinSweepCli.d.ts.map +1 -0
- package/dist/pinSweepCli.js +287 -0
- package/dist/pinSweepCli.js.map +1 -0
- package/dist/run-claude.sh +36 -5
- package/dist/sessionCapture.d.ts.map +1 -1
- package/dist/sessionCapture.js +7 -1
- package/dist/sessionCapture.js.map +1 -1
- package/dist/sessionCapture.test.js +20 -0
- package/dist/sessionCapture.test.js.map +1 -1
- package/dist/setup.js +11 -0
- package/dist/setup.js.map +1 -1
- package/dist/shiftFiles.d.ts +33 -0
- package/dist/shiftFiles.d.ts.map +1 -0
- package/dist/shiftFiles.js +57 -0
- package/dist/shiftFiles.js.map +1 -0
- package/dist/shiftFiles.test.d.ts +2 -0
- package/dist/shiftFiles.test.d.ts.map +1 -0
- package/dist/shiftFiles.test.js +141 -0
- package/dist/shiftFiles.test.js.map +1 -0
- package/dist/skillInstaller.d.ts.map +1 -1
- package/dist/skillInstaller.gate.test.js +2 -0
- package/dist/skillInstaller.gate.test.js.map +1 -1
- package/dist/skillInstaller.js +8 -0
- package/dist/skillInstaller.js.map +1 -1
- package/dist/skillInstaller.test.js +8 -1
- package/dist/skillInstaller.test.js.map +1 -1
- package/dist/tools/goshtiRead.d.ts +72 -0
- package/dist/tools/goshtiRead.d.ts.map +1 -0
- package/dist/tools/goshtiRead.js +274 -0
- package/dist/tools/goshtiRead.js.map +1 -0
- package/dist/tools/goshtiRead.test.d.ts +8 -0
- package/dist/tools/goshtiRead.test.d.ts.map +1 -0
- package/dist/tools/goshtiRead.test.js +182 -0
- package/dist/tools/goshtiRead.test.js.map +1 -0
- package/dist/tools/inboxBounds.d.ts +80 -0
- package/dist/tools/inboxBounds.d.ts.map +1 -0
- package/dist/tools/inboxBounds.js +105 -0
- package/dist/tools/inboxBounds.js.map +1 -0
- package/dist/tools/inboxBounds.test.d.ts +15 -0
- package/dist/tools/inboxBounds.test.d.ts.map +1 -0
- package/dist/tools/inboxBounds.test.js +135 -0
- package/dist/tools/inboxBounds.test.js.map +1 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +79 -18
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/intakeRequests.d.ts +35 -0
- package/dist/tools/intakeRequests.d.ts.map +1 -0
- package/dist/tools/intakeRequests.js +344 -0
- package/dist/tools/intakeRequests.js.map +1 -0
- package/dist/tools/intakeRequests.test.d.ts +8 -0
- package/dist/tools/intakeRequests.test.d.ts.map +1 -0
- package/dist/tools/intakeRequests.test.js +183 -0
- package/dist/tools/intakeRequests.test.js.map +1 -0
- package/dist/tools/pushedItemsSafety.test.d.ts +12 -0
- package/dist/tools/pushedItemsSafety.test.d.ts.map +1 -0
- package/dist/tools/pushedItemsSafety.test.js +179 -0
- package/dist/tools/pushedItemsSafety.test.js.map +1 -0
- package/dist/tools/tools.test.js +17 -5
- package/dist/tools/tools.test.js.map +1 -1
- package/dist/tools/wrapperCost.test.d.ts +13 -0
- package/dist/tools/wrapperCost.test.d.ts.map +1 -0
- package/dist/tools/wrapperCost.test.js +149 -0
- package/dist/tools/wrapperCost.test.js.map +1 -0
- package/dist/websocket.d.ts +30 -1
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +40 -3
- package/dist/websocket.js.map +1 -1
- package/dist/websocketQueue.test.d.ts +10 -0
- package/dist/websocketQueue.test.d.ts.map +1 -0
- package/dist/websocketQueue.test.js +78 -0
- package/dist/websocketQueue.test.js.map +1 -0
- package/package.json +4 -4
- package/skills/catalog.json +23 -10
- package/skills/commands/close-session.md +144 -144
- package/skills/commands/start-session.md +25 -6
- package/skills/manifest.json +8 -1
- package/skills/skills/code-brain-hook/SKILL.md +61 -61
- package/skills/skills/file-bug/SKILL.md +75 -75
- package/skills/skills/inbox/SKILL.md +99 -0
- package/skills/skills/multiagent-brainstorm/SKILL.md +164 -164
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending-items cache for the MCP tool wrapper
|
|
3
|
+
* (fix-inbox-delivery-and-tool-wrapping §3.2).
|
|
4
|
+
*
|
|
5
|
+
* `wrapWithPendingItems` used to await two HTTP calls — the unread inbox and
|
|
6
|
+
* the pending-task list — on EVERY wrapped tool invocation, of which there are
|
|
7
|
+
* 58. Each carries the 45s DEFAULT_TIMEOUT_MS, so with a slow or restarting
|
|
8
|
+
* server a call to something as unrelated as `now` could sit for ~90s before
|
|
9
|
+
* doing its own work. `fix-mcp-connection-resilience` already records where
|
|
10
|
+
* that ends: a hang long enough for Claude Code to tear down the MCP subprocess
|
|
11
|
+
* loses the agent for the rest of the session.
|
|
12
|
+
*
|
|
13
|
+
* So the wrapper no longer waits. It reads whatever the cache holds and returns
|
|
14
|
+
* immediately, while a refresh runs in the background. This mirrors the 60s
|
|
15
|
+
* cache `server_time` has used in this same wrapper since add-time-aware-agents,
|
|
16
|
+
* rather than inventing a second mechanism.
|
|
17
|
+
*
|
|
18
|
+
* The trade, stated plainly: a pending message can be up to one TTL stale on a
|
|
19
|
+
* tool response, and the very first wrapped call of a session carries none at
|
|
20
|
+
* all. That is acceptable because this was never the primary delivery path —
|
|
21
|
+
* the WebSocket push and an explicit `check_inbox` are — and because latency
|
|
22
|
+
* high enough to kill the subprocess loses far more than one stale message.
|
|
23
|
+
*/
|
|
24
|
+
import type { ApiClient, Message } from "../client.js";
|
|
25
|
+
/**
|
|
26
|
+
* Shorter than server_time's 60s: a teammate's message is more
|
|
27
|
+
* time-sensitive than the clock, and the refresh is off the hot path anyway.
|
|
28
|
+
*/
|
|
29
|
+
export declare const PENDING_ITEMS_TTL_MS = 15000;
|
|
30
|
+
/** Messages fetched for the wrapper; small page — the wrapper is a nudge. */
|
|
31
|
+
export declare const PENDING_ITEMS_FETCH_LIMIT = 10;
|
|
32
|
+
export interface PendingTask {
|
|
33
|
+
id: string;
|
|
34
|
+
assigned_by: string;
|
|
35
|
+
assigned_by_name?: string;
|
|
36
|
+
task: string;
|
|
37
|
+
priority: string;
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface PendingItems {
|
|
41
|
+
messages: Message[];
|
|
42
|
+
tasks: PendingTask[];
|
|
43
|
+
/** True when nothing has been fetched yet, or the entry is past its TTL. */
|
|
44
|
+
stale: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare function clearPendingItemsCache(): void;
|
|
47
|
+
/** Visible for tests: has a refresh for this agent settled? */
|
|
48
|
+
export declare function pendingItemsRefreshInFlight(agentId: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Read the cached pending items, scheduling a background refresh when stale.
|
|
51
|
+
*
|
|
52
|
+
* Never awaits the network: the returned value is whatever is already known,
|
|
53
|
+
* which on a cold cache is nothing. `stale` tells the caller which it got.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getPendingItems(client: ApiClient, agentId: string): PendingItems;
|
|
56
|
+
/**
|
|
57
|
+
* Await the next refresh for an agent, if one is in flight.
|
|
58
|
+
*
|
|
59
|
+
* For tests and for `check_inbox`-adjacent callers that genuinely want
|
|
60
|
+
* freshness. Production tool calls must not use this — waiting is the bug.
|
|
61
|
+
*/
|
|
62
|
+
export declare function awaitPendingItemsRefresh(agentId: string): Promise<void>;
|
|
63
|
+
//# sourceMappingURL=pending-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-items.d.ts","sourceRoot":"","sources":["../../src/lib/pending-items.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,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,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;CAChB;AAYD,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED,+DAA+D;AAC/D,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpE;AAwBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,GACd,YAAY,CAcd;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7E"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pending-items cache for the MCP tool wrapper
|
|
3
|
+
* (fix-inbox-delivery-and-tool-wrapping §3.2).
|
|
4
|
+
*
|
|
5
|
+
* `wrapWithPendingItems` used to await two HTTP calls — the unread inbox and
|
|
6
|
+
* the pending-task list — on EVERY wrapped tool invocation, of which there are
|
|
7
|
+
* 58. Each carries the 45s DEFAULT_TIMEOUT_MS, so with a slow or restarting
|
|
8
|
+
* server a call to something as unrelated as `now` could sit for ~90s before
|
|
9
|
+
* doing its own work. `fix-mcp-connection-resilience` already records where
|
|
10
|
+
* that ends: a hang long enough for Claude Code to tear down the MCP subprocess
|
|
11
|
+
* loses the agent for the rest of the session.
|
|
12
|
+
*
|
|
13
|
+
* So the wrapper no longer waits. It reads whatever the cache holds and returns
|
|
14
|
+
* immediately, while a refresh runs in the background. This mirrors the 60s
|
|
15
|
+
* cache `server_time` has used in this same wrapper since add-time-aware-agents,
|
|
16
|
+
* rather than inventing a second mechanism.
|
|
17
|
+
*
|
|
18
|
+
* The trade, stated plainly: a pending message can be up to one TTL stale on a
|
|
19
|
+
* tool response, and the very first wrapped call of a session carries none at
|
|
20
|
+
* all. That is acceptable because this was never the primary delivery path —
|
|
21
|
+
* the WebSocket push and an explicit `check_inbox` are — and because latency
|
|
22
|
+
* high enough to kill the subprocess loses far more than one stale message.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Shorter than server_time's 60s: a teammate's message is more
|
|
26
|
+
* time-sensitive than the clock, and the refresh is off the hot path anyway.
|
|
27
|
+
*/
|
|
28
|
+
export const PENDING_ITEMS_TTL_MS = 15_000;
|
|
29
|
+
/** Messages fetched for the wrapper; small page — the wrapper is a nudge. */
|
|
30
|
+
export const PENDING_ITEMS_FETCH_LIMIT = 10;
|
|
31
|
+
const cache = new Map();
|
|
32
|
+
/** In-flight refreshes, so N tool calls in a burst cause one fetch, not N. */
|
|
33
|
+
const inFlight = new Map();
|
|
34
|
+
export function clearPendingItemsCache() {
|
|
35
|
+
cache.clear();
|
|
36
|
+
inFlight.clear();
|
|
37
|
+
}
|
|
38
|
+
/** Visible for tests: has a refresh for this agent settled? */
|
|
39
|
+
export function pendingItemsRefreshInFlight(agentId) {
|
|
40
|
+
return inFlight.has(agentId);
|
|
41
|
+
}
|
|
42
|
+
async function refresh(client, agentId) {
|
|
43
|
+
try {
|
|
44
|
+
const [inbox, tasksResponse] = await Promise.all([
|
|
45
|
+
client.getInbox(agentId, true, PENDING_ITEMS_FETCH_LIMIT),
|
|
46
|
+
client.getPendingTasks(agentId),
|
|
47
|
+
]);
|
|
48
|
+
cache.set(agentId, {
|
|
49
|
+
messages: inbox.messages ?? [],
|
|
50
|
+
tasks: (tasksResponse.tasks ?? []),
|
|
51
|
+
fetched_at_ms: Date.now(),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// A failed refresh leaves the previous entry in place and is not
|
|
56
|
+
// propagated: the tool call that triggered it has already returned, and
|
|
57
|
+
// nothing about it should surface as that tool failing. The entry stays
|
|
58
|
+
// stale, so the next call tries again.
|
|
59
|
+
console.warn("[AgentHub] pending-items refresh failed:", err);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
inFlight.delete(agentId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Read the cached pending items, scheduling a background refresh when stale.
|
|
67
|
+
*
|
|
68
|
+
* Never awaits the network: the returned value is whatever is already known,
|
|
69
|
+
* which on a cold cache is nothing. `stale` tells the caller which it got.
|
|
70
|
+
*/
|
|
71
|
+
export function getPendingItems(client, agentId) {
|
|
72
|
+
const entry = cache.get(agentId);
|
|
73
|
+
const fresh = entry !== undefined && Date.now() - entry.fetched_at_ms < PENDING_ITEMS_TTL_MS;
|
|
74
|
+
if (!fresh && !inFlight.has(agentId)) {
|
|
75
|
+
// Fire and forget. The promise is tracked only to collapse a burst of
|
|
76
|
+
// tool calls into a single fetch; nothing awaits it.
|
|
77
|
+
const p = refresh(client, agentId);
|
|
78
|
+
inFlight.set(agentId, p);
|
|
79
|
+
void p;
|
|
80
|
+
}
|
|
81
|
+
if (!entry)
|
|
82
|
+
return { messages: [], tasks: [], stale: true };
|
|
83
|
+
return { messages: entry.messages, tasks: entry.tasks, stale: !fresh };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Await the next refresh for an agent, if one is in flight.
|
|
87
|
+
*
|
|
88
|
+
* For tests and for `check_inbox`-adjacent callers that genuinely want
|
|
89
|
+
* freshness. Production tool calls must not use this — waiting is the bug.
|
|
90
|
+
*/
|
|
91
|
+
export async function awaitPendingItemsRefresh(agentId) {
|
|
92
|
+
const p = inFlight.get(agentId);
|
|
93
|
+
if (p)
|
|
94
|
+
await p;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=pending-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-items.js","sourceRoot":"","sources":["../../src/lib/pending-items.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAwB5C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;AAC5C,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;AAElD,MAAM,UAAU,sBAAsB;IACpC,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,2BAA2B,CAAC,OAAe;IACzD,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,MAAiB,EAAE,OAAe;IACvD,IAAI,CAAC;QACH,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC/C,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC;YACzD,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC;SAChC,CAAC,CAAC;QACH,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE;YACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;YAC9B,KAAK,EAAG,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAA8B;YAChE,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;SAC1B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iEAAiE;QACjE,wEAAwE;QACxE,wEAAwE;QACxE,uCAAuC;QACvC,OAAO,CAAC,IAAI,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAiB,EACjB,OAAe;IAEf,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,aAAa,GAAG,oBAAoB,CAAC;IAE7F,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,sEAAsE;QACtE,qDAAqD;QACrD,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACzB,KAAK,CAAC,CAAC;IACT,CAAC;IAED,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC5D,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAAe;IAC5D,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC;QAAE,MAAM,CAAC,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find and fix `agenthub-multiagent-mcp` version pins on a machine
|
|
3
|
+
* (add-machine-wide-mcp-update §1).
|
|
4
|
+
*
|
|
5
|
+
* THE PROBLEM THIS EXISTS FOR. `@latest` plus a restart is the whole update
|
|
6
|
+
* mechanism, and a pin silently defeats it. Swept on the reference machine
|
|
7
|
+
* 2026-10-17: `~/.claude.json` read `@latest` in all 52 of its agenthub
|
|
8
|
+
* entries, so the machine looked current, while five hard pins sat elsewhere —
|
|
9
|
+
* three project `.mcp.json` files at 1.52.0, one at 1.61.0, and the channel
|
|
10
|
+
* plugin's own manifest at 1.61.0. Restarting those agents forever would never
|
|
11
|
+
* have moved them, and nobody can act on what they cannot enumerate.
|
|
12
|
+
*
|
|
13
|
+
* The plugin manifest is the one that matters most and is hardest to see: it is
|
|
14
|
+
* a SINGLE SHARED FILE governing every channel process on the machine, so a
|
|
15
|
+
* survey that counts agents never finds it.
|
|
16
|
+
*
|
|
17
|
+
* Everything here is a pure function over file contents. The impure shell —
|
|
18
|
+
* walking directories, reading, backing up, writing — lives in the CLI, so the
|
|
19
|
+
* matching and rewriting are testable without a machine.
|
|
20
|
+
*/
|
|
21
|
+
export interface Pin {
|
|
22
|
+
/** The file the pin was found in. */
|
|
23
|
+
file: string;
|
|
24
|
+
/** The pinned version, e.g. "1.52.0". */
|
|
25
|
+
version: string;
|
|
26
|
+
/** Whether this file is the shared channel-plugin manifest. */
|
|
27
|
+
pluginManifest: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface ScanInput {
|
|
30
|
+
file: string;
|
|
31
|
+
content: string;
|
|
32
|
+
}
|
|
33
|
+
export interface Summary {
|
|
34
|
+
pins: Pin[];
|
|
35
|
+
/** How many pinned references were found, across all files. */
|
|
36
|
+
pinned: number;
|
|
37
|
+
/** How many `@latest` references were found. */
|
|
38
|
+
floating: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether a path is the channel plugin's manifest.
|
|
42
|
+
*
|
|
43
|
+
* Matched on the path rather than the contents because the contents look like
|
|
44
|
+
* any other MCP server entry. Deliberately narrow: a plugin manifest sits under
|
|
45
|
+
* a plugins directory AND is named `plugin.json`, so a project file that merely
|
|
46
|
+
* mentions a plugin is not caught.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isPluginManifest(file: string): boolean;
|
|
49
|
+
/** Every pinned reference in one file's contents. */
|
|
50
|
+
export declare function findPins(file: string, content: string): Pin[];
|
|
51
|
+
/**
|
|
52
|
+
* Scan several files.
|
|
53
|
+
*
|
|
54
|
+
* Reports floating references alongside pinned ones because "no pins found" and
|
|
55
|
+
* "this tool found nothing at all" are different answers, and a summary that
|
|
56
|
+
* cannot tell them apart is how a broken scanner reads as a clean machine.
|
|
57
|
+
*/
|
|
58
|
+
export declare function summarize(inputs: ScanInput[]): Summary;
|
|
59
|
+
/**
|
|
60
|
+
* Replace every pinned version in `content` with `target`.
|
|
61
|
+
*
|
|
62
|
+
* A targeted string replacement rather than a JSON round-trip, on purpose:
|
|
63
|
+
* these files are hand-edited, several carry comments (which `JSON.parse` would
|
|
64
|
+
* reject and a reserialise would strip), and reformatting somebody's
|
|
65
|
+
* configuration to change one version string is not a trade worth making. An
|
|
66
|
+
* `@latest` reference is left exactly as it is.
|
|
67
|
+
*/
|
|
68
|
+
export declare function rewritePins(content: string, target: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Which pins a rewrite should touch, given whether the plugin was named.
|
|
71
|
+
*
|
|
72
|
+
* The plugin manifest is held back by default and the reason is not timidity:
|
|
73
|
+
* floating that pin has caused `EBUSY` on a shared npx cache while another
|
|
74
|
+
* session held the native addon, so it wants a considered bump rather than
|
|
75
|
+
* being swept along with four project files.
|
|
76
|
+
*/
|
|
77
|
+
export declare function selectForRewrite(pins: Pin[], includePlugin: boolean): {
|
|
78
|
+
apply: Pin[];
|
|
79
|
+
heldBack: Pin[];
|
|
80
|
+
};
|
|
81
|
+
/** Whether a target string is an acceptable version to pin to. */
|
|
82
|
+
export declare function validTarget(target: string): boolean;
|
|
83
|
+
//# sourceMappingURL=pinSweep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinSweep.d.ts","sourceRoot":"","sources":["../src/pinSweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAkBH,MAAM,WAAW,GAAG;IAClB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAED,qDAAqD;AACrD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,EAAE,CAM7D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAQtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,GAAG,EAAE,EACX,aAAa,EAAE,OAAO,GACrB;IAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAAC,QAAQ,EAAE,GAAG,EAAE,CAAA;CAAE,CAQnC;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
package/dist/pinSweep.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find and fix `agenthub-multiagent-mcp` version pins on a machine
|
|
3
|
+
* (add-machine-wide-mcp-update §1).
|
|
4
|
+
*
|
|
5
|
+
* THE PROBLEM THIS EXISTS FOR. `@latest` plus a restart is the whole update
|
|
6
|
+
* mechanism, and a pin silently defeats it. Swept on the reference machine
|
|
7
|
+
* 2026-10-17: `~/.claude.json` read `@latest` in all 52 of its agenthub
|
|
8
|
+
* entries, so the machine looked current, while five hard pins sat elsewhere —
|
|
9
|
+
* three project `.mcp.json` files at 1.52.0, one at 1.61.0, and the channel
|
|
10
|
+
* plugin's own manifest at 1.61.0. Restarting those agents forever would never
|
|
11
|
+
* have moved them, and nobody can act on what they cannot enumerate.
|
|
12
|
+
*
|
|
13
|
+
* The plugin manifest is the one that matters most and is hardest to see: it is
|
|
14
|
+
* a SINGLE SHARED FILE governing every channel process on the machine, so a
|
|
15
|
+
* survey that counts agents never finds it.
|
|
16
|
+
*
|
|
17
|
+
* Everything here is a pure function over file contents. The impure shell —
|
|
18
|
+
* walking directories, reading, backing up, writing — lives in the CLI, so the
|
|
19
|
+
* matching and rewriting are testable without a machine.
|
|
20
|
+
*/
|
|
21
|
+
/** The package whose pins we care about. */
|
|
22
|
+
const PKG = "agenthub-multiagent-mcp";
|
|
23
|
+
/**
|
|
24
|
+
* A pinned reference, i.e. `<pkg>@<semver>`.
|
|
25
|
+
*
|
|
26
|
+
* The negative lookahead on the package name is load-bearing: without it,
|
|
27
|
+
* `agenthub-multiagent-mcp-extras@1.2.3` matches and a different package's
|
|
28
|
+
* version gets rewritten. The `(?![\w.-])` after the version stops a longer
|
|
29
|
+
* version string being truncated mid-number.
|
|
30
|
+
*/
|
|
31
|
+
const PIN_RE = new RegExp(`${PKG}(?![\\w-])@(\\d+\\.\\d+\\.\\d+)(?![\\w.-])`, "g");
|
|
32
|
+
/** A floating reference. Counted, never rewritten — it is already correct. */
|
|
33
|
+
const FLOAT_RE = new RegExp(`${PKG}(?![\\w-])@latest(?![\\w.-])`, "g");
|
|
34
|
+
/**
|
|
35
|
+
* Whether a path is the channel plugin's manifest.
|
|
36
|
+
*
|
|
37
|
+
* Matched on the path rather than the contents because the contents look like
|
|
38
|
+
* any other MCP server entry. Deliberately narrow: a plugin manifest sits under
|
|
39
|
+
* a plugins directory AND is named `plugin.json`, so a project file that merely
|
|
40
|
+
* mentions a plugin is not caught.
|
|
41
|
+
*/
|
|
42
|
+
export function isPluginManifest(file) {
|
|
43
|
+
const p = file.replace(/\\/g, "/").toLowerCase();
|
|
44
|
+
return p.includes("/plugins/") && p.endsWith("plugin.json");
|
|
45
|
+
}
|
|
46
|
+
/** Every pinned reference in one file's contents. */
|
|
47
|
+
export function findPins(file, content) {
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const m of content.matchAll(PIN_RE)) {
|
|
50
|
+
out.push({ file, version: m[1], pluginManifest: isPluginManifest(file) });
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Scan several files.
|
|
56
|
+
*
|
|
57
|
+
* Reports floating references alongside pinned ones because "no pins found" and
|
|
58
|
+
* "this tool found nothing at all" are different answers, and a summary that
|
|
59
|
+
* cannot tell them apart is how a broken scanner reads as a clean machine.
|
|
60
|
+
*/
|
|
61
|
+
export function summarize(inputs) {
|
|
62
|
+
const pins = [];
|
|
63
|
+
let floating = 0;
|
|
64
|
+
for (const { file, content } of inputs) {
|
|
65
|
+
pins.push(...findPins(file, content));
|
|
66
|
+
floating += [...content.matchAll(FLOAT_RE)].length;
|
|
67
|
+
}
|
|
68
|
+
return { pins, pinned: pins.length, floating };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Replace every pinned version in `content` with `target`.
|
|
72
|
+
*
|
|
73
|
+
* A targeted string replacement rather than a JSON round-trip, on purpose:
|
|
74
|
+
* these files are hand-edited, several carry comments (which `JSON.parse` would
|
|
75
|
+
* reject and a reserialise would strip), and reformatting somebody's
|
|
76
|
+
* configuration to change one version string is not a trade worth making. An
|
|
77
|
+
* `@latest` reference is left exactly as it is.
|
|
78
|
+
*/
|
|
79
|
+
export function rewritePins(content, target) {
|
|
80
|
+
return content.replace(PIN_RE, `${PKG}@${target}`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Which pins a rewrite should touch, given whether the plugin was named.
|
|
84
|
+
*
|
|
85
|
+
* The plugin manifest is held back by default and the reason is not timidity:
|
|
86
|
+
* floating that pin has caused `EBUSY` on a shared npx cache while another
|
|
87
|
+
* session held the native addon, so it wants a considered bump rather than
|
|
88
|
+
* being swept along with four project files.
|
|
89
|
+
*/
|
|
90
|
+
export function selectForRewrite(pins, includePlugin) {
|
|
91
|
+
const apply = [];
|
|
92
|
+
const heldBack = [];
|
|
93
|
+
for (const p of pins) {
|
|
94
|
+
if (p.pluginManifest && !includePlugin)
|
|
95
|
+
heldBack.push(p);
|
|
96
|
+
else
|
|
97
|
+
apply.push(p);
|
|
98
|
+
}
|
|
99
|
+
return { apply, heldBack };
|
|
100
|
+
}
|
|
101
|
+
/** Whether a target string is an acceptable version to pin to. */
|
|
102
|
+
export function validTarget(target) {
|
|
103
|
+
return target === "latest" || /^\d+\.\d+\.\d+$/.test(target);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=pinSweep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinSweep.js","sourceRoot":"","sources":["../src/pinSweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,4CAA4C;AAC5C,MAAM,GAAG,GAAG,yBAAyB,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,4CAA4C,EAAE,GAAG,CAAC,CAAC;AAEnF,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,8BAA8B,EAAE,GAAG,CAAC,CAAC;AAwBvE;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC9D,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,OAAe;IACpD,MAAM,GAAG,GAAU,EAAE,CAAC;IACtB,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,MAAmB;IAC3C,MAAM,IAAI,GAAU,EAAE,CAAC;IACvB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,MAAM,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACtC,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;IACrD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,MAAc;IACzD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAW,EACX,aAAsB;IAEtB,MAAM,KAAK,GAAU,EAAE,CAAC;IACxB,MAAM,QAAQ,GAAU,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,cAAc,IAAI,CAAC,aAAa;YAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;YACpD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC7B,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,OAAO,MAAM,KAAK,QAAQ,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* add-machine-wide-mcp-update §1 — find every MCP version pin on a machine.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `~/.claude.json` on the reference machine reads `@latest` in
|
|
5
|
+
* all 52 of its agenthub entries, so the machine LOOKS current. Five hard pins
|
|
6
|
+
* live elsewhere — three project `.mcp.json` files at 1.52.0, one at 1.61.0, and
|
|
7
|
+
* the channel plugin's own manifest at 1.61.0 — and no number of restarts moves
|
|
8
|
+
* them. The last one is a single shared file governing every channel process on
|
|
9
|
+
* the machine, which is precisely the one a survey that counts agents misses.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=pinSweep.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinSweep.test.d.ts","sourceRoot":"","sources":["../src/pinSweep.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* add-machine-wide-mcp-update §1 — find every MCP version pin on a machine.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `~/.claude.json` on the reference machine reads `@latest` in
|
|
5
|
+
* all 52 of its agenthub entries, so the machine LOOKS current. Five hard pins
|
|
6
|
+
* live elsewhere — three project `.mcp.json` files at 1.52.0, one at 1.61.0, and
|
|
7
|
+
* the channel plugin's own manifest at 1.61.0 — and no number of restarts moves
|
|
8
|
+
* them. The last one is a single shared file governing every channel process on
|
|
9
|
+
* the machine, which is precisely the one a survey that counts agents misses.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from "vitest";
|
|
12
|
+
import { findPins, isPluginManifest, rewritePins, summarize } from "./pinSweep.js";
|
|
13
|
+
describe("pin scanning", () => {
|
|
14
|
+
it("reports a project pin with its version", () => {
|
|
15
|
+
const content = JSON.stringify({
|
|
16
|
+
mcpServers: {
|
|
17
|
+
agenthub: {
|
|
18
|
+
command: "npx",
|
|
19
|
+
args: ["-y", "agenthub-multiagent-mcp@1.52.0"],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const pins = findPins("D:/proj/.mcp.json", content);
|
|
24
|
+
expect(pins).toHaveLength(1);
|
|
25
|
+
expect(pins[0]).toMatchObject({
|
|
26
|
+
file: "D:/proj/.mcp.json",
|
|
27
|
+
version: "1.52.0",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
it("does NOT report @latest as a pin", () => {
|
|
31
|
+
const content = JSON.stringify({
|
|
32
|
+
mcpServers: {
|
|
33
|
+
agenthub: { args: ["-y", "agenthub-multiagent-mcp@latest"] },
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
expect(findPins("D:/proj/.mcp.json", content)).toEqual([]);
|
|
37
|
+
});
|
|
38
|
+
it("finds a pin in a plugin manifest and in the -p form", () => {
|
|
39
|
+
// The channel plugin is invoked as `-p <pkg>@<ver> agenthub-channel`, a
|
|
40
|
+
// different shape from the bare package argument.
|
|
41
|
+
const content = `{"mcpServers":{"agenthub-channel":{"args":["-y","-p","agenthub-multiagent-mcp@1.61.0","agenthub-channel"]}}}`;
|
|
42
|
+
const pins = findPins("/c/plugins/agenthub-channel/0.1.0/.claude-plugin/plugin.json", content);
|
|
43
|
+
expect(pins).toHaveLength(1);
|
|
44
|
+
expect(pins[0].version).toBe("1.61.0");
|
|
45
|
+
});
|
|
46
|
+
it("reports several pins in one file separately", () => {
|
|
47
|
+
const content = `{"a":"agenthub-multiagent-mcp@1.52.0","b":"agenthub-multiagent-mcp@1.61.0"}`;
|
|
48
|
+
const pins = findPins("x", content);
|
|
49
|
+
expect(pins.map((p) => p.version).sort()).toEqual(["1.52.0", "1.61.0"]);
|
|
50
|
+
});
|
|
51
|
+
it("ignores a different package that merely shares a prefix", () => {
|
|
52
|
+
const content = `{"a":"agenthub-multiagent-mcp-extras@1.2.3"}`;
|
|
53
|
+
expect(findPins("x", content)).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
it("counts floating entries separately from pinned ones", () => {
|
|
56
|
+
const content = `{
|
|
57
|
+
"a":"agenthub-multiagent-mcp@latest",
|
|
58
|
+
"b":"agenthub-multiagent-mcp@latest",
|
|
59
|
+
"c":"agenthub-multiagent-mcp@1.52.0"
|
|
60
|
+
}`;
|
|
61
|
+
const s = summarize([{ file: "x", content }]);
|
|
62
|
+
expect(s.pinned).toBe(1);
|
|
63
|
+
expect(s.floating).toBe(2);
|
|
64
|
+
});
|
|
65
|
+
it("reports nothing pinned when everything floats", () => {
|
|
66
|
+
const s = summarize([
|
|
67
|
+
{ file: "x", content: `{"a":"agenthub-multiagent-mcp@latest"}` },
|
|
68
|
+
]);
|
|
69
|
+
expect(s.pinned).toBe(0);
|
|
70
|
+
expect(s.pins).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe("the plugin manifest is recognised", () => {
|
|
74
|
+
it("matches a plugin manifest path", () => {
|
|
75
|
+
expect(isPluginManifest("/c/Users/x/.claude/plugins/cache/contetial/agenthub-channel/0.1.0/.claude-plugin/plugin.json")).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
it("does not match a project .mcp.json", () => {
|
|
78
|
+
expect(isPluginManifest("D:/Krishi_AI/Krishi-Dev/.mcp.json")).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
it("does not match the user configuration", () => {
|
|
81
|
+
expect(isPluginManifest("/c/Users/x/.claude.json")).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("rewriting", () => {
|
|
85
|
+
it("replaces a pinned version with the target", () => {
|
|
86
|
+
const before = `{"args":["-y","agenthub-multiagent-mcp@1.52.0"]}`;
|
|
87
|
+
const after = rewritePins(before, "latest");
|
|
88
|
+
expect(after).toContain("agenthub-multiagent-mcp@latest");
|
|
89
|
+
expect(after).not.toContain("1.52.0");
|
|
90
|
+
});
|
|
91
|
+
it("replaces every pin in the file, leaving @latest alone", () => {
|
|
92
|
+
const before = `{"a":"agenthub-multiagent-mcp@1.52.0","b":"agenthub-multiagent-mcp@latest","c":"agenthub-multiagent-mcp@1.61.0"}`;
|
|
93
|
+
const after = rewritePins(before, "1.64.0");
|
|
94
|
+
expect(after.match(/agenthub-multiagent-mcp@1\.64\.0/g)).toHaveLength(2);
|
|
95
|
+
expect(after).toContain("agenthub-multiagent-mcp@latest");
|
|
96
|
+
});
|
|
97
|
+
it("changes nothing when there is no pin", () => {
|
|
98
|
+
const before = `{"a":"agenthub-multiagent-mcp@latest"}`;
|
|
99
|
+
expect(rewritePins(before, "1.64.0")).toBe(before);
|
|
100
|
+
});
|
|
101
|
+
it("leaves the rest of the file byte-identical", () => {
|
|
102
|
+
// These files are hand-edited and carry comments and formatting the tool has
|
|
103
|
+
// no business reformatting. A targeted string replacement, not a JSON
|
|
104
|
+
// round-trip.
|
|
105
|
+
const before = `{\n // keep me\n "args": ["-y", "agenthub-multiagent-mcp@1.52.0"]\n}\n`;
|
|
106
|
+
const after = rewritePins(before, "latest");
|
|
107
|
+
expect(after).toBe(`{\n // keep me\n "args": ["-y", "agenthub-multiagent-mcp@latest"]\n}\n`);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=pinSweep.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinSweep.test.js","sourceRoot":"","sources":["../src/pinSweep.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agenthub-setup --pins` — the impure shell around pinSweep.ts
|
|
3
|
+
* (add-machine-wide-mcp-update §1).
|
|
4
|
+
*
|
|
5
|
+
* Reports every `agenthub-multiagent-mcp` version pin on this machine and, when
|
|
6
|
+
* asked, rewrites them. Reporting is the default: a tool that edits
|
|
7
|
+
* configuration the moment you run it is a tool people stop running.
|
|
8
|
+
*
|
|
9
|
+
* WHERE IT LOOKS, and why not a disk walk. Claude Code's own configuration
|
|
10
|
+
* carries a `projects` map keyed by project path — 83 entries on the reference
|
|
11
|
+
* machine — which is the authoritative list of projects on the box. Scanning
|
|
12
|
+
* those plus the plugins directory finds what actually loads, instead of
|
|
13
|
+
* trawling drives and hoping. A project Claude Code has never opened cannot be
|
|
14
|
+
* running an agent — but a project it has never OPENED can still hold a pin,
|
|
15
|
+
* which is why --root exists. See scanCaveat.
|
|
16
|
+
*/
|
|
17
|
+
import { isPluginManifest } from "./pinSweep.js";
|
|
18
|
+
export declare function runPinsSubcommand(argv: string[]): number;
|
|
19
|
+
export { isPluginManifest };
|
|
20
|
+
//# sourceMappingURL=pinSweepCli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pinSweepCli.d.ts","sourceRoot":"","sources":["../src/pinSweepCli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAEL,gBAAgB,EAOjB,MAAM,eAAe,CAAC;AAsKvB,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAoGxD;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|