heyspex 0.2.2 → 0.2.3
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 +10 -0
- package/bin/hs-vault-binding.mjs +157 -0
- package/bin/hs.mjs +81 -77
- package/bin/spexcode.mjs +91 -0
- package/dev-factory/engine/worktree.mjs +36 -7
- package/dev-factory/lib/epic-detect.mjs +2 -2
- package/dev-factory/lib/fleet-readiness.mjs +3 -5
- package/dev-factory/lib/fleet-state.mjs +2 -2
- package/dev-factory/lib/sufficiency-score.mjs +3 -5
- package/dist/cli/hs-check.mjs +76 -46
- package/dist/cli/hs-claim.mjs +63 -33
- package/dist/cli/hs-connect.mjs +520 -214
- package/dist/cli/hs-defer.mjs +161 -19
- package/dist/cli/hs-dev.mjs +11 -5
- package/dist/cli/hs-env.mjs +187 -161
- package/dist/cli/hs-index-rebuild.mjs +123 -78
- package/dist/cli/hs-index-reconcile.mjs +203 -107
- package/dist/cli/hs-init.mjs +426 -179
- package/dist/cli/hs-install.mjs +759 -437
- package/dist/cli/hs-join.mjs +590 -383
- package/dist/cli/hs-vault-pull.mjs +359 -254
- package/dist/cli/hs-vault-push.mjs +151 -113
- package/dist/cli/hs-vault.mjs +271 -104
- package/dist/cli/hs-workspace.mjs +90 -57
- package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +178 -91
- package/docs/copilot-plugin-install.md +189 -177
- package/docs/driving-heyspex-from-copilot.md +174 -165
- package/docs/mcp-client-config.md +93 -25
- package/package.json +255 -250
- package/plugins/heyspex/.mcp.json +3 -4
- package/plugins/heyspex/README.md +323 -312
- package/plugins/heyspex/bin/launch-mcp.mjs +21 -7
- package/plugins/heyspex/mcp.json +12 -13
- package/scripts/check-mcp-freshness.mjs +6 -4
- package/scripts/claude-hooks/_intent-classifier-router.mjs +518 -456
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +305 -279
- package/scripts/claude-hooks/research-method-guard.mjs +2 -2
- package/scripts/claude-hooks/wip-checkpoint.mjs +33 -9
- package/scripts/claude-statusline/fonts/HeySpexIcons.ttf +0 -0
- package/scripts/claude-statusline/statusline-command.mjs +1058 -0
- package/scripts/dev-server-status.mjs +3 -2
- package/scripts/dev-server-stop.mjs +3 -2
- package/scripts/hs-capture-cli.mjs +2 -2
- package/scripts/hs-settings.mjs +2 -2
- package/scripts/hs-view.mjs +3 -2
- package/scripts/install-copilot-hooks.mjs +64 -34
- package/scripts/lib/copilot-cli.mjs +108 -0
- package/scripts/lib/customer-kit.mjs +223 -30
- package/scripts/lib/dev-mode.mjs +28 -2
- package/scripts/lib/shared-path-helpers.mjs +32 -0
- package/scripts/lib/standalone-doctor.mjs +131 -32
- package/scripts/wip-ledger-health.mjs +3 -2
- package/spexharness/src/gates/vault-mirror-catchup.mjs +113 -24
- package/spexharness/src/gates/vault-mirror-catchup.runner.mjs +52 -0
- package/spexharness/src/intent-directive.mjs +839 -626
- package/spexharness/src/rule-table.mjs +1 -1
- package/vendor/heyspex-mcp/cli-verbs.mjs +16 -2
- package/vendor/heyspex-mcp/cli.mjs +20 -4
- package/vendor/heyspex-mcp/console-fetch.mjs +15 -3
- package/vendor/heyspex-mcp/dev-tools.mjs +11 -4
- package/vendor/heyspex-mcp/events-tools.mjs +1 -1
- package/vendor/heyspex-mcp/factory-run-status.mjs +42 -0
- package/vendor/heyspex-mcp/mcp-config.mjs +4 -2
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +26 -1
- package/vendor/heyspex-mcp/test-helpers.mjs +5 -2
- package/vendor/heyspex-mcp/tool-catalog.mjs +12 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +59 -23
- package/vendor/heyspex-mcp/usable-dir-env.mjs +82 -0
- package/vendor/heyspex-mcp/vault-binding.mjs +839 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +353 -245
package/dist/cli/hs-join.mjs
CHANGED
|
@@ -158,9 +158,9 @@ function resolveBearer({ envToken, credentialsText, accountId, legacyAccountId }
|
|
|
158
158
|
if (entry?.mcp_token) return { token: entry.mcp_token, source: "credentials-file" };
|
|
159
159
|
return { token: null, source: null };
|
|
160
160
|
}
|
|
161
|
-
function readTextOrNull(read2,
|
|
161
|
+
function readTextOrNull(read2, path11) {
|
|
162
162
|
try {
|
|
163
|
-
const raw = read2(
|
|
163
|
+
const raw = read2(path11);
|
|
164
164
|
return typeof raw === "string" ? raw : null;
|
|
165
165
|
} catch {
|
|
166
166
|
return null;
|
|
@@ -264,6 +264,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome2(), readFile: readFile3
|
|
|
264
264
|
return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
268
|
+
if (source !== "local-override") return "not-applicable";
|
|
269
|
+
if (!cloudKnown) return "unverified";
|
|
270
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
271
|
+
}
|
|
267
272
|
function resolveDevMode({
|
|
268
273
|
env = process.env,
|
|
269
274
|
homeDir = heyspexHome2(),
|
|
@@ -303,6 +308,7 @@ function resolveDevMode({
|
|
|
303
308
|
const semantics = deriveSemantics(mode, reliance);
|
|
304
309
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
305
310
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
311
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
306
312
|
return {
|
|
307
313
|
mode,
|
|
308
314
|
reliance: semantics.reliance,
|
|
@@ -320,7 +326,8 @@ function resolveDevMode({
|
|
|
320
326
|
present: local.record !== null,
|
|
321
327
|
record: local.record,
|
|
322
328
|
parseError: local.parseError,
|
|
323
|
-
staleAgainstCloud
|
|
329
|
+
staleAgainstCloud,
|
|
330
|
+
cloudCheck
|
|
324
331
|
},
|
|
325
332
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
326
333
|
};
|
|
@@ -1072,7 +1079,7 @@ var require_main = __commonJS({
|
|
|
1072
1079
|
"node_modules/dotenv/lib/main.js"(exports2, module2) {
|
|
1073
1080
|
"use strict";
|
|
1074
1081
|
var fs4 = __require("fs");
|
|
1075
|
-
var
|
|
1082
|
+
var path11 = __require("path");
|
|
1076
1083
|
var os3 = __require("os");
|
|
1077
1084
|
var crypto3 = __require("crypto");
|
|
1078
1085
|
var packageJson = require_package();
|
|
@@ -1188,7 +1195,7 @@ var require_main = __commonJS({
|
|
|
1188
1195
|
possibleVaultPath = options2.path.endsWith(".vault") ? options2.path : `${options2.path}.vault`;
|
|
1189
1196
|
}
|
|
1190
1197
|
} else {
|
|
1191
|
-
possibleVaultPath =
|
|
1198
|
+
possibleVaultPath = path11.resolve(process.cwd(), ".env.vault");
|
|
1192
1199
|
}
|
|
1193
1200
|
if (fs4.existsSync(possibleVaultPath)) {
|
|
1194
1201
|
return possibleVaultPath;
|
|
@@ -1196,7 +1203,7 @@ var require_main = __commonJS({
|
|
|
1196
1203
|
return null;
|
|
1197
1204
|
}
|
|
1198
1205
|
function _resolveHome(envPath) {
|
|
1199
|
-
return envPath[0] === "~" ?
|
|
1206
|
+
return envPath[0] === "~" ? path11.join(os3.homedir(), envPath.slice(1)) : envPath;
|
|
1200
1207
|
}
|
|
1201
1208
|
function _configVault(options2) {
|
|
1202
1209
|
const debug = Boolean(options2 && options2.debug);
|
|
@@ -1213,7 +1220,7 @@ var require_main = __commonJS({
|
|
|
1213
1220
|
return { parsed };
|
|
1214
1221
|
}
|
|
1215
1222
|
function configDotenv(options2) {
|
|
1216
|
-
const dotenvPath2 =
|
|
1223
|
+
const dotenvPath2 = path11.resolve(process.cwd(), ".env");
|
|
1217
1224
|
let encoding = "utf8";
|
|
1218
1225
|
const debug = Boolean(options2 && options2.debug);
|
|
1219
1226
|
const quiet = options2 && "quiet" in options2 ? options2.quiet : true;
|
|
@@ -1237,13 +1244,13 @@ var require_main = __commonJS({
|
|
|
1237
1244
|
}
|
|
1238
1245
|
let lastError;
|
|
1239
1246
|
const parsedAll = {};
|
|
1240
|
-
for (const
|
|
1247
|
+
for (const path12 of optionPaths) {
|
|
1241
1248
|
try {
|
|
1242
|
-
const parsed = DotenvModule.parse(fs4.readFileSync(
|
|
1249
|
+
const parsed = DotenvModule.parse(fs4.readFileSync(path12, { encoding }));
|
|
1243
1250
|
DotenvModule.populate(parsedAll, parsed, options2);
|
|
1244
1251
|
} catch (e) {
|
|
1245
1252
|
if (debug) {
|
|
1246
|
-
_debug(`Failed to load ${
|
|
1253
|
+
_debug(`Failed to load ${path12} ${e.message}`);
|
|
1247
1254
|
}
|
|
1248
1255
|
lastError = e;
|
|
1249
1256
|
}
|
|
@@ -1258,7 +1265,7 @@ var require_main = __commonJS({
|
|
|
1258
1265
|
const shortPaths = [];
|
|
1259
1266
|
for (const filePath of optionPaths) {
|
|
1260
1267
|
try {
|
|
1261
|
-
const relative =
|
|
1268
|
+
const relative = path11.relative(process.cwd(), filePath);
|
|
1262
1269
|
shortPaths.push(relative);
|
|
1263
1270
|
} catch (e) {
|
|
1264
1271
|
if (debug) {
|
|
@@ -1435,17 +1442,17 @@ var require_visit = __commonJS({
|
|
|
1435
1442
|
visit.BREAK = BREAK;
|
|
1436
1443
|
visit.SKIP = SKIP;
|
|
1437
1444
|
visit.REMOVE = REMOVE;
|
|
1438
|
-
function visit_(key, node, visitor,
|
|
1439
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
1445
|
+
function visit_(key, node, visitor, path11) {
|
|
1446
|
+
const ctrl = callVisitor(key, node, visitor, path11);
|
|
1440
1447
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
1441
|
-
replaceNode(key,
|
|
1442
|
-
return visit_(key, ctrl, visitor,
|
|
1448
|
+
replaceNode(key, path11, ctrl);
|
|
1449
|
+
return visit_(key, ctrl, visitor, path11);
|
|
1443
1450
|
}
|
|
1444
1451
|
if (typeof ctrl !== "symbol") {
|
|
1445
1452
|
if (identity.isCollection(node)) {
|
|
1446
|
-
|
|
1453
|
+
path11 = Object.freeze(path11.concat(node));
|
|
1447
1454
|
for (let i = 0; i < node.items.length; ++i) {
|
|
1448
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
1455
|
+
const ci = visit_(i, node.items[i], visitor, path11);
|
|
1449
1456
|
if (typeof ci === "number")
|
|
1450
1457
|
i = ci - 1;
|
|
1451
1458
|
else if (ci === BREAK)
|
|
@@ -1456,13 +1463,13 @@ var require_visit = __commonJS({
|
|
|
1456
1463
|
}
|
|
1457
1464
|
}
|
|
1458
1465
|
} else if (identity.isPair(node)) {
|
|
1459
|
-
|
|
1460
|
-
const ck = visit_("key", node.key, visitor,
|
|
1466
|
+
path11 = Object.freeze(path11.concat(node));
|
|
1467
|
+
const ck = visit_("key", node.key, visitor, path11);
|
|
1461
1468
|
if (ck === BREAK)
|
|
1462
1469
|
return BREAK;
|
|
1463
1470
|
else if (ck === REMOVE)
|
|
1464
1471
|
node.key = null;
|
|
1465
|
-
const cv = visit_("value", node.value, visitor,
|
|
1472
|
+
const cv = visit_("value", node.value, visitor, path11);
|
|
1466
1473
|
if (cv === BREAK)
|
|
1467
1474
|
return BREAK;
|
|
1468
1475
|
else if (cv === REMOVE)
|
|
@@ -1483,17 +1490,17 @@ var require_visit = __commonJS({
|
|
|
1483
1490
|
visitAsync.BREAK = BREAK;
|
|
1484
1491
|
visitAsync.SKIP = SKIP;
|
|
1485
1492
|
visitAsync.REMOVE = REMOVE;
|
|
1486
|
-
async function visitAsync_(key, node, visitor,
|
|
1487
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
1493
|
+
async function visitAsync_(key, node, visitor, path11) {
|
|
1494
|
+
const ctrl = await callVisitor(key, node, visitor, path11);
|
|
1488
1495
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
1489
|
-
replaceNode(key,
|
|
1490
|
-
return visitAsync_(key, ctrl, visitor,
|
|
1496
|
+
replaceNode(key, path11, ctrl);
|
|
1497
|
+
return visitAsync_(key, ctrl, visitor, path11);
|
|
1491
1498
|
}
|
|
1492
1499
|
if (typeof ctrl !== "symbol") {
|
|
1493
1500
|
if (identity.isCollection(node)) {
|
|
1494
|
-
|
|
1501
|
+
path11 = Object.freeze(path11.concat(node));
|
|
1495
1502
|
for (let i = 0; i < node.items.length; ++i) {
|
|
1496
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
1503
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path11);
|
|
1497
1504
|
if (typeof ci === "number")
|
|
1498
1505
|
i = ci - 1;
|
|
1499
1506
|
else if (ci === BREAK)
|
|
@@ -1504,13 +1511,13 @@ var require_visit = __commonJS({
|
|
|
1504
1511
|
}
|
|
1505
1512
|
}
|
|
1506
1513
|
} else if (identity.isPair(node)) {
|
|
1507
|
-
|
|
1508
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
1514
|
+
path11 = Object.freeze(path11.concat(node));
|
|
1515
|
+
const ck = await visitAsync_("key", node.key, visitor, path11);
|
|
1509
1516
|
if (ck === BREAK)
|
|
1510
1517
|
return BREAK;
|
|
1511
1518
|
else if (ck === REMOVE)
|
|
1512
1519
|
node.key = null;
|
|
1513
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
1520
|
+
const cv = await visitAsync_("value", node.value, visitor, path11);
|
|
1514
1521
|
if (cv === BREAK)
|
|
1515
1522
|
return BREAK;
|
|
1516
1523
|
else if (cv === REMOVE)
|
|
@@ -1537,23 +1544,23 @@ var require_visit = __commonJS({
|
|
|
1537
1544
|
}
|
|
1538
1545
|
return visitor;
|
|
1539
1546
|
}
|
|
1540
|
-
function callVisitor(key, node, visitor,
|
|
1547
|
+
function callVisitor(key, node, visitor, path11) {
|
|
1541
1548
|
if (typeof visitor === "function")
|
|
1542
|
-
return visitor(key, node,
|
|
1549
|
+
return visitor(key, node, path11);
|
|
1543
1550
|
if (identity.isMap(node))
|
|
1544
|
-
return visitor.Map?.(key, node,
|
|
1551
|
+
return visitor.Map?.(key, node, path11);
|
|
1545
1552
|
if (identity.isSeq(node))
|
|
1546
|
-
return visitor.Seq?.(key, node,
|
|
1553
|
+
return visitor.Seq?.(key, node, path11);
|
|
1547
1554
|
if (identity.isPair(node))
|
|
1548
|
-
return visitor.Pair?.(key, node,
|
|
1555
|
+
return visitor.Pair?.(key, node, path11);
|
|
1549
1556
|
if (identity.isScalar(node))
|
|
1550
|
-
return visitor.Scalar?.(key, node,
|
|
1557
|
+
return visitor.Scalar?.(key, node, path11);
|
|
1551
1558
|
if (identity.isAlias(node))
|
|
1552
|
-
return visitor.Alias?.(key, node,
|
|
1559
|
+
return visitor.Alias?.(key, node, path11);
|
|
1553
1560
|
return void 0;
|
|
1554
1561
|
}
|
|
1555
|
-
function replaceNode(key,
|
|
1556
|
-
const parent =
|
|
1562
|
+
function replaceNode(key, path11, node) {
|
|
1563
|
+
const parent = path11[path11.length - 1];
|
|
1557
1564
|
if (identity.isCollection(parent)) {
|
|
1558
1565
|
parent.items[key] = node;
|
|
1559
1566
|
} else if (identity.isPair(parent)) {
|
|
@@ -2163,10 +2170,10 @@ var require_Collection = __commonJS({
|
|
|
2163
2170
|
var createNode = require_createNode();
|
|
2164
2171
|
var identity = require_identity();
|
|
2165
2172
|
var Node = require_Node();
|
|
2166
|
-
function collectionFromPath(schema,
|
|
2173
|
+
function collectionFromPath(schema, path11, value) {
|
|
2167
2174
|
let v = value;
|
|
2168
|
-
for (let i =
|
|
2169
|
-
const k =
|
|
2175
|
+
for (let i = path11.length - 1; i >= 0; --i) {
|
|
2176
|
+
const k = path11[i];
|
|
2170
2177
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
2171
2178
|
const a = [];
|
|
2172
2179
|
a[k] = v;
|
|
@@ -2185,7 +2192,7 @@ var require_Collection = __commonJS({
|
|
|
2185
2192
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
2186
2193
|
});
|
|
2187
2194
|
}
|
|
2188
|
-
var isEmptyPath = (
|
|
2195
|
+
var isEmptyPath = (path11) => path11 == null || typeof path11 === "object" && !!path11[Symbol.iterator]().next().done;
|
|
2189
2196
|
var Collection = class extends Node.NodeBase {
|
|
2190
2197
|
constructor(type, schema) {
|
|
2191
2198
|
super(type);
|
|
@@ -2215,11 +2222,11 @@ var require_Collection = __commonJS({
|
|
|
2215
2222
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
2216
2223
|
* that already exists in the map.
|
|
2217
2224
|
*/
|
|
2218
|
-
addIn(
|
|
2219
|
-
if (isEmptyPath(
|
|
2225
|
+
addIn(path11, value) {
|
|
2226
|
+
if (isEmptyPath(path11))
|
|
2220
2227
|
this.add(value);
|
|
2221
2228
|
else {
|
|
2222
|
-
const [key, ...rest] =
|
|
2229
|
+
const [key, ...rest] = path11;
|
|
2223
2230
|
const node = this.get(key, true);
|
|
2224
2231
|
if (identity.isCollection(node))
|
|
2225
2232
|
node.addIn(rest, value);
|
|
@@ -2233,8 +2240,8 @@ var require_Collection = __commonJS({
|
|
|
2233
2240
|
* Removes a value from the collection.
|
|
2234
2241
|
* @returns `true` if the item was found and removed.
|
|
2235
2242
|
*/
|
|
2236
|
-
deleteIn(
|
|
2237
|
-
const [key, ...rest] =
|
|
2243
|
+
deleteIn(path11) {
|
|
2244
|
+
const [key, ...rest] = path11;
|
|
2238
2245
|
if (rest.length === 0)
|
|
2239
2246
|
return this.delete(key);
|
|
2240
2247
|
const node = this.get(key, true);
|
|
@@ -2248,8 +2255,8 @@ var require_Collection = __commonJS({
|
|
|
2248
2255
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
2249
2256
|
* `true` (collections are always returned intact).
|
|
2250
2257
|
*/
|
|
2251
|
-
getIn(
|
|
2252
|
-
const [key, ...rest] =
|
|
2258
|
+
getIn(path11, keepScalar) {
|
|
2259
|
+
const [key, ...rest] = path11;
|
|
2253
2260
|
const node = this.get(key, true);
|
|
2254
2261
|
if (rest.length === 0)
|
|
2255
2262
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -2267,8 +2274,8 @@ var require_Collection = __commonJS({
|
|
|
2267
2274
|
/**
|
|
2268
2275
|
* Checks if the collection includes a value with the key `key`.
|
|
2269
2276
|
*/
|
|
2270
|
-
hasIn(
|
|
2271
|
-
const [key, ...rest] =
|
|
2277
|
+
hasIn(path11) {
|
|
2278
|
+
const [key, ...rest] = path11;
|
|
2272
2279
|
if (rest.length === 0)
|
|
2273
2280
|
return this.has(key);
|
|
2274
2281
|
const node = this.get(key, true);
|
|
@@ -2278,8 +2285,8 @@ var require_Collection = __commonJS({
|
|
|
2278
2285
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
2279
2286
|
* boolean to add/remove the item from the set.
|
|
2280
2287
|
*/
|
|
2281
|
-
setIn(
|
|
2282
|
-
const [key, ...rest] =
|
|
2288
|
+
setIn(path11, value) {
|
|
2289
|
+
const [key, ...rest] = path11;
|
|
2283
2290
|
if (rest.length === 0) {
|
|
2284
2291
|
this.set(key, value);
|
|
2285
2292
|
} else {
|
|
@@ -4794,9 +4801,9 @@ var require_Document = __commonJS({
|
|
|
4794
4801
|
this.contents.add(value);
|
|
4795
4802
|
}
|
|
4796
4803
|
/** Adds a value to the document. */
|
|
4797
|
-
addIn(
|
|
4804
|
+
addIn(path11, value) {
|
|
4798
4805
|
if (assertCollection(this.contents))
|
|
4799
|
-
this.contents.addIn(
|
|
4806
|
+
this.contents.addIn(path11, value);
|
|
4800
4807
|
}
|
|
4801
4808
|
/**
|
|
4802
4809
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -4871,14 +4878,14 @@ var require_Document = __commonJS({
|
|
|
4871
4878
|
* Removes a value from the document.
|
|
4872
4879
|
* @returns `true` if the item was found and removed.
|
|
4873
4880
|
*/
|
|
4874
|
-
deleteIn(
|
|
4875
|
-
if (Collection.isEmptyPath(
|
|
4881
|
+
deleteIn(path11) {
|
|
4882
|
+
if (Collection.isEmptyPath(path11)) {
|
|
4876
4883
|
if (this.contents == null)
|
|
4877
4884
|
return false;
|
|
4878
4885
|
this.contents = null;
|
|
4879
4886
|
return true;
|
|
4880
4887
|
}
|
|
4881
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
4888
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path11) : false;
|
|
4882
4889
|
}
|
|
4883
4890
|
/**
|
|
4884
4891
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -4893,10 +4900,10 @@ var require_Document = __commonJS({
|
|
|
4893
4900
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
4894
4901
|
* `true` (collections are always returned intact).
|
|
4895
4902
|
*/
|
|
4896
|
-
getIn(
|
|
4897
|
-
if (Collection.isEmptyPath(
|
|
4903
|
+
getIn(path11, keepScalar) {
|
|
4904
|
+
if (Collection.isEmptyPath(path11))
|
|
4898
4905
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
4899
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
4906
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path11, keepScalar) : void 0;
|
|
4900
4907
|
}
|
|
4901
4908
|
/**
|
|
4902
4909
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -4907,10 +4914,10 @@ var require_Document = __commonJS({
|
|
|
4907
4914
|
/**
|
|
4908
4915
|
* Checks if the document includes a value at `path`.
|
|
4909
4916
|
*/
|
|
4910
|
-
hasIn(
|
|
4911
|
-
if (Collection.isEmptyPath(
|
|
4917
|
+
hasIn(path11) {
|
|
4918
|
+
if (Collection.isEmptyPath(path11))
|
|
4912
4919
|
return this.contents !== void 0;
|
|
4913
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
4920
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path11) : false;
|
|
4914
4921
|
}
|
|
4915
4922
|
/**
|
|
4916
4923
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -4927,13 +4934,13 @@ var require_Document = __commonJS({
|
|
|
4927
4934
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
4928
4935
|
* boolean to add/remove the item from the set.
|
|
4929
4936
|
*/
|
|
4930
|
-
setIn(
|
|
4931
|
-
if (Collection.isEmptyPath(
|
|
4937
|
+
setIn(path11, value) {
|
|
4938
|
+
if (Collection.isEmptyPath(path11)) {
|
|
4932
4939
|
this.contents = value;
|
|
4933
4940
|
} else if (this.contents == null) {
|
|
4934
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
4941
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path11), value);
|
|
4935
4942
|
} else if (assertCollection(this.contents)) {
|
|
4936
|
-
this.contents.setIn(
|
|
4943
|
+
this.contents.setIn(path11, value);
|
|
4937
4944
|
}
|
|
4938
4945
|
}
|
|
4939
4946
|
/**
|
|
@@ -6893,9 +6900,9 @@ var require_cst_visit = __commonJS({
|
|
|
6893
6900
|
visit.BREAK = BREAK;
|
|
6894
6901
|
visit.SKIP = SKIP;
|
|
6895
6902
|
visit.REMOVE = REMOVE;
|
|
6896
|
-
visit.itemAtPath = (cst,
|
|
6903
|
+
visit.itemAtPath = (cst, path11) => {
|
|
6897
6904
|
let item = cst;
|
|
6898
|
-
for (const [field, index2] of
|
|
6905
|
+
for (const [field, index2] of path11) {
|
|
6899
6906
|
const tok = item?.[field];
|
|
6900
6907
|
if (tok && "items" in tok) {
|
|
6901
6908
|
item = tok.items[index2];
|
|
@@ -6904,23 +6911,23 @@ var require_cst_visit = __commonJS({
|
|
|
6904
6911
|
}
|
|
6905
6912
|
return item;
|
|
6906
6913
|
};
|
|
6907
|
-
visit.parentCollection = (cst,
|
|
6908
|
-
const parent = visit.itemAtPath(cst,
|
|
6909
|
-
const field =
|
|
6914
|
+
visit.parentCollection = (cst, path11) => {
|
|
6915
|
+
const parent = visit.itemAtPath(cst, path11.slice(0, -1));
|
|
6916
|
+
const field = path11[path11.length - 1][0];
|
|
6910
6917
|
const coll = parent?.[field];
|
|
6911
6918
|
if (coll && "items" in coll)
|
|
6912
6919
|
return coll;
|
|
6913
6920
|
throw new Error("Parent collection not found");
|
|
6914
6921
|
};
|
|
6915
|
-
function _visit(
|
|
6916
|
-
let ctrl = visitor(item,
|
|
6922
|
+
function _visit(path11, item, visitor) {
|
|
6923
|
+
let ctrl = visitor(item, path11);
|
|
6917
6924
|
if (typeof ctrl === "symbol")
|
|
6918
6925
|
return ctrl;
|
|
6919
6926
|
for (const field of ["key", "value"]) {
|
|
6920
6927
|
const token = item[field];
|
|
6921
6928
|
if (token && "items" in token) {
|
|
6922
6929
|
for (let i = 0; i < token.items.length; ++i) {
|
|
6923
|
-
const ci = _visit(Object.freeze(
|
|
6930
|
+
const ci = _visit(Object.freeze(path11.concat([[field, i]])), token.items[i], visitor);
|
|
6924
6931
|
if (typeof ci === "number")
|
|
6925
6932
|
i = ci - 1;
|
|
6926
6933
|
else if (ci === BREAK)
|
|
@@ -6931,10 +6938,10 @@ var require_cst_visit = __commonJS({
|
|
|
6931
6938
|
}
|
|
6932
6939
|
}
|
|
6933
6940
|
if (typeof ctrl === "function" && field === "key")
|
|
6934
|
-
ctrl = ctrl(item,
|
|
6941
|
+
ctrl = ctrl(item, path11);
|
|
6935
6942
|
}
|
|
6936
6943
|
}
|
|
6937
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
6944
|
+
return typeof ctrl === "function" ? ctrl(item, path11) : ctrl;
|
|
6938
6945
|
}
|
|
6939
6946
|
exports2.visit = visit;
|
|
6940
6947
|
}
|
|
@@ -10789,11 +10796,11 @@ var require_baseGet = __commonJS({
|
|
|
10789
10796
|
"use strict";
|
|
10790
10797
|
var castPath = require_castPath();
|
|
10791
10798
|
var toKey = require_toKey();
|
|
10792
|
-
function baseGet(object,
|
|
10793
|
-
|
|
10794
|
-
var index2 = 0, length =
|
|
10799
|
+
function baseGet(object, path11) {
|
|
10800
|
+
path11 = castPath(path11, object);
|
|
10801
|
+
var index2 = 0, length = path11.length;
|
|
10795
10802
|
while (object != null && index2 < length) {
|
|
10796
|
-
object = object[toKey(
|
|
10803
|
+
object = object[toKey(path11[index2++])];
|
|
10797
10804
|
}
|
|
10798
10805
|
return index2 && index2 == length ? object : void 0;
|
|
10799
10806
|
}
|
|
@@ -10806,8 +10813,8 @@ var require_get = __commonJS({
|
|
|
10806
10813
|
"node_modules/lodash/get.js"(exports2, module2) {
|
|
10807
10814
|
"use strict";
|
|
10808
10815
|
var baseGet = require_baseGet();
|
|
10809
|
-
function get2(object,
|
|
10810
|
-
var result = object == null ? void 0 : baseGet(object,
|
|
10816
|
+
function get2(object, path11, defaultValue) {
|
|
10817
|
+
var result = object == null ? void 0 : baseGet(object, path11);
|
|
10811
10818
|
return result === void 0 ? defaultValue : result;
|
|
10812
10819
|
}
|
|
10813
10820
|
module2.exports = get2;
|
|
@@ -22739,9 +22746,9 @@ var require_lib2 = __commonJS({
|
|
|
22739
22746
|
|
|
22740
22747
|
// spex-console/bin/hs-join.ts
|
|
22741
22748
|
import { spawn, spawnSync as spawnSync5 } from "node:child_process";
|
|
22742
|
-
import { existsSync as
|
|
22749
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
|
|
22743
22750
|
import { userInfo } from "node:os";
|
|
22744
|
-
import { basename as
|
|
22751
|
+
import { basename as basename5, dirname as dirname6, join as join16, resolve as resolve5 } from "node:path";
|
|
22745
22752
|
import process4 from "node:process";
|
|
22746
22753
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
22747
22754
|
|
|
@@ -22852,10 +22859,10 @@ function classifyHttpFailureReason(res) {
|
|
|
22852
22859
|
if (res.status === 401) return "unauthorized";
|
|
22853
22860
|
return "server-error";
|
|
22854
22861
|
}
|
|
22855
|
-
async function cloudHttpRequest(http, method,
|
|
22856
|
-
const scopeHeaders = http.scope ? http.scope.headersFor(method,
|
|
22862
|
+
async function cloudHttpRequest(http, method, path11, body) {
|
|
22863
|
+
const scopeHeaders = http.scope ? http.scope.headersFor(method, path11) : {};
|
|
22857
22864
|
try {
|
|
22858
|
-
const res = await http.fetchImpl(`${http.consoleUrl}${
|
|
22865
|
+
const res = await http.fetchImpl(`${http.consoleUrl}${path11}`, {
|
|
22859
22866
|
method,
|
|
22860
22867
|
headers: {
|
|
22861
22868
|
"content-type": "application/json",
|
|
@@ -26127,8 +26134,16 @@ var LAUNCHER_TEMPLATE = [
|
|
|
26127
26134
|
"// 4. node_modules/heyspex up from $__ENV__, $CLAUDE_PROJECT_DIR, $CURSOR_PROJECT_DIR, cwd",
|
|
26128
26135
|
"// 5. <npm root -g>/heyspex a global install, last resort",
|
|
26129
26136
|
"// Nothing found -> one stderr line and exit 0 (fail-open, like the shim itself).",
|
|
26137
|
+
"//",
|
|
26138
|
+
"// The same resolution starts the package's other entrypoints, so no committed",
|
|
26139
|
+
"// file names node_modules/heyspex directly and a repo with no node_modules of",
|
|
26140
|
+
"// its own still works off a global install:",
|
|
26141
|
+
"// --mcp the stdio MCP server (.mcp.json, .cursor/mcp.json)",
|
|
26142
|
+
"// --statusline the Claude Code status line (statusLine.command)",
|
|
26143
|
+
"// --resolve print the package root, exit 0; exit 1 when none (hs doctor)",
|
|
26130
26144
|
"import { spawnSync } from 'node:child_process';",
|
|
26131
|
-
"import { existsSync } from 'node:fs';",
|
|
26145
|
+
"import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs';",
|
|
26146
|
+
"import { homedir } from 'node:os';",
|
|
26132
26147
|
"import { dirname, resolve } from 'node:path';",
|
|
26133
26148
|
"import { fileURLToPath, pathToFileURL } from 'node:url';",
|
|
26134
26149
|
"",
|
|
@@ -26138,6 +26153,7 @@ var LAUNCHER_TEMPLATE = [
|
|
|
26138
26153
|
"const text = (v) => (typeof v === 'string' && v.trim() ? v.trim() : null);",
|
|
26139
26154
|
"const hasShim = (root) => Boolean(root) && existsSync(resolve(root, ...SHIM));",
|
|
26140
26155
|
"",
|
|
26156
|
+
"__YIELD__",
|
|
26141
26157
|
"function packageUpFrom(start) {",
|
|
26142
26158
|
" let dir = resolve(start);",
|
|
26143
26159
|
" for (;;) {",
|
|
@@ -26172,31 +26188,109 @@ var LAUNCHER_TEMPLATE = [
|
|
|
26172
26188
|
" if (hasShim(global)) root = global;",
|
|
26173
26189
|
"}",
|
|
26174
26190
|
"",
|
|
26191
|
+
"const ENTRYPOINTS = {",
|
|
26192
|
+
" '--mcp': 'vendor/heyspex-mcp/cli.mjs',",
|
|
26193
|
+
" '--statusline': 'scripts/claude-statusline/statusline-command.mjs',",
|
|
26194
|
+
"};",
|
|
26195
|
+
"const mode = process.argv[2];",
|
|
26196
|
+
"const entry = Object.hasOwn(ENTRYPOINTS, mode) ? ENTRYPOINTS[mode] : null;",
|
|
26197
|
+
"",
|
|
26175
26198
|
"if (!root) {",
|
|
26199
|
+
" const what = entry || mode === '--resolve' ? mode : 'hook ' + (mode ?? '?');",
|
|
26176
26200
|
" process.stderr.write(",
|
|
26177
|
-
" '[heyspex]
|
|
26178
|
-
` '(run "npm install heyspex" in this repo, or set HEYSPEX_PLATFORM_ROOT).
|
|
26179
|
-
" 'HEYSPEX_PLATFORM_ROOT, HEYSPEX_MCP_ENTRY, node_modules/heyspex up from ' + starts.join(', ') + ', and npm root -g.\\n',",
|
|
26201
|
+
" '[heyspex] ' + what + ' skipped: no heyspex package found ' +",
|
|
26202
|
+
` '(run "npm install heyspex" or "npm link heyspex" in this repo, install it globally, or set HEYSPEX_PLATFORM_ROOT). ' +`,
|
|
26203
|
+
" 'Looked for __SHIM__ under HEYSPEX_PLATFORM_ROOT, HEYSPEX_MCP_ENTRY, node_modules/heyspex up from ' + starts.join(', ') + ', and npm root -g.\\n',",
|
|
26180
26204
|
" );",
|
|
26205
|
+
" // Hooks and the status line fail open; the MCP server and --resolve fail",
|
|
26206
|
+
" // loudly, so the client and hs doctor both see it.",
|
|
26207
|
+
" process.exit(mode === '--mcp' || mode === '--resolve' ? 1 : 0);",
|
|
26208
|
+
"}",
|
|
26209
|
+
"if (mode === '--resolve') {",
|
|
26210
|
+
" process.stdout.write(root + '\\n');",
|
|
26181
26211
|
" process.exit(0);",
|
|
26182
26212
|
"}",
|
|
26183
26213
|
"",
|
|
26184
26214
|
"// argv is already [node, <this file>, <hook>, ...args] -- the shape the shim",
|
|
26185
|
-
'// reads; the
|
|
26186
|
-
"//
|
|
26187
|
-
"
|
|
26188
|
-
"
|
|
26215
|
+
'// reads; the target replaces this file so a script guarded by "am I main?"',
|
|
26216
|
+
"// runs exactly as if it had been invoked directly. Real path, so that guard",
|
|
26217
|
+
"// still matches when node_modules/heyspex is an npm link or a junction (node",
|
|
26218
|
+
"// realpaths import.meta.url but leaves argv[1] alone).",
|
|
26219
|
+
"let target = resolve(root, ...(entry ? entry.split('/') : SHIM));",
|
|
26189
26220
|
"try {",
|
|
26190
|
-
"
|
|
26221
|
+
" target = realpathSync(target);",
|
|
26222
|
+
"} catch {",
|
|
26223
|
+
" // keep the resolved path",
|
|
26224
|
+
"}",
|
|
26225
|
+
"if (entry) process.argv.splice(2, 1);",
|
|
26226
|
+
"process.argv[1] = target;",
|
|
26227
|
+
"try {",
|
|
26228
|
+
" await import(pathToFileURL(target).href);",
|
|
26191
26229
|
"} catch (err) {",
|
|
26192
|
-
" process.stderr.write('[heyspex] hook ' + (process.argv[2] ?? '?') + ' skipped: ' + (err && err.message ? err.message : err) + '\\n');",
|
|
26230
|
+
" process.stderr.write('[heyspex] ' + (entry ? mode : 'hook ' + (process.argv[2] ?? '?')) + ' skipped: ' + (err && err.message ? err.message : err) + '\\n');",
|
|
26231
|
+
" // Hooks and the status line fail open; the MCP server fails loudly.",
|
|
26232
|
+
" if (mode === '--mcp') process.exit(1);",
|
|
26193
26233
|
" process.exit(0);",
|
|
26194
26234
|
"}",
|
|
26195
26235
|
""
|
|
26196
26236
|
].join("\n");
|
|
26237
|
+
var LAUNCHER_YIELD_MARKER = "// yields-to-plugin: v1";
|
|
26238
|
+
var LAUNCHER_YIELD_BLOCK = [
|
|
26239
|
+
LAUNCHER_YIELD_MARKER + " -- one source of hooks: when the heyspex plugin is enabled on this machine",
|
|
26240
|
+
"// its own launcher (plugins/heyspex/bin/run-hook.mjs, which never calls this file) already runs every",
|
|
26241
|
+
"// hook, so this committed row exits 0 silently before resolving anything and each hook fires ONCE.",
|
|
26242
|
+
"// HEYSPEX_REPO_HOOKS=1 never yield (the repo rows are the chosen source);",
|
|
26243
|
+
`// enabledPlugins["heyspex@..."] === false in ~/.claude/settings.json or this repo's .claude/`,
|
|
26244
|
+
"// settings[.local].json: plugin off here, the row runs;",
|
|
26245
|
+
"// plugin evidence (what `hs doctor` hook-double-fire reads): a heyspex@... key in",
|
|
26246
|
+
"// ~/.claude/plugins/installed_plugins.json, enabledPlugins naming heyspex with a non-false",
|
|
26247
|
+
"// value, or a heyspex directory under ~/.copilot/installed-plugins | ~/.copilot/plugins.",
|
|
26248
|
+
"function readJson(file) {",
|
|
26249
|
+
" try { return JSON.parse(readFileSync(file, 'utf8')); } catch { return undefined; }",
|
|
26250
|
+
"}",
|
|
26251
|
+
"function listDir(dir) {",
|
|
26252
|
+
" try { return readdirSync(dir); } catch { return []; }",
|
|
26253
|
+
"}",
|
|
26254
|
+
"function pluginTakesThisHook() {",
|
|
26255
|
+
" const flag = text(env.HEYSPEX_REPO_HOOKS);",
|
|
26256
|
+
" if (flag && flag !== '0' && flag.toLowerCase() !== 'false') return false;",
|
|
26257
|
+
" const home = homedir();",
|
|
26258
|
+
" const repo = resolve(here, '..', '..');",
|
|
26259
|
+
" const isHeyspex = (k) => /^heyspex(@|$)/i.test(k);",
|
|
26260
|
+
" let enabled = false;",
|
|
26261
|
+
" for (const f of [resolve(home, '.claude', 'settings.json'), resolve(repo, '.claude', 'settings.json'), resolve(repo, '.claude', 'settings.local.json')]) {",
|
|
26262
|
+
" const settings = readJson(f);",
|
|
26263
|
+
" const ep = settings && typeof settings === 'object' ? settings.enabledPlugins : null;",
|
|
26264
|
+
" if (!ep || typeof ep !== 'object') continue;",
|
|
26265
|
+
" for (const [k, v] of Object.entries(ep)) {",
|
|
26266
|
+
" if (!isHeyspex(k)) continue;",
|
|
26267
|
+
" if (v === false) return false;",
|
|
26268
|
+
" enabled = true;",
|
|
26269
|
+
" }",
|
|
26270
|
+
" }",
|
|
26271
|
+
" if (enabled) return true;",
|
|
26272
|
+
" const installed = readJson(resolve(home, '.claude', 'plugins', 'installed_plugins.json'));",
|
|
26273
|
+
" if (installed && typeof installed === 'object' && Object.keys(installed.plugins && typeof installed.plugins === 'object' ? installed.plugins : installed).some(isHeyspex)) return true;",
|
|
26274
|
+
" for (const sub of ['installed-plugins', 'plugins']) {",
|
|
26275
|
+
" const dir = resolve(home, '.copilot', sub);",
|
|
26276
|
+
" const names = listDir(dir);",
|
|
26277
|
+
" if (names.some((n) => /heyspex/i.test(n))) return true;",
|
|
26278
|
+
" if (names.some((n) => listDir(resolve(dir, n)).some((m) => /heyspex/i.test(m)))) return true;",
|
|
26279
|
+
" }",
|
|
26280
|
+
" return false;",
|
|
26281
|
+
"}",
|
|
26282
|
+
"// Only a HOOK EVENT yields: --mcp / --statusline / --resolve and an agent-run",
|
|
26283
|
+
"// `<script> --directive <key>` fetch are not hook events the plugin could run in",
|
|
26284
|
+
"// our place, so they always go on to the package below.",
|
|
26285
|
+
"const yieldMode = process.argv[2];",
|
|
26286
|
+
"const notAHookEvent = ['--mcp', '--statusline', '--resolve'].includes(yieldMode) || process.argv.includes('--directive');",
|
|
26287
|
+
"if (!notAHookEvent && pluginTakesThisHook()) process.exit(0);",
|
|
26288
|
+
"",
|
|
26289
|
+
""
|
|
26290
|
+
].join("\n");
|
|
26197
26291
|
function renderCustomerLauncher(surface = "claude") {
|
|
26198
26292
|
const l = CUSTOMER_LAUNCHERS[surface];
|
|
26199
|
-
return LAUNCHER_TEMPLATE.replace(
|
|
26293
|
+
return LAUNCHER_TEMPLATE.replace("__YIELD__\n", surface === "claude" ? LAUNCHER_YIELD_BLOCK : "").replace(
|
|
26200
26294
|
/__(REGISTRY|ENV|LAUNCHER|SHIM)__/g,
|
|
26201
26295
|
(_m, token) => token === "REGISTRY" ? l.registry : token === "ENV" ? l.projectDirEnv : token === "LAUNCHER" ? l.rel.join("/") : l.shim
|
|
26202
26296
|
);
|
|
@@ -26422,25 +26516,33 @@ var MARKER_RE = /\n<!-- heyspex-kit: managed by hs install \(sha256:([0-9a-f]{16
|
|
|
26422
26516
|
function shortHash(text2) {
|
|
26423
26517
|
return createHash("sha256").update(text2, "utf8").digest("hex").slice(0, 16);
|
|
26424
26518
|
}
|
|
26519
|
+
function normalizeEol(text2) {
|
|
26520
|
+
return text2 === null || text2 === void 0 ? text2 : String(text2).replace(/\r\n/g, "\n");
|
|
26521
|
+
}
|
|
26425
26522
|
function renderKitCommand(content, platformRoot) {
|
|
26426
|
-
const body = `${rewritePlatformPaths(content, platformRoot).replace(/\s+$/, "")}
|
|
26523
|
+
const body = `${rewritePlatformPaths(normalizeEol(content), platformRoot).replace(/\s+$/, "")}
|
|
26427
26524
|
`;
|
|
26428
26525
|
return `${body}
|
|
26429
26526
|
<!-- heyspex-kit: managed by hs install (sha256:${shortHash(body)}). Edit freely: once edited, hs install leaves this file alone. -->
|
|
26430
26527
|
`;
|
|
26431
26528
|
}
|
|
26432
26529
|
function kitFileState(content) {
|
|
26433
|
-
const
|
|
26530
|
+
const text2 = normalizeEol(String(content));
|
|
26531
|
+
const m = text2.match(MARKER_RE);
|
|
26434
26532
|
if (!m) return "unmanaged";
|
|
26435
|
-
const body =
|
|
26436
|
-
|
|
26437
|
-
|
|
26533
|
+
const body = `${text2.slice(0, m.index).replace(/\n$/, "")}
|
|
26534
|
+
`;
|
|
26535
|
+
if (shortHash(body) === m[1]) return "managed";
|
|
26536
|
+
if (shortHash(`${body.slice(0, -1).replace(/\n/g, "\r\n")}
|
|
26537
|
+
`) === m[1]) return "managed";
|
|
26538
|
+
return "customized";
|
|
26438
26539
|
}
|
|
26439
26540
|
function planKitFileWrite({ existing, rendered, raw, force }) {
|
|
26440
26541
|
if (existing === null || existing === void 0) return { write: true, reason: "missing" };
|
|
26441
|
-
|
|
26442
|
-
if (
|
|
26443
|
-
if (
|
|
26542
|
+
const have = normalizeEol(existing);
|
|
26543
|
+
if (have === normalizeEol(rendered)) return { write: false, reason: "unchanged" };
|
|
26544
|
+
if (kitFileState(have) === "managed") return { write: true, reason: "update" };
|
|
26545
|
+
if (have === normalizeEol(raw)) return { write: true, reason: "legacy-copy" };
|
|
26444
26546
|
return force ? { write: true, reason: "forced" } : { write: false, reason: "customized" };
|
|
26445
26547
|
}
|
|
26446
26548
|
function listKitCommandFiles(platformRoot, deps, opts = {}) {
|
|
@@ -26537,6 +26639,11 @@ import { join as join6 } from "node:path";
|
|
|
26537
26639
|
init_credentials();
|
|
26538
26640
|
var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
|
|
26539
26641
|
var LOCAL_CONSOLE_URL2 = "http://localhost:3000";
|
|
26642
|
+
function overrideCloudCheck2(source, cloudKnown, staleAgainstCloud) {
|
|
26643
|
+
if (source !== "local-override") return "not-applicable";
|
|
26644
|
+
if (!cloudKnown) return "unverified";
|
|
26645
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
26646
|
+
}
|
|
26540
26647
|
function normalizeMode2(raw) {
|
|
26541
26648
|
const v = String(raw ?? "").trim().toLowerCase();
|
|
26542
26649
|
if (v === "prod" || v === "production") return { mode: "prod", reliance: "cloud" };
|
|
@@ -26644,6 +26751,7 @@ function resolveDevMode2({
|
|
|
26644
26751
|
const semantics = deriveSemantics2(mode, reliance);
|
|
26645
26752
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
26646
26753
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
26754
|
+
const cloudCheck = overrideCloudCheck2(source, cloudKnown, staleAgainstCloud);
|
|
26647
26755
|
return {
|
|
26648
26756
|
mode,
|
|
26649
26757
|
reliance: semantics.reliance,
|
|
@@ -26661,7 +26769,8 @@ function resolveDevMode2({
|
|
|
26661
26769
|
present: local.record !== null,
|
|
26662
26770
|
record: local.record,
|
|
26663
26771
|
parseError: local.parseError,
|
|
26664
|
-
staleAgainstCloud
|
|
26772
|
+
staleAgainstCloud,
|
|
26773
|
+
cloudCheck
|
|
26665
26774
|
},
|
|
26666
26775
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
26667
26776
|
};
|
|
@@ -26697,7 +26806,7 @@ init_credentials();
|
|
|
26697
26806
|
import { randomUUID } from "node:crypto";
|
|
26698
26807
|
|
|
26699
26808
|
// spex-console/src/lib/cli/control-shared.ts
|
|
26700
|
-
import
|
|
26809
|
+
import path4 from "node:path";
|
|
26701
26810
|
|
|
26702
26811
|
// scripts/lib/hs-command.mjs
|
|
26703
26812
|
import { existsSync as existsSync2 } from "node:fs";
|
|
@@ -26720,8 +26829,8 @@ var HS_DISPATCHER_PREFIX = HS_DISPATCHER_PATH ? `node ${shellQuote(HS_DISPATCHER
|
|
|
26720
26829
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
26721
26830
|
init_heyspex_home();
|
|
26722
26831
|
init_credentials();
|
|
26723
|
-
import { readFileSync as
|
|
26724
|
-
import { homedir as
|
|
26832
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
26833
|
+
import { homedir as homedir9 } from "node:os";
|
|
26725
26834
|
import { join as join10 } from "node:path";
|
|
26726
26835
|
|
|
26727
26836
|
// vendor/heyspex-mcp/vault-less-endpoints.mjs
|
|
@@ -26734,14 +26843,14 @@ var VAULT_LESS_ENDPOINTS = Object.freeze([
|
|
|
26734
26843
|
"GET /api/workspaces",
|
|
26735
26844
|
"* /api/admin/dev-mode"
|
|
26736
26845
|
]);
|
|
26737
|
-
function pathnameOf(
|
|
26738
|
-
const raw = String(
|
|
26846
|
+
function pathnameOf(path11) {
|
|
26847
|
+
const raw = String(path11 ?? "").split("?")[0].split("#")[0];
|
|
26739
26848
|
const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
|
|
26740
26849
|
return noTrailing || "/";
|
|
26741
26850
|
}
|
|
26742
|
-
function isVaultLessEndpoint(method,
|
|
26851
|
+
function isVaultLessEndpoint(method, path11) {
|
|
26743
26852
|
const m = String(method || "GET").toUpperCase();
|
|
26744
|
-
const pathname = pathnameOf(
|
|
26853
|
+
const pathname = pathnameOf(path11);
|
|
26745
26854
|
return VAULT_LESS_ENDPOINTS.some((row2) => {
|
|
26746
26855
|
const [rowMethod, rowPath] = row2.split(" ");
|
|
26747
26856
|
return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
|
|
@@ -26761,7 +26870,9 @@ import {
|
|
|
26761
26870
|
import { basename, join as join8, isAbsolute as isAbsolute2 } from "node:path";
|
|
26762
26871
|
|
|
26763
26872
|
// scripts/lib/shared-path-helpers.mjs
|
|
26873
|
+
import { realpathSync } from "node:fs";
|
|
26764
26874
|
import { isAbsolute, resolve as resolve3, sep } from "node:path";
|
|
26875
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
26765
26876
|
function resolveCodeRoot({
|
|
26766
26877
|
explicit,
|
|
26767
26878
|
packageRoot,
|
|
@@ -26780,9 +26891,30 @@ function resolveCodeRoot({
|
|
|
26780
26891
|
|
|
26781
26892
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
26782
26893
|
init_heyspex_home();
|
|
26783
|
-
import { existsSync as
|
|
26784
|
-
import { homedir as
|
|
26785
|
-
|
|
26894
|
+
import { existsSync as existsSync5, readFileSync as readFileSync10, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
26895
|
+
import { homedir as homedir8, tmpdir } from "node:os";
|
|
26896
|
+
|
|
26897
|
+
// vendor/heyspex-mcp/usable-dir-env.mjs
|
|
26898
|
+
import { statSync } from "node:fs";
|
|
26899
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
26900
|
+
|
|
26901
|
+
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
26902
|
+
import { basename as basename2, dirname as dirname4, join as join9, resolve as resolve4 } from "node:path";
|
|
26903
|
+
|
|
26904
|
+
// vendor/heyspex-mcp/vault-binding.mjs
|
|
26905
|
+
import {
|
|
26906
|
+
copyFileSync,
|
|
26907
|
+
existsSync as existsSync4,
|
|
26908
|
+
mkdirSync as mkdirSync6,
|
|
26909
|
+
readFileSync as readFileSync9,
|
|
26910
|
+
realpathSync as realpathSync2,
|
|
26911
|
+
renameSync as renameSync4,
|
|
26912
|
+
statSync as statSync2,
|
|
26913
|
+
unlinkSync,
|
|
26914
|
+
writeFileSync as writeFileSync6
|
|
26915
|
+
} from "node:fs";
|
|
26916
|
+
import { homedir as homedir7 } from "node:os";
|
|
26917
|
+
import path3 from "node:path";
|
|
26786
26918
|
|
|
26787
26919
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
26788
26920
|
var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMATCH", "VAULT_NOT_REGISTERED"]);
|
|
@@ -26884,11 +27016,11 @@ function readPairedBearer(opts = {}) {
|
|
|
26884
27016
|
function readPairedToken(opts = {}) {
|
|
26885
27017
|
return readPairedBearer(opts).token ?? void 0;
|
|
26886
27018
|
}
|
|
26887
|
-
function scopeHeadersFor(scope, method,
|
|
27019
|
+
function scopeHeadersFor(scope, method, path11) {
|
|
26888
27020
|
if (scope === "none") {
|
|
26889
|
-
if (!isVaultLessEndpoint(method,
|
|
27021
|
+
if (!isVaultLessEndpoint(method, path11)) {
|
|
26890
27022
|
throw new Error(
|
|
26891
|
-
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${
|
|
27023
|
+
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path11} is vault-scoped and must name { accountId, vaultId }.`
|
|
26892
27024
|
);
|
|
26893
27025
|
}
|
|
26894
27026
|
return {};
|
|
@@ -26899,7 +27031,7 @@ function cloudRequestScope(scope) {
|
|
|
26899
27031
|
const named = { accountId: scope.accountId, vaultId: scope.vaultId };
|
|
26900
27032
|
return {
|
|
26901
27033
|
...named,
|
|
26902
|
-
headersFor: (method,
|
|
27034
|
+
headersFor: (method, path11) => isVaultLessEndpoint(method, path11) ? {} : scopeHeadersFor(named, method, path11)
|
|
26903
27035
|
};
|
|
26904
27036
|
}
|
|
26905
27037
|
function withCloudScope(deps, accountId, vaultId) {
|
|
@@ -27146,12 +27278,15 @@ var HELP = [
|
|
|
27146
27278
|
"(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
|
|
27147
27279
|
"copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
|
|
27148
27280
|
"plus a generated CLAUDE.md, the kept-principles index, and the template_version",
|
|
27149
|
-
"pin; with --git, one initial `git init` + commit.
|
|
27150
|
-
"holds heyspex.yaml
|
|
27151
|
-
"
|
|
27152
|
-
"
|
|
27153
|
-
"
|
|
27154
|
-
"
|
|
27281
|
+
"pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
|
|
27282
|
+
"holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
|
|
27283
|
+
"vault-template row names: only missing shipped files are added, template_version",
|
|
27284
|
+
"is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
|
|
27285
|
+
"nothing existing is ever overwritten (an existing CLAUDE.md gets a",
|
|
27286
|
+
"CLAUDE.md.template beside it), the result lists each manifest row as created or",
|
|
27287
|
+
"kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
|
|
27288
|
+
"names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
|
|
27289
|
+
"non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
|
|
27155
27290
|
"",
|
|
27156
27291
|
"Flags:",
|
|
27157
27292
|
" --console-url <url> console_url value (default: the mode-aware console --",
|
|
@@ -27415,9 +27550,9 @@ function detectMarkerAttachment(repoRoot, deps) {
|
|
|
27415
27550
|
function readPinnedVaultRoot(repoRoot, deps) {
|
|
27416
27551
|
const pins = [];
|
|
27417
27552
|
for (const rel of SATELLITE_MANIFEST_RELS) {
|
|
27418
|
-
const
|
|
27419
|
-
if (!deps.fileExists(
|
|
27420
|
-
const raw = deps.readFile(
|
|
27553
|
+
const path11 = deps.joinPath(repoRoot, ...rel);
|
|
27554
|
+
if (!deps.fileExists(path11)) continue;
|
|
27555
|
+
const raw = deps.readFile(path11);
|
|
27421
27556
|
if (!raw) continue;
|
|
27422
27557
|
let value;
|
|
27423
27558
|
try {
|
|
@@ -27487,7 +27622,7 @@ function checkHarnessCommandDrift(repoRoot, deps) {
|
|
|
27487
27622
|
const installedContent = deps.fileExists(installedPath) ? deps.readFile(installedPath) : null;
|
|
27488
27623
|
if (installedContent === null) {
|
|
27489
27624
|
out.missingFiles.push(`${label}/${f.file}`);
|
|
27490
|
-
} else if (installedContent !== f.content) {
|
|
27625
|
+
} else if (normalizeEol(installedContent) !== normalizeEol(f.content)) {
|
|
27491
27626
|
out.staleFiles.push(`${label}/${f.file}`);
|
|
27492
27627
|
}
|
|
27493
27628
|
}
|
|
@@ -27507,8 +27642,8 @@ function row(id, label, present, detail) {
|
|
|
27507
27642
|
...detail ? { detail } : {}
|
|
27508
27643
|
};
|
|
27509
27644
|
}
|
|
27510
|
-
function readIfExists(deps,
|
|
27511
|
-
return deps.fileExists(
|
|
27645
|
+
function readIfExists(deps, path11) {
|
|
27646
|
+
return deps.fileExists(path11) ? deps.readFile(path11) : null;
|
|
27512
27647
|
}
|
|
27513
27648
|
var PRE_COMMIT_HOOK_MARKER = "run-pre-commit-checks.mjs";
|
|
27514
27649
|
function hasHeyspexMcpEntry(raw) {
|
|
@@ -27818,12 +27953,12 @@ function runConnectChecks(repoRoot, deps, opts = {}) {
|
|
|
27818
27953
|
};
|
|
27819
27954
|
}
|
|
27820
27955
|
var CLOUD_READ_TIMEOUT_MS = 8e3;
|
|
27821
|
-
async function cloudGet(http, token,
|
|
27956
|
+
async function cloudGet(http, token, path11) {
|
|
27822
27957
|
const controller = new AbortController();
|
|
27823
27958
|
const timer = setTimeout(() => controller.abort(), CLOUD_READ_TIMEOUT_MS);
|
|
27824
27959
|
timer.unref?.();
|
|
27825
27960
|
try {
|
|
27826
|
-
const res = await http.fetch(`${http.consoleUrl.replace(/\/+$/, "")}${
|
|
27961
|
+
const res = await http.fetch(`${http.consoleUrl.replace(/\/+$/, "")}${path11}`, {
|
|
27827
27962
|
method: "GET",
|
|
27828
27963
|
headers: token ? { authorization: `Bearer ${token}` } : {},
|
|
27829
27964
|
signal: controller.signal
|
|
@@ -27841,7 +27976,7 @@ async function cloudGet(http, token, path9) {
|
|
|
27841
27976
|
clearTimeout(timer);
|
|
27842
27977
|
}
|
|
27843
27978
|
}
|
|
27844
|
-
function readFailure(read2,
|
|
27979
|
+
function readFailure(read2, path11, http) {
|
|
27845
27980
|
if (read2 === null) {
|
|
27846
27981
|
return {
|
|
27847
27982
|
cause: "no sign-in token on this machine, so the console was not asked",
|
|
@@ -27857,9 +27992,9 @@ function readFailure(read2, path9, http) {
|
|
|
27857
27992
|
};
|
|
27858
27993
|
}
|
|
27859
27994
|
if (read2.status === 401) {
|
|
27860
|
-
return { cause: `GET ${
|
|
27995
|
+
return { cause: `GET ${path11} answered HTTP 401 (the sign-in was refused)`, repair: "hs login" };
|
|
27861
27996
|
}
|
|
27862
|
-
return { cause: `GET ${
|
|
27997
|
+
return { cause: `GET ${path11} answered HTTP ${read2.status}`, repair: retry };
|
|
27863
27998
|
}
|
|
27864
27999
|
var str2 = (v) => typeof v === "string" && v ? v : null;
|
|
27865
28000
|
function cloudRow(id, fields) {
|
|
@@ -28047,7 +28182,13 @@ async function defaultCheckMcpBuild(deps, opts) {
|
|
|
28047
28182
|
}
|
|
28048
28183
|
}
|
|
28049
28184
|
var shortFp = (fp) => fp ? fp.slice(0, 12) : null;
|
|
28050
|
-
function hookScriptPath(command, repoRoot) {
|
|
28185
|
+
function hookScriptPath(command, repoRoot, joinPath) {
|
|
28186
|
+
const bootstrap = /^\s*node\s+-e\s+"[^"]*\/\/([^"\s]+)"/.exec(command);
|
|
28187
|
+
if (bootstrap) {
|
|
28188
|
+
const rel = bootstrap[1].replace(/^\/+/, "");
|
|
28189
|
+
if (rel === "bin/run-hook.mjs") return null;
|
|
28190
|
+
return joinPath(repoRoot, ...rel.split("/"));
|
|
28191
|
+
}
|
|
28051
28192
|
const m = /^\s*node\s+(?:"([^"]+)"|(\S+))/.exec(command);
|
|
28052
28193
|
const raw = m?.[1] ?? m?.[2];
|
|
28053
28194
|
if (!raw) return null;
|
|
@@ -28081,7 +28222,7 @@ function hooksRow(repoRoot, deps) {
|
|
|
28081
28222
|
const list = Array.isArray(group2?.hooks) ? group2.hooks : [];
|
|
28082
28223
|
for (const h of list) {
|
|
28083
28224
|
if (typeof h?.command !== "string" || ourHookTail(h.command) === null) continue;
|
|
28084
|
-
const script = hookScriptPath(h.command, repoRoot);
|
|
28225
|
+
const script = hookScriptPath(h.command, repoRoot, deps.joinPath);
|
|
28085
28226
|
if (script) scripts.add(script);
|
|
28086
28227
|
}
|
|
28087
28228
|
}
|
|
@@ -28121,7 +28262,7 @@ async function mcpBuildRow(vaultSubject, deps) {
|
|
|
28121
28262
|
);
|
|
28122
28263
|
}
|
|
28123
28264
|
async function readCloudView(http, tokens) {
|
|
28124
|
-
const get2 = (token,
|
|
28265
|
+
const get2 = (token, path11) => http && token ? cloudGet(http, token, path11) : Promise.resolve(null);
|
|
28125
28266
|
const [session, vaults2, workspaces2, devMode] = await Promise.all([
|
|
28126
28267
|
get2(tokens.current, "/api/mcp/session"),
|
|
28127
28268
|
get2(tokens.repo, "/api/vaults?include_archived=true"),
|
|
@@ -28132,9 +28273,9 @@ async function readCloudView(http, tokens) {
|
|
|
28132
28273
|
cause: "no console URL configured for this machine",
|
|
28133
28274
|
repair: "hs login"
|
|
28134
28275
|
};
|
|
28135
|
-
const fail = (read2,
|
|
28136
|
-
const shapeFailure = (
|
|
28137
|
-
cause: `GET ${
|
|
28276
|
+
const fail = (read2, path11) => http ? readFailure(read2, path11, http) : noConsole;
|
|
28277
|
+
const shapeFailure = (path11, what) => ({
|
|
28278
|
+
cause: `GET ${path11} answered without ${what}`,
|
|
28138
28279
|
repair: `retry \`hs connect --check\` once ${http?.consoleUrl ?? "the console"} answers`
|
|
28139
28280
|
});
|
|
28140
28281
|
const sessionBody = session?.ok ? session.json : null;
|
|
@@ -28442,7 +28583,7 @@ function writeProjectContract(ctx, options2 = {}) {
|
|
|
28442
28583
|
}
|
|
28443
28584
|
|
|
28444
28585
|
// spex-console/src/lib/cli/init.customer-vault.ts
|
|
28445
|
-
import { basename as
|
|
28586
|
+
import { basename as basename3 } from "node:path";
|
|
28446
28587
|
|
|
28447
28588
|
// spex-console/src/lib/cli/template-manifest.sources.mjs
|
|
28448
28589
|
var UNIVERSAL_TEMPLATE_ROWS = [
|
|
@@ -28927,41 +29068,42 @@ var SCAFFOLD_FOLDERS = [
|
|
|
28927
29068
|
"checks",
|
|
28928
29069
|
"events"
|
|
28929
29070
|
];
|
|
28930
|
-
|
|
28931
|
-
|
|
28932
|
-
if (deps.isDirectory) return deps.isDirectory(path9);
|
|
28933
|
-
try {
|
|
28934
|
-
const kids = deps.readdir(path9);
|
|
28935
|
-
return kids.length > 0 || !name.includes(".");
|
|
28936
|
-
} catch {
|
|
28937
|
-
return false;
|
|
28938
|
-
}
|
|
29071
|
+
function defaultVaultName(targetDir) {
|
|
29072
|
+
return basename3(targetDir) || targetDir;
|
|
28939
29073
|
}
|
|
28940
|
-
function
|
|
28941
|
-
const
|
|
28942
|
-
|
|
28943
|
-
|
|
28944
|
-
|
|
28945
|
-
|
|
28946
|
-
if (isDir(deps, childAbs, name)) walk(childAbs, childRel);
|
|
28947
|
-
else if (!PLACEHOLDER_FILES.has(name) && name.endsWith(".md") && !manifestDests.has(childRel))
|
|
28948
|
-
out.push(childRel);
|
|
28949
|
-
}
|
|
28950
|
-
};
|
|
28951
|
-
const specsRoot = deps.joinPath(targetDir, "specs");
|
|
28952
|
-
if (deps.exists(specsRoot)) walk(specsRoot, "specs");
|
|
28953
|
-
return out;
|
|
29074
|
+
function readTemplateVersionPin(yamlText) {
|
|
29075
|
+
const m = yamlText.match(/^template_version:[ \t]*(.*?)[ \t]*$/m);
|
|
29076
|
+
if (!m) return null;
|
|
29077
|
+
const raw = m[1].trim();
|
|
29078
|
+
const unquoted = raw.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
|
|
29079
|
+
return unquoted.length > 0 ? unquoted : null;
|
|
28954
29080
|
}
|
|
28955
|
-
function
|
|
28956
|
-
|
|
29081
|
+
function stampTemplateVersion(content, version) {
|
|
29082
|
+
if (readTemplateVersionPin(content) === version) return content;
|
|
29083
|
+
const eol = content.includes("\r\n") ? "\r\n" : "\n";
|
|
29084
|
+
const line = `template_version: ${version}`;
|
|
29085
|
+
if (/^template_version:[^\r\n]*$/m.test(content)) {
|
|
29086
|
+
return content.replace(/^template_version:[^\r\n]*$/m, line);
|
|
29087
|
+
}
|
|
29088
|
+
const body = content.replace(/(?:\r?\n)+$/, "");
|
|
29089
|
+
return body.length === 0 ? `${line}${eol}` : `${body}${eol}${line}${eol}`;
|
|
29090
|
+
}
|
|
29091
|
+
function parentDirOf(deps, targetDir, dest) {
|
|
29092
|
+
const segments = dest.split("/");
|
|
29093
|
+
if (segments.length < 2) return null;
|
|
29094
|
+
return deps.joinPath(targetDir, ...segments.slice(0, -1));
|
|
28957
29095
|
}
|
|
28958
29096
|
function runCustomerVaultInit(args, deps, options2 = {}) {
|
|
28959
29097
|
const { targetDir } = args;
|
|
28960
29098
|
const written = [];
|
|
28961
29099
|
const filter = options2.manifestFilter ?? (() => true);
|
|
28962
29100
|
const rows = TEMPLATE_MANIFEST.filter(filter);
|
|
29101
|
+
const heyspexYamlPath = deps.joinPath(targetDir, "heyspex.yaml");
|
|
29102
|
+
const markerPresent = deps.exists(targetDir) && deps.exists(heyspexYamlPath);
|
|
29103
|
+
const existingYaml = markerPresent ? deps.readFile?.(heyspexYamlPath) ?? null : null;
|
|
29104
|
+
const existingVaultId = existingYaml ? parseVaultId(existingYaml) : null;
|
|
29105
|
+
const mode = markerPresent && existingVaultId ? "refresh" : "scaffold";
|
|
28963
29106
|
if (deps.exists(targetDir)) {
|
|
28964
|
-
const markerPresent = deps.exists(deps.joinPath(targetDir, "heyspex.yaml"));
|
|
28965
29107
|
if (!markerPresent && deps.readdir(targetDir).length > 0) {
|
|
28966
29108
|
return {
|
|
28967
29109
|
code: EXIT.INVALID_ARG,
|
|
@@ -28971,43 +29113,43 @@ function runCustomerVaultInit(args, deps, options2 = {}) {
|
|
|
28971
29113
|
written: []
|
|
28972
29114
|
};
|
|
28973
29115
|
}
|
|
28974
|
-
if (markerPresent) {
|
|
28975
|
-
const manifestDests = new Set(TEMPLATE_MANIFEST.map((r) => r.dest));
|
|
28976
|
-
const managed = findManagedSpecs(deps, targetDir, manifestDests);
|
|
28977
|
-
if (managed.length > 0) {
|
|
28978
|
-
return {
|
|
28979
|
-
code: EXIT.INVALID_ARG,
|
|
28980
|
-
message: `${targetDir}/specs already holds managed spec(s) (${managed.slice(0, 3).join(", ")}${managed.length > 3 ? ", ..." : ""}) \u2014 customer-vault init refuses to write over an existing vault.`,
|
|
28981
|
-
payload: { ok: false, error: "specs_not_empty", path: targetDir, specs: managed },
|
|
28982
|
-
isError: true,
|
|
28983
|
-
written: []
|
|
28984
|
-
};
|
|
28985
|
-
}
|
|
28986
|
-
}
|
|
28987
29116
|
} else {
|
|
28988
29117
|
deps.mkdirRecursive(targetDir);
|
|
28989
29118
|
}
|
|
28990
|
-
|
|
28991
|
-
|
|
28992
|
-
|
|
28993
|
-
|
|
29119
|
+
if (mode === "scaffold") {
|
|
29120
|
+
const specsRoot = deps.joinPath(targetDir, "specs");
|
|
29121
|
+
deps.mkdirRecursive(specsRoot);
|
|
29122
|
+
for (const folder of SCAFFOLD_FOLDERS) {
|
|
29123
|
+
deps.mkdirRecursive(deps.joinPath(specsRoot, folder));
|
|
29124
|
+
}
|
|
28994
29125
|
}
|
|
28995
|
-
const heyspexYamlPath = deps.joinPath(targetDir, "heyspex.yaml");
|
|
28996
29126
|
let vaultId;
|
|
28997
|
-
|
|
28998
|
-
|
|
28999
|
-
vaultId =
|
|
29127
|
+
let pin;
|
|
29128
|
+
if (markerPresent) {
|
|
29129
|
+
vaultId = existingVaultId ?? "";
|
|
29130
|
+
const stamped = existingYaml === null ? null : stampTemplateVersion(existingYaml, TEMPLATE_VERSION);
|
|
29131
|
+
if (stamped !== null && stamped !== existingYaml) {
|
|
29132
|
+
deps.writeFile(heyspexYamlPath, stamped);
|
|
29133
|
+
written.push(heyspexYamlPath);
|
|
29134
|
+
pin = "written";
|
|
29135
|
+
} else {
|
|
29136
|
+
pin = "kept";
|
|
29137
|
+
}
|
|
29000
29138
|
} else {
|
|
29001
29139
|
vaultId = mintVaultId();
|
|
29002
29140
|
deps.writeFile(
|
|
29003
29141
|
heyspexYamlPath,
|
|
29004
|
-
|
|
29005
|
-
|
|
29006
|
-
|
|
29007
|
-
|
|
29008
|
-
|
|
29142
|
+
stampTemplateVersion(
|
|
29143
|
+
renderHeyspexYaml({
|
|
29144
|
+
consoleUrl: options2.consoleUrl ?? DEFAULT_CONSOLE_URL2,
|
|
29145
|
+
vaultRoot: "specs/",
|
|
29146
|
+
vaultId
|
|
29147
|
+
}),
|
|
29148
|
+
TEMPLATE_VERSION
|
|
29149
|
+
)
|
|
29009
29150
|
);
|
|
29010
29151
|
written.push(heyspexYamlPath);
|
|
29152
|
+
pin = "written";
|
|
29011
29153
|
}
|
|
29012
29154
|
const readConfigSeed = deps.readConfigSeed;
|
|
29013
29155
|
const configSeed = seedConfigYaml(
|
|
@@ -29023,6 +29165,10 @@ function runCustomerVaultInit(args, deps, options2 = {}) {
|
|
|
29023
29165
|
rowResults.push({ dest: row2.dest, action: "kept" });
|
|
29024
29166
|
continue;
|
|
29025
29167
|
}
|
|
29168
|
+
if (mode === "refresh") {
|
|
29169
|
+
const parent = parentDirOf(deps, targetDir, row2.dest);
|
|
29170
|
+
if (parent && !deps.exists(parent)) deps.mkdirRecursive(parent);
|
|
29171
|
+
}
|
|
29026
29172
|
if (row2.classification === "universal-template") {
|
|
29027
29173
|
const srcAbs = deps.joinPath(deps.platformRoot, ...row2.source.split("/"));
|
|
29028
29174
|
deps.copyFile(srcAbs, destAbs);
|
|
@@ -29042,24 +29188,41 @@ function runCustomerVaultInit(args, deps, options2 = {}) {
|
|
|
29042
29188
|
targetDir,
|
|
29043
29189
|
vaultName: options2.vaultName ?? defaultVaultName(targetDir),
|
|
29044
29190
|
templateVersion: TEMPLATE_VERSION,
|
|
29191
|
+
mode,
|
|
29045
29192
|
manifestRows: rows,
|
|
29046
29193
|
rows: rowResults,
|
|
29047
29194
|
deps,
|
|
29048
|
-
recordWrite: (
|
|
29049
|
-
written.push(
|
|
29195
|
+
recordWrite: (path11) => {
|
|
29196
|
+
written.push(path11);
|
|
29050
29197
|
}
|
|
29051
29198
|
};
|
|
29052
29199
|
for (const hook of options2.afterScaffold) hook(ctx);
|
|
29053
29200
|
}
|
|
29054
29201
|
const createdCount = rowResults.filter((r) => r.action === "created").length;
|
|
29055
29202
|
const keptCount = rowResults.length - createdCount;
|
|
29203
|
+
const added = rowResults.filter((r) => r.action === "created").map((r) => r.dest);
|
|
29204
|
+
let message2;
|
|
29205
|
+
if (mode === "refresh") {
|
|
29206
|
+
const parts = [];
|
|
29207
|
+
if (added.length > 0)
|
|
29208
|
+
parts.push(`added ${added.length} missing template file(s): ${added.join(", ")}`);
|
|
29209
|
+
if (pin === "written")
|
|
29210
|
+
parts.push(`pinned template_version: ${TEMPLATE_VERSION} in heyspex.yaml`);
|
|
29211
|
+
const hookWrites = written.length - added.length - (pin === "written" ? 1 : 0);
|
|
29212
|
+
if (hookWrites > 0) parts.push(`${hookWrites} other file(s) written`);
|
|
29213
|
+
message2 = parts.length > 0 ? `Refreshed customer vault at ${targetDir}: ${parts.join("; ")} (${keptCount} existing file(s) kept untouched).` : `Customer vault at ${targetDir} is already current (template v${TEMPLATE_VERSION}, ${keptCount} file(s) kept) \u2014 nothing written.`;
|
|
29214
|
+
} else {
|
|
29215
|
+
message2 = `Initialized customer vault at ${targetDir} (${written.length} files written, ${createdCount} manifest rows created, ${keptCount} kept, template v${TEMPLATE_VERSION}).`;
|
|
29216
|
+
}
|
|
29056
29217
|
return {
|
|
29057
29218
|
code: EXIT.OK,
|
|
29058
|
-
message:
|
|
29219
|
+
message: message2,
|
|
29059
29220
|
payload: {
|
|
29060
29221
|
ok: true,
|
|
29222
|
+
mode,
|
|
29061
29223
|
targetDir,
|
|
29062
29224
|
templateVersion: TEMPLATE_VERSION,
|
|
29225
|
+
templatePin: pin,
|
|
29063
29226
|
vaultId,
|
|
29064
29227
|
written,
|
|
29065
29228
|
rows: rowResults,
|
|
@@ -29101,14 +29264,14 @@ function templatePinHook() {
|
|
|
29101
29264
|
}
|
|
29102
29265
|
|
|
29103
29266
|
// spex-console/src/lib/cli/principle-selection.ts
|
|
29104
|
-
import { basename as
|
|
29267
|
+
import { basename as basename4 } from "node:path";
|
|
29105
29268
|
var PRINCIPLES_INDEX_DEST = "specs/principles-index.md";
|
|
29106
29269
|
function principleKey(row2) {
|
|
29107
|
-
const base =
|
|
29270
|
+
const base = basename4(row2.dest, ".md");
|
|
29108
29271
|
return base.startsWith("principle-") ? base.slice("principle-".length) : base;
|
|
29109
29272
|
}
|
|
29110
29273
|
function principleWikilink(row2) {
|
|
29111
|
-
return
|
|
29274
|
+
return basename4(row2.dest, ".md");
|
|
29112
29275
|
}
|
|
29113
29276
|
function parsePrinciplesFlag(value, rows) {
|
|
29114
29277
|
const principleRows = rows.filter((row2) => row2.group === "principle");
|
|
@@ -29174,13 +29337,13 @@ function applyCustomerTemplate(args) {
|
|
|
29174
29337
|
templateVersion: ctx.templateVersion
|
|
29175
29338
|
});
|
|
29176
29339
|
for (const file of contractFiles) {
|
|
29177
|
-
const
|
|
29178
|
-
if (force && ctx.deps.exists(
|
|
29179
|
-
if (ctx.deps.readFile?.(
|
|
29340
|
+
const path11 = ctx.deps.joinPath(ctx.targetDir, file);
|
|
29341
|
+
if (force && ctx.deps.exists(path11)) {
|
|
29342
|
+
if (ctx.deps.readFile?.(path11) === content) {
|
|
29180
29343
|
contracts.push({ file, action: "unchanged" });
|
|
29181
29344
|
} else {
|
|
29182
|
-
ctx.deps.writeFile(
|
|
29183
|
-
ctx.recordWrite(
|
|
29345
|
+
ctx.deps.writeFile(path11, content);
|
|
29346
|
+
ctx.recordWrite(path11);
|
|
29184
29347
|
contracts.push({ file, action: "overwritten" });
|
|
29185
29348
|
}
|
|
29186
29349
|
continue;
|
|
@@ -29219,12 +29382,17 @@ function applyCustomerTemplate(args) {
|
|
|
29219
29382
|
}
|
|
29220
29383
|
|
|
29221
29384
|
// spex-console/src/lib/cli/install.ts
|
|
29222
|
-
import { isAbsolute as
|
|
29385
|
+
import { isAbsolute as isAbsolute4, resolve as resolveNodePath2 } from "node:path";
|
|
29386
|
+
|
|
29387
|
+
// spex-console/src/lib/vault-resolve/index.ts
|
|
29388
|
+
import { existsSync as existsSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
29389
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
29390
|
+
import path5 from "node:path";
|
|
29223
29391
|
|
|
29224
29392
|
// scripts/install-cursor-hooks.mjs
|
|
29225
29393
|
import fs2 from "node:fs";
|
|
29226
|
-
import
|
|
29227
|
-
import { fileURLToPath as
|
|
29394
|
+
import path6 from "node:path";
|
|
29395
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
29228
29396
|
|
|
29229
29397
|
// scripts/cursor-hooks/manifest.mjs
|
|
29230
29398
|
var SHIM = "scripts/cursor-hooks/_run.mjs";
|
|
@@ -29368,9 +29536,9 @@ function timeoutFor(entry) {
|
|
|
29368
29536
|
}
|
|
29369
29537
|
|
|
29370
29538
|
// scripts/install-cursor-hooks.mjs
|
|
29371
|
-
var PLATFORM_ROOT =
|
|
29372
|
-
var REPO_ROOT = process.env.HS_SETUP_TARGET?.trim() ?
|
|
29373
|
-
var SETTINGS_PATH =
|
|
29539
|
+
var PLATFORM_ROOT = path6.resolve(path6.dirname(fileURLToPath4(import.meta.url)), "..");
|
|
29540
|
+
var REPO_ROOT = process.env.HS_SETUP_TARGET?.trim() ? path6.resolve(process.env.HS_SETUP_TARGET.trim()) : PLATFORM_ROOT;
|
|
29541
|
+
var SETTINGS_PATH = path6.join(REPO_ROOT, ".cursor", "hooks.json");
|
|
29374
29542
|
function cursorKitFor(targetRoot, platformRoot = PLATFORM_ROOT) {
|
|
29375
29543
|
const customer = !samePlatformPath(targetRoot, platformRoot);
|
|
29376
29544
|
return {
|
|
@@ -29382,7 +29550,7 @@ function cursorKitFor(targetRoot, platformRoot = PLATFORM_ROOT) {
|
|
|
29382
29550
|
}
|
|
29383
29551
|
var KIT = cursorKitFor(REPO_ROOT);
|
|
29384
29552
|
var ACTIVE_HOOKS = KIT.hooks;
|
|
29385
|
-
var LAUNCHER_PATH =
|
|
29553
|
+
var LAUNCHER_PATH = path6.join(REPO_ROOT, ...CUSTOMER_LAUNCHERS.cursor.rel);
|
|
29386
29554
|
var LEGACY_MARKER = "scripts/claude-hooks/";
|
|
29387
29555
|
function readSettings() {
|
|
29388
29556
|
try {
|
|
@@ -29401,7 +29569,7 @@ function readSettings() {
|
|
|
29401
29569
|
return { version: 1, hooks: {} };
|
|
29402
29570
|
}
|
|
29403
29571
|
function writeSettings(settings) {
|
|
29404
|
-
fs2.mkdirSync(
|
|
29572
|
+
fs2.mkdirSync(path6.dirname(SETTINGS_PATH), { recursive: true });
|
|
29405
29573
|
fs2.writeFileSync(SETTINGS_PATH, `${JSON.stringify(settings, null, 2)}
|
|
29406
29574
|
`, "utf8");
|
|
29407
29575
|
}
|
|
@@ -29436,7 +29604,7 @@ function sameEntry(raw, entry, render = KIT.commandFor) {
|
|
|
29436
29604
|
const want = desiredEntry(entry, render);
|
|
29437
29605
|
return commandOf(raw) === want.command && raw.timeout === want.timeout && (raw.matcher ?? void 0) === (want.matcher ?? void 0);
|
|
29438
29606
|
}
|
|
29439
|
-
var PLUGIN_HOOKS_PATH =
|
|
29607
|
+
var PLUGIN_HOOKS_PATH = path6.join(REPO_ROOT, "plugins", "heyspex", "hooks", "cursor-hooks.json");
|
|
29440
29608
|
function pluginHooksDocument() {
|
|
29441
29609
|
const hooks = {};
|
|
29442
29610
|
for (const entry of HOOKS) {
|
|
@@ -29466,7 +29634,7 @@ function syncPluginHooks() {
|
|
|
29466
29634
|
have = null;
|
|
29467
29635
|
}
|
|
29468
29636
|
if (have === want) return false;
|
|
29469
|
-
fs2.mkdirSync(
|
|
29637
|
+
fs2.mkdirSync(path6.dirname(PLUGIN_HOOKS_PATH), { recursive: true });
|
|
29470
29638
|
fs2.writeFileSync(PLUGIN_HOOKS_PATH, want, "utf8");
|
|
29471
29639
|
return true;
|
|
29472
29640
|
}
|
|
@@ -29539,7 +29707,7 @@ function install({ json = false } = {}) {
|
|
|
29539
29707
|
if (KIT.customer) {
|
|
29540
29708
|
const plan = launcherPlan();
|
|
29541
29709
|
if (plan.write) {
|
|
29542
|
-
fs2.mkdirSync(
|
|
29710
|
+
fs2.mkdirSync(path6.dirname(LAUNCHER_PATH), { recursive: true });
|
|
29543
29711
|
fs2.writeFileSync(LAUNCHER_PATH, plan.text, "utf8");
|
|
29544
29712
|
launcherWritten = true;
|
|
29545
29713
|
}
|
|
@@ -29566,10 +29734,10 @@ function install({ json = false } = {}) {
|
|
|
29566
29734
|
return;
|
|
29567
29735
|
}
|
|
29568
29736
|
if (pluginChanged) {
|
|
29569
|
-
console.log(`install-cursor-hooks: regenerated ${
|
|
29737
|
+
console.log(`install-cursor-hooks: regenerated ${path6.relative(REPO_ROOT, PLUGIN_HOOKS_PATH)}`);
|
|
29570
29738
|
}
|
|
29571
29739
|
if (launcherWritten) {
|
|
29572
|
-
console.log(`install-cursor-hooks: wrote ${
|
|
29740
|
+
console.log(`install-cursor-hooks: wrote ${path6.relative(REPO_ROOT, LAUNCHER_PATH)} (resolves node_modules/heyspex)`);
|
|
29573
29741
|
}
|
|
29574
29742
|
if (!settingsChanged) {
|
|
29575
29743
|
console.log(`install-cursor-hooks: already installed (${ACTIVE_HOOKS.length} hooks, no changes).`);
|
|
@@ -29580,7 +29748,7 @@ function install({ json = false } = {}) {
|
|
|
29580
29748
|
if (repaired.length) parts.push(`repaired ${repaired.length}`);
|
|
29581
29749
|
if (removed.length) parts.push(`removed ${removed.length}`);
|
|
29582
29750
|
console.log(
|
|
29583
|
-
`install-cursor-hooks: ${parts.join("; ")} \u2192 ${
|
|
29751
|
+
`install-cursor-hooks: ${parts.join("; ")} \u2192 ${path6.relative(REPO_ROOT, SETTINGS_PATH)} (${ACTIVE_HOOKS.length} hooks registered)`
|
|
29584
29752
|
);
|
|
29585
29753
|
for (const line of removed) console.log(` - ${line}`);
|
|
29586
29754
|
console.log(
|
|
@@ -29868,11 +30036,11 @@ function readLocalReceiptSide(subject, registryOpts = {}) {
|
|
|
29868
30036
|
return unreadSide(`registry.json could not be read: ${message(err)}`);
|
|
29869
30037
|
}
|
|
29870
30038
|
}
|
|
29871
|
-
async function readList(http,
|
|
29872
|
-
const res = await cloudHttpRequest(http, "GET",
|
|
30039
|
+
async function readList(http, path11, key) {
|
|
30040
|
+
const res = await cloudHttpRequest(http, "GET", path11);
|
|
29873
30041
|
const rows = res.json?.[key];
|
|
29874
30042
|
if (!res.ok || !Array.isArray(rows)) {
|
|
29875
|
-
return { reason: `GET ${
|
|
30043
|
+
return { reason: `GET ${path11.split("?")[0]} answered HTTP ${res.status}` };
|
|
29876
30044
|
}
|
|
29877
30045
|
return { rows: rows.filter((r) => Boolean(r) && typeof r === "object") };
|
|
29878
30046
|
}
|
|
@@ -29947,7 +30115,7 @@ async function readReceiptSides(subject, deps) {
|
|
|
29947
30115
|
var import_gray_matter = __toESM(require_gray_matter(), 1);
|
|
29948
30116
|
import { createHash as createHash2 } from "node:crypto";
|
|
29949
30117
|
import { promises as fs3 } from "node:fs";
|
|
29950
|
-
import
|
|
30118
|
+
import path7 from "node:path";
|
|
29951
30119
|
|
|
29952
30120
|
// spex-console/src/lib/vault-id.ts
|
|
29953
30121
|
var FILE_KEY_RE = /^(?=[0-9]*[A-Z])[A-Z0-9]{4,}$/;
|
|
@@ -30529,8 +30697,8 @@ async function indexTemplateToCloud(args) {
|
|
|
30529
30697
|
const res = await cloudHttpRequest(args.http, "GET", REINDEX_PATH);
|
|
30530
30698
|
return { status: res.status, json: res.json };
|
|
30531
30699
|
},
|
|
30532
|
-
post: async (
|
|
30533
|
-
const res = await cloudHttpRequest(args.http, "POST",
|
|
30700
|
+
post: async (path11, body) => {
|
|
30701
|
+
const res = await cloudHttpRequest(args.http, "POST", path11, body);
|
|
30534
30702
|
return { status: res.status, json: res.json };
|
|
30535
30703
|
}
|
|
30536
30704
|
});
|
|
@@ -30576,39 +30744,58 @@ async function indexTemplateToCloud(args) {
|
|
|
30576
30744
|
// spex-console/src/lib/cli/install.ts
|
|
30577
30745
|
var MCP_REL = ["vendor", "heyspex-mcp", "cli.mjs"];
|
|
30578
30746
|
var PORTABLE_MCP_CLI = Object.freeze({
|
|
30747
|
+
claude: `\${CLAUDE_PROJECT_DIR:-.}/${CUSTOMER_LAUNCHERS.claude.rel.join("/")}`,
|
|
30748
|
+
cursor: `\${workspaceFolder}/${CUSTOMER_LAUNCHERS.cursor.rel.join("/")}`
|
|
30749
|
+
});
|
|
30750
|
+
var LEGACY_PORTABLE_MCP_CLI = Object.freeze({
|
|
30579
30751
|
claude: `\${CLAUDE_PROJECT_DIR:-.}/${PORTABLE_PACKAGE_DIR}/${MCP_REL.join("/")}`,
|
|
30580
30752
|
cursor: `\${workspaceFolder}/${PORTABLE_PACKAGE_DIR}/${MCP_REL.join("/")}`
|
|
30581
30753
|
});
|
|
30754
|
+
var LAUNCHER_MCP_FLAG = "--mcp";
|
|
30755
|
+
function mcpServerArgs(cliPath) {
|
|
30756
|
+
return /[\\/]heyspex[\\/]run-hook\.mjs$/.test(cliPath) ? [cliPath, LAUNCHER_MCP_FLAG] : [cliPath];
|
|
30757
|
+
}
|
|
30758
|
+
function repairLegacyMcpManifest(manifest, surface) {
|
|
30759
|
+
const servers = manifest?.mcpServers;
|
|
30760
|
+
const entry = servers && typeof servers === "object" ? servers.heyspex : void 0;
|
|
30761
|
+
if (!entry || !Array.isArray(entry.args) || entry.args[0] !== LEGACY_PORTABLE_MCP_CLI[surface]) {
|
|
30762
|
+
return false;
|
|
30763
|
+
}
|
|
30764
|
+
entry.args = mcpServerArgs(PORTABLE_MCP_CLI[surface]);
|
|
30765
|
+
return true;
|
|
30766
|
+
}
|
|
30582
30767
|
function mcpCliArgFor(surface, platformRoot, targetDir, joinPath) {
|
|
30583
30768
|
return samePlatformPath(targetDir, platformRoot) ? mcpCliPath(platformRoot, joinPath) : PORTABLE_MCP_CLI[surface];
|
|
30584
30769
|
}
|
|
30585
30770
|
function mcpCliPath(platformRoot, joinPath) {
|
|
30586
30771
|
return joinPath(platformRoot, ...MCP_REL);
|
|
30587
30772
|
}
|
|
30588
|
-
function renderCursorMcpJson(cliAbsPath, attachedVaultRoot) {
|
|
30773
|
+
function renderCursorMcpJson(cliAbsPath, attachedVaultRoot, opts = {}) {
|
|
30774
|
+
const pin = vaultRootPin(attachedVaultRoot, "${workspaceFolder}", opts);
|
|
30589
30775
|
return {
|
|
30590
30776
|
mcpServers: {
|
|
30591
30777
|
heyspex: {
|
|
30592
30778
|
type: "stdio",
|
|
30593
30779
|
command: "node",
|
|
30594
|
-
args:
|
|
30595
|
-
env: {
|
|
30596
|
-
HEYSPEX_VAULT_ROOT: attachedVaultRoot ?? "${workspaceFolder}"
|
|
30597
|
-
}
|
|
30780
|
+
args: mcpServerArgs(cliAbsPath),
|
|
30781
|
+
...pin ? { env: { HEYSPEX_VAULT_ROOT: pin } } : {}
|
|
30598
30782
|
}
|
|
30599
30783
|
}
|
|
30600
30784
|
};
|
|
30601
30785
|
}
|
|
30602
|
-
function
|
|
30786
|
+
function vaultRootPin(attachedVaultRoot, placeholder, opts = {}) {
|
|
30787
|
+
if (attachedVaultRoot) return attachedVaultRoot;
|
|
30788
|
+
return opts.targetIsVault === false ? null : placeholder;
|
|
30789
|
+
}
|
|
30790
|
+
function renderClaudeMcpJson(cliAbsPath, attachedVaultRoot, opts = {}) {
|
|
30791
|
+
const pin = vaultRootPin(attachedVaultRoot, "${CLAUDE_PROJECT_DIR:-.}", opts);
|
|
30603
30792
|
return {
|
|
30604
30793
|
mcpServers: {
|
|
30605
30794
|
heyspex: {
|
|
30606
30795
|
type: "stdio",
|
|
30607
30796
|
command: "node",
|
|
30608
|
-
args:
|
|
30609
|
-
env: {
|
|
30610
|
-
HEYSPEX_VAULT_ROOT: attachedVaultRoot ?? "${CLAUDE_PROJECT_DIR:-.}"
|
|
30611
|
-
}
|
|
30797
|
+
args: mcpServerArgs(cliAbsPath),
|
|
30798
|
+
...pin ? { env: { HEYSPEX_VAULT_ROOT: pin } } : {}
|
|
30612
30799
|
}
|
|
30613
30800
|
}
|
|
30614
30801
|
};
|
|
@@ -30886,7 +31073,6 @@ var WRITE_TARGET_KIND = {
|
|
|
30886
31073
|
claudeMcp: "claude_mcp",
|
|
30887
31074
|
claudeMd: "claude_md",
|
|
30888
31075
|
agentsMd: "agents_md",
|
|
30889
|
-
statuslineVendor: "statusline_vendor",
|
|
30890
31076
|
gitignore: "gitignore",
|
|
30891
31077
|
claudeMdTemplate: "claude_md_template",
|
|
30892
31078
|
agentsMdTemplate: "agents_md_template"
|
|
@@ -30943,12 +31129,6 @@ var INSTALL_WRITE_TARGETS = [
|
|
|
30943
31129
|
path: (targetDir, joinPath) => joinPath(targetDir, "AGENTS.md.template"),
|
|
30944
31130
|
appliesTo: (args, opts) => Boolean(args.registerVault) && !opts.attached && opts.installContract
|
|
30945
31131
|
},
|
|
30946
|
-
{
|
|
30947
|
-
kind: WRITE_TARGET_KIND.statuslineVendor,
|
|
30948
|
-
rel: "scripts/claude-statusline",
|
|
30949
|
-
path: (targetDir, joinPath) => joinPath(targetDir, "scripts", "claude-statusline"),
|
|
30950
|
-
appliesTo: (args) => (args.statusline ?? true) && args.claude
|
|
30951
|
-
},
|
|
30952
31132
|
{
|
|
30953
31133
|
kind: WRITE_TARGET_KIND.gitignore,
|
|
30954
31134
|
rel: ".gitignore",
|
|
@@ -31081,7 +31261,7 @@ var INSTALL_HELP = [
|
|
|
31081
31261
|
|
|
31082
31262
|
// spex-console/src/lib/cli/install-identity.ts
|
|
31083
31263
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
31084
|
-
import { existsSync as
|
|
31264
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
31085
31265
|
import { join as join11 } from "node:path";
|
|
31086
31266
|
import { createInterface } from "node:readline/promises";
|
|
31087
31267
|
init_credentials();
|
|
@@ -31089,7 +31269,7 @@ init_credentials();
|
|
|
31089
31269
|
// vendor/heyspex-mcp/whoami.mjs
|
|
31090
31270
|
init_heyspex_home();
|
|
31091
31271
|
init_credentials();
|
|
31092
|
-
import { homedir as
|
|
31272
|
+
import { homedir as homedir10 } from "node:os";
|
|
31093
31273
|
var SESSION_PATH = "/api/mcp/session";
|
|
31094
31274
|
async function probeCredentialIdentity({ consoleUrl, token, fetchImpl = fetch } = {}) {
|
|
31095
31275
|
if (!token) return { kind: "no_credential" };
|
|
@@ -31373,7 +31553,7 @@ function isInteractiveTerminal() {
|
|
|
31373
31553
|
}
|
|
31374
31554
|
function defaultIdentityGateIo(opts) {
|
|
31375
31555
|
const consoleUrl = opts.consoleUrl.trim().replace(/\/+$/, "");
|
|
31376
|
-
const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join11(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) =>
|
|
31556
|
+
const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join11(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) => existsSync7(p));
|
|
31377
31557
|
return {
|
|
31378
31558
|
consoleUrl,
|
|
31379
31559
|
interactive: opts.interactive ?? isInteractiveTerminal(),
|
|
@@ -31666,9 +31846,9 @@ function detachedSatelliteWarning(repoRoot, gitRemote, registryBefore, deps) {
|
|
|
31666
31846
|
reasons.push(`${wiredManifests.join(" + ")} already declares the heyspex MCP server`);
|
|
31667
31847
|
}
|
|
31668
31848
|
const contractFiles = ["CLAUDE.md", "AGENTS.md"].filter((name) => {
|
|
31669
|
-
const
|
|
31670
|
-
if (!deps.fileExists(
|
|
31671
|
-
const content = deps.readFile(
|
|
31849
|
+
const path11 = deps.joinPath(repoRoot, name);
|
|
31850
|
+
if (!deps.fileExists(path11)) return false;
|
|
31851
|
+
const content = deps.readFile(path11);
|
|
31672
31852
|
return content !== null && HEYSPEX_CONTRACT_MARKERS.some((m) => content.includes(m));
|
|
31673
31853
|
});
|
|
31674
31854
|
if (contractFiles.length > 0) {
|
|
@@ -32487,7 +32667,32 @@ async function runConnectFullRun(args, deps) {
|
|
|
32487
32667
|
const mcpConfirmedBy = "read .mcp.json + .cursor/mcp.json";
|
|
32488
32668
|
const claudeMissing = !deps.fileExists(claudeMcpPath);
|
|
32489
32669
|
const cursorMissing = !deps.fileExists(cursorMcpPath);
|
|
32490
|
-
|
|
32670
|
+
const repairedLegacy = [];
|
|
32671
|
+
for (const [file, surface, missing] of [
|
|
32672
|
+
[claudeMcpPath, "claude", claudeMissing],
|
|
32673
|
+
[cursorMcpPath, "cursor", cursorMissing]
|
|
32674
|
+
]) {
|
|
32675
|
+
if (missing || samePlatformPath(repoRoot, platformRoot)) continue;
|
|
32676
|
+
let parsed = null;
|
|
32677
|
+
try {
|
|
32678
|
+
parsed = JSON.parse(deps.readFile(file) ?? "");
|
|
32679
|
+
} catch {
|
|
32680
|
+
continue;
|
|
32681
|
+
}
|
|
32682
|
+
if (!repairLegacyMcpManifest(parsed, surface)) continue;
|
|
32683
|
+
if (!args.dryRun) deps.writeFile(file, `${JSON.stringify(parsed, null, 2)}
|
|
32684
|
+
`);
|
|
32685
|
+
repairedLegacy.push(file);
|
|
32686
|
+
}
|
|
32687
|
+
if (repairedLegacy.length > 0 && !claudeMissing && !cursorMissing) {
|
|
32688
|
+
steps.push({
|
|
32689
|
+
id: "mcp_entry",
|
|
32690
|
+
label,
|
|
32691
|
+
status: "run",
|
|
32692
|
+
detail: `${args.dryRun ? "would repoint" : "repointed"} ${repairedLegacy.join(", ")} at the committed launcher (${PORTABLE_MCP_CLI.claude})`,
|
|
32693
|
+
confirmed_by: mcpConfirmedBy
|
|
32694
|
+
});
|
|
32695
|
+
} else if (!claudeMissing && !cursorMissing) {
|
|
32491
32696
|
steps.push({
|
|
32492
32697
|
id: "mcp_entry",
|
|
32493
32698
|
label,
|
|
@@ -32510,12 +32715,14 @@ async function runConnectFullRun(args, deps) {
|
|
|
32510
32715
|
try {
|
|
32511
32716
|
const claudeCli = mcpCliArgFor("claude", platformRoot, repoRoot, deps.joinPath);
|
|
32512
32717
|
const cursorCli = mcpCliArgFor("cursor", platformRoot, repoRoot, deps.joinPath);
|
|
32513
|
-
const
|
|
32718
|
+
const markerRel = satellite?.viaMarker ? satellite.vaultPath : void 0;
|
|
32719
|
+
const claudePin = markerRel ? "${CLAUDE_PROJECT_DIR:-.}/" + markerRel : satellite?.vaultRootAbs;
|
|
32720
|
+
const cursorPin = markerRel ? "${workspaceFolder}/" + markerRel : satellite?.vaultRootAbs;
|
|
32514
32721
|
const written = [];
|
|
32515
32722
|
if (claudeMissing) {
|
|
32516
32723
|
deps.writeFile(
|
|
32517
32724
|
claudeMcpPath,
|
|
32518
|
-
`${JSON.stringify(renderClaudeMcpJson(claudeCli,
|
|
32725
|
+
`${JSON.stringify(renderClaudeMcpJson(claudeCli, claudePin), null, 2)}
|
|
32519
32726
|
`
|
|
32520
32727
|
);
|
|
32521
32728
|
written.push(claudeMcpPath);
|
|
@@ -32524,7 +32731,7 @@ async function runConnectFullRun(args, deps) {
|
|
|
32524
32731
|
deps.mkdir(deps.joinPath(repoRoot, ".cursor"));
|
|
32525
32732
|
deps.writeFile(
|
|
32526
32733
|
cursorMcpPath,
|
|
32527
|
-
`${JSON.stringify(renderCursorMcpJson(cursorCli,
|
|
32734
|
+
`${JSON.stringify(renderCursorMcpJson(cursorCli, cursorPin), null, 2)}
|
|
32528
32735
|
`
|
|
32529
32736
|
);
|
|
32530
32737
|
written.push(cursorMcpPath);
|
|
@@ -32900,11 +33107,11 @@ function resolveDoctorCommand(repoRoot, platformRoot, deps) {
|
|
|
32900
33107
|
const hs = deps.joinPath(platformRoot, "bin", "hs.mjs");
|
|
32901
33108
|
return deps.fileExists(hs) ? { args: [hs, "doctor", "--json"], confirmedBy: "hs doctor --json" } : null;
|
|
32902
33109
|
}
|
|
32903
|
-
function assertMarker(content,
|
|
32904
|
-
if (content === null) throw new Error(`${
|
|
33110
|
+
function assertMarker(content, path11, vaultId, accountId) {
|
|
33111
|
+
if (content === null) throw new Error(`${path11} could not be read back after the write`);
|
|
32905
33112
|
const back = parseVaultId(content);
|
|
32906
33113
|
if (vaultId && back !== vaultId) {
|
|
32907
|
-
throw new Error(`${
|
|
33114
|
+
throw new Error(`${path11} reads back vault_id ${back ?? "(none)"}, not ${vaultId}`);
|
|
32908
33115
|
}
|
|
32909
33116
|
}
|
|
32910
33117
|
var MIRRORED_SKILL_DIRS = [
|
|
@@ -33058,7 +33265,7 @@ async function runFixHarnessCommands(args, deps) {
|
|
|
33058
33265
|
continue;
|
|
33059
33266
|
}
|
|
33060
33267
|
const existingContent = deps.fileExists(installedPath) ? deps.readFile(installedPath) : null;
|
|
33061
|
-
if (existingContent === f.content) {
|
|
33268
|
+
if (existingContent !== null && normalizeEol(existingContent) === normalizeEol(f.content)) {
|
|
33062
33269
|
unchangedCount += 1;
|
|
33063
33270
|
continue;
|
|
33064
33271
|
}
|
|
@@ -33563,14 +33770,14 @@ import {
|
|
|
33563
33770
|
import {
|
|
33564
33771
|
appendFileSync,
|
|
33565
33772
|
closeSync,
|
|
33566
|
-
existsSync as
|
|
33567
|
-
mkdirSync as
|
|
33773
|
+
existsSync as existsSync8,
|
|
33774
|
+
mkdirSync as mkdirSync7,
|
|
33568
33775
|
openSync,
|
|
33569
|
-
readFileSync as
|
|
33570
|
-
renameSync as
|
|
33571
|
-
statSync,
|
|
33572
|
-
unlinkSync,
|
|
33573
|
-
writeFileSync as
|
|
33776
|
+
readFileSync as readFileSync13,
|
|
33777
|
+
renameSync as renameSync5,
|
|
33778
|
+
statSync as statSync5,
|
|
33779
|
+
unlinkSync as unlinkSync2,
|
|
33780
|
+
writeFileSync as writeFileSync7,
|
|
33574
33781
|
writeSync
|
|
33575
33782
|
} from "node:fs";
|
|
33576
33783
|
import { join as join12 } from "node:path";
|
|
@@ -33610,14 +33817,14 @@ function unwrapPrivateKey(token, wrapSaltHex, wrapped) {
|
|
|
33610
33817
|
}
|
|
33611
33818
|
function heyspexDir(vaultRoot) {
|
|
33612
33819
|
const dir = join12(vaultRoot, ".heyspex");
|
|
33613
|
-
|
|
33820
|
+
mkdirSync7(dir, { recursive: true });
|
|
33614
33821
|
return dir;
|
|
33615
33822
|
}
|
|
33616
33823
|
function memberTokenStorePath(vaultRoot) {
|
|
33617
33824
|
return join12(vaultRoot, ".heyspex", STORE_FILE);
|
|
33618
33825
|
}
|
|
33619
33826
|
function memberTokenStorePresent(vaultRoot) {
|
|
33620
|
-
return
|
|
33827
|
+
return existsSync8(memberTokenStorePath(vaultRoot));
|
|
33621
33828
|
}
|
|
33622
33829
|
function sleepSync(ms) {
|
|
33623
33830
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
@@ -33632,8 +33839,8 @@ function acquireLock(lockPath) {
|
|
|
33632
33839
|
} catch (err) {
|
|
33633
33840
|
if (err?.code !== "EEXIST") throw err;
|
|
33634
33841
|
try {
|
|
33635
|
-
if (Date.now() -
|
|
33636
|
-
|
|
33842
|
+
if (Date.now() - statSync5(lockPath).mtimeMs > LOCK_STALE_MS) {
|
|
33843
|
+
unlinkSync2(lockPath);
|
|
33637
33844
|
continue;
|
|
33638
33845
|
}
|
|
33639
33846
|
} catch {
|
|
@@ -33658,16 +33865,16 @@ function withTokenLock(vaultRoot, fn) {
|
|
|
33658
33865
|
} catch {
|
|
33659
33866
|
}
|
|
33660
33867
|
try {
|
|
33661
|
-
|
|
33868
|
+
unlinkSync2(lockPath);
|
|
33662
33869
|
} catch {
|
|
33663
33870
|
}
|
|
33664
33871
|
}
|
|
33665
33872
|
}
|
|
33666
33873
|
function readStore(vaultRoot) {
|
|
33667
33874
|
const p = memberTokenStorePath(vaultRoot);
|
|
33668
|
-
if (!
|
|
33875
|
+
if (!existsSync8(p)) return [];
|
|
33669
33876
|
try {
|
|
33670
|
-
const parsed = JSON.parse(
|
|
33877
|
+
const parsed = JSON.parse(readFileSync13(p, "utf8"));
|
|
33671
33878
|
return Array.isArray(parsed) ? parsed : [];
|
|
33672
33879
|
} catch {
|
|
33673
33880
|
return [];
|
|
@@ -33676,8 +33883,8 @@ function readStore(vaultRoot) {
|
|
|
33676
33883
|
function writeStore(vaultRoot, records) {
|
|
33677
33884
|
const p = join12(heyspexDir(vaultRoot), STORE_FILE);
|
|
33678
33885
|
const tmp = `${p}.tmp`;
|
|
33679
|
-
|
|
33680
|
-
|
|
33886
|
+
writeFileSync7(tmp, JSON.stringify(records, null, 2) + "\n", { mode: FILE_MODE });
|
|
33887
|
+
renameSync5(tmp, p);
|
|
33681
33888
|
}
|
|
33682
33889
|
function isLive(r, nowMs) {
|
|
33683
33890
|
if (r.revokedAt) return false;
|
|
@@ -33732,14 +33939,14 @@ import { randomBytes as randomBytes3, scryptSync as scryptSync2, timingSafeEqual
|
|
|
33732
33939
|
import {
|
|
33733
33940
|
appendFileSync as appendFileSync2,
|
|
33734
33941
|
closeSync as closeSync2,
|
|
33735
|
-
existsSync as
|
|
33736
|
-
mkdirSync as
|
|
33942
|
+
existsSync as existsSync10,
|
|
33943
|
+
mkdirSync as mkdirSync8,
|
|
33737
33944
|
openSync as openSync2,
|
|
33738
|
-
readFileSync as
|
|
33739
|
-
renameSync as
|
|
33740
|
-
statSync as
|
|
33741
|
-
unlinkSync as
|
|
33742
|
-
writeFileSync as
|
|
33945
|
+
readFileSync as readFileSync14,
|
|
33946
|
+
renameSync as renameSync6,
|
|
33947
|
+
statSync as statSync6,
|
|
33948
|
+
unlinkSync as unlinkSync3,
|
|
33949
|
+
writeFileSync as writeFileSync8,
|
|
33743
33950
|
writeSync as writeSync2
|
|
33744
33951
|
} from "node:fs";
|
|
33745
33952
|
import { join as join13 } from "node:path";
|
|
@@ -33747,9 +33954,9 @@ import { join as join13 } from "node:path";
|
|
|
33747
33954
|
// spex-console/src/lib/env-vault/sops.ts
|
|
33748
33955
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
33749
33956
|
import { createHash as createHash5 } from "node:crypto";
|
|
33750
|
-
import { existsSync as
|
|
33751
|
-
import { homedir as
|
|
33752
|
-
import
|
|
33957
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
33958
|
+
import { homedir as homedir11 } from "node:os";
|
|
33959
|
+
import path8 from "node:path";
|
|
33753
33960
|
function ageRecipientFingerprint(recipient, length = 12) {
|
|
33754
33961
|
return createHash5("sha256").update(recipient, "utf8").digest("hex").slice(0, length);
|
|
33755
33962
|
}
|
|
@@ -33766,14 +33973,14 @@ var toolCache = /* @__PURE__ */ new Map();
|
|
|
33766
33973
|
function withExtendedPath(extraDirs) {
|
|
33767
33974
|
const env = { ...process.env };
|
|
33768
33975
|
const key = Object.keys(env).find((k) => k.toUpperCase() === "PATH") ?? "PATH";
|
|
33769
|
-
env[key] = [env[key], ...extraDirs].filter(Boolean).join(
|
|
33976
|
+
env[key] = [env[key], ...extraDirs].filter(Boolean).join(path8.delimiter);
|
|
33770
33977
|
return env;
|
|
33771
33978
|
}
|
|
33772
33979
|
function candidateInvocations(tool) {
|
|
33773
33980
|
const candidates = [{ command: tool, env: process.env }];
|
|
33774
33981
|
const local = process.env.LOCALAPPDATA;
|
|
33775
33982
|
if (local) {
|
|
33776
|
-
const miseBin =
|
|
33983
|
+
const miseBin = path8.join(
|
|
33777
33984
|
local,
|
|
33778
33985
|
"Microsoft",
|
|
33779
33986
|
"WinGet",
|
|
@@ -33782,14 +33989,14 @@ function candidateInvocations(tool) {
|
|
|
33782
33989
|
"mise",
|
|
33783
33990
|
"bin"
|
|
33784
33991
|
);
|
|
33785
|
-
const extra =
|
|
33992
|
+
const extra = existsSync9(miseBin) ? [miseBin] : [];
|
|
33786
33993
|
candidates.push({
|
|
33787
|
-
command:
|
|
33994
|
+
command: path8.join(local, "mise", "shims", `${tool}.exe`),
|
|
33788
33995
|
env: withExtendedPath(extra)
|
|
33789
33996
|
});
|
|
33790
33997
|
}
|
|
33791
33998
|
candidates.push({
|
|
33792
|
-
command:
|
|
33999
|
+
command: path8.join(homedir11(), ".local", "share", "mise", "shims", tool),
|
|
33793
34000
|
env: process.env
|
|
33794
34001
|
});
|
|
33795
34002
|
return candidates;
|
|
@@ -34114,14 +34321,14 @@ function hashEquals2(a, b) {
|
|
|
34114
34321
|
}
|
|
34115
34322
|
function heyspexDir2(vaultRoot) {
|
|
34116
34323
|
const dir = join13(vaultRoot, ".heyspex");
|
|
34117
|
-
|
|
34324
|
+
mkdirSync8(dir, { recursive: true });
|
|
34118
34325
|
return dir;
|
|
34119
34326
|
}
|
|
34120
34327
|
function envPairingStorePath(vaultRoot) {
|
|
34121
34328
|
return join13(vaultRoot, ".heyspex", STORE_FILE2);
|
|
34122
34329
|
}
|
|
34123
34330
|
function envPairingStorePresent(vaultRoot) {
|
|
34124
|
-
return
|
|
34331
|
+
return existsSync10(envPairingStorePath(vaultRoot));
|
|
34125
34332
|
}
|
|
34126
34333
|
function auditPath(vaultRoot) {
|
|
34127
34334
|
return join13(heyspexDir2(vaultRoot), AUDIT_FILE);
|
|
@@ -34139,8 +34346,8 @@ function acquireLock2(lockPath) {
|
|
|
34139
34346
|
} catch (err) {
|
|
34140
34347
|
if (err?.code !== "EEXIST") throw err;
|
|
34141
34348
|
try {
|
|
34142
|
-
if (Date.now() -
|
|
34143
|
-
|
|
34349
|
+
if (Date.now() - statSync6(lockPath).mtimeMs > LOCK_STALE_MS2) {
|
|
34350
|
+
unlinkSync3(lockPath);
|
|
34144
34351
|
continue;
|
|
34145
34352
|
}
|
|
34146
34353
|
} catch {
|
|
@@ -34165,16 +34372,16 @@ function withPairingLock(vaultRoot, fn) {
|
|
|
34165
34372
|
} catch {
|
|
34166
34373
|
}
|
|
34167
34374
|
try {
|
|
34168
|
-
|
|
34375
|
+
unlinkSync3(lockPath);
|
|
34169
34376
|
} catch {
|
|
34170
34377
|
}
|
|
34171
34378
|
}
|
|
34172
34379
|
}
|
|
34173
34380
|
function readStore2(vaultRoot) {
|
|
34174
34381
|
const p = envPairingStorePath(vaultRoot);
|
|
34175
|
-
if (!
|
|
34382
|
+
if (!existsSync10(p)) return [];
|
|
34176
34383
|
try {
|
|
34177
|
-
const parsed = JSON.parse(
|
|
34384
|
+
const parsed = JSON.parse(readFileSync14(p, "utf8"));
|
|
34178
34385
|
return Array.isArray(parsed) ? parsed : [];
|
|
34179
34386
|
} catch {
|
|
34180
34387
|
return [];
|
|
@@ -34190,8 +34397,8 @@ function prune(records) {
|
|
|
34190
34397
|
function writeStore2(vaultRoot, records) {
|
|
34191
34398
|
const p = join13(heyspexDir2(vaultRoot), STORE_FILE2);
|
|
34192
34399
|
const tmp = `${p}.tmp`;
|
|
34193
|
-
|
|
34194
|
-
|
|
34400
|
+
writeFileSync8(tmp, JSON.stringify(prune(records), null, 2) + "\n", { mode: FILE_MODE2 });
|
|
34401
|
+
renameSync6(tmp, p);
|
|
34195
34402
|
}
|
|
34196
34403
|
function appendEnvPairingAudit(vaultRoot, event) {
|
|
34197
34404
|
const line = {
|
|
@@ -34209,14 +34416,14 @@ function strikesPath(vaultRoot) {
|
|
|
34209
34416
|
}
|
|
34210
34417
|
function readStrikes(vaultRoot) {
|
|
34211
34418
|
try {
|
|
34212
|
-
const parsed = JSON.parse(
|
|
34419
|
+
const parsed = JSON.parse(readFileSync14(strikesPath(vaultRoot), "utf8"));
|
|
34213
34420
|
return typeof parsed.count === "number" && Number.isFinite(parsed.count) ? Math.max(0, Math.floor(parsed.count)) : 0;
|
|
34214
34421
|
} catch {
|
|
34215
34422
|
return 0;
|
|
34216
34423
|
}
|
|
34217
34424
|
}
|
|
34218
34425
|
function writeStrikes(vaultRoot, count) {
|
|
34219
|
-
|
|
34426
|
+
writeFileSync8(
|
|
34220
34427
|
strikesPath(vaultRoot),
|
|
34221
34428
|
JSON.stringify({ count, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }) + "\n",
|
|
34222
34429
|
{ mode: FILE_MODE2 }
|
|
@@ -34425,8 +34632,8 @@ function markEnvInviteApplied(vaultRoot, id) {
|
|
|
34425
34632
|
}
|
|
34426
34633
|
|
|
34427
34634
|
// spex-console/src/lib/env-vault/policy.ts
|
|
34428
|
-
import { existsSync as
|
|
34429
|
-
import
|
|
34635
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
34636
|
+
import path9 from "node:path";
|
|
34430
34637
|
var POLICY_REL_PATH = ".heyspex/env/policy.json";
|
|
34431
34638
|
var POLICY_VERSION = 1;
|
|
34432
34639
|
var MODES = ["allow", "ask", "deny"];
|
|
@@ -34434,7 +34641,7 @@ function isPolicyMode(value) {
|
|
|
34434
34641
|
return typeof value === "string" && MODES.includes(value);
|
|
34435
34642
|
}
|
|
34436
34643
|
function policyPath(vaultRoot) {
|
|
34437
|
-
return
|
|
34644
|
+
return path9.join(vaultRoot, ".heyspex", "env", "policy.json");
|
|
34438
34645
|
}
|
|
34439
34646
|
function parsePolicyFile(text2) {
|
|
34440
34647
|
let raw;
|
|
@@ -34458,7 +34665,7 @@ function readPolicy(vaultRoot, profile) {
|
|
|
34458
34665
|
const file = policyPath(vaultRoot);
|
|
34459
34666
|
let text2;
|
|
34460
34667
|
try {
|
|
34461
|
-
text2 =
|
|
34668
|
+
text2 = readFileSync15(file, "utf-8");
|
|
34462
34669
|
} catch {
|
|
34463
34670
|
return { mode: "allow", source: "default" };
|
|
34464
34671
|
}
|
|
@@ -34478,8 +34685,8 @@ function serializePolicyFile(file) {
|
|
|
34478
34685
|
}
|
|
34479
34686
|
function loadForWrite(vaultRoot) {
|
|
34480
34687
|
const file = policyPath(vaultRoot);
|
|
34481
|
-
if (!
|
|
34482
|
-
const parsed = parsePolicyFile(
|
|
34688
|
+
if (!existsSync11(file)) return { version: POLICY_VERSION, profiles: {} };
|
|
34689
|
+
const parsed = parsePolicyFile(readFileSync15(file, "utf-8"));
|
|
34483
34690
|
if (parsed === null) {
|
|
34484
34691
|
throw new Error(
|
|
34485
34692
|
`${POLICY_REL_PATH} exists but is not a valid v${POLICY_VERSION} policy file \u2014 fix or remove it before changing policy (refusing to overwrite what it meant to say).`
|
|
@@ -34489,10 +34696,10 @@ function loadForWrite(vaultRoot) {
|
|
|
34489
34696
|
}
|
|
34490
34697
|
function writePolicyFile(vaultRoot, file) {
|
|
34491
34698
|
const target = policyPath(vaultRoot);
|
|
34492
|
-
|
|
34699
|
+
mkdirSync9(path9.dirname(target), { recursive: true });
|
|
34493
34700
|
const tmp = `${target}.tmp`;
|
|
34494
|
-
|
|
34495
|
-
|
|
34701
|
+
writeFileSync9(tmp, serializePolicyFile(file), "utf-8");
|
|
34702
|
+
renameSync7(tmp, target);
|
|
34496
34703
|
}
|
|
34497
34704
|
function writePolicy(vaultRoot, profile, mode) {
|
|
34498
34705
|
if (!isPolicyMode(mode)) {
|
|
@@ -34504,7 +34711,7 @@ function writePolicy(vaultRoot, profile, mode) {
|
|
|
34504
34711
|
}
|
|
34505
34712
|
function clearPolicy(vaultRoot, profile) {
|
|
34506
34713
|
const file = policyPath(vaultRoot);
|
|
34507
|
-
if (!
|
|
34714
|
+
if (!existsSync11(file)) return;
|
|
34508
34715
|
const parsed = loadForWrite(vaultRoot);
|
|
34509
34716
|
if (!(profile in parsed.profiles)) return;
|
|
34510
34717
|
delete parsed.profiles[profile];
|
|
@@ -36111,17 +36318,17 @@ import { spawnSync as spawnSync4 } from "node:child_process";
|
|
|
36111
36318
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
36112
36319
|
import {
|
|
36113
36320
|
chmodSync,
|
|
36114
|
-
existsSync as
|
|
36115
|
-
mkdirSync as
|
|
36321
|
+
existsSync as existsSync12,
|
|
36322
|
+
mkdirSync as mkdirSync10,
|
|
36116
36323
|
readdirSync as readdirSync3,
|
|
36117
|
-
readFileSync as
|
|
36118
|
-
renameSync as
|
|
36119
|
-
statSync as
|
|
36120
|
-
unlinkSync as
|
|
36121
|
-
writeFileSync as
|
|
36324
|
+
readFileSync as readFileSync16,
|
|
36325
|
+
renameSync as renameSync8,
|
|
36326
|
+
statSync as statSync7,
|
|
36327
|
+
unlinkSync as unlinkSync4,
|
|
36328
|
+
writeFileSync as writeFileSync10
|
|
36122
36329
|
} from "node:fs";
|
|
36123
|
-
import { homedir as
|
|
36124
|
-
import
|
|
36330
|
+
import { homedir as homedir13 } from "node:os";
|
|
36331
|
+
import path10 from "node:path";
|
|
36125
36332
|
|
|
36126
36333
|
// node_modules/sops-age/dist/index.js
|
|
36127
36334
|
var import_dotenv = __toESM(require_main(), 1);
|
|
@@ -36605,8 +36812,8 @@ function getErrorMap() {
|
|
|
36605
36812
|
|
|
36606
36813
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
36607
36814
|
var makeIssue = (params) => {
|
|
36608
|
-
const { data, path:
|
|
36609
|
-
const fullPath = [...
|
|
36815
|
+
const { data, path: path11, errorMaps, issueData } = params;
|
|
36816
|
+
const fullPath = [...path11, ...issueData.path || []];
|
|
36610
36817
|
const fullIssue = {
|
|
36611
36818
|
...issueData,
|
|
36612
36819
|
path: fullPath
|
|
@@ -36722,11 +36929,11 @@ var errorUtil;
|
|
|
36722
36929
|
|
|
36723
36930
|
// node_modules/zod/v3/types.js
|
|
36724
36931
|
var ParseInputLazyPath = class {
|
|
36725
|
-
constructor(parent, value,
|
|
36932
|
+
constructor(parent, value, path11, key) {
|
|
36726
36933
|
this._cachedPath = [];
|
|
36727
36934
|
this.parent = parent;
|
|
36728
36935
|
this.data = value;
|
|
36729
|
-
this._path =
|
|
36936
|
+
this._path = path11;
|
|
36730
36937
|
this._key = key;
|
|
36731
36938
|
}
|
|
36732
36939
|
get path() {
|
|
@@ -44791,22 +44998,22 @@ var gcm = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 12, tagLength
|
|
|
44791
44998
|
// node_modules/sops-age/dist/index.js
|
|
44792
44999
|
import { readFile } from "node:fs/promises";
|
|
44793
45000
|
import { join as join15 } from "node:path";
|
|
44794
|
-
import { homedir as
|
|
45001
|
+
import { homedir as homedir12, platform } from "node:os";
|
|
44795
45002
|
import { exec } from "node:child_process";
|
|
44796
45003
|
import { promisify } from "node:util";
|
|
44797
45004
|
var import_sshpk = __toESM(require_lib2(), 1);
|
|
44798
|
-
async function loadFromFile(
|
|
45005
|
+
async function loadFromFile(path11) {
|
|
44799
45006
|
if (globalThis.Deno) {
|
|
44800
|
-
return Deno.readTextFile(
|
|
45007
|
+
return Deno.readTextFile(path11);
|
|
44801
45008
|
}
|
|
44802
45009
|
if (globalThis.Bun) {
|
|
44803
|
-
return Bun.file(
|
|
45010
|
+
return Bun.file(path11).text();
|
|
44804
45011
|
}
|
|
44805
45012
|
if (typeof process !== "undefined" && process.versions?.node) {
|
|
44806
45013
|
const fs4 = await import("fs/promises");
|
|
44807
|
-
return fs4.readFile(
|
|
45014
|
+
return fs4.readFile(path11, "utf-8");
|
|
44808
45015
|
}
|
|
44809
|
-
throw new Error(`Unable to determine method to load file "${
|
|
45016
|
+
throw new Error(`Unable to determine method to load file "${path11}"`);
|
|
44810
45017
|
}
|
|
44811
45018
|
function isSopsInput(value) {
|
|
44812
45019
|
if (!value) {
|
|
@@ -44914,13 +45121,13 @@ async function parseSops(input, sopsFileType) {
|
|
|
44914
45121
|
});
|
|
44915
45122
|
}
|
|
44916
45123
|
}
|
|
44917
|
-
async function loadSopsFile(
|
|
45124
|
+
async function loadSopsFile(path11, sopsFileType) {
|
|
44918
45125
|
try {
|
|
44919
|
-
const content = await loadFromFile(
|
|
45126
|
+
const content = await loadFromFile(path11);
|
|
44920
45127
|
return await parseSops(content, sopsFileType);
|
|
44921
45128
|
} catch (err) {
|
|
44922
45129
|
throw new Error(
|
|
44923
|
-
`Failed to load SOPS file '${
|
|
45130
|
+
`Failed to load SOPS file '${path11}': ${err.message}`,
|
|
44924
45131
|
{
|
|
44925
45132
|
cause: err
|
|
44926
45133
|
}
|
|
@@ -45132,11 +45339,11 @@ async function getUserConfigDir() {
|
|
|
45132
45339
|
if (!appData) throw new Error("APPDATA env var not set");
|
|
45133
45340
|
return appData;
|
|
45134
45341
|
case "darwin":
|
|
45135
|
-
return join15(
|
|
45342
|
+
return join15(homedir12(), "Library", "Application Support");
|
|
45136
45343
|
default:
|
|
45137
45344
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
45138
45345
|
if (xdgConfigHome && xdgConfigHome.trim() !== "") return xdgConfigHome;
|
|
45139
|
-
return join15(
|
|
45346
|
+
return join15(homedir12(), ".config");
|
|
45140
45347
|
}
|
|
45141
45348
|
}
|
|
45142
45349
|
async function findAllAgeKeys() {
|
|
@@ -45157,7 +45364,7 @@ async function findAllAgeKeys() {
|
|
|
45157
45364
|
);
|
|
45158
45365
|
}
|
|
45159
45366
|
} else {
|
|
45160
|
-
const userHomeDir =
|
|
45367
|
+
const userHomeDir = homedir12();
|
|
45161
45368
|
if (userHomeDir) {
|
|
45162
45369
|
const defaultSshPaths = [
|
|
45163
45370
|
join15(userHomeDir, ".ssh", "id_ed25519"),
|
|
@@ -45337,15 +45544,15 @@ async function getSopsEncryptionKey(sops, secretKeys) {
|
|
|
45337
45544
|
${errors.join("\n")}`
|
|
45338
45545
|
);
|
|
45339
45546
|
}
|
|
45340
|
-
function path2gopath(
|
|
45341
|
-
return `${
|
|
45547
|
+
function path2gopath(path11) {
|
|
45548
|
+
return `${path11.filter((x) => !/^\d+$/.test(x)).join(":")}:`;
|
|
45342
45549
|
}
|
|
45343
|
-
function decryptSOPSValue(ciphertext, decryptionKey,
|
|
45550
|
+
function decryptSOPSValue(ciphertext, decryptionKey, path11) {
|
|
45344
45551
|
if (!ciphertext) {
|
|
45345
45552
|
return "";
|
|
45346
45553
|
}
|
|
45347
45554
|
const encryptedValue = parse2(ciphertext);
|
|
45348
|
-
const aad = path2gopath(
|
|
45555
|
+
const aad = path2gopath(path11);
|
|
45349
45556
|
let decrypted;
|
|
45350
45557
|
try {
|
|
45351
45558
|
decrypted = decryptAesGcm(
|
|
@@ -45355,22 +45562,22 @@ function decryptSOPSValue(ciphertext, decryptionKey, path9) {
|
|
|
45355
45562
|
);
|
|
45356
45563
|
} catch (err) {
|
|
45357
45564
|
throw new Error(
|
|
45358
|
-
`AES-GCM decryption failed at path ${JSON.stringify(
|
|
45565
|
+
`AES-GCM decryption failed at path ${JSON.stringify(path11)} for value "${ciphertext}": ${err instanceof Error ? err.message : String(err)}`
|
|
45359
45566
|
);
|
|
45360
45567
|
}
|
|
45361
45568
|
const decryptedValue = new TextDecoder().decode(decrypted);
|
|
45362
45569
|
return convertDecryptedValue(decryptedValue, encryptedValue.datatype);
|
|
45363
45570
|
}
|
|
45364
|
-
function decryptObject(obj, decryptionKey,
|
|
45571
|
+
function decryptObject(obj, decryptionKey, path11 = []) {
|
|
45365
45572
|
if (typeof obj !== "object" || obj === null) {
|
|
45366
45573
|
return obj;
|
|
45367
45574
|
}
|
|
45368
45575
|
for (const key of Object.keys(obj)) {
|
|
45369
45576
|
const value = obj[key];
|
|
45370
45577
|
if (typeof value === "string" && value.startsWith("ENC[AES256_GCM,data:")) {
|
|
45371
|
-
obj[key] = decryptSOPSValue(value, decryptionKey, [...
|
|
45578
|
+
obj[key] = decryptSOPSValue(value, decryptionKey, [...path11, key]);
|
|
45372
45579
|
} else if (typeof value === "object") {
|
|
45373
|
-
obj[key] = decryptObject(value, decryptionKey, [...
|
|
45580
|
+
obj[key] = decryptObject(value, decryptionKey, [...path11, key]);
|
|
45374
45581
|
}
|
|
45375
45582
|
}
|
|
45376
45583
|
return obj;
|
|
@@ -45722,23 +45929,23 @@ function isExpiredDate(expires, today = todayIso()) {
|
|
|
45722
45929
|
return expires !== null && today > expires;
|
|
45723
45930
|
}
|
|
45724
45931
|
function envDir(vaultRoot) {
|
|
45725
|
-
return
|
|
45932
|
+
return path10.join(vaultRoot, ".heyspex", "env");
|
|
45726
45933
|
}
|
|
45727
45934
|
function profilePath(vaultRoot, name) {
|
|
45728
|
-
return
|
|
45935
|
+
return path10.join(envDir(vaultRoot), `${name}.env`);
|
|
45729
45936
|
}
|
|
45730
45937
|
function keysPath(vaultRoot) {
|
|
45731
|
-
return
|
|
45938
|
+
return path10.join(vaultRoot, ".heyspex", "env.keys");
|
|
45732
45939
|
}
|
|
45733
45940
|
function homeKeysPath() {
|
|
45734
|
-
return
|
|
45941
|
+
return path10.join(process.env.HEYSPEX_ENV_KEYS_HOME || homedir13(), ".heyspex", "env.keys");
|
|
45735
45942
|
}
|
|
45736
45943
|
function dotenvPath(vaultRoot) {
|
|
45737
|
-
return
|
|
45944
|
+
return path10.join(vaultRoot, ".env");
|
|
45738
45945
|
}
|
|
45739
45946
|
function readOrNull(file) {
|
|
45740
45947
|
try {
|
|
45741
|
-
return
|
|
45948
|
+
return readFileSync16(file, "utf-8");
|
|
45742
45949
|
} catch {
|
|
45743
45950
|
return null;
|
|
45744
45951
|
}
|
|
@@ -45752,7 +45959,7 @@ function renameWithRetry(tmp, file) {
|
|
|
45752
45959
|
const retryable = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
45753
45960
|
for (let attempt = 0; ; attempt++) {
|
|
45754
45961
|
try {
|
|
45755
|
-
|
|
45962
|
+
renameSync8(tmp, file);
|
|
45756
45963
|
return;
|
|
45757
45964
|
} catch (err) {
|
|
45758
45965
|
const code = err.code ?? "";
|
|
@@ -45763,21 +45970,21 @@ function renameWithRetry(tmp, file) {
|
|
|
45763
45970
|
}
|
|
45764
45971
|
function unlinkQuiet(file) {
|
|
45765
45972
|
try {
|
|
45766
|
-
|
|
45973
|
+
unlinkSync4(file);
|
|
45767
45974
|
} catch {
|
|
45768
45975
|
}
|
|
45769
45976
|
}
|
|
45770
45977
|
function atomicWrite(file, content, mode = 420) {
|
|
45771
45978
|
const secret = mode === SECRET_MODE;
|
|
45772
|
-
const dir =
|
|
45773
|
-
|
|
45774
|
-
const tmp =
|
|
45979
|
+
const dir = path10.dirname(file);
|
|
45980
|
+
mkdirSync10(dir, { recursive: true, ...secret ? { mode: SECRET_DIR_MODE } : {} });
|
|
45981
|
+
const tmp = path10.join(
|
|
45775
45982
|
dir,
|
|
45776
|
-
`.${
|
|
45983
|
+
`.${path10.basename(file)}.${process.pid}.${randomBytes5(4).toString("hex")}.tmp`
|
|
45777
45984
|
);
|
|
45778
45985
|
let renamed = false;
|
|
45779
45986
|
try {
|
|
45780
|
-
|
|
45987
|
+
writeFileSync10(tmp, content, { encoding: "utf-8", mode });
|
|
45781
45988
|
if (secret) chmodOwnerOnly(tmp);
|
|
45782
45989
|
renameWithRetry(tmp, file);
|
|
45783
45990
|
renamed = true;
|
|
@@ -45791,18 +45998,18 @@ var LOCK_RETRY_MS3 = 50;
|
|
|
45791
45998
|
var LOCK_STALE_MS3 = 5e3;
|
|
45792
45999
|
function withFileLock(file, fn) {
|
|
45793
46000
|
const lock = `${file}.lock`;
|
|
45794
|
-
|
|
46001
|
+
mkdirSync10(path10.dirname(file), { recursive: true, mode: SECRET_DIR_MODE });
|
|
45795
46002
|
let held = false;
|
|
45796
46003
|
for (let attempt = 0; attempt < LOCK_RETRIES && !held; attempt++) {
|
|
45797
46004
|
try {
|
|
45798
|
-
|
|
46005
|
+
writeFileSync10(lock, `${process.pid}
|
|
45799
46006
|
`, { flag: "wx" });
|
|
45800
46007
|
held = true;
|
|
45801
46008
|
} catch (err) {
|
|
45802
46009
|
if (err.code !== "EEXIST") throw err;
|
|
45803
46010
|
let ageMs = Infinity;
|
|
45804
46011
|
try {
|
|
45805
|
-
ageMs = Date.now() -
|
|
46012
|
+
ageMs = Date.now() - statSync7(lock).mtimeMs;
|
|
45806
46013
|
} catch {
|
|
45807
46014
|
ageMs = Infinity;
|
|
45808
46015
|
}
|
|
@@ -45844,7 +46051,7 @@ function writeHomeKeyLine(profileName, privateKey, vaultId) {
|
|
|
45844
46051
|
}
|
|
45845
46052
|
function readVaultId(vaultRoot) {
|
|
45846
46053
|
try {
|
|
45847
|
-
const raw = readOrNull(
|
|
46054
|
+
const raw = readOrNull(path10.join(vaultRoot, "heyspex.yaml"));
|
|
45848
46055
|
return raw === null ? null : parseVaultId(raw);
|
|
45849
46056
|
} catch {
|
|
45850
46057
|
return null;
|
|
@@ -45987,7 +46194,7 @@ function legacyDecryptAllSync(vaultRoot, name) {
|
|
|
45987
46194
|
}
|
|
45988
46195
|
function initProfile(vaultRoot, name = DEFAULT_PROFILE2, opts = {}) {
|
|
45989
46196
|
const pFile = profilePath(vaultRoot, name);
|
|
45990
|
-
if (
|
|
46197
|
+
if (existsSync12(pFile) && !opts.force) {
|
|
45991
46198
|
throw new EnvVaultConfigError(
|
|
45992
46199
|
`Env profile "${name}" already exists at ${pFile}. Pass --force to re-key it (this discards existing values and mints a new keypair).`
|
|
45993
46200
|
);
|
|
@@ -46029,7 +46236,7 @@ function saveProfile(vaultRoot, name, profile) {
|
|
|
46029
46236
|
function listProfiles(vaultRoot) {
|
|
46030
46237
|
const dir = envDir(vaultRoot);
|
|
46031
46238
|
try {
|
|
46032
|
-
if (!
|
|
46239
|
+
if (!statSync7(dir).isDirectory()) return [];
|
|
46033
46240
|
} catch {
|
|
46034
46241
|
return [];
|
|
46035
46242
|
}
|
|
@@ -46373,7 +46580,7 @@ function removeGrant(vaultRoot, profileName, name, opts = {}) {
|
|
|
46373
46580
|
}
|
|
46374
46581
|
async function exportProfile(vaultRoot, name = DEFAULT_PROFILE2, opts = {}) {
|
|
46375
46582
|
const target = dotenvPath(vaultRoot);
|
|
46376
|
-
if (
|
|
46583
|
+
if (existsSync12(target) && !opts.force) {
|
|
46377
46584
|
throw new EnvVaultConfigError(
|
|
46378
46585
|
`${target} already exists. Pass --force to overwrite it (it is disposable \u2014 the encrypted profile is the system of record).`
|
|
46379
46586
|
);
|
|
@@ -46385,10 +46592,10 @@ async function exportProfile(vaultRoot, name = DEFAULT_PROFILE2, opts = {}) {
|
|
|
46385
46592
|
return { path: target };
|
|
46386
46593
|
}
|
|
46387
46594
|
function resolveVaultPath(vaultRoot, filePath) {
|
|
46388
|
-
const root =
|
|
46389
|
-
const resolved =
|
|
46390
|
-
const rel =
|
|
46391
|
-
if (
|
|
46595
|
+
const root = path10.resolve(vaultRoot);
|
|
46596
|
+
const resolved = path10.resolve(root, filePath);
|
|
46597
|
+
const rel = path10.relative(root, resolved);
|
|
46598
|
+
if (path10.isAbsolute(filePath) || path10.isAbsolute(rel) || rel === ".." || rel.startsWith(`..${path10.sep}`)) {
|
|
46392
46599
|
throw new EnvVaultConfigError(
|
|
46393
46600
|
`Refusing to touch "${filePath}" \u2014 it resolves outside the vault root (${root}). Vault file paths must stay inside the repo (no "..", no absolute paths).`
|
|
46394
46601
|
);
|
|
@@ -46402,12 +46609,12 @@ function writeTextFile(vaultRoot, filePath, content) {
|
|
|
46402
46609
|
atomicWrite(resolveVaultPath(vaultRoot, filePath), content, SECRET_MODE);
|
|
46403
46610
|
}
|
|
46404
46611
|
function pathExists(vaultRoot, filePath) {
|
|
46405
|
-
return
|
|
46612
|
+
return existsSync12(resolveVaultPath(vaultRoot, filePath));
|
|
46406
46613
|
}
|
|
46407
46614
|
function fileMtime(vaultRoot, filePath) {
|
|
46408
46615
|
const resolved = resolveVaultPath(vaultRoot, filePath);
|
|
46409
46616
|
try {
|
|
46410
|
-
return
|
|
46617
|
+
return statSync7(resolved).mtimeMs;
|
|
46411
46618
|
} catch {
|
|
46412
46619
|
return null;
|
|
46413
46620
|
}
|
|
@@ -46436,7 +46643,7 @@ function listRepoSourceFiles(vaultRoot) {
|
|
|
46436
46643
|
for (const entry of entries) {
|
|
46437
46644
|
const childRel = rel === "" ? entry.name : `${rel}/${entry.name}`;
|
|
46438
46645
|
if (entry.isDirectory()) {
|
|
46439
|
-
if (!WALK_SKIP_DIRS.has(entry.name)) walk(
|
|
46646
|
+
if (!WALK_SKIP_DIRS.has(entry.name)) walk(path10.join(dir, entry.name), childRel);
|
|
46440
46647
|
} else if (entry.isFile()) {
|
|
46441
46648
|
out.push(childRel);
|
|
46442
46649
|
}
|
|
@@ -46462,7 +46669,7 @@ function isKeysFileGitignored(vaultRoot) {
|
|
|
46462
46669
|
if (res.status === 1) return false;
|
|
46463
46670
|
} catch {
|
|
46464
46671
|
}
|
|
46465
|
-
const gitignore = readOrNull(
|
|
46672
|
+
const gitignore = readOrNull(path10.join(vaultRoot, ".gitignore"));
|
|
46466
46673
|
if (gitignore === null) return false;
|
|
46467
46674
|
const ignorePatterns = /* @__PURE__ */ new Set([
|
|
46468
46675
|
".heyspex/env.keys",
|
|
@@ -46584,7 +46791,7 @@ async function confirm(question) {
|
|
|
46584
46791
|
function findRepoRoot(start) {
|
|
46585
46792
|
let dir = resolve5(start);
|
|
46586
46793
|
for (; ; ) {
|
|
46587
|
-
if (
|
|
46794
|
+
if (existsSync13(join16(dir, ".git"))) return dir;
|
|
46588
46795
|
const parent = dirname6(dir);
|
|
46589
46796
|
if (parent === dir) return null;
|
|
46590
46797
|
dir = parent;
|
|
@@ -46592,7 +46799,7 @@ function findRepoRoot(start) {
|
|
|
46592
46799
|
}
|
|
46593
46800
|
function readFile2(p) {
|
|
46594
46801
|
try {
|
|
46595
|
-
return
|
|
46802
|
+
return readFileSync17(p, "utf-8");
|
|
46596
46803
|
} catch {
|
|
46597
46804
|
return null;
|
|
46598
46805
|
}
|
|
@@ -46600,12 +46807,12 @@ function readFile2(p) {
|
|
|
46600
46807
|
function connectCliDeps(repoRoot) {
|
|
46601
46808
|
return {
|
|
46602
46809
|
repoRoot,
|
|
46603
|
-
fileExists: (p) =>
|
|
46810
|
+
fileExists: (p) => existsSync13(p),
|
|
46604
46811
|
readFile: readFile2,
|
|
46605
|
-
writeFile: (p, c) =>
|
|
46606
|
-
mkdir: (p) =>
|
|
46812
|
+
writeFile: (p, c) => writeFileSync11(p, c, "utf-8"),
|
|
46813
|
+
mkdir: (p) => mkdirSync11(p, { recursive: true }),
|
|
46607
46814
|
joinPath: (...s) => join16(...s),
|
|
46608
|
-
basename: (p) =>
|
|
46815
|
+
basename: (p) => basename5(p),
|
|
46609
46816
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
46610
46817
|
spawn: (command, spawnArgs, opts) => {
|
|
46611
46818
|
const res = spawnSync5(command, spawnArgs, {
|
|
@@ -46654,7 +46861,7 @@ async function main() {
|
|
|
46654
46861
|
const launchCwd = process4.env.HS_JOIN_CWD ? resolve5(process4.env.HS_JOIN_CWD) : process4.cwd();
|
|
46655
46862
|
const walked = findRepoRoot(launchCwd);
|
|
46656
46863
|
const rawHint = walked ?? (process4.env.HS_VAULT_ROOT ? resolve5(process4.env.HS_VAULT_ROOT) : null);
|
|
46657
|
-
const repoRootHint = rawHint &&
|
|
46864
|
+
const repoRootHint = rawHint && existsSync13(join16(rawHint, "package.json")) ? rawHint : null;
|
|
46658
46865
|
let userName = null;
|
|
46659
46866
|
try {
|
|
46660
46867
|
userName = userInfo().username || null;
|
|
@@ -46667,7 +46874,7 @@ async function main() {
|
|
|
46667
46874
|
platform: process4.platform,
|
|
46668
46875
|
interactive: INTERACTIVE,
|
|
46669
46876
|
userName,
|
|
46670
|
-
pathExists: (p) =>
|
|
46877
|
+
pathExists: (p) => existsSync13(p),
|
|
46671
46878
|
readFile: readFile2,
|
|
46672
46879
|
joinPath: (...s) => join16(...s),
|
|
46673
46880
|
resolvePath: (...s) => resolve5(...s),
|
|
@@ -46716,7 +46923,7 @@ async function main() {
|
|
|
46716
46923
|
connect: {
|
|
46717
46924
|
isConnected: (repoRoot) => Promise.resolve(
|
|
46718
46925
|
runConnectChecks(repoRoot, {
|
|
46719
|
-
fileExists: (p) =>
|
|
46926
|
+
fileExists: (p) => existsSync13(p),
|
|
46720
46927
|
readFile: readFile2,
|
|
46721
46928
|
joinPath: (...s) => join16(...s)
|
|
46722
46929
|
}).connected
|