@korso/shepherd 0.10.0 → 0.11.1
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 +173 -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;
|
|
@@ -1616,8 +1723,12 @@ function registerTools(server, deps) {
|
|
|
1616
1723
|
syncToolSurface();
|
|
1617
1724
|
}
|
|
1618
1725
|
let joinFailure = null;
|
|
1726
|
+
let joinFailureTransient = false;
|
|
1727
|
+
let targetWorkspaceSlug = null;
|
|
1728
|
+
let gateRetry = null;
|
|
1619
1729
|
let joinInFlight = Promise.resolve();
|
|
1620
1730
|
async function activate(workspaceSlug) {
|
|
1731
|
+
targetWorkspaceSlug = workspaceSlug;
|
|
1621
1732
|
if (sessionId !== null && activeWorkspaceSlug === workspaceSlug) {
|
|
1622
1733
|
return { ok: true };
|
|
1623
1734
|
}
|
|
@@ -1637,6 +1748,7 @@ function registerTools(server, deps) {
|
|
|
1637
1748
|
const parsed = JoinResponse.safeParse(raw);
|
|
1638
1749
|
if (!parsed.success || !parsed.data.sessionId) {
|
|
1639
1750
|
joinFailure = "validation";
|
|
1751
|
+
joinFailureTransient = false;
|
|
1640
1752
|
console.error(
|
|
1641
1753
|
"[shepherd] join failed (validation): hub returned a malformed join response (no usable sessionId)"
|
|
1642
1754
|
);
|
|
@@ -1646,14 +1758,18 @@ function registerTools(server, deps) {
|
|
|
1646
1758
|
heartbeat.start(newSessionId);
|
|
1647
1759
|
sessionId = newSessionId;
|
|
1648
1760
|
agentName = parsed.data.agentName;
|
|
1761
|
+
latestClientVersion = parsed.data.latestClientVersion;
|
|
1762
|
+
minimumClientVersion = parsed.data.minimumClientVersion;
|
|
1649
1763
|
activeWorkspaceSlug = workspaceSlug;
|
|
1650
1764
|
linked = true;
|
|
1651
1765
|
hostedWorkspaceRejected = false;
|
|
1652
1766
|
joinFailure = null;
|
|
1767
|
+
joinFailureTransient = false;
|
|
1653
1768
|
return { ok: true };
|
|
1654
1769
|
} catch (err) {
|
|
1655
1770
|
const reason = classifyActivateFailure(err);
|
|
1656
1771
|
joinFailure = classifyJoinFailure(err);
|
|
1772
|
+
joinFailureTransient = err instanceof HubUnreachable || err instanceof HubRequestError && reason === "unknown";
|
|
1657
1773
|
if (reason === "workspaceRejected") {
|
|
1658
1774
|
hostedWorkspaceRejected = true;
|
|
1659
1775
|
console.error(
|
|
@@ -1714,6 +1830,13 @@ function registerTools(server, deps) {
|
|
|
1714
1830
|
await awaitJoin();
|
|
1715
1831
|
if (!linked) return notLinked();
|
|
1716
1832
|
if (selfHostMismatch || hostedWorkspaceRejected) return workspaceMismatch();
|
|
1833
|
+
if (sessionId === null && targetWorkspaceSlug !== null && joinFailureTransient) {
|
|
1834
|
+
gateRetry ??= activate(targetWorkspaceSlug).finally(() => {
|
|
1835
|
+
gateRetry = null;
|
|
1836
|
+
});
|
|
1837
|
+
await gateRetry;
|
|
1838
|
+
if (hostedWorkspaceRejected) return workspaceMismatch();
|
|
1839
|
+
}
|
|
1717
1840
|
if (sessionId === null) return sessionNotReady();
|
|
1718
1841
|
return null;
|
|
1719
1842
|
}
|
|
@@ -1787,7 +1910,7 @@ ${section}` : body;
|
|
|
1787
1910
|
You hold this claim until you call done (workItemId: ${result.workItemId}) or it expires (~60 min). Calling work or sync renews it.`
|
|
1788
1911
|
)
|
|
1789
1912
|
);
|
|
1790
|
-
return { content: [{ type: "text", text }] };
|
|
1913
|
+
return { content: [{ type: "text", text: withUpdateNudge(text) }] };
|
|
1791
1914
|
} catch (err) {
|
|
1792
1915
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
1793
1916
|
return degradedResult(err);
|
|
@@ -1818,9 +1941,14 @@ You hold this claim until you call done (workItemId: ${result.workItemId}) or it
|
|
|
1818
1941
|
mergeAnnouncements(result.announcements, drainLocalInbox())
|
|
1819
1942
|
);
|
|
1820
1943
|
return {
|
|
1821
|
-
content: [
|
|
1944
|
+
content: [
|
|
1945
|
+
{
|
|
1946
|
+
type: "text",
|
|
1947
|
+
text: withUpdateNudge(msgs ? `${base}
|
|
1822
1948
|
|
|
1823
|
-
${msgs}` : base
|
|
1949
|
+
${msgs}` : base)
|
|
1950
|
+
}
|
|
1951
|
+
]
|
|
1824
1952
|
};
|
|
1825
1953
|
} catch (err) {
|
|
1826
1954
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
@@ -1852,9 +1980,14 @@ ${msgs}` : base }]
|
|
|
1852
1980
|
mergeAnnouncements(result.announcements, drainLocalInbox())
|
|
1853
1981
|
);
|
|
1854
1982
|
return {
|
|
1855
|
-
content: [
|
|
1983
|
+
content: [
|
|
1984
|
+
{
|
|
1985
|
+
type: "text",
|
|
1986
|
+
text: withUpdateNudge(msgs ? `${base}
|
|
1856
1987
|
|
|
1857
|
-
${msgs}` : base
|
|
1988
|
+
${msgs}` : base)
|
|
1989
|
+
}
|
|
1990
|
+
]
|
|
1858
1991
|
};
|
|
1859
1992
|
} catch (err) {
|
|
1860
1993
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
@@ -1892,7 +2025,7 @@ ${msgs}` : base }]
|
|
|
1892
2025
|
formatLandscape(result.landscape)
|
|
1893
2026
|
)
|
|
1894
2027
|
);
|
|
1895
|
-
return { content: [{ type: "text", text }] };
|
|
2028
|
+
return { content: [{ type: "text", text: withUpdateNudge(text) }] };
|
|
1896
2029
|
} catch (err) {
|
|
1897
2030
|
if (err instanceof HubUnreachable || err instanceof HubRequestError) {
|
|
1898
2031
|
return degradedResult(err);
|
|
@@ -2100,13 +2233,13 @@ function postLinkGuidance(workspace) {
|
|
|
2100
2233
|
}
|
|
2101
2234
|
|
|
2102
2235
|
// src/identityCache.ts
|
|
2103
|
-
import { mkdirSync as
|
|
2104
|
-
import { homedir as
|
|
2105
|
-
import { dirname as
|
|
2236
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2237
|
+
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
2238
|
+
import { dirname as dirname5, join as join4 } from "node:path";
|
|
2106
2239
|
function defaultIdentityCachePath() {
|
|
2107
2240
|
let base = "";
|
|
2108
2241
|
try {
|
|
2109
|
-
base =
|
|
2242
|
+
base = homedir4();
|
|
2110
2243
|
} catch {
|
|
2111
2244
|
base = "";
|
|
2112
2245
|
}
|
|
@@ -2116,7 +2249,7 @@ function defaultIdentityCachePath() {
|
|
|
2116
2249
|
function readCachedHuman(filePath = defaultIdentityCachePath()) {
|
|
2117
2250
|
let raw;
|
|
2118
2251
|
try {
|
|
2119
|
-
raw =
|
|
2252
|
+
raw = readFileSync5(filePath, "utf8");
|
|
2120
2253
|
} catch {
|
|
2121
2254
|
return null;
|
|
2122
2255
|
}
|
|
@@ -2131,9 +2264,9 @@ function readCachedHuman(filePath = defaultIdentityCachePath()) {
|
|
|
2131
2264
|
function writeCachedHuman(human, filePath = defaultIdentityCachePath()) {
|
|
2132
2265
|
if (typeof human !== "string" || human.trim().length === 0) return;
|
|
2133
2266
|
try {
|
|
2134
|
-
|
|
2267
|
+
mkdirSync4(dirname5(filePath), { recursive: true });
|
|
2135
2268
|
const payload = JSON.stringify({ human });
|
|
2136
|
-
|
|
2269
|
+
writeFileSync5(filePath, payload + "\n", "utf8");
|
|
2137
2270
|
} catch {
|
|
2138
2271
|
}
|
|
2139
2272
|
}
|
|
@@ -2397,30 +2530,16 @@ async function ancestorChain(maxDepth = 32, snapshot = snapshotParentMap) {
|
|
|
2397
2530
|
|
|
2398
2531
|
// src/hookInstall.ts
|
|
2399
2532
|
import {
|
|
2400
|
-
readFileSync as
|
|
2401
|
-
writeFileSync as
|
|
2402
|
-
mkdirSync as
|
|
2533
|
+
readFileSync as readFileSync6,
|
|
2534
|
+
writeFileSync as writeFileSync6,
|
|
2535
|
+
mkdirSync as mkdirSync5,
|
|
2403
2536
|
copyFileSync,
|
|
2404
2537
|
existsSync as existsSync4,
|
|
2405
2538
|
renameSync as renameSync2
|
|
2406
2539
|
} from "node:fs";
|
|
2407
|
-
import { homedir as
|
|
2408
|
-
import { dirname as
|
|
2540
|
+
import { homedir as homedir5 } from "node:os";
|
|
2541
|
+
import { dirname as dirname6, join as join5 } from "node:path";
|
|
2409
2542
|
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
2543
|
function detectClient(clientName) {
|
|
2425
2544
|
const name = (clientName ?? "").toLowerCase();
|
|
2426
2545
|
if (!name) return "unknown";
|
|
@@ -2433,16 +2552,16 @@ function detectClient(clientName) {
|
|
|
2433
2552
|
var HOOK_COMMAND = `npx -y --package=@korso/shepherd@${PACKAGE_VERSION} shepherd-inbox-hook`;
|
|
2434
2553
|
var HOOK_MARKER = "shepherd-inbox-hook";
|
|
2435
2554
|
function ensureHookScript(homeDir, hookScriptSource) {
|
|
2436
|
-
const source = hookScriptSource ?? join5(
|
|
2555
|
+
const source = hookScriptSource ?? join5(dirname6(fileURLToPath(import.meta.url)), "inboxHook.js");
|
|
2437
2556
|
try {
|
|
2438
2557
|
if (!existsSync4(source)) return null;
|
|
2439
2558
|
const dest = join5(homeDir, ".shepherd", "hooks", "shepherd-inbox-hook.mjs");
|
|
2440
|
-
const next =
|
|
2441
|
-
const current = existsSync4(dest) ?
|
|
2559
|
+
const next = readFileSync6(source);
|
|
2560
|
+
const current = existsSync4(dest) ? readFileSync6(dest) : null;
|
|
2442
2561
|
if (current === null || !current.equals(next)) {
|
|
2443
|
-
|
|
2562
|
+
mkdirSync5(dirname6(dest), { recursive: true });
|
|
2444
2563
|
const tmp = dest + ".tmp";
|
|
2445
|
-
|
|
2564
|
+
writeFileSync6(tmp, next);
|
|
2446
2565
|
renameSync2(tmp, dest);
|
|
2447
2566
|
}
|
|
2448
2567
|
return dest;
|
|
@@ -2465,7 +2584,7 @@ function codexHookBlock(scriptPath) {
|
|
|
2465
2584
|
}
|
|
2466
2585
|
async function autoInstallHooks({
|
|
2467
2586
|
clientName,
|
|
2468
|
-
homeDir =
|
|
2587
|
+
homeDir = homedir5(),
|
|
2469
2588
|
disabled = false,
|
|
2470
2589
|
extensionSource,
|
|
2471
2590
|
hookScriptSource,
|
|
@@ -2490,8 +2609,8 @@ async function autoInstallHooks({
|
|
|
2490
2609
|
} else {
|
|
2491
2610
|
status = installPi(homeDir, extensionSource, log);
|
|
2492
2611
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2612
|
+
mkdirSync5(dirname6(recordFile), { recursive: true });
|
|
2613
|
+
writeFileSync6(
|
|
2495
2614
|
recordFile,
|
|
2496
2615
|
JSON.stringify({ status, at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n",
|
|
2497
2616
|
"utf8"
|
|
@@ -2513,7 +2632,7 @@ function installClaude(homeDir, scriptPath, log) {
|
|
|
2513
2632
|
const settingsFile = join5(homeDir, ".claude", "settings.json");
|
|
2514
2633
|
let raw = "";
|
|
2515
2634
|
if (existsSync4(settingsFile)) {
|
|
2516
|
-
raw =
|
|
2635
|
+
raw = readFileSync6(settingsFile, "utf8");
|
|
2517
2636
|
if (raw.includes(HOOK_MARKER)) return "already-present";
|
|
2518
2637
|
}
|
|
2519
2638
|
let settings = {};
|
|
@@ -2556,8 +2675,8 @@ function installClaude(homeDir, scriptPath, log) {
|
|
|
2556
2675
|
matcher: "*",
|
|
2557
2676
|
hooks: [{ type: "command", command }]
|
|
2558
2677
|
});
|
|
2559
|
-
|
|
2560
|
-
|
|
2678
|
+
mkdirSync5(dirname6(settingsFile), { recursive: true });
|
|
2679
|
+
writeFileSync6(settingsFile, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
2561
2680
|
return "installed";
|
|
2562
2681
|
}
|
|
2563
2682
|
function installCodex(homeDir, scriptPath, log) {
|
|
@@ -2565,13 +2684,13 @@ function installCodex(homeDir, scriptPath, log) {
|
|
|
2565
2684
|
const manualHint = "Add the hook manually (see the dashboard's Connect screen).";
|
|
2566
2685
|
const hookBlock = codexHookBlock(scriptPath);
|
|
2567
2686
|
if (!existsSync4(configFile)) {
|
|
2568
|
-
|
|
2569
|
-
|
|
2687
|
+
mkdirSync5(dirname6(configFile), { recursive: true });
|
|
2688
|
+
writeFileSync6(configFile, `[features]
|
|
2570
2689
|
hooks = true
|
|
2571
2690
|
${hookBlock}`, "utf8");
|
|
2572
2691
|
return "installed";
|
|
2573
2692
|
}
|
|
2574
|
-
const toml =
|
|
2693
|
+
const toml = readFileSync6(configFile, "utf8");
|
|
2575
2694
|
if (toml.includes(HOOK_MARKER)) return "already-present";
|
|
2576
2695
|
if (/^\s*\[hooks\.UserPromptSubmit\]\s*$/m.test(toml)) {
|
|
2577
2696
|
log(
|
|
@@ -2592,10 +2711,10 @@ ${hookBlock}`, "utf8");
|
|
|
2592
2711
|
updated = toml.replace(/^(\s*\[features\]\s*)$/m, `$1
|
|
2593
2712
|
hooks = true`);
|
|
2594
2713
|
}
|
|
2595
|
-
|
|
2714
|
+
writeFileSync6(configFile, updated + hookBlock, "utf8");
|
|
2596
2715
|
return "installed";
|
|
2597
2716
|
}
|
|
2598
|
-
|
|
2717
|
+
writeFileSync6(
|
|
2599
2718
|
configFile,
|
|
2600
2719
|
`${toml}
|
|
2601
2720
|
[features]
|
|
@@ -2609,7 +2728,7 @@ function installCursor(homeDir, scriptPath, log) {
|
|
|
2609
2728
|
const hooksFile = join5(homeDir, ".cursor", "hooks.json");
|
|
2610
2729
|
let raw = "";
|
|
2611
2730
|
if (existsSync4(hooksFile)) {
|
|
2612
|
-
raw =
|
|
2731
|
+
raw = readFileSync6(hooksFile, "utf8");
|
|
2613
2732
|
if (raw.includes(HOOK_MARKER)) return "already-present";
|
|
2614
2733
|
}
|
|
2615
2734
|
let config = {};
|
|
@@ -2644,12 +2763,12 @@ function installCursor(homeDir, scriptPath, log) {
|
|
|
2644
2763
|
return "skipped";
|
|
2645
2764
|
}
|
|
2646
2765
|
entries.push({ command: hookCommandFor(scriptPath) });
|
|
2647
|
-
|
|
2648
|
-
|
|
2766
|
+
mkdirSync5(dirname6(hooksFile), { recursive: true });
|
|
2767
|
+
writeFileSync6(hooksFile, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
2649
2768
|
return "installed";
|
|
2650
2769
|
}
|
|
2651
2770
|
function installPi(homeDir, extensionSource, log) {
|
|
2652
|
-
const source = extensionSource ?? join5(
|
|
2771
|
+
const source = extensionSource ?? join5(dirname6(fileURLToPath(import.meta.url)), "inboxExtension.js");
|
|
2653
2772
|
const dest = join5(homeDir, ".pi", "agent", "extensions", "shepherd-inbox.js");
|
|
2654
2773
|
if (existsSync4(dest)) return "already-present";
|
|
2655
2774
|
if (!existsSync4(source)) {
|
|
@@ -2658,7 +2777,7 @@ function installPi(homeDir, extensionSource, log) {
|
|
|
2658
2777
|
);
|
|
2659
2778
|
return "skipped";
|
|
2660
2779
|
}
|
|
2661
|
-
|
|
2780
|
+
mkdirSync5(dirname6(dest), { recursive: true });
|
|
2662
2781
|
copyFileSync(source, dest);
|
|
2663
2782
|
return "installed";
|
|
2664
2783
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korso/shepherd",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
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": {
|