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-vault.mjs
CHANGED
|
@@ -129,9 +129,9 @@ function resolveBearer({ envToken, credentialsText, accountId: accountId2, legac
|
|
|
129
129
|
if (entry?.mcp_token) return { token: entry.mcp_token, source: "credentials-file" };
|
|
130
130
|
return { token: null, source: null };
|
|
131
131
|
}
|
|
132
|
-
function readTextOrNull(read,
|
|
132
|
+
function readTextOrNull(read, path6) {
|
|
133
133
|
try {
|
|
134
|
-
const raw = read(
|
|
134
|
+
const raw = read(path6);
|
|
135
135
|
return typeof raw === "string" ? raw : null;
|
|
136
136
|
} catch {
|
|
137
137
|
return null;
|
|
@@ -196,13 +196,13 @@ var init_session_owner = __esm({
|
|
|
196
196
|
});
|
|
197
197
|
|
|
198
198
|
// spex-console/src/lib/heyspex-home.ts
|
|
199
|
-
import { homedir as
|
|
199
|
+
import { homedir as homedir7 } from "node:os";
|
|
200
200
|
function heyspexHome2(env = process.env) {
|
|
201
201
|
const override = env?.HEYSPEX_HOME;
|
|
202
202
|
if (typeof override === "string" && override.trim().length > 0) {
|
|
203
203
|
return override.trim();
|
|
204
204
|
}
|
|
205
|
-
return
|
|
205
|
+
return homedir7();
|
|
206
206
|
}
|
|
207
207
|
var init_heyspex_home2 = __esm({
|
|
208
208
|
"spex-console/src/lib/heyspex-home.ts"() {
|
|
@@ -230,7 +230,7 @@ var init_types = __esm({
|
|
|
230
230
|
});
|
|
231
231
|
|
|
232
232
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
233
|
-
import { existsSync as
|
|
233
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
234
234
|
import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
|
|
235
235
|
function vaultRegistryDir(opts = {}) {
|
|
236
236
|
return opts.registryDir ?? join7(heyspexHome2(), ".heyspex");
|
|
@@ -337,9 +337,9 @@ function parseVaultRegistryFile(json) {
|
|
|
337
337
|
}
|
|
338
338
|
function readRegistryJson(opts) {
|
|
339
339
|
const filePath = vaultRegistryFilePath(opts);
|
|
340
|
-
if (!
|
|
340
|
+
if (!existsSync5(filePath)) return { kind: "missing" };
|
|
341
341
|
try {
|
|
342
|
-
const raw =
|
|
342
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
343
343
|
const json = JSON.parse(raw);
|
|
344
344
|
if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
|
|
345
345
|
return { kind: "ok", raw, json };
|
|
@@ -409,10 +409,10 @@ function assertNotRealHomeRegistryUnderTest(filePath) {
|
|
|
409
409
|
function writeVaultRegistry(registry, opts = {}) {
|
|
410
410
|
const filePath = vaultRegistryFilePath(opts);
|
|
411
411
|
assertNotRealHomeRegistryUnderTest(filePath);
|
|
412
|
-
|
|
412
|
+
mkdirSync4(dirname4(filePath), { recursive: true });
|
|
413
413
|
const tmpPath = `${filePath}.tmp`;
|
|
414
|
-
|
|
415
|
-
|
|
414
|
+
writeFileSync4(tmpPath, serializeVaultRegistry(registry), "utf8");
|
|
415
|
+
renameSync4(tmpPath, filePath);
|
|
416
416
|
}
|
|
417
417
|
function isValidId(id) {
|
|
418
418
|
if (!isNonEmptyString(id)) return false;
|
|
@@ -581,10 +581,10 @@ function rekeyVaultRowsToCloudIds(registry, proofs, plan) {
|
|
|
581
581
|
};
|
|
582
582
|
}
|
|
583
583
|
function planRegistryMigration(registry, opts = {}) {
|
|
584
|
-
const pathExists = opts.pathExists ?? ((p) =>
|
|
584
|
+
const pathExists = opts.pathExists ?? ((p) => existsSync5(p));
|
|
585
585
|
const readMarker = opts.readMarker ?? ((root) => {
|
|
586
586
|
try {
|
|
587
|
-
return
|
|
587
|
+
return readFileSync7(join7(root, "heyspex.yaml"), "utf8");
|
|
588
588
|
} catch {
|
|
589
589
|
return null;
|
|
590
590
|
}
|
|
@@ -735,8 +735,8 @@ var init_registry_file = __esm({
|
|
|
735
735
|
});
|
|
736
736
|
|
|
737
737
|
// scripts/lib/dev-mode.mjs
|
|
738
|
-
import { mkdirSync as
|
|
739
|
-
import { homedir as
|
|
738
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
739
|
+
import { homedir as homedir8 } from "node:os";
|
|
740
740
|
import { join as join8 } from "node:path";
|
|
741
741
|
function devModeFilePath(homeDir = heyspexHome()) {
|
|
742
742
|
return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
|
|
@@ -791,6 +791,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile: readFile2 =
|
|
|
791
791
|
return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
|
|
792
792
|
}
|
|
793
793
|
}
|
|
794
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
795
|
+
if (source !== "local-override") return "not-applicable";
|
|
796
|
+
if (!cloudKnown) return "unverified";
|
|
797
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
798
|
+
}
|
|
794
799
|
function resolveDevMode({
|
|
795
800
|
env = process.env,
|
|
796
801
|
homeDir = heyspexHome(),
|
|
@@ -830,6 +835,7 @@ function resolveDevMode({
|
|
|
830
835
|
const semantics = deriveSemantics(mode, reliance);
|
|
831
836
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
832
837
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
838
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
833
839
|
return {
|
|
834
840
|
mode,
|
|
835
841
|
reliance: semantics.reliance,
|
|
@@ -847,7 +853,8 @@ function resolveDevMode({
|
|
|
847
853
|
present: local.record !== null,
|
|
848
854
|
record: local.record,
|
|
849
855
|
parseError: local.parseError,
|
|
850
|
-
staleAgainstCloud
|
|
856
|
+
staleAgainstCloud,
|
|
857
|
+
cloudCheck
|
|
851
858
|
},
|
|
852
859
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
853
860
|
};
|
|
@@ -861,7 +868,7 @@ var init_dev_mode = __esm({
|
|
|
861
868
|
CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
862
869
|
LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
863
870
|
DEV_MODE_FILENAME = "dev-mode.json";
|
|
864
|
-
defaultReadFile = (p) =>
|
|
871
|
+
defaultReadFile = (p) => readFileSync8(p, "utf8");
|
|
865
872
|
}
|
|
866
873
|
});
|
|
867
874
|
|
|
@@ -936,12 +943,12 @@ var init_default_workspace = __esm({
|
|
|
936
943
|
});
|
|
937
944
|
|
|
938
945
|
// spex-console/src/lib/cli/vault-workspace-shared.ts
|
|
939
|
-
import { readFileSync as
|
|
946
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
940
947
|
function resolveConfiguredAccountId(opts = {}) {
|
|
941
948
|
return resolveSessionOwnerUncached({
|
|
942
949
|
env: opts.env ?? process.env,
|
|
943
950
|
homeDir: opts.homeDir ?? heyspexHome2(),
|
|
944
|
-
readFile: opts.readFile ?? ((p) =>
|
|
951
|
+
readFile: opts.readFile ?? ((p) => readFileSync10(p, "utf-8"))
|
|
945
952
|
});
|
|
946
953
|
}
|
|
947
954
|
function resolveCloudAccountIdOrRefuse(opts = {}) {
|
|
@@ -962,10 +969,10 @@ function classifyHttpFailureReason(res) {
|
|
|
962
969
|
if (res.status === 401) return "unauthorized";
|
|
963
970
|
return "server-error";
|
|
964
971
|
}
|
|
965
|
-
async function cloudHttpRequest(http, method,
|
|
966
|
-
const scopeHeaders = http.scope ? http.scope.headersFor(method,
|
|
972
|
+
async function cloudHttpRequest(http, method, path6, body) {
|
|
973
|
+
const scopeHeaders = http.scope ? http.scope.headersFor(method, path6) : {};
|
|
967
974
|
try {
|
|
968
|
-
const res = await http.fetchImpl(`${http.consoleUrl}${
|
|
975
|
+
const res = await http.fetchImpl(`${http.consoleUrl}${path6}`, {
|
|
969
976
|
method,
|
|
970
977
|
headers: {
|
|
971
978
|
"content-type": "application/json",
|
|
@@ -1036,8 +1043,8 @@ async function classifyWorkspaceConflictHttp(id, conflictJson, http) {
|
|
|
1036
1043
|
if (!Array.isArray(rows)) return { written: false, reason: "server-error" };
|
|
1037
1044
|
return rows.some((w) => w.id === id) ? { written: true } : { written: false, reason: "owned-by-other-account" };
|
|
1038
1045
|
}
|
|
1039
|
-
async function mutateHttp(http, method,
|
|
1040
|
-
const res = await cloudHttpRequest(http, method,
|
|
1046
|
+
async function mutateHttp(http, method, path6, body, landed) {
|
|
1047
|
+
const res = await cloudHttpRequest(http, method, path6, body);
|
|
1041
1048
|
if (res.ok) return landed;
|
|
1042
1049
|
if (res.status === 404) return "not_found";
|
|
1043
1050
|
if (res.status === 403 && isOwnedByOtherAccountCode(errorCodeOf(res.json))) {
|
|
@@ -3432,13 +3439,13 @@ var init_spec_read_service = __esm({
|
|
|
3432
3439
|
});
|
|
3433
3440
|
|
|
3434
3441
|
// spex-console/bin/hs-vault.ts
|
|
3435
|
-
import { existsSync as
|
|
3436
|
-
import
|
|
3442
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12 } from "node:fs";
|
|
3443
|
+
import path5 from "node:path";
|
|
3437
3444
|
import process4 from "node:process";
|
|
3438
3445
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
3439
3446
|
|
|
3440
3447
|
// spex-console/src/lib/cli/control-shared.ts
|
|
3441
|
-
import
|
|
3448
|
+
import path2 from "node:path";
|
|
3442
3449
|
|
|
3443
3450
|
// scripts/lib/hs-command.mjs
|
|
3444
3451
|
import { existsSync } from "node:fs";
|
|
@@ -3462,8 +3469,8 @@ var HS_DISPATCHER_PREFIX = HS_DISPATCHER_PATH ? `node ${shellQuote(HS_DISPATCHER
|
|
|
3462
3469
|
init_heyspex_home();
|
|
3463
3470
|
init_credentials();
|
|
3464
3471
|
init_session_owner();
|
|
3465
|
-
import { readFileSync as
|
|
3466
|
-
import { homedir as
|
|
3472
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3473
|
+
import { homedir as homedir6 } from "node:os";
|
|
3467
3474
|
import { join as join6 } from "node:path";
|
|
3468
3475
|
|
|
3469
3476
|
// vendor/heyspex-mcp/vault-less-endpoints.mjs
|
|
@@ -3476,14 +3483,14 @@ var VAULT_LESS_ENDPOINTS = Object.freeze([
|
|
|
3476
3483
|
"GET /api/workspaces",
|
|
3477
3484
|
"* /api/admin/dev-mode"
|
|
3478
3485
|
]);
|
|
3479
|
-
function pathnameOf(
|
|
3480
|
-
const raw = String(
|
|
3486
|
+
function pathnameOf(path6) {
|
|
3487
|
+
const raw = String(path6 ?? "").split("?")[0].split("#")[0];
|
|
3481
3488
|
const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
|
|
3482
3489
|
return noTrailing || "/";
|
|
3483
3490
|
}
|
|
3484
|
-
function isVaultLessEndpoint(method,
|
|
3491
|
+
function isVaultLessEndpoint(method, path6) {
|
|
3485
3492
|
const m = String(method || "GET").toUpperCase();
|
|
3486
|
-
const pathname = pathnameOf(
|
|
3493
|
+
const pathname = pathnameOf(path6);
|
|
3487
3494
|
return VAULT_LESS_ENDPOINTS.some((row) => {
|
|
3488
3495
|
const [rowMethod, rowPath] = row.split(" ");
|
|
3489
3496
|
return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
|
|
@@ -3503,7 +3510,9 @@ import {
|
|
|
3503
3510
|
import { basename, join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
3504
3511
|
|
|
3505
3512
|
// scripts/lib/shared-path-helpers.mjs
|
|
3513
|
+
import { realpathSync } from "node:fs";
|
|
3506
3514
|
import { isAbsolute, resolve as resolve2, sep } from "node:path";
|
|
3515
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3507
3516
|
function resolveCodeRoot({
|
|
3508
3517
|
explicit,
|
|
3509
3518
|
packageRoot,
|
|
@@ -3522,9 +3531,30 @@ function resolveCodeRoot({
|
|
|
3522
3531
|
|
|
3523
3532
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
3524
3533
|
init_heyspex_home();
|
|
3525
|
-
import { existsSync as
|
|
3526
|
-
import { homedir as
|
|
3527
|
-
|
|
3534
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
3535
|
+
import { homedir as homedir5, tmpdir } from "node:os";
|
|
3536
|
+
|
|
3537
|
+
// vendor/heyspex-mcp/usable-dir-env.mjs
|
|
3538
|
+
import { statSync } from "node:fs";
|
|
3539
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
3540
|
+
|
|
3541
|
+
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
3542
|
+
import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
|
|
3543
|
+
|
|
3544
|
+
// vendor/heyspex-mcp/vault-binding.mjs
|
|
3545
|
+
import {
|
|
3546
|
+
copyFileSync,
|
|
3547
|
+
existsSync as existsSync3,
|
|
3548
|
+
mkdirSync as mkdirSync3,
|
|
3549
|
+
readFileSync as readFileSync4,
|
|
3550
|
+
realpathSync as realpathSync2,
|
|
3551
|
+
renameSync as renameSync3,
|
|
3552
|
+
statSync as statSync2,
|
|
3553
|
+
unlinkSync,
|
|
3554
|
+
writeFileSync as writeFileSync3
|
|
3555
|
+
} from "node:fs";
|
|
3556
|
+
import { homedir as homedir4 } from "node:os";
|
|
3557
|
+
import path from "node:path";
|
|
3528
3558
|
|
|
3529
3559
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
3530
3560
|
var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMATCH", "VAULT_NOT_REGISTERED"]);
|
|
@@ -3628,11 +3658,11 @@ function readPairedBearer(opts = {}) {
|
|
|
3628
3658
|
function readPairedToken(opts = {}) {
|
|
3629
3659
|
return readPairedBearer(opts).token ?? void 0;
|
|
3630
3660
|
}
|
|
3631
|
-
function scopeHeadersFor(scope, method,
|
|
3661
|
+
function scopeHeadersFor(scope, method, path6) {
|
|
3632
3662
|
if (scope === "none") {
|
|
3633
|
-
if (!isVaultLessEndpoint(method,
|
|
3663
|
+
if (!isVaultLessEndpoint(method, path6)) {
|
|
3634
3664
|
throw new Error(
|
|
3635
|
-
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${
|
|
3665
|
+
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path6} is vault-scoped and must name { accountId, vaultId }.`
|
|
3636
3666
|
);
|
|
3637
3667
|
}
|
|
3638
3668
|
return {};
|
|
@@ -3643,7 +3673,7 @@ function cloudRequestScope(scope) {
|
|
|
3643
3673
|
const named = { accountId: scope.accountId, vaultId: scope.vaultId };
|
|
3644
3674
|
return {
|
|
3645
3675
|
...named,
|
|
3646
|
-
headersFor: (method,
|
|
3676
|
+
headersFor: (method, path6) => isVaultLessEndpoint(method, path6) ? {} : scopeHeadersFor(named, method, path6)
|
|
3647
3677
|
};
|
|
3648
3678
|
}
|
|
3649
3679
|
function withCloudScope(deps, accountId2, vaultId) {
|
|
@@ -3654,8 +3684,8 @@ function withCloudScope(deps, accountId2, vaultId) {
|
|
|
3654
3684
|
// scripts/lib/console-url-resolver.mjs
|
|
3655
3685
|
init_heyspex_home();
|
|
3656
3686
|
init_dev_mode();
|
|
3657
|
-
import { readFileSync as
|
|
3658
|
-
import { homedir as
|
|
3687
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
3688
|
+
import { homedir as homedir9 } from "node:os";
|
|
3659
3689
|
import { join as join9 } from "node:path";
|
|
3660
3690
|
var DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
|
|
3661
3691
|
function trimTrailingSlash(url) {
|
|
@@ -3693,7 +3723,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile: readFile
|
|
|
3693
3723
|
}
|
|
3694
3724
|
}
|
|
3695
3725
|
function defaultReadFile2(p) {
|
|
3696
|
-
return
|
|
3726
|
+
return readFileSync9(p, "utf8");
|
|
3697
3727
|
}
|
|
3698
3728
|
function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery = defaultReadFile2 } = {}) {
|
|
3699
3729
|
if (!repoRoot) return null;
|
|
@@ -3793,7 +3823,7 @@ var HELP = [
|
|
|
3793
3823
|
init_heyspex_home2();
|
|
3794
3824
|
init_registry_file();
|
|
3795
3825
|
import { spawnSync } from "node:child_process";
|
|
3796
|
-
import { existsSync as
|
|
3826
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
3797
3827
|
import { join as join10 } from "node:path";
|
|
3798
3828
|
import { createInterface } from "node:readline/promises";
|
|
3799
3829
|
init_credentials();
|
|
@@ -3802,7 +3832,7 @@ init_session_owner();
|
|
|
3802
3832
|
// vendor/heyspex-mcp/whoami.mjs
|
|
3803
3833
|
init_heyspex_home();
|
|
3804
3834
|
init_credentials();
|
|
3805
|
-
import { homedir as
|
|
3835
|
+
import { homedir as homedir10 } from "node:os";
|
|
3806
3836
|
var SESSION_PATH = "/api/mcp/session";
|
|
3807
3837
|
async function probeCredentialIdentity({ consoleUrl, token, fetchImpl = fetch } = {}) {
|
|
3808
3838
|
if (!token) return { kind: "no_credential" };
|
|
@@ -3877,12 +3907,15 @@ var HELP2 = [
|
|
|
3877
3907
|
"(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
|
|
3878
3908
|
"copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
|
|
3879
3909
|
"plus a generated CLAUDE.md, the kept-principles index, and the template_version",
|
|
3880
|
-
"pin; with --git, one initial `git init` + commit.
|
|
3881
|
-
"holds heyspex.yaml
|
|
3882
|
-
"
|
|
3883
|
-
"
|
|
3884
|
-
"
|
|
3885
|
-
"
|
|
3910
|
+
"pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
|
|
3911
|
+
"holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
|
|
3912
|
+
"vault-template row names: only missing shipped files are added, template_version",
|
|
3913
|
+
"is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
|
|
3914
|
+
"nothing existing is ever overwritten (an existing CLAUDE.md gets a",
|
|
3915
|
+
"CLAUDE.md.template beside it), the result lists each manifest row as created or",
|
|
3916
|
+
"kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
|
|
3917
|
+
"names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
|
|
3918
|
+
"non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
|
|
3886
3919
|
"",
|
|
3887
3920
|
"Flags:",
|
|
3888
3921
|
" --console-url <url> console_url value (default: the mode-aware console --",
|
|
@@ -3955,9 +3988,9 @@ function parseMarkerVaultPath(content) {
|
|
|
3955
3988
|
function readPinnedVaultRoot(repoRoot, deps) {
|
|
3956
3989
|
const pins = [];
|
|
3957
3990
|
for (const rel of SATELLITE_MANIFEST_RELS) {
|
|
3958
|
-
const
|
|
3959
|
-
if (!deps.fileExists(
|
|
3960
|
-
const raw = deps.readFile(
|
|
3991
|
+
const path6 = deps.joinPath(repoRoot, ...rel);
|
|
3992
|
+
if (!deps.fileExists(path6)) continue;
|
|
3993
|
+
const raw = deps.readFile(path6);
|
|
3961
3994
|
if (!raw) continue;
|
|
3962
3995
|
let value;
|
|
3963
3996
|
try {
|
|
@@ -4222,7 +4255,7 @@ function isInteractiveTerminal() {
|
|
|
4222
4255
|
}
|
|
4223
4256
|
function defaultIdentityGateIo(opts) {
|
|
4224
4257
|
const consoleUrl = opts.consoleUrl.trim().replace(/\/+$/, "");
|
|
4225
|
-
const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join10(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) =>
|
|
4258
|
+
const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join10(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) => existsSync6(p));
|
|
4226
4259
|
return {
|
|
4227
4260
|
consoleUrl,
|
|
4228
4261
|
interactive: opts.interactive ?? isInteractiveTerminal(),
|
|
@@ -4294,7 +4327,7 @@ init_postgres();
|
|
|
4294
4327
|
init_neon_probe();
|
|
4295
4328
|
|
|
4296
4329
|
// spex-console/src/lib/vault-registry/repo-identity.ts
|
|
4297
|
-
import
|
|
4330
|
+
import path3 from "node:path";
|
|
4298
4331
|
|
|
4299
4332
|
// spex-console/src/lib/vault-registry/vault-workspace-store.ts
|
|
4300
4333
|
function mapProbeFailureReason(reason) {
|
|
@@ -4775,7 +4808,7 @@ function buildCloseNotDestroyConfirm(kind, verb, displayName) {
|
|
|
4775
4808
|
}
|
|
4776
4809
|
|
|
4777
4810
|
// spex-console/src/lib/vault-registry/list.ts
|
|
4778
|
-
import { existsSync as
|
|
4811
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
4779
4812
|
init_account_pointer();
|
|
4780
4813
|
init_registry_file();
|
|
4781
4814
|
async function defaultNeonReadTierEnabled() {
|
|
@@ -4808,7 +4841,7 @@ async function listVaultsForWorkspace(accountId2, workspaceId, deps = {}) {
|
|
|
4808
4841
|
const activeId = await getActiveVault(accountId2, sharedPointerDeps) ?? await adoptSole(accountId2, sharedPointerDeps);
|
|
4809
4842
|
const rows = deps.includeArchived ? allRows : allRows.filter((v) => !v.archived);
|
|
4810
4843
|
const scoped = workspaceId ? rows.filter((v) => v.workspace_id === workspaceId) : rows;
|
|
4811
|
-
const pathExists = deps.pathExists ??
|
|
4844
|
+
const pathExists = deps.pathExists ?? existsSync7;
|
|
4812
4845
|
const anyPathUnusable = scoped.some((v) => !v.repo_root_abs || !pathExists(v.repo_root_abs));
|
|
4813
4846
|
const neonBackedServeable = anyPathUnusable && neonRows !== null ? await (deps.neonReadTierEnabled ?? defaultNeonReadTierEnabled)() : false;
|
|
4814
4847
|
return {
|
|
@@ -4976,9 +5009,9 @@ async function unregisterVault(input, deps = {}) {
|
|
|
4976
5009
|
}
|
|
4977
5010
|
|
|
4978
5011
|
// spex-console/src/lib/vault-resolve/index.ts
|
|
4979
|
-
import { existsSync as
|
|
5012
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
4980
5013
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4981
|
-
import
|
|
5014
|
+
import path4 from "node:path";
|
|
4982
5015
|
var UNREGISTERED_VAULT_ID2 = "unregistered";
|
|
4983
5016
|
var NoActiveVaultError = class _NoActiveVaultError extends Error {
|
|
4984
5017
|
constructor(message2 = "resolveActiveVaultRoot: no active vault found -- no explicit vaultId, no heyspex.yaml or specs/ directory at or above cwd, no HEYSPEX_VAULT_ROOT/HEYSPEX_VAULT_DIR env, and no account active_vault_id. Run `hs vault register` to register a vault.") {
|
|
@@ -4986,6 +5019,22 @@ var NoActiveVaultError = class _NoActiveVaultError extends Error {
|
|
|
4986
5019
|
this.name = "NoActiveVaultError";
|
|
4987
5020
|
Object.setPrototypeOf(this, _NoActiveVaultError.prototype);
|
|
4988
5021
|
}
|
|
5022
|
+
/** Stable machine code ([[HS-TYJA]]), shared with `vault-binding.mjs`. */
|
|
5023
|
+
code = "VAULT_UNBOUND";
|
|
5024
|
+
/** The exact repair command. */
|
|
5025
|
+
fix = "hs vault bind --vault <name|id>";
|
|
5026
|
+
/** The folder resolution walked from (forward slashes), when known. */
|
|
5027
|
+
from = null;
|
|
5028
|
+
};
|
|
5029
|
+
var VaultResolutionFailure = class _VaultResolutionFailure extends NoActiveVaultError {
|
|
5030
|
+
constructor(code, message2, fix, from) {
|
|
5031
|
+
super(message2);
|
|
5032
|
+
this.name = "VaultResolutionError";
|
|
5033
|
+
this.code = code;
|
|
5034
|
+
this.fix = fix;
|
|
5035
|
+
this.from = from ? from.replace(/\\/g, "/") : null;
|
|
5036
|
+
Object.setPrototypeOf(this, _VaultResolutionFailure.prototype);
|
|
5037
|
+
}
|
|
4989
5038
|
};
|
|
4990
5039
|
async function defaultRegistryLookup() {
|
|
4991
5040
|
return null;
|
|
@@ -4994,13 +5043,13 @@ async function defaultAccountPointerLookup() {
|
|
|
4994
5043
|
return null;
|
|
4995
5044
|
}
|
|
4996
5045
|
function finalize(vaultId, repoRootAbs, source) {
|
|
4997
|
-
const resolvedRoot =
|
|
5046
|
+
const resolvedRoot = path4.resolve(repoRootAbs);
|
|
4998
5047
|
return {
|
|
4999
5048
|
vaultId,
|
|
5000
5049
|
repoRootAbs: resolvedRoot,
|
|
5001
5050
|
// Always derived the same way regardless of which layer resolved the
|
|
5002
5051
|
// root, per this task's acceptance criteria.
|
|
5003
|
-
specsDirAbs:
|
|
5052
|
+
specsDirAbs: path4.join(resolvedRoot, "specs"),
|
|
5004
5053
|
source
|
|
5005
5054
|
};
|
|
5006
5055
|
}
|
|
@@ -5012,7 +5061,7 @@ function parseMarkerVaultId(content) {
|
|
|
5012
5061
|
}
|
|
5013
5062
|
function readMarkerVaultId(dir) {
|
|
5014
5063
|
try {
|
|
5015
|
-
return parseMarkerVaultId(
|
|
5064
|
+
return parseMarkerVaultId(readFileSync11(path4.join(dir, "heyspex.yaml"), "utf8"));
|
|
5016
5065
|
} catch {
|
|
5017
5066
|
return null;
|
|
5018
5067
|
}
|
|
@@ -5020,39 +5069,75 @@ function readMarkerVaultId(dir) {
|
|
|
5020
5069
|
function readMarkerSatellite(dir) {
|
|
5021
5070
|
let raw;
|
|
5022
5071
|
try {
|
|
5023
|
-
raw =
|
|
5072
|
+
raw = readFileSync11(path4.join(dir, "heyspex.yaml"), "utf8");
|
|
5024
5073
|
} catch {
|
|
5025
5074
|
return null;
|
|
5026
5075
|
}
|
|
5027
5076
|
const vaultPath = parseMarkerVaultPath(raw);
|
|
5028
5077
|
if (!vaultPath) return null;
|
|
5029
5078
|
const vaultId = parseMarkerVaultId(raw);
|
|
5030
|
-
const candidate =
|
|
5079
|
+
const candidate = path4.resolve(dir, vaultPath);
|
|
5031
5080
|
let vaultRootAbs = null;
|
|
5032
|
-
if (candidate.toLowerCase() !==
|
|
5081
|
+
if (candidate.toLowerCase() !== path4.resolve(dir).toLowerCase() && isVaultRootDir(candidate)) {
|
|
5033
5082
|
const theirs = readMarkerVaultId(candidate);
|
|
5034
5083
|
if (!vaultId || !theirs || theirs === vaultId) vaultRootAbs = candidate;
|
|
5035
5084
|
}
|
|
5036
5085
|
return { vaultId, vaultRootAbs };
|
|
5037
5086
|
}
|
|
5038
5087
|
function isVaultRootDir(dir) {
|
|
5039
|
-
const norm = (p) =>
|
|
5088
|
+
const norm = (p) => path4.resolve(p).replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
5040
5089
|
if (norm(dir) === norm(tmpdir2())) return false;
|
|
5041
|
-
|
|
5090
|
+
if (!existsSync8(path4.join(dir, "specs")) && readRepoBinding2(dir)) return false;
|
|
5091
|
+
return existsSync8(path4.join(dir, "heyspex.yaml")) || existsSync8(path4.join(dir, "specs"));
|
|
5092
|
+
}
|
|
5093
|
+
function parseRepoBinding(content) {
|
|
5094
|
+
const src = String(content ?? "");
|
|
5095
|
+
if (/^(vault_id|vault_root):/m.test(src)) return null;
|
|
5096
|
+
const kind = src.match(/^kind:\s*(.+?)\s*$/m)?.[1]?.replace(/^["']|["']$/g, "").trim();
|
|
5097
|
+
if (kind && kind !== "repo-binding") return null;
|
|
5098
|
+
const lines = src.split(/\r?\n/);
|
|
5099
|
+
const start = lines.findIndex((l) => /^vault:(\s|$)/.test(l));
|
|
5100
|
+
if (start === -1) return null;
|
|
5101
|
+
const unq = (v) => v.trim().replace(/\s+#.*$/, "").replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1").trim();
|
|
5102
|
+
const scalar = lines[start].slice("vault:".length).trim();
|
|
5103
|
+
if (scalar && !scalar.startsWith("#")) {
|
|
5104
|
+
const id = unq(scalar);
|
|
5105
|
+
return id ? { id, name: null, remote: null } : null;
|
|
5106
|
+
}
|
|
5107
|
+
const out = {
|
|
5108
|
+
id: null,
|
|
5109
|
+
name: null,
|
|
5110
|
+
remote: null
|
|
5111
|
+
};
|
|
5112
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
5113
|
+
const l = lines[i];
|
|
5114
|
+
if (!l.trim() || /^\s*#/.test(l)) continue;
|
|
5115
|
+
if (!/^\s+/.test(l)) break;
|
|
5116
|
+
const m = l.match(/^\s+([a-z_]+):\s*(.*)$/);
|
|
5117
|
+
if (m && (m[1] === "id" || m[1] === "name" || m[1] === "remote")) out[m[1]] = unq(m[2]) || null;
|
|
5118
|
+
}
|
|
5119
|
+
return out.id ? { id: out.id, name: out.name, remote: out.remote } : null;
|
|
5120
|
+
}
|
|
5121
|
+
function readRepoBinding2(dir) {
|
|
5122
|
+
try {
|
|
5123
|
+
return parseRepoBinding(readFileSync11(path4.join(dir, "heyspex.yaml"), "utf8"));
|
|
5124
|
+
} catch {
|
|
5125
|
+
return null;
|
|
5126
|
+
}
|
|
5042
5127
|
}
|
|
5043
5128
|
function findCwdVaultOrAttachment(startDir, stopAt) {
|
|
5044
5129
|
const deps = {
|
|
5045
|
-
fileExists: (p) =>
|
|
5130
|
+
fileExists: (p) => existsSync8(p),
|
|
5046
5131
|
readFile: (p) => {
|
|
5047
5132
|
try {
|
|
5048
|
-
return
|
|
5133
|
+
return readFileSync11(p, "utf8");
|
|
5049
5134
|
} catch {
|
|
5050
5135
|
return null;
|
|
5051
5136
|
}
|
|
5052
5137
|
},
|
|
5053
|
-
joinPath: (...segments) =>
|
|
5138
|
+
joinPath: (...segments) => path4.join(...segments)
|
|
5054
5139
|
};
|
|
5055
|
-
const boundary = stopAt ?
|
|
5140
|
+
const boundary = stopAt ? path4.resolve(stopAt) : null;
|
|
5056
5141
|
let dir = startDir;
|
|
5057
5142
|
for (let i = 0; i < 256; i += 1) {
|
|
5058
5143
|
const marked = readMarkerSatellite(dir);
|
|
@@ -5064,15 +5149,33 @@ function findCwdVaultOrAttachment(startDir, stopAt) {
|
|
|
5064
5149
|
};
|
|
5065
5150
|
}
|
|
5066
5151
|
const pinned = readPinnedVaultRoot(dir, deps).find((pin) => !isAtOrUnder(pin.value, dir));
|
|
5067
|
-
if (pinned) return { repoRootAbs:
|
|
5152
|
+
if (pinned) return { repoRootAbs: path4.resolve(pinned.value), satellite: true };
|
|
5068
5153
|
if (isVaultRootDir(dir)) return { repoRootAbs: dir, satellite: false };
|
|
5154
|
+
const binding = readRepoBinding2(dir);
|
|
5155
|
+
if (binding) return { repoRootAbs: dir, satellite: false, binding };
|
|
5069
5156
|
if (boundary && dir === boundary) return null;
|
|
5070
|
-
const parent =
|
|
5157
|
+
const parent = path4.dirname(dir);
|
|
5071
5158
|
if (parent === dir) return null;
|
|
5072
5159
|
dir = parent;
|
|
5073
5160
|
}
|
|
5074
5161
|
return null;
|
|
5075
5162
|
}
|
|
5163
|
+
function isExistingDir(p) {
|
|
5164
|
+
try {
|
|
5165
|
+
return statSync4(p).isDirectory();
|
|
5166
|
+
} catch {
|
|
5167
|
+
return false;
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
function envNotVaultError(name, raw, abs, from) {
|
|
5171
|
+
const shown = abs.replace(/\\/g, "/");
|
|
5172
|
+
return new VaultResolutionFailure(
|
|
5173
|
+
"VAULT_NOT_FOUND",
|
|
5174
|
+
`${name}=${raw} does not name a vault checkout (${shown}). Unset ${name} so the vault bound to this repo is used (\`hs vault resolve\` shows it), or point it at a vault checkout.`,
|
|
5175
|
+
`unset ${name}`,
|
|
5176
|
+
from
|
|
5177
|
+
);
|
|
5178
|
+
}
|
|
5076
5179
|
async function resolveActiveVaultRoot(options = {}) {
|
|
5077
5180
|
const registryLookup = options.registryLookup ?? defaultRegistryLookup;
|
|
5078
5181
|
const accountPointerLookup = options.accountPointerLookup ?? defaultAccountPointerLookup;
|
|
@@ -5084,7 +5187,7 @@ async function resolveActiveVaultRoot(options = {}) {
|
|
|
5084
5187
|
}
|
|
5085
5188
|
};
|
|
5086
5189
|
const finalizePathTier = async (repoRootAbs, source) => {
|
|
5087
|
-
const resolvedRoot =
|
|
5190
|
+
const resolvedRoot = path4.resolve(repoRootAbs);
|
|
5088
5191
|
const recovered = await registryLookupByRoot(resolvedRoot);
|
|
5089
5192
|
if (recovered) return finalize(recovered.id, resolvedRoot, source);
|
|
5090
5193
|
return finalize(UNREGISTERED_VAULT_ID2, resolvedRoot, source);
|
|
@@ -5104,17 +5207,79 @@ async function resolveActiveVaultRoot(options = {}) {
|
|
|
5104
5207
|
detail: `explicit vaultId "${options.vaultId}" has no registry.json row and no Neon row`
|
|
5105
5208
|
});
|
|
5106
5209
|
}
|
|
5107
|
-
const
|
|
5210
|
+
const fromDir = path4.resolve(options.cwd ?? process.cwd());
|
|
5211
|
+
const envRoot = process.env.HEYSPEX_VAULT_ROOT;
|
|
5212
|
+
if (envRoot) {
|
|
5213
|
+
const root = path4.resolve(envRoot.trim());
|
|
5214
|
+
const envIsVault = isExistingDir(root) && isVaultRootDir(root);
|
|
5215
|
+
if (!envIsVault && !await registryLookupByRoot(root)) {
|
|
5216
|
+
onTier({
|
|
5217
|
+
tier: "env-vault-root",
|
|
5218
|
+
outcome: "empty",
|
|
5219
|
+
detail: `${envRoot} is not a vault checkout`
|
|
5220
|
+
});
|
|
5221
|
+
throw envNotVaultError("HEYSPEX_VAULT_ROOT", envRoot, root, fromDir);
|
|
5222
|
+
}
|
|
5223
|
+
onTier({ tier: "env-vault-root", outcome: "resolved", detail: envRoot });
|
|
5224
|
+
return finalizePathTier(root, "env-vault-root");
|
|
5225
|
+
}
|
|
5226
|
+
onTier({ tier: "env-vault-root", outcome: "skipped", detail: "HEYSPEX_VAULT_ROOT unset" });
|
|
5227
|
+
const envDir = process.env.HEYSPEX_VAULT_DIR;
|
|
5228
|
+
if (envDir) {
|
|
5229
|
+
const specsDirFromEnv = path4.resolve(envDir.trim());
|
|
5230
|
+
const envDirIsSpecs = isExistingDir(specsDirFromEnv) && path4.basename(specsDirFromEnv).toLowerCase() === "specs";
|
|
5231
|
+
const envDirRegistered = Boolean(await registryLookupByRoot(path4.dirname(specsDirFromEnv)));
|
|
5232
|
+
if (!envDirIsSpecs && !envDirRegistered) {
|
|
5233
|
+
onTier({
|
|
5234
|
+
tier: "env-vault-dir",
|
|
5235
|
+
outcome: "empty",
|
|
5236
|
+
detail: `${envDir} is not a specs/ folder`
|
|
5237
|
+
});
|
|
5238
|
+
throw envNotVaultError("HEYSPEX_VAULT_DIR", envDir, specsDirFromEnv, fromDir);
|
|
5239
|
+
}
|
|
5240
|
+
onTier({ tier: "env-vault-dir", outcome: "resolved", detail: envDir });
|
|
5241
|
+
return finalizePathTier(path4.dirname(specsDirFromEnv), "env-vault-dir");
|
|
5242
|
+
}
|
|
5243
|
+
onTier({ tier: "env-vault-dir", outcome: "skipped", detail: "HEYSPEX_VAULT_DIR unset" });
|
|
5244
|
+
let cwdBase = path4.resolve(options.cwd ?? process.cwd());
|
|
5245
|
+
try {
|
|
5246
|
+
cwdBase = realpathSync4(cwdBase);
|
|
5247
|
+
} catch {
|
|
5248
|
+
}
|
|
5108
5249
|
const cwdMatch = findCwdVaultOrAttachment(cwdBase, options.cwdWalkRoot);
|
|
5250
|
+
if (cwdMatch?.binding) {
|
|
5251
|
+
const record = await registryLookup(cwdMatch.binding.id);
|
|
5252
|
+
const root = record ? record.repoRootAbs : null;
|
|
5253
|
+
if (root) {
|
|
5254
|
+
onTier({
|
|
5255
|
+
tier: "repo-binding",
|
|
5256
|
+
outcome: "resolved",
|
|
5257
|
+
detail: `${cwdMatch.repoRootAbs} -> ${cwdMatch.binding.id}`
|
|
5258
|
+
});
|
|
5259
|
+
return finalize(cwdMatch.binding.id, root, "repo-binding");
|
|
5260
|
+
}
|
|
5261
|
+
onTier({
|
|
5262
|
+
tier: "repo-binding",
|
|
5263
|
+
outcome: "no-registry-row",
|
|
5264
|
+
detail: `${cwdMatch.repoRootAbs}/heyspex.yaml binds ${cwdMatch.binding.id}, which has no registry row (or folder) on this machine`
|
|
5265
|
+
});
|
|
5266
|
+
const bindingDir = cwdMatch.repoRootAbs.replace(/\\/g, "/");
|
|
5267
|
+
throw new VaultResolutionFailure(
|
|
5268
|
+
"VAULT_BINDING_BROKEN",
|
|
5269
|
+
`${bindingDir}/heyspex.yaml binds this repo to vault ${cwdMatch.binding.name ?? cwdMatch.binding.id} (${cwdMatch.binding.id}), but this machine has no registered checkout of it. Clone it (or \`hs pull\` in an existing checkout), \`hs vault register <path>\`, then \`hs vault bind --vault ${cwdMatch.binding.id}\` in ${bindingDir}.`,
|
|
5270
|
+
`hs vault bind --vault ${cwdMatch.binding.id}`,
|
|
5271
|
+
cwdBase
|
|
5272
|
+
);
|
|
5273
|
+
}
|
|
5109
5274
|
if (cwdMatch?.satellite) {
|
|
5110
5275
|
onTier({ tier: "satellite-pin", outcome: "resolved", detail: cwdMatch.repoRootAbs });
|
|
5111
5276
|
if (cwdMatch.markerVaultId) {
|
|
5112
|
-
return finalize(cwdMatch.markerVaultId,
|
|
5277
|
+
return finalize(cwdMatch.markerVaultId, path4.resolve(cwdMatch.repoRootAbs), "satellite-pin");
|
|
5113
5278
|
}
|
|
5114
5279
|
return finalizePathTier(cwdMatch.repoRootAbs, "satellite-pin");
|
|
5115
5280
|
}
|
|
5116
|
-
if (cwdMatch) {
|
|
5117
|
-
const root =
|
|
5281
|
+
if (cwdMatch && !cwdMatch.binding) {
|
|
5282
|
+
const root = path4.resolve(cwdMatch.repoRootAbs);
|
|
5118
5283
|
const record = await registryLookupByRoot(root);
|
|
5119
5284
|
const markerVaultId = readMarkerVaultId(root);
|
|
5120
5285
|
if (markerVaultId) {
|
|
@@ -5137,19 +5302,18 @@ async function resolveActiveVaultRoot(options = {}) {
|
|
|
5137
5302
|
outcome: "empty",
|
|
5138
5303
|
detail: `no satellite pin, heyspex.yaml or specs/ at or above ${cwdBase}`
|
|
5139
5304
|
});
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5305
|
+
if (!options.allowAccountPointer) {
|
|
5306
|
+
onTier({
|
|
5307
|
+
tier: "account-pointer",
|
|
5308
|
+
outcome: "skipped",
|
|
5309
|
+
detail: "account pointer retired for local resolution (HS-TYJA)"
|
|
5310
|
+
});
|
|
5311
|
+
const unbound = new NoActiveVaultError(
|
|
5312
|
+
`No HeySpex vault is bound to ${cwdBase.replace(/\\/g, "/")}, and it is not inside a vault checkout. There is no default "active" vault. Bind this repo: \`hs vault bind --vault <name|id>\` (register a vault checkout first with \`hs vault register <path>\` if \`hs vault list\` shows none).`
|
|
5313
|
+
);
|
|
5314
|
+
unbound.from = cwdBase.replace(/\\/g, "/");
|
|
5315
|
+
throw unbound;
|
|
5151
5316
|
}
|
|
5152
|
-
onTier({ tier: "env-vault-dir", outcome: "skipped", detail: "HEYSPEX_VAULT_DIR unset" });
|
|
5153
5317
|
const pointerVaultId = await accountPointerLookup(options.accountId);
|
|
5154
5318
|
if (pointerVaultId) {
|
|
5155
5319
|
const record = await registryLookup(pointerVaultId);
|
|
@@ -5485,11 +5649,11 @@ function readLocalReceiptSide(subject, registryOpts = {}) {
|
|
|
5485
5649
|
return unreadSide(`registry.json could not be read: ${message(err)}`);
|
|
5486
5650
|
}
|
|
5487
5651
|
}
|
|
5488
|
-
async function readList(http,
|
|
5489
|
-
const res = await cloudHttpRequest(http, "GET",
|
|
5652
|
+
async function readList(http, path6, key) {
|
|
5653
|
+
const res = await cloudHttpRequest(http, "GET", path6);
|
|
5490
5654
|
const rows = res.json?.[key];
|
|
5491
5655
|
if (!res.ok || !Array.isArray(rows)) {
|
|
5492
|
-
return { reason: `GET ${
|
|
5656
|
+
return { reason: `GET ${path6.split("?")[0]} answered HTTP ${res.status}` };
|
|
5493
5657
|
}
|
|
5494
5658
|
return { rows: rows.filter((r) => Boolean(r) && typeof r === "object") };
|
|
5495
5659
|
}
|
|
@@ -6034,6 +6198,7 @@ async function readActivePointers(deps) {
|
|
|
6034
6198
|
const str2 = (v) => typeof v === "string" && v ? v : null;
|
|
6035
6199
|
return { local, cloud: { vault: str2(body.vault_id), workspace: str2(body.workspace_id) } };
|
|
6036
6200
|
}
|
|
6201
|
+
var SWITCH_DEPRECATION_NOTE = "Note (deprecated): local agents, hs, spexcode and the MCP server ignore the active vault -- they write to the vault bound to the repo they run in. Use `hs vault bind --vault <name|id>` in a repo, and `hs vault resolve` to see which vault a folder writes to.";
|
|
6037
6202
|
async function runSwitch(args, deps) {
|
|
6038
6203
|
deps = scopedTo(deps, args.id);
|
|
6039
6204
|
const subject = { accountId: accountId(deps), vaultId: args.id };
|
|
@@ -6117,11 +6282,13 @@ async function runSwitch(args, deps) {
|
|
|
6117
6282
|
return finishVaultWrite(
|
|
6118
6283
|
{
|
|
6119
6284
|
code: EXIT.OK,
|
|
6120
|
-
message: `Switched active vault to "${args.id}"${workspaceSwitched ? ` (workspace: ${workspaceId})` : ""}
|
|
6285
|
+
message: `Switched active vault to "${args.id}"${workspaceSwitched ? ` (workspace: ${workspaceId})` : ""}.
|
|
6286
|
+
` + SWITCH_DEPRECATION_NOTE,
|
|
6121
6287
|
payload: {
|
|
6122
6288
|
ok: true,
|
|
6123
6289
|
vault_id: args.id,
|
|
6124
|
-
workspace_id: workspaceSwitched ? workspaceId : null
|
|
6290
|
+
workspace_id: workspaceSwitched ? workspaceId : null,
|
|
6291
|
+
deprecation: SWITCH_DEPRECATION_NOTE
|
|
6125
6292
|
},
|
|
6126
6293
|
isError: false
|
|
6127
6294
|
},
|
|
@@ -6556,7 +6723,7 @@ function runEntry(name, main2, options = {}) {
|
|
|
6556
6723
|
// spex-console/bin/hs-vault.ts
|
|
6557
6724
|
function readFile(p) {
|
|
6558
6725
|
try {
|
|
6559
|
-
return
|
|
6726
|
+
return readFileSync12(p, "utf8");
|
|
6560
6727
|
} catch {
|
|
6561
6728
|
return null;
|
|
6562
6729
|
}
|
|
@@ -6594,7 +6761,7 @@ async function main() {
|
|
|
6594
6761
|
return;
|
|
6595
6762
|
}
|
|
6596
6763
|
const vaultIdOverride = process4.env.HS_VAULT_ID_OVERRIDE?.trim() || void 0;
|
|
6597
|
-
const cwd = process4.env.HS_INVOKED_CWD?.trim() || (process4.env.HS_VAULT_ROOT ?
|
|
6764
|
+
const cwd = process4.env.HS_INVOKED_CWD?.trim() || (process4.env.HS_VAULT_ROOT ? path5.resolve(process4.env.HS_VAULT_ROOT) : process4.cwd());
|
|
6598
6765
|
const invokedCwd = process4.env.HS_INVOKED_CWD?.trim() || process4.cwd();
|
|
6599
6766
|
const consoleUrl = resolveSpexConsoleUrl(cwd, readFile);
|
|
6600
6767
|
const gate = await gateWriteVerb({
|
|
@@ -6602,7 +6769,7 @@ async function main() {
|
|
|
6602
6769
|
sub: parsed.args.sub,
|
|
6603
6770
|
writeVerbs: VAULT_WRITE_VERBS,
|
|
6604
6771
|
flags: identityArgv.flags,
|
|
6605
|
-
io: defaultIdentityGateIo({ consoleUrl, platformRoot:
|
|
6772
|
+
io: defaultIdentityGateIo({ consoleUrl, platformRoot: path5.resolve(process4.cwd(), "..") })
|
|
6606
6773
|
});
|
|
6607
6774
|
if (gate && !gate.ok) {
|
|
6608
6775
|
process4.stderr.write(
|
|
@@ -6615,17 +6782,17 @@ async function main() {
|
|
|
6615
6782
|
const result = await runVault(
|
|
6616
6783
|
parsed.args,
|
|
6617
6784
|
{
|
|
6618
|
-
fileExists: (p) =>
|
|
6785
|
+
fileExists: (p) => existsSync9(p),
|
|
6619
6786
|
readFile,
|
|
6620
|
-
resolvePath: (p) =>
|
|
6621
|
-
joinPath: (...s) =>
|
|
6622
|
-
basename: (p) =>
|
|
6787
|
+
resolvePath: (p) => path5.resolve(invokedCwd, p),
|
|
6788
|
+
joinPath: (...s) => path5.join(...s),
|
|
6789
|
+
basename: (p) => path5.basename(p),
|
|
6623
6790
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
6624
6791
|
cwd,
|
|
6625
6792
|
conflictScan: {
|
|
6626
|
-
fileExists: (p) =>
|
|
6793
|
+
fileExists: (p) => existsSync9(p),
|
|
6627
6794
|
readFile,
|
|
6628
|
-
joinPath: (...s) =>
|
|
6795
|
+
joinPath: (...s) => path5.join(...s)
|
|
6629
6796
|
},
|
|
6630
6797
|
confirm,
|
|
6631
6798
|
neon: { http: { fetchImpl: fetch, consoleUrl, token: readPairedToken() } }
|