heyspex 0.2.2 → 0.2.4
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/.claude-plugin/marketplace.json +2 -1
- package/README.md +11 -0
- package/bin/hs-vault-binding.mjs +157 -0
- package/bin/hs.mjs +83 -79
- 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 +200 -177
- package/docs/driving-heyspex-from-copilot.md +174 -165
- package/docs/mcp-client-config.md +93 -25
- package/package.json +257 -250
- package/plugins/heyspex/.claude-plugin/plugin.json +13 -12
- package/plugins/heyspex/.cursor-plugin/plugin.json +16 -15
- package/plugins/heyspex/.mcp.json +3 -4
- package/plugins/heyspex/README.md +323 -312
- package/plugins/heyspex/assets/heyspex-glyph-128.png +0 -0
- package/plugins/heyspex/assets/heyspex-glyph-256.png +0 -0
- package/plugins/heyspex/assets/heyspex-glyph.svg +12 -0
- 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 +1073 -0
- package/scripts/copilot-statusline.mjs +130 -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 +152 -36
- 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/http-transport.mjs +6 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +4 -2
- package/vendor/heyspex-mcp/server-identity.mjs +13 -0
- 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 +63 -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
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
import { loadFleetState } from "./fleet-state.mjs";
|
|
15
15
|
import { readFileSync } from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
|
-
import { fileURLToPath
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { isMainModule } from "../../scripts/lib/shared-path-helpers.mjs";
|
|
18
19
|
|
|
19
20
|
export const DEFAULT_FLEET_FRESHNESS_MS = 15 * 60 * 1000; // 15 minutes
|
|
20
21
|
|
|
@@ -266,9 +267,6 @@ function runSelfTest() {
|
|
|
266
267
|
return ok ? 0 : 1;
|
|
267
268
|
}
|
|
268
269
|
|
|
269
|
-
if (
|
|
270
|
-
process.argv[1] &&
|
|
271
|
-
pathToFileURL(process.argv[1]).href === import.meta.url
|
|
272
|
-
) {
|
|
270
|
+
if (isMainModule(import.meta.url)) {
|
|
273
271
|
if (process.argv.includes("--self-test")) process.exit(runSelfTest());
|
|
274
272
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
|
|
32
32
|
import { join } from "node:path";
|
|
33
|
-
import {
|
|
33
|
+
import { isMainModule } from "../../scripts/lib/shared-path-helpers.mjs";
|
|
34
34
|
import { tmpdir } from "node:os";
|
|
35
35
|
import { codeRepoRoot } from "./code-repo-root.mjs";
|
|
36
36
|
import { OLLAMA } from "./model-catalog.mjs";
|
|
@@ -232,7 +232,7 @@ function runSelfTest() {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
// ── Main ─────────────────────────────────────────────────────────────────────────
|
|
235
|
-
if (
|
|
235
|
+
if (isMainModule(import.meta.url)) {
|
|
236
236
|
if (process.argv.includes("--self-test")) process.exit(runSelfTest());
|
|
237
237
|
console.log([
|
|
238
238
|
"lib/fleet-state.mjs — per-machine runtime state sidecar (.heyspex/fleet-state.json).",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
* `node dev-factory/lib/sufficiency-score.mjs --self-test`).
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
import { fileURLToPath
|
|
45
|
+
import { fileURLToPath } from "node:url";
|
|
46
|
+
import { isMainModule } from "../../scripts/lib/shared-path-helpers.mjs";
|
|
46
47
|
import { readFileSync, existsSync } from "node:fs";
|
|
47
48
|
import { join, dirname } from "node:path";
|
|
48
49
|
|
|
@@ -439,9 +440,6 @@ function runSelfTest() {
|
|
|
439
440
|
return ok ? 0 : 1;
|
|
440
441
|
}
|
|
441
442
|
|
|
442
|
-
if (
|
|
443
|
-
process.argv[1] &&
|
|
444
|
-
pathToFileURL(process.argv[1]).href === import.meta.url
|
|
445
|
-
) {
|
|
443
|
+
if (isMainModule(import.meta.url)) {
|
|
446
444
|
if (process.argv.includes("--self-test")) process.exit(runSelfTest());
|
|
447
445
|
}
|
package/dist/cli/hs-check.mjs
CHANGED
|
@@ -124,9 +124,9 @@ function resolveBearer({ envToken, credentialsText, accountId, legacyAccountId }
|
|
|
124
124
|
if (entry?.mcp_token) return { token: entry.mcp_token, source: "credentials-file" };
|
|
125
125
|
return { token: null, source: null };
|
|
126
126
|
}
|
|
127
|
-
function readTextOrNull(read,
|
|
127
|
+
function readTextOrNull(read, path4) {
|
|
128
128
|
try {
|
|
129
|
-
const raw = read(
|
|
129
|
+
const raw = read(path4);
|
|
130
130
|
return typeof raw === "string" ? raw : null;
|
|
131
131
|
} catch {
|
|
132
132
|
return null;
|
|
@@ -169,8 +169,8 @@ var init_credentials = __esm({
|
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
// scripts/lib/dev-mode.mjs
|
|
172
|
-
import { mkdirSync as
|
|
173
|
-
import { homedir as
|
|
172
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
173
|
+
import { homedir as homedir8 } from "node:os";
|
|
174
174
|
import { join as join8 } from "node:path";
|
|
175
175
|
function devModeFilePath(homeDir = heyspexHome()) {
|
|
176
176
|
return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
|
|
@@ -225,6 +225,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultRea
|
|
|
225
225
|
return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
229
|
+
if (source !== "local-override") return "not-applicable";
|
|
230
|
+
if (!cloudKnown) return "unverified";
|
|
231
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
232
|
+
}
|
|
228
233
|
function resolveDevMode({
|
|
229
234
|
env = process.env,
|
|
230
235
|
homeDir = heyspexHome(),
|
|
@@ -264,6 +269,7 @@ function resolveDevMode({
|
|
|
264
269
|
const semantics = deriveSemantics(mode, reliance);
|
|
265
270
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
266
271
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
272
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
267
273
|
return {
|
|
268
274
|
mode,
|
|
269
275
|
reliance: semantics.reliance,
|
|
@@ -281,7 +287,8 @@ function resolveDevMode({
|
|
|
281
287
|
present: local.record !== null,
|
|
282
288
|
record: local.record,
|
|
283
289
|
parseError: local.parseError,
|
|
284
|
-
staleAgainstCloud
|
|
290
|
+
staleAgainstCloud,
|
|
291
|
+
cloudCheck
|
|
285
292
|
},
|
|
286
293
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
287
294
|
};
|
|
@@ -295,18 +302,20 @@ var init_dev_mode = __esm({
|
|
|
295
302
|
CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
296
303
|
LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
297
304
|
DEV_MODE_FILENAME = "dev-mode.json";
|
|
298
|
-
defaultReadFile = (p) =>
|
|
305
|
+
defaultReadFile = (p) => readFileSync8(p, "utf8");
|
|
299
306
|
}
|
|
300
307
|
});
|
|
301
308
|
|
|
302
309
|
// spex-console/bin/hs-check.ts
|
|
303
310
|
import { spawn, spawnSync } from "node:child_process";
|
|
304
|
-
import { existsSync as
|
|
305
|
-
import
|
|
311
|
+
import { existsSync as existsSync6, readFileSync as readFileSync10 } from "node:fs";
|
|
312
|
+
import path3 from "node:path";
|
|
306
313
|
import process3 from "node:process";
|
|
307
314
|
|
|
308
315
|
// scripts/lib/shared-path-helpers.mjs
|
|
316
|
+
import { realpathSync } from "node:fs";
|
|
309
317
|
import { isAbsolute, resolve, sep } from "node:path";
|
|
318
|
+
import { fileURLToPath } from "node:url";
|
|
310
319
|
function resolveCodeRoot({
|
|
311
320
|
explicit,
|
|
312
321
|
packageRoot,
|
|
@@ -751,16 +760,16 @@ var HELP = [
|
|
|
751
760
|
].join("\n");
|
|
752
761
|
|
|
753
762
|
// spex-console/src/lib/cli/control-shared.ts
|
|
754
|
-
import
|
|
763
|
+
import path2 from "node:path";
|
|
755
764
|
|
|
756
765
|
// scripts/lib/hs-command.mjs
|
|
757
766
|
import { existsSync } from "node:fs";
|
|
758
767
|
import { delimiter, dirname, join } from "node:path";
|
|
759
|
-
import { fileURLToPath } from "node:url";
|
|
768
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
760
769
|
var HS_BIN_NAME = "hs";
|
|
761
770
|
var HS_DISPATCHER_PATH = (() => {
|
|
762
771
|
try {
|
|
763
|
-
const here = dirname(
|
|
772
|
+
const here = dirname(fileURLToPath2(import.meta.url));
|
|
764
773
|
return join(dirname(dirname(here)), "bin", "hs.mjs");
|
|
765
774
|
} catch {
|
|
766
775
|
return null;
|
|
@@ -841,8 +850,8 @@ function hsCommand(args = "", options = {}) {
|
|
|
841
850
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
842
851
|
init_heyspex_home();
|
|
843
852
|
init_credentials();
|
|
844
|
-
import { readFileSync as
|
|
845
|
-
import { homedir as
|
|
853
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
854
|
+
import { homedir as homedir6 } from "node:os";
|
|
846
855
|
import { join as join6 } from "node:path";
|
|
847
856
|
|
|
848
857
|
// vendor/heyspex-mcp/session-owner.mjs
|
|
@@ -867,14 +876,14 @@ var VAULT_LESS_ENDPOINTS = Object.freeze([
|
|
|
867
876
|
"GET /api/workspaces",
|
|
868
877
|
"* /api/admin/dev-mode"
|
|
869
878
|
]);
|
|
870
|
-
function pathnameOf(
|
|
871
|
-
const raw = String(
|
|
879
|
+
function pathnameOf(path4) {
|
|
880
|
+
const raw = String(path4 ?? "").split("?")[0].split("#")[0];
|
|
872
881
|
const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
|
|
873
882
|
return noTrailing || "/";
|
|
874
883
|
}
|
|
875
|
-
function isVaultLessEndpoint(method,
|
|
884
|
+
function isVaultLessEndpoint(method, path4) {
|
|
876
885
|
const m = String(method || "GET").toUpperCase();
|
|
877
|
-
const pathname = pathnameOf(
|
|
886
|
+
const pathname = pathnameOf(path4);
|
|
878
887
|
return VAULT_LESS_ENDPOINTS.some((row) => {
|
|
879
888
|
const [rowMethod, rowPath] = row.split(" ");
|
|
880
889
|
return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
|
|
@@ -895,9 +904,30 @@ import { basename, join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
|
895
904
|
|
|
896
905
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
897
906
|
init_heyspex_home();
|
|
898
|
-
import { existsSync as
|
|
899
|
-
import { homedir as
|
|
900
|
-
|
|
907
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
908
|
+
import { homedir as homedir5, tmpdir } from "node:os";
|
|
909
|
+
|
|
910
|
+
// vendor/heyspex-mcp/usable-dir-env.mjs
|
|
911
|
+
import { statSync } from "node:fs";
|
|
912
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
913
|
+
|
|
914
|
+
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
915
|
+
import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
|
|
916
|
+
|
|
917
|
+
// vendor/heyspex-mcp/vault-binding.mjs
|
|
918
|
+
import {
|
|
919
|
+
copyFileSync,
|
|
920
|
+
existsSync as existsSync3,
|
|
921
|
+
mkdirSync as mkdirSync3,
|
|
922
|
+
readFileSync as readFileSync4,
|
|
923
|
+
realpathSync as realpathSync2,
|
|
924
|
+
renameSync as renameSync3,
|
|
925
|
+
statSync as statSync2,
|
|
926
|
+
unlinkSync,
|
|
927
|
+
writeFileSync as writeFileSync3
|
|
928
|
+
} from "node:fs";
|
|
929
|
+
import { homedir as homedir4 } from "node:os";
|
|
930
|
+
import path from "node:path";
|
|
901
931
|
|
|
902
932
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
903
933
|
var VAULT_NOT_REGISTERED = "VAULT_NOT_REGISTERED";
|
|
@@ -929,7 +959,7 @@ function isCloudVaultId(value) {
|
|
|
929
959
|
var MARKER_VAULT_ID_RE = /^vault_id:[ \t]*["']?([^"'\r\n#]+)/m;
|
|
930
960
|
function readMarkerVaultId(repoRootAbs, { readFile } = {}) {
|
|
931
961
|
if (typeof repoRootAbs !== "string" || !repoRootAbs.trim()) return null;
|
|
932
|
-
const read = readFile ?? ((p) =>
|
|
962
|
+
const read = readFile ?? ((p) => readFileSync6(p, "utf8"));
|
|
933
963
|
try {
|
|
934
964
|
const raw = read(join6(repoRootAbs, "heyspex.yaml"));
|
|
935
965
|
const value = String(raw ?? "").match(MARKER_VAULT_ID_RE)?.[1]?.trim() ?? "";
|
|
@@ -951,17 +981,17 @@ var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMAT
|
|
|
951
981
|
init_credentials();
|
|
952
982
|
|
|
953
983
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
954
|
-
import { existsSync as
|
|
984
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
955
985
|
import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
|
|
956
986
|
|
|
957
987
|
// spex-console/src/lib/heyspex-home.ts
|
|
958
|
-
import { homedir as
|
|
988
|
+
import { homedir as homedir7 } from "node:os";
|
|
959
989
|
function heyspexHome2(env = process.env) {
|
|
960
990
|
const override = env?.HEYSPEX_HOME;
|
|
961
991
|
if (typeof override === "string" && override.trim().length > 0) {
|
|
962
992
|
return override.trim();
|
|
963
993
|
}
|
|
964
|
-
return
|
|
994
|
+
return homedir7();
|
|
965
995
|
}
|
|
966
996
|
|
|
967
997
|
// spex-console/src/lib/vault-registry/types.ts
|
|
@@ -1083,9 +1113,9 @@ function parseVaultRegistryFile(json) {
|
|
|
1083
1113
|
}
|
|
1084
1114
|
function readRegistryJson(opts) {
|
|
1085
1115
|
const filePath = vaultRegistryFilePath(opts);
|
|
1086
|
-
if (!
|
|
1116
|
+
if (!existsSync5(filePath)) return { kind: "missing" };
|
|
1087
1117
|
try {
|
|
1088
|
-
const raw =
|
|
1118
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
1089
1119
|
const json = JSON.parse(raw);
|
|
1090
1120
|
if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
|
|
1091
1121
|
return { kind: "ok", raw, json };
|
|
@@ -1220,11 +1250,11 @@ function bearerHeaderFor(consoleUrl, opts = {}) {
|
|
|
1220
1250
|
return {};
|
|
1221
1251
|
}
|
|
1222
1252
|
}
|
|
1223
|
-
function scopeHeadersFor(scope, method,
|
|
1253
|
+
function scopeHeadersFor(scope, method, path4) {
|
|
1224
1254
|
if (scope === "none") {
|
|
1225
|
-
if (!isVaultLessEndpoint(method,
|
|
1255
|
+
if (!isVaultLessEndpoint(method, path4)) {
|
|
1226
1256
|
throw new Error(
|
|
1227
|
-
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${
|
|
1257
|
+
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path4} is vault-scoped and must name { accountId, vaultId }.`
|
|
1228
1258
|
);
|
|
1229
1259
|
}
|
|
1230
1260
|
return {};
|
|
@@ -1293,7 +1323,7 @@ async function controlRequest(deps, req) {
|
|
|
1293
1323
|
return { ok: true, status: res.status, body: parsed };
|
|
1294
1324
|
}
|
|
1295
1325
|
function normalizeRoot(p) {
|
|
1296
|
-
return
|
|
1326
|
+
return path2.resolve(p).replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
1297
1327
|
}
|
|
1298
1328
|
function resolveControlScope(input) {
|
|
1299
1329
|
const target = normalizeRoot(input.vaultRoot);
|
|
@@ -1362,8 +1392,8 @@ function resolveControlScopeFromMachine(vaultRoot, opts = {}) {
|
|
|
1362
1392
|
// scripts/lib/console-url-resolver.mjs
|
|
1363
1393
|
init_heyspex_home();
|
|
1364
1394
|
init_dev_mode();
|
|
1365
|
-
import { readFileSync as
|
|
1366
|
-
import { homedir as
|
|
1395
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
1396
|
+
import { homedir as homedir9 } from "node:os";
|
|
1367
1397
|
import { join as join9 } from "node:path";
|
|
1368
1398
|
var DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
|
|
1369
1399
|
function trimTrailingSlash(url) {
|
|
@@ -1401,7 +1431,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile = default
|
|
|
1401
1431
|
}
|
|
1402
1432
|
}
|
|
1403
1433
|
function defaultReadFile2(p) {
|
|
1404
|
-
return
|
|
1434
|
+
return readFileSync9(p, "utf8");
|
|
1405
1435
|
}
|
|
1406
1436
|
function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery = defaultReadFile2 } = {}) {
|
|
1407
1437
|
if (!repoRoot) return null;
|
|
@@ -1527,14 +1557,14 @@ function runEntry(name, main2, options = {}) {
|
|
|
1527
1557
|
|
|
1528
1558
|
// spex-console/bin/hs-check.ts
|
|
1529
1559
|
function manifestForSpecId(vaultRoot, specId) {
|
|
1530
|
-
return
|
|
1560
|
+
return path3.join(vaultRoot, "specs", "checks", `${specId}.checks.json`);
|
|
1531
1561
|
}
|
|
1532
1562
|
function resolveManifestPath(target, vaultRoot, invokedCwd) {
|
|
1533
|
-
const literal =
|
|
1534
|
-
if (
|
|
1563
|
+
const literal = path3.resolve(invokedCwd, target);
|
|
1564
|
+
if (existsSync6(literal)) return literal;
|
|
1535
1565
|
if (target.endsWith(".json")) return null;
|
|
1536
1566
|
const byId = manifestForSpecId(vaultRoot, target);
|
|
1537
|
-
return
|
|
1567
|
+
return existsSync6(byId) ? byId : null;
|
|
1538
1568
|
}
|
|
1539
1569
|
function countTscErrors(output) {
|
|
1540
1570
|
const matches = output.match(/error TS\d+/g);
|
|
@@ -1548,12 +1578,12 @@ function resolveWindowsPosixShell() {
|
|
|
1548
1578
|
"C:\\Program Files\\Git\\bin\\bash.exe"
|
|
1549
1579
|
].filter((c) => typeof c === "string" && c.length > 0);
|
|
1550
1580
|
for (const candidate of candidates) {
|
|
1551
|
-
if (
|
|
1581
|
+
if (existsSync6(candidate)) return cachedPosixShell = candidate;
|
|
1552
1582
|
}
|
|
1553
1583
|
const where = spawnSync("where", ["bash"], { encoding: "utf8", windowsHide: true });
|
|
1554
1584
|
if (where.status === 0 && where.stdout) {
|
|
1555
1585
|
const hit = where.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).find((line) => !/\\system32\\/i.test(line));
|
|
1556
|
-
if (hit &&
|
|
1586
|
+
if (hit && existsSync6(hit)) return cachedPosixShell = hit;
|
|
1557
1587
|
}
|
|
1558
1588
|
return cachedPosixShell = null;
|
|
1559
1589
|
}
|
|
@@ -1610,7 +1640,7 @@ function makeRunCheck(vaultRoot, codeRoot) {
|
|
|
1610
1640
|
return async (check) => {
|
|
1611
1641
|
const cwds = resolveCheckCwds(check, {
|
|
1612
1642
|
repoRoot: codeRoot,
|
|
1613
|
-
consoleRoot:
|
|
1643
|
+
consoleRoot: path3.join(codeRoot, "spex-console"),
|
|
1614
1644
|
vaultRoot
|
|
1615
1645
|
});
|
|
1616
1646
|
let lastFailure = null;
|
|
@@ -1641,8 +1671,8 @@ async function main() {
|
|
|
1641
1671
|
}
|
|
1642
1672
|
if (process3.argv[3] === "judge") {
|
|
1643
1673
|
const pkgRoot = resolveCodeRoot({ vaultRoot });
|
|
1644
|
-
const judgeScript =
|
|
1645
|
-
if (!
|
|
1674
|
+
const judgeScript = path3.join(pkgRoot, "dev-factory", "judge.mjs");
|
|
1675
|
+
if (!existsSync6(judgeScript)) {
|
|
1646
1676
|
process3.stderr.write(
|
|
1647
1677
|
`hs check judge: script not found at ${judgeScript} (source checkout only \u2014 not available in a published install).
|
|
1648
1678
|
`
|
|
@@ -1678,13 +1708,13 @@ async function main() {
|
|
|
1678
1708
|
if (!manifestPath) {
|
|
1679
1709
|
process3.stderr.write(
|
|
1680
1710
|
`hs check: no manifest for "${args.target}".
|
|
1681
|
-
Pass a manifest path, or create ${
|
|
1711
|
+
Pass a manifest path, or create ${path3.join("specs", "checks", `${args.target}.checks.json`)} under the vault root.
|
|
1682
1712
|
`
|
|
1683
1713
|
);
|
|
1684
1714
|
process3.exitCode = EXIT.NOT_FOUND;
|
|
1685
1715
|
return;
|
|
1686
1716
|
}
|
|
1687
|
-
const { manifest, error } = parseChecksManifest(
|
|
1717
|
+
const { manifest, error } = parseChecksManifest(readFileSync10(manifestPath, "utf-8"));
|
|
1688
1718
|
if (error) {
|
|
1689
1719
|
process3.stderr.write(`hs check: ${manifestPath}: ${error}
|
|
1690
1720
|
`);
|
|
@@ -1716,7 +1746,7 @@ async function main() {
|
|
|
1716
1746
|
process3.exitCode = reportExitCode(report);
|
|
1717
1747
|
}
|
|
1718
1748
|
async function reportVerdict(target, report, vaultRoot, unattended = false) {
|
|
1719
|
-
if (
|
|
1749
|
+
if (existsSync6(target) || target.endsWith(".json")) {
|
|
1720
1750
|
process3.stderr.write(
|
|
1721
1751
|
"hs check: --report needs a spec-id target (to identify the claim), not a manifest path.\n"
|
|
1722
1752
|
);
|
|
@@ -1724,7 +1754,7 @@ async function reportVerdict(target, report, vaultRoot, unattended = false) {
|
|
|
1724
1754
|
}
|
|
1725
1755
|
const consoleUrl = resolveSpexConsoleUrl(vaultRoot, (p) => {
|
|
1726
1756
|
try {
|
|
1727
|
-
return
|
|
1757
|
+
return readFileSync10(p, "utf-8");
|
|
1728
1758
|
} catch {
|
|
1729
1759
|
return null;
|
|
1730
1760
|
}
|
package/dist/cli/hs-claim.mjs
CHANGED
|
@@ -124,9 +124,9 @@ function resolveBearer({ envToken, credentialsText, accountId, legacyAccountId }
|
|
|
124
124
|
if (entry?.mcp_token) return { token: entry.mcp_token, source: "credentials-file" };
|
|
125
125
|
return { token: null, source: null };
|
|
126
126
|
}
|
|
127
|
-
function readTextOrNull(read,
|
|
127
|
+
function readTextOrNull(read, path3) {
|
|
128
128
|
try {
|
|
129
|
-
const raw = read(
|
|
129
|
+
const raw = read(path3);
|
|
130
130
|
return typeof raw === "string" ? raw : null;
|
|
131
131
|
} catch {
|
|
132
132
|
return null;
|
|
@@ -169,8 +169,8 @@ var init_credentials = __esm({
|
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
// scripts/lib/dev-mode.mjs
|
|
172
|
-
import { mkdirSync as
|
|
173
|
-
import { homedir as
|
|
172
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
173
|
+
import { homedir as homedir8 } from "node:os";
|
|
174
174
|
import { join as join8 } from "node:path";
|
|
175
175
|
function devModeFilePath(homeDir = heyspexHome()) {
|
|
176
176
|
return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
|
|
@@ -225,6 +225,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultRea
|
|
|
225
225
|
return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
229
|
+
if (source !== "local-override") return "not-applicable";
|
|
230
|
+
if (!cloudKnown) return "unverified";
|
|
231
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
232
|
+
}
|
|
228
233
|
function resolveDevMode({
|
|
229
234
|
env = process.env,
|
|
230
235
|
homeDir = heyspexHome(),
|
|
@@ -264,6 +269,7 @@ function resolveDevMode({
|
|
|
264
269
|
const semantics = deriveSemantics(mode, reliance);
|
|
265
270
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
266
271
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
272
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
267
273
|
return {
|
|
268
274
|
mode,
|
|
269
275
|
reliance: semantics.reliance,
|
|
@@ -281,7 +287,8 @@ function resolveDevMode({
|
|
|
281
287
|
present: local.record !== null,
|
|
282
288
|
record: local.record,
|
|
283
289
|
parseError: local.parseError,
|
|
284
|
-
staleAgainstCloud
|
|
290
|
+
staleAgainstCloud,
|
|
291
|
+
cloudCheck
|
|
285
292
|
},
|
|
286
293
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
287
294
|
};
|
|
@@ -295,12 +302,12 @@ var init_dev_mode = __esm({
|
|
|
295
302
|
CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
296
303
|
LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
297
304
|
DEV_MODE_FILENAME = "dev-mode.json";
|
|
298
|
-
defaultReadFile = (p) =>
|
|
305
|
+
defaultReadFile = (p) => readFileSync8(p, "utf8");
|
|
299
306
|
}
|
|
300
307
|
});
|
|
301
308
|
|
|
302
309
|
// spex-console/bin/hs-claim.ts
|
|
303
|
-
import { readFileSync as
|
|
310
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
304
311
|
import process3 from "node:process";
|
|
305
312
|
|
|
306
313
|
// spex-console/src/lib/work-stages.ts
|
|
@@ -344,7 +351,7 @@ function resolveAgentIdentity(argAgent, argSession) {
|
|
|
344
351
|
}
|
|
345
352
|
|
|
346
353
|
// spex-console/src/lib/cli/control-shared.ts
|
|
347
|
-
import
|
|
354
|
+
import path2 from "node:path";
|
|
348
355
|
|
|
349
356
|
// scripts/lib/hs-command.mjs
|
|
350
357
|
import { existsSync } from "node:fs";
|
|
@@ -434,8 +441,8 @@ function hsCommand(args = "", options = {}) {
|
|
|
434
441
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
435
442
|
init_heyspex_home();
|
|
436
443
|
init_credentials();
|
|
437
|
-
import { readFileSync as
|
|
438
|
-
import { homedir as
|
|
444
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
445
|
+
import { homedir as homedir6 } from "node:os";
|
|
439
446
|
import { join as join6 } from "node:path";
|
|
440
447
|
|
|
441
448
|
// vendor/heyspex-mcp/session-owner.mjs
|
|
@@ -460,14 +467,14 @@ var VAULT_LESS_ENDPOINTS = Object.freeze([
|
|
|
460
467
|
"GET /api/workspaces",
|
|
461
468
|
"* /api/admin/dev-mode"
|
|
462
469
|
]);
|
|
463
|
-
function pathnameOf(
|
|
464
|
-
const raw = String(
|
|
470
|
+
function pathnameOf(path3) {
|
|
471
|
+
const raw = String(path3 ?? "").split("?")[0].split("#")[0];
|
|
465
472
|
const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
|
|
466
473
|
return noTrailing || "/";
|
|
467
474
|
}
|
|
468
|
-
function isVaultLessEndpoint(method,
|
|
475
|
+
function isVaultLessEndpoint(method, path3) {
|
|
469
476
|
const m = String(method || "GET").toUpperCase();
|
|
470
|
-
const pathname = pathnameOf(
|
|
477
|
+
const pathname = pathnameOf(path3);
|
|
471
478
|
return VAULT_LESS_ENDPOINTS.some((row) => {
|
|
472
479
|
const [rowMethod, rowPath] = row.split(" ");
|
|
473
480
|
return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
|
|
@@ -487,13 +494,36 @@ import {
|
|
|
487
494
|
import { basename, join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
488
495
|
|
|
489
496
|
// scripts/lib/shared-path-helpers.mjs
|
|
497
|
+
import { realpathSync } from "node:fs";
|
|
490
498
|
import { isAbsolute, resolve as resolve2, sep } from "node:path";
|
|
499
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
491
500
|
|
|
492
501
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
493
502
|
init_heyspex_home();
|
|
494
|
-
import { existsSync as
|
|
495
|
-
import { homedir as
|
|
496
|
-
|
|
503
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
504
|
+
import { homedir as homedir5, tmpdir } from "node:os";
|
|
505
|
+
|
|
506
|
+
// vendor/heyspex-mcp/usable-dir-env.mjs
|
|
507
|
+
import { statSync } from "node:fs";
|
|
508
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
509
|
+
|
|
510
|
+
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
511
|
+
import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
|
|
512
|
+
|
|
513
|
+
// vendor/heyspex-mcp/vault-binding.mjs
|
|
514
|
+
import {
|
|
515
|
+
copyFileSync,
|
|
516
|
+
existsSync as existsSync3,
|
|
517
|
+
mkdirSync as mkdirSync3,
|
|
518
|
+
readFileSync as readFileSync4,
|
|
519
|
+
realpathSync as realpathSync2,
|
|
520
|
+
renameSync as renameSync3,
|
|
521
|
+
statSync as statSync2,
|
|
522
|
+
unlinkSync,
|
|
523
|
+
writeFileSync as writeFileSync3
|
|
524
|
+
} from "node:fs";
|
|
525
|
+
import { homedir as homedir4 } from "node:os";
|
|
526
|
+
import path from "node:path";
|
|
497
527
|
|
|
498
528
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
499
529
|
var VAULT_NOT_REGISTERED = "VAULT_NOT_REGISTERED";
|
|
@@ -525,7 +555,7 @@ function isCloudVaultId(value) {
|
|
|
525
555
|
var MARKER_VAULT_ID_RE = /^vault_id:[ \t]*["']?([^"'\r\n#]+)/m;
|
|
526
556
|
function readMarkerVaultId(repoRootAbs, { readFile } = {}) {
|
|
527
557
|
if (typeof repoRootAbs !== "string" || !repoRootAbs.trim()) return null;
|
|
528
|
-
const read = readFile ?? ((p) =>
|
|
558
|
+
const read = readFile ?? ((p) => readFileSync6(p, "utf8"));
|
|
529
559
|
try {
|
|
530
560
|
const raw = read(join6(repoRootAbs, "heyspex.yaml"));
|
|
531
561
|
const value = String(raw ?? "").match(MARKER_VAULT_ID_RE)?.[1]?.trim() ?? "";
|
|
@@ -547,17 +577,17 @@ var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMAT
|
|
|
547
577
|
init_credentials();
|
|
548
578
|
|
|
549
579
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
550
|
-
import { existsSync as
|
|
580
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
551
581
|
import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
|
|
552
582
|
|
|
553
583
|
// spex-console/src/lib/heyspex-home.ts
|
|
554
|
-
import { homedir as
|
|
584
|
+
import { homedir as homedir7 } from "node:os";
|
|
555
585
|
function heyspexHome2(env = process.env) {
|
|
556
586
|
const override = env?.HEYSPEX_HOME;
|
|
557
587
|
if (typeof override === "string" && override.trim().length > 0) {
|
|
558
588
|
return override.trim();
|
|
559
589
|
}
|
|
560
|
-
return
|
|
590
|
+
return homedir7();
|
|
561
591
|
}
|
|
562
592
|
|
|
563
593
|
// spex-console/src/lib/vault-registry/types.ts
|
|
@@ -679,9 +709,9 @@ function parseVaultRegistryFile(json) {
|
|
|
679
709
|
}
|
|
680
710
|
function readRegistryJson(opts) {
|
|
681
711
|
const filePath = vaultRegistryFilePath(opts);
|
|
682
|
-
if (!
|
|
712
|
+
if (!existsSync5(filePath)) return { kind: "missing" };
|
|
683
713
|
try {
|
|
684
|
-
const raw =
|
|
714
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
685
715
|
const json = JSON.parse(raw);
|
|
686
716
|
if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
|
|
687
717
|
return { kind: "ok", raw, json };
|
|
@@ -816,11 +846,11 @@ function bearerHeaderFor(consoleUrl, opts = {}) {
|
|
|
816
846
|
return {};
|
|
817
847
|
}
|
|
818
848
|
}
|
|
819
|
-
function scopeHeadersFor(scope, method,
|
|
849
|
+
function scopeHeadersFor(scope, method, path3) {
|
|
820
850
|
if (scope === "none") {
|
|
821
|
-
if (!isVaultLessEndpoint(method,
|
|
851
|
+
if (!isVaultLessEndpoint(method, path3)) {
|
|
822
852
|
throw new Error(
|
|
823
|
-
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${
|
|
853
|
+
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path3} is vault-scoped and must name { accountId, vaultId }.`
|
|
824
854
|
);
|
|
825
855
|
}
|
|
826
856
|
return {};
|
|
@@ -914,7 +944,7 @@ function parseTtl(token) {
|
|
|
914
944
|
return { value: n };
|
|
915
945
|
}
|
|
916
946
|
function normalizeRoot(p) {
|
|
917
|
-
return
|
|
947
|
+
return path2.resolve(p).replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
918
948
|
}
|
|
919
949
|
function resolveControlScope(input) {
|
|
920
950
|
const target = normalizeRoot(input.vaultRoot);
|
|
@@ -1184,8 +1214,8 @@ ${lines}`,
|
|
|
1184
1214
|
return writeFailed(res2.status, res2.body);
|
|
1185
1215
|
}
|
|
1186
1216
|
const releaser = resolveAgentIdentity(args.agent, args.session).agent;
|
|
1187
|
-
const
|
|
1188
|
-
const res = await controlRequest(deps, { method: "DELETE", path:
|
|
1217
|
+
const path3 = `/api/control/active-claims/${encodeURIComponent(args.specId)}?agent=${encodeURIComponent(releaser)}`;
|
|
1218
|
+
const res = await controlRequest(deps, { method: "DELETE", path: path3 });
|
|
1189
1219
|
if (!res.ok) return res.error;
|
|
1190
1220
|
if (res.status === 200) {
|
|
1191
1221
|
return {
|
|
@@ -1245,8 +1275,8 @@ var HELP = [
|
|
|
1245
1275
|
// scripts/lib/console-url-resolver.mjs
|
|
1246
1276
|
init_heyspex_home();
|
|
1247
1277
|
init_dev_mode();
|
|
1248
|
-
import { readFileSync as
|
|
1249
|
-
import { homedir as
|
|
1278
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
1279
|
+
import { homedir as homedir9 } from "node:os";
|
|
1250
1280
|
import { join as join9 } from "node:path";
|
|
1251
1281
|
var DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
|
|
1252
1282
|
function trimTrailingSlash(url) {
|
|
@@ -1284,7 +1314,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile = default
|
|
|
1284
1314
|
}
|
|
1285
1315
|
}
|
|
1286
1316
|
function defaultReadFile2(p) {
|
|
1287
|
-
return
|
|
1317
|
+
return readFileSync9(p, "utf8");
|
|
1288
1318
|
}
|
|
1289
1319
|
function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery: readDiscovery2 = defaultReadFile2 } = {}) {
|
|
1290
1320
|
if (!repoRoot) return null;
|
|
@@ -1411,7 +1441,7 @@ function runEntry(name, main2, options = {}) {
|
|
|
1411
1441
|
// spex-console/bin/hs-claim.ts
|
|
1412
1442
|
function readDiscovery(p) {
|
|
1413
1443
|
try {
|
|
1414
|
-
return
|
|
1444
|
+
return readFileSync10(p, "utf-8");
|
|
1415
1445
|
} catch {
|
|
1416
1446
|
return null;
|
|
1417
1447
|
}
|