@korso/shepherd 0.10.0 → 0.11.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/index.js +159 -54
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -481,7 +481,12 @@ var JoinRequest = z2.object({
|
|
|
481
481
|
});
|
|
482
482
|
var JoinResponse = z2.object({
|
|
483
483
|
agentName: z2.string(),
|
|
484
|
-
sessionId: z2.string().uuid()
|
|
484
|
+
sessionId: z2.string().uuid(),
|
|
485
|
+
// Advertised so clients can nudge their humans to update. Optional: older
|
|
486
|
+
// hubs omit them, and a hub that cannot determine its bundled client
|
|
487
|
+
// version fails open by leaving them out.
|
|
488
|
+
latestClientVersion: z2.string().optional(),
|
|
489
|
+
minimumClientVersion: z2.string().optional()
|
|
485
490
|
});
|
|
486
491
|
var WorkRequest = z2.object({
|
|
487
492
|
sessionId: z2.string().uuid(),
|
|
@@ -1368,6 +1373,81 @@ function defaultRunGitStatus(cwd) {
|
|
|
1368
1373
|
});
|
|
1369
1374
|
}
|
|
1370
1375
|
|
|
1376
|
+
// src/updateNudge.ts
|
|
1377
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1378
|
+
import { dirname as dirname4 } from "node:path";
|
|
1379
|
+
var NUDGE_COOLDOWN_MS = 24 * 60 * 60 * 1e3;
|
|
1380
|
+
function parseVersion(v) {
|
|
1381
|
+
const m = /^v?(\d+(?:\.\d+)*)/.exec(v.trim());
|
|
1382
|
+
if (!m) return null;
|
|
1383
|
+
return m[1].split(".").map(Number);
|
|
1384
|
+
}
|
|
1385
|
+
function compareVersions(a, b) {
|
|
1386
|
+
const pa = parseVersion(a) ?? [];
|
|
1387
|
+
const pb = parseVersion(b) ?? [];
|
|
1388
|
+
const len = Math.max(pa.length, pb.length);
|
|
1389
|
+
for (let i = 0; i < len; i++) {
|
|
1390
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1391
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
1392
|
+
}
|
|
1393
|
+
return 0;
|
|
1394
|
+
}
|
|
1395
|
+
function readStamp(stampFile) {
|
|
1396
|
+
try {
|
|
1397
|
+
const parsed = JSON.parse(readFileSync4(stampFile, "utf8"));
|
|
1398
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.latest === "string" && typeof parsed.at === "number") {
|
|
1399
|
+
return parsed;
|
|
1400
|
+
}
|
|
1401
|
+
} catch {
|
|
1402
|
+
}
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
function writeStamp(stampFile, stamp) {
|
|
1406
|
+
try {
|
|
1407
|
+
mkdirSync3(dirname4(stampFile), { recursive: true });
|
|
1408
|
+
writeFileSync4(stampFile, JSON.stringify(stamp), "utf8");
|
|
1409
|
+
} catch {
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
var SUGGEST = "Let your human know, and suggest the update command that matches how Shepherd is installed on this machine (global npm, an npx cache, a version manager, \u2026).";
|
|
1413
|
+
function maybeUpdateNudge(opts) {
|
|
1414
|
+
const now = opts.nowMs ?? Date.now();
|
|
1415
|
+
if (!parseVersion(opts.current)) return "";
|
|
1416
|
+
const latest = opts.latest !== void 0 && parseVersion(opts.latest) ? opts.latest : void 0;
|
|
1417
|
+
const minimum = opts.minimum !== void 0 && parseVersion(opts.minimum) ? opts.minimum : void 0;
|
|
1418
|
+
const belowMinimum = minimum !== void 0 && compareVersions(opts.current, minimum) < 0;
|
|
1419
|
+
const behind = latest !== void 0 && compareVersions(opts.current, latest) < 0;
|
|
1420
|
+
if (!belowMinimum && !behind) return "";
|
|
1421
|
+
if (!belowMinimum) {
|
|
1422
|
+
const stamp = readStamp(opts.stampFile);
|
|
1423
|
+
if (stamp !== null && compareVersions(latest, stamp.latest) <= 0 && now - stamp.at < NUDGE_COOLDOWN_MS) {
|
|
1424
|
+
return "";
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
writeStamp(opts.stampFile, { latest: latest ?? opts.current, at: now });
|
|
1428
|
+
if (belowMinimum) {
|
|
1429
|
+
const latestPart = latest !== void 0 ? ` (latest: ${latest})` : "";
|
|
1430
|
+
return `[shepherd] This client (${opts.current}) is below the minimum supported version ${minimum}${latestPart} \u2014 coordination may misbehave until it is updated. ${SUGGEST}`;
|
|
1431
|
+
}
|
|
1432
|
+
return `[shepherd] Update available: @korso/shepherd ${latest} (this machine runs ${opts.current}). ${SUGGEST}`;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/version.ts
|
|
1436
|
+
import { createRequire } from "node:module";
|
|
1437
|
+
var PACKAGE_VERSION = (() => {
|
|
1438
|
+
try {
|
|
1439
|
+
const req = createRequire(import.meta.url);
|
|
1440
|
+
const pkg = req("../package.json");
|
|
1441
|
+
return pkg.version ?? "0.0.0";
|
|
1442
|
+
} catch {
|
|
1443
|
+
return "0.0.0";
|
|
1444
|
+
}
|
|
1445
|
+
})();
|
|
1446
|
+
|
|
1447
|
+
// src/tools.ts
|
|
1448
|
+
import { homedir as homedir3 } from "node:os";
|
|
1449
|
+
import nodePath from "node:path";
|
|
1450
|
+
|
|
1371
1451
|
// src/linkPopup.ts
|
|
1372
1452
|
var NEVER_ASK_CHOICE = "No \u2014 don't ask again";
|
|
1373
1453
|
async function offerLinkPopup({
|
|
@@ -1582,6 +1662,33 @@ function registerTools(server, deps) {
|
|
|
1582
1662
|
const { hubClient, config, context, heartbeat, inboxFile } = deps;
|
|
1583
1663
|
const markerCwd = deps.cwd ?? process.cwd();
|
|
1584
1664
|
const declinedDir = deps.declinedDir;
|
|
1665
|
+
const updateNudge = deps.updateNudge ?? ((versions) => maybeUpdateNudge({
|
|
1666
|
+
current: PACKAGE_VERSION,
|
|
1667
|
+
latest: versions.latest,
|
|
1668
|
+
minimum: versions.minimum,
|
|
1669
|
+
stampFile: nodePath.join(homedir3(), ".shepherd", "update-nudge.json")
|
|
1670
|
+
}));
|
|
1671
|
+
let latestClientVersion;
|
|
1672
|
+
let minimumClientVersion;
|
|
1673
|
+
let nudgeDelivered = false;
|
|
1674
|
+
function withUpdateNudge(text) {
|
|
1675
|
+
if (nudgeDelivered) return text;
|
|
1676
|
+
if (latestClientVersion === void 0 && minimumClientVersion === void 0) {
|
|
1677
|
+
return text;
|
|
1678
|
+
}
|
|
1679
|
+
nudgeDelivered = true;
|
|
1680
|
+
let nudge = "";
|
|
1681
|
+
try {
|
|
1682
|
+
nudge = updateNudge({
|
|
1683
|
+
latest: latestClientVersion,
|
|
1684
|
+
minimum: minimumClientVersion
|
|
1685
|
+
});
|
|
1686
|
+
} catch {
|
|
1687
|
+
}
|
|
1688
|
+
return nudge ? `${text}
|
|
1689
|
+
|
|
1690
|
+
${nudge}` : text;
|
|
1691
|
+
}
|
|
1585
1692
|
const repoRoot = findRepoRoot(markerCwd);
|
|
1586
1693
|
let sessionId = null;
|
|
1587
1694
|
let agentName = null;
|
|
@@ -1646,6 +1753,8 @@ function registerTools(server, deps) {
|
|
|
1646
1753
|
heartbeat.start(newSessionId);
|
|
1647
1754
|
sessionId = newSessionId;
|
|
1648
1755
|
agentName = parsed.data.agentName;
|
|
1756
|
+
latestClientVersion = parsed.data.latestClientVersion;
|
|
1757
|
+
minimumClientVersion = parsed.data.minimumClientVersion;
|
|
1649
1758
|
activeWorkspaceSlug = workspaceSlug;
|
|
1650
1759
|
linked = true;
|
|
1651
1760
|
hostedWorkspaceRejected = false;
|
|
@@ -1787,7 +1896,7 @@ ${section}` : body;
|
|
|
1787
1896
|
You hold this claim until you call done (workItemId: ${result.workItemId}) or it expires (~60 min). Calling work or sync renews it.`
|
|
1788
1897
|
)
|
|
1789
1898
|
);
|
|
1790
|
-
return { content: [{ type: "text", text }] };
|
|
1899
|
+
return { content: [{ type: "text", text: withUpdateNudge(text) }] };
|
|
1791
1900
|
} catch (err) {
|
|
1792
1901
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
1793
1902
|
return degradedResult(err);
|
|
@@ -1818,9 +1927,14 @@ You hold this claim until you call done (workItemId: ${result.workItemId}) or it
|
|
|
1818
1927
|
mergeAnnouncements(result.announcements, drainLocalInbox())
|
|
1819
1928
|
);
|
|
1820
1929
|
return {
|
|
1821
|
-
content: [
|
|
1930
|
+
content: [
|
|
1931
|
+
{
|
|
1932
|
+
type: "text",
|
|
1933
|
+
text: withUpdateNudge(msgs ? `${base}
|
|
1822
1934
|
|
|
1823
|
-
${msgs}` : base
|
|
1935
|
+
${msgs}` : base)
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1824
1938
|
};
|
|
1825
1939
|
} catch (err) {
|
|
1826
1940
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
@@ -1852,9 +1966,14 @@ ${msgs}` : base }]
|
|
|
1852
1966
|
mergeAnnouncements(result.announcements, drainLocalInbox())
|
|
1853
1967
|
);
|
|
1854
1968
|
return {
|
|
1855
|
-
content: [
|
|
1969
|
+
content: [
|
|
1970
|
+
{
|
|
1971
|
+
type: "text",
|
|
1972
|
+
text: withUpdateNudge(msgs ? `${base}
|
|
1856
1973
|
|
|
1857
|
-
${msgs}` : base
|
|
1974
|
+
${msgs}` : base)
|
|
1975
|
+
}
|
|
1976
|
+
]
|
|
1858
1977
|
};
|
|
1859
1978
|
} catch (err) {
|
|
1860
1979
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
@@ -1892,7 +2011,7 @@ ${msgs}` : base }]
|
|
|
1892
2011
|
formatLandscape(result.landscape)
|
|
1893
2012
|
)
|
|
1894
2013
|
);
|
|
1895
|
-
return { content: [{ type: "text", text }] };
|
|
2014
|
+
return { content: [{ type: "text", text: withUpdateNudge(text) }] };
|
|
1896
2015
|
} catch (err) {
|
|
1897
2016
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
1898
2017
|
return degradedResult(err);
|
|
@@ -2100,13 +2219,13 @@ function postLinkGuidance(workspace) {
|
|
|
2100
2219
|
}
|
|
2101
2220
|
|
|
2102
2221
|
// src/identityCache.ts
|
|
2103
|
-
import { mkdirSync as
|
|
2104
|
-
import { homedir as
|
|
2105
|
-
import { dirname as
|
|
2222
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2223
|
+
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
2224
|
+
import { dirname as dirname5, join as join4 } from "node:path";
|
|
2106
2225
|
function defaultIdentityCachePath() {
|
|
2107
2226
|
let base = "";
|
|
2108
2227
|
try {
|
|
2109
|
-
base =
|
|
2228
|
+
base = homedir4();
|
|
2110
2229
|
} catch {
|
|
2111
2230
|
base = "";
|
|
2112
2231
|
}
|
|
@@ -2116,7 +2235,7 @@ function defaultIdentityCachePath() {
|
|
|
2116
2235
|
function readCachedHuman(filePath = defaultIdentityCachePath()) {
|
|
2117
2236
|
let raw;
|
|
2118
2237
|
try {
|
|
2119
|
-
raw =
|
|
2238
|
+
raw = readFileSync5(filePath, "utf8");
|
|
2120
2239
|
} catch {
|
|
2121
2240
|
return null;
|
|
2122
2241
|
}
|
|
@@ -2131,9 +2250,9 @@ function readCachedHuman(filePath = defaultIdentityCachePath()) {
|
|
|
2131
2250
|
function writeCachedHuman(human, filePath = defaultIdentityCachePath()) {
|
|
2132
2251
|
if (typeof human !== "string" || human.trim().length === 0) return;
|
|
2133
2252
|
try {
|
|
2134
|
-
|
|
2253
|
+
mkdirSync4(dirname5(filePath), { recursive: true });
|
|
2135
2254
|
const payload = JSON.stringify({ human });
|
|
2136
|
-
|
|
2255
|
+
writeFileSync5(filePath, payload + "\n", "utf8");
|
|
2137
2256
|
} catch {
|
|
2138
2257
|
}
|
|
2139
2258
|
}
|
|
@@ -2397,30 +2516,16 @@ async function ancestorChain(maxDepth = 32, snapshot = snapshotParentMap) {
|
|
|
2397
2516
|
|
|
2398
2517
|
// src/hookInstall.ts
|
|
2399
2518
|
import {
|
|
2400
|
-
readFileSync as
|
|
2401
|
-
writeFileSync as
|
|
2402
|
-
mkdirSync as
|
|
2519
|
+
readFileSync as readFileSync6,
|
|
2520
|
+
writeFileSync as writeFileSync6,
|
|
2521
|
+
mkdirSync as mkdirSync5,
|
|
2403
2522
|
copyFileSync,
|
|
2404
2523
|
existsSync as existsSync4,
|
|
2405
2524
|
renameSync as renameSync2
|
|
2406
2525
|
} from "node:fs";
|
|
2407
|
-
import { homedir as
|
|
2408
|
-
import { dirname as
|
|
2526
|
+
import { homedir as homedir5 } from "node:os";
|
|
2527
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
2409
2528
|
import { fileURLToPath } from "node:url";
|
|
2410
|
-
|
|
2411
|
-
// src/version.ts
|
|
2412
|
-
import { createRequire } from "node:module";
|
|
2413
|
-
var PACKAGE_VERSION = (() => {
|
|
2414
|
-
try {
|
|
2415
|
-
const req = createRequire(import.meta.url);
|
|
2416
|
-
const pkg = req("../package.json");
|
|
2417
|
-
return pkg.version ?? "0.0.0";
|
|
2418
|
-
} catch {
|
|
2419
|
-
return "0.0.0";
|
|
2420
|
-
}
|
|
2421
|
-
})();
|
|
2422
|
-
|
|
2423
|
-
// src/hookInstall.ts
|
|
2424
2529
|
function detectClient(clientName) {
|
|
2425
2530
|
const name = (clientName ?? "").toLowerCase();
|
|
2426
2531
|
if (!name) return "unknown";
|
|
@@ -2433,16 +2538,16 @@ function detectClient(clientName) {
|
|
|
2433
2538
|
var HOOK_COMMAND = `npx -y --package=@korso/shepherd@${PACKAGE_VERSION} shepherd-inbox-hook`;
|
|
2434
2539
|
var HOOK_MARKER = "shepherd-inbox-hook";
|
|
2435
2540
|
function ensureHookScript(homeDir, hookScriptSource) {
|
|
2436
|
-
const source = hookScriptSource ?? join5(
|
|
2541
|
+
const source = hookScriptSource ?? join5(dirname6(fileURLToPath(import.meta.url)), "inboxHook.js");
|
|
2437
2542
|
try {
|
|
2438
2543
|
if (!existsSync4(source)) return null;
|
|
2439
2544
|
const dest = join5(homeDir, ".shepherd", "hooks", "shepherd-inbox-hook.mjs");
|
|
2440
|
-
const next =
|
|
2441
|
-
const current = existsSync4(dest) ?
|
|
2545
|
+
const next = readFileSync6(source);
|
|
2546
|
+
const current = existsSync4(dest) ? readFileSync6(dest) : null;
|
|
2442
2547
|
if (current === null || !current.equals(next)) {
|
|
2443
|
-
|
|
2548
|
+
mkdirSync5(dirname6(dest), { recursive: true });
|
|
2444
2549
|
const tmp = dest + ".tmp";
|
|
2445
|
-
|
|
2550
|
+
writeFileSync6(tmp, next);
|
|
2446
2551
|
renameSync2(tmp, dest);
|
|
2447
2552
|
}
|
|
2448
2553
|
return dest;
|
|
@@ -2465,7 +2570,7 @@ function codexHookBlock(scriptPath) {
|
|
|
2465
2570
|
}
|
|
2466
2571
|
async function autoInstallHooks({
|
|
2467
2572
|
clientName,
|
|
2468
|
-
homeDir =
|
|
2573
|
+
homeDir = homedir5(),
|
|
2469
2574
|
disabled = false,
|
|
2470
2575
|
extensionSource,
|
|
2471
2576
|
hookScriptSource,
|
|
@@ -2490,8 +2595,8 @@ async function autoInstallHooks({
|
|
|
2490
2595
|
} else {
|
|
2491
2596
|
status = installPi(homeDir, extensionSource, log);
|
|
2492
2597
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2598
|
+
mkdirSync5(dirname6(recordFile), { recursive: true });
|
|
2599
|
+
writeFileSync6(
|
|
2495
2600
|
recordFile,
|
|
2496
2601
|
JSON.stringify({ status, at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n",
|
|
2497
2602
|
"utf8"
|
|
@@ -2513,7 +2618,7 @@ function installClaude(homeDir, scriptPath, log) {
|
|
|
2513
2618
|
const settingsFile = join5(homeDir, ".claude", "settings.json");
|
|
2514
2619
|
let raw = "";
|
|
2515
2620
|
if (existsSync4(settingsFile)) {
|
|
2516
|
-
raw =
|
|
2621
|
+
raw = readFileSync6(settingsFile, "utf8");
|
|
2517
2622
|
if (raw.includes(HOOK_MARKER)) return "already-present";
|
|
2518
2623
|
}
|
|
2519
2624
|
let settings = {};
|
|
@@ -2556,8 +2661,8 @@ function installClaude(homeDir, scriptPath, log) {
|
|
|
2556
2661
|
matcher: "*",
|
|
2557
2662
|
hooks: [{ type: "command", command }]
|
|
2558
2663
|
});
|
|
2559
|
-
|
|
2560
|
-
|
|
2664
|
+
mkdirSync5(dirname6(settingsFile), { recursive: true });
|
|
2665
|
+
writeFileSync6(settingsFile, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
2561
2666
|
return "installed";
|
|
2562
2667
|
}
|
|
2563
2668
|
function installCodex(homeDir, scriptPath, log) {
|
|
@@ -2565,13 +2670,13 @@ function installCodex(homeDir, scriptPath, log) {
|
|
|
2565
2670
|
const manualHint = "Add the hook manually (see the dashboard's Connect screen).";
|
|
2566
2671
|
const hookBlock = codexHookBlock(scriptPath);
|
|
2567
2672
|
if (!existsSync4(configFile)) {
|
|
2568
|
-
|
|
2569
|
-
|
|
2673
|
+
mkdirSync5(dirname6(configFile), { recursive: true });
|
|
2674
|
+
writeFileSync6(configFile, `[features]
|
|
2570
2675
|
hooks = true
|
|
2571
2676
|
${hookBlock}`, "utf8");
|
|
2572
2677
|
return "installed";
|
|
2573
2678
|
}
|
|
2574
|
-
const toml =
|
|
2679
|
+
const toml = readFileSync6(configFile, "utf8");
|
|
2575
2680
|
if (toml.includes(HOOK_MARKER)) return "already-present";
|
|
2576
2681
|
if (/^\s*\[hooks\.UserPromptSubmit\]\s*$/m.test(toml)) {
|
|
2577
2682
|
log(
|
|
@@ -2592,10 +2697,10 @@ ${hookBlock}`, "utf8");
|
|
|
2592
2697
|
updated = toml.replace(/^(\s*\[features\]\s*)$/m, `$1
|
|
2593
2698
|
hooks = true`);
|
|
2594
2699
|
}
|
|
2595
|
-
|
|
2700
|
+
writeFileSync6(configFile, updated + hookBlock, "utf8");
|
|
2596
2701
|
return "installed";
|
|
2597
2702
|
}
|
|
2598
|
-
|
|
2703
|
+
writeFileSync6(
|
|
2599
2704
|
configFile,
|
|
2600
2705
|
`${toml}
|
|
2601
2706
|
[features]
|
|
@@ -2609,7 +2714,7 @@ function installCursor(homeDir, scriptPath, log) {
|
|
|
2609
2714
|
const hooksFile = join5(homeDir, ".cursor", "hooks.json");
|
|
2610
2715
|
let raw = "";
|
|
2611
2716
|
if (existsSync4(hooksFile)) {
|
|
2612
|
-
raw =
|
|
2717
|
+
raw = readFileSync6(hooksFile, "utf8");
|
|
2613
2718
|
if (raw.includes(HOOK_MARKER)) return "already-present";
|
|
2614
2719
|
}
|
|
2615
2720
|
let config = {};
|
|
@@ -2644,12 +2749,12 @@ function installCursor(homeDir, scriptPath, log) {
|
|
|
2644
2749
|
return "skipped";
|
|
2645
2750
|
}
|
|
2646
2751
|
entries.push({ command: hookCommandFor(scriptPath) });
|
|
2647
|
-
|
|
2648
|
-
|
|
2752
|
+
mkdirSync5(dirname6(hooksFile), { recursive: true });
|
|
2753
|
+
writeFileSync6(hooksFile, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
2649
2754
|
return "installed";
|
|
2650
2755
|
}
|
|
2651
2756
|
function installPi(homeDir, extensionSource, log) {
|
|
2652
|
-
const source = extensionSource ?? join5(
|
|
2757
|
+
const source = extensionSource ?? join5(dirname6(fileURLToPath(import.meta.url)), "inboxExtension.js");
|
|
2653
2758
|
const dest = join5(homeDir, ".pi", "agent", "extensions", "shepherd-inbox.js");
|
|
2654
2759
|
if (existsSync4(dest)) return "already-present";
|
|
2655
2760
|
if (!existsSync4(source)) {
|
|
@@ -2658,7 +2763,7 @@ function installPi(homeDir, extensionSource, log) {
|
|
|
2658
2763
|
);
|
|
2659
2764
|
return "skipped";
|
|
2660
2765
|
}
|
|
2661
|
-
|
|
2766
|
+
mkdirSync5(dirname6(dest), { recursive: true });
|
|
2662
2767
|
copyFileSync(source, dest);
|
|
2663
2768
|
return "installed";
|
|
2664
2769
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korso/shepherd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Shepherd MCP server — gives any MCP-capable agent (Claude Code, Codex, etc.) advisory cross-session coordination tools (work/done/announce/sync, plus link/unlink/decline) backed by the shared Shepherd hub. Joins the workspace automatically and ships standing instructions so the agent self-coordinates.",
|
|
5
5
|
"homepage": "https://github.com/Korso-AI/shepherd#readme",
|
|
6
6
|
"bugs": {
|