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.
Files changed (70) hide show
  1. package/README.md +10 -0
  2. package/bin/hs-vault-binding.mjs +157 -0
  3. package/bin/hs.mjs +81 -77
  4. package/bin/spexcode.mjs +91 -0
  5. package/dev-factory/engine/worktree.mjs +36 -7
  6. package/dev-factory/lib/epic-detect.mjs +2 -2
  7. package/dev-factory/lib/fleet-readiness.mjs +3 -5
  8. package/dev-factory/lib/fleet-state.mjs +2 -2
  9. package/dev-factory/lib/sufficiency-score.mjs +3 -5
  10. package/dist/cli/hs-check.mjs +76 -46
  11. package/dist/cli/hs-claim.mjs +63 -33
  12. package/dist/cli/hs-connect.mjs +520 -214
  13. package/dist/cli/hs-defer.mjs +161 -19
  14. package/dist/cli/hs-dev.mjs +11 -5
  15. package/dist/cli/hs-env.mjs +187 -161
  16. package/dist/cli/hs-index-rebuild.mjs +123 -78
  17. package/dist/cli/hs-index-reconcile.mjs +203 -107
  18. package/dist/cli/hs-init.mjs +426 -179
  19. package/dist/cli/hs-install.mjs +759 -437
  20. package/dist/cli/hs-join.mjs +590 -383
  21. package/dist/cli/hs-vault-pull.mjs +359 -254
  22. package/dist/cli/hs-vault-push.mjs +151 -113
  23. package/dist/cli/hs-vault.mjs +271 -104
  24. package/dist/cli/hs-workspace.mjs +90 -57
  25. package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +178 -91
  26. package/docs/copilot-plugin-install.md +189 -177
  27. package/docs/driving-heyspex-from-copilot.md +174 -165
  28. package/docs/mcp-client-config.md +93 -25
  29. package/package.json +255 -250
  30. package/plugins/heyspex/.mcp.json +3 -4
  31. package/plugins/heyspex/README.md +323 -312
  32. package/plugins/heyspex/bin/launch-mcp.mjs +21 -7
  33. package/plugins/heyspex/mcp.json +12 -13
  34. package/scripts/check-mcp-freshness.mjs +6 -4
  35. package/scripts/claude-hooks/_intent-classifier-router.mjs +518 -456
  36. package/scripts/claude-hooks/intent-classifier-trigger.mjs +305 -279
  37. package/scripts/claude-hooks/research-method-guard.mjs +2 -2
  38. package/scripts/claude-hooks/wip-checkpoint.mjs +33 -9
  39. package/scripts/claude-statusline/fonts/HeySpexIcons.ttf +0 -0
  40. package/scripts/claude-statusline/statusline-command.mjs +1058 -0
  41. package/scripts/dev-server-status.mjs +3 -2
  42. package/scripts/dev-server-stop.mjs +3 -2
  43. package/scripts/hs-capture-cli.mjs +2 -2
  44. package/scripts/hs-settings.mjs +2 -2
  45. package/scripts/hs-view.mjs +3 -2
  46. package/scripts/install-copilot-hooks.mjs +64 -34
  47. package/scripts/lib/copilot-cli.mjs +108 -0
  48. package/scripts/lib/customer-kit.mjs +223 -30
  49. package/scripts/lib/dev-mode.mjs +28 -2
  50. package/scripts/lib/shared-path-helpers.mjs +32 -0
  51. package/scripts/lib/standalone-doctor.mjs +131 -32
  52. package/scripts/wip-ledger-health.mjs +3 -2
  53. package/spexharness/src/gates/vault-mirror-catchup.mjs +113 -24
  54. package/spexharness/src/gates/vault-mirror-catchup.runner.mjs +52 -0
  55. package/spexharness/src/intent-directive.mjs +839 -626
  56. package/spexharness/src/rule-table.mjs +1 -1
  57. package/vendor/heyspex-mcp/cli-verbs.mjs +16 -2
  58. package/vendor/heyspex-mcp/cli.mjs +20 -4
  59. package/vendor/heyspex-mcp/console-fetch.mjs +15 -3
  60. package/vendor/heyspex-mcp/dev-tools.mjs +11 -4
  61. package/vendor/heyspex-mcp/events-tools.mjs +1 -1
  62. package/vendor/heyspex-mcp/factory-run-status.mjs +42 -0
  63. package/vendor/heyspex-mcp/mcp-config.mjs +4 -2
  64. package/vendor/heyspex-mcp/spec-edit-tools.mjs +26 -1
  65. package/vendor/heyspex-mcp/test-helpers.mjs +5 -2
  66. package/vendor/heyspex-mcp/tool-catalog.mjs +12 -0
  67. package/vendor/heyspex-mcp/tool-dispatch.mjs +59 -23
  68. package/vendor/heyspex-mcp/usable-dir-env.mjs +82 -0
  69. package/vendor/heyspex-mcp/vault-binding.mjs +839 -0
  70. package/vendor/heyspex-mcp/vault-resolve.mjs +353 -245
@@ -124,9 +124,9 @@ function resolveBearer({ envToken, credentialsText, accountId: accountId2, legac
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, path4) {
127
+ function readTextOrNull(read, path5) {
128
128
  try {
129
- const raw = read(path4);
129
+ const raw = read(path5);
130
130
  return typeof raw === "string" ? raw : null;
131
131
  } catch {
132
132
  return null;
@@ -174,8 +174,8 @@ var init_credentials = __esm({
174
174
  });
175
175
 
176
176
  // scripts/lib/dev-mode.mjs
177
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
178
- import { homedir as homedir7 } from "node:os";
177
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
178
+ import { homedir as homedir8 } from "node:os";
179
179
  import { join as join8 } from "node:path";
180
180
  function devModeFilePath(homeDir = heyspexHome()) {
181
181
  return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
@@ -230,6 +230,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile: readFile2 =
230
230
  return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
231
231
  }
232
232
  }
233
+ function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
234
+ if (source !== "local-override") return "not-applicable";
235
+ if (!cloudKnown) return "unverified";
236
+ return staleAgainstCloud ? "stale" : "in-sync";
237
+ }
233
238
  function resolveDevMode({
234
239
  env = process.env,
235
240
  homeDir = heyspexHome(),
@@ -269,6 +274,7 @@ function resolveDevMode({
269
274
  const semantics = deriveSemantics(mode, reliance);
270
275
  const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
271
276
  const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
277
+ const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
272
278
  return {
273
279
  mode,
274
280
  reliance: semantics.reliance,
@@ -286,7 +292,8 @@ function resolveDevMode({
286
292
  present: local.record !== null,
287
293
  record: local.record,
288
294
  parseError: local.parseError,
289
- staleAgainstCloud
295
+ staleAgainstCloud,
296
+ cloudCheck
290
297
  },
291
298
  cloudSetting: { known: cloudKnown, value: cloudValue }
292
299
  };
@@ -300,7 +307,7 @@ var init_dev_mode = __esm({
300
307
  CLOUD_CONSOLE_URL = "https://app.heyspex.com";
301
308
  LOCAL_CONSOLE_URL = "http://localhost:3000";
302
309
  DEV_MODE_FILENAME = "dev-mode.json";
303
- defaultReadFile = (p) => readFileSync7(p, "utf8");
310
+ defaultReadFile = (p) => readFileSync8(p, "utf8");
304
311
  }
305
312
  });
306
313
 
@@ -2053,13 +2060,13 @@ var init_postgres = __esm({
2053
2060
  });
2054
2061
 
2055
2062
  // spex-console/bin/hs-workspace.ts
2056
- import { readFileSync as readFileSync10 } from "node:fs";
2057
- import path3 from "node:path";
2063
+ import { readFileSync as readFileSync11 } from "node:fs";
2064
+ import path4 from "node:path";
2058
2065
  import process4 from "node:process";
2059
2066
  import { createInterface as createInterface2 } from "node:readline/promises";
2060
2067
 
2061
2068
  // spex-console/src/lib/cli/control-shared.ts
2062
- import path from "node:path";
2069
+ import path2 from "node:path";
2063
2070
 
2064
2071
  // scripts/lib/hs-command.mjs
2065
2072
  import { existsSync } from "node:fs";
@@ -2082,8 +2089,8 @@ var HS_DISPATCHER_PREFIX = HS_DISPATCHER_PATH ? `node ${shellQuote(HS_DISPATCHER
2082
2089
  // vendor/heyspex-mcp/console-fetch.mjs
2083
2090
  init_heyspex_home();
2084
2091
  init_credentials();
2085
- import { readFileSync as readFileSync5 } from "node:fs";
2086
- import { homedir as homedir5 } from "node:os";
2092
+ import { readFileSync as readFileSync6 } from "node:fs";
2093
+ import { homedir as homedir6 } from "node:os";
2087
2094
  import { join as join6 } from "node:path";
2088
2095
 
2089
2096
  // vendor/heyspex-mcp/session-owner.mjs
@@ -2108,14 +2115,14 @@ var VAULT_LESS_ENDPOINTS = Object.freeze([
2108
2115
  "GET /api/workspaces",
2109
2116
  "* /api/admin/dev-mode"
2110
2117
  ]);
2111
- function pathnameOf(path4) {
2112
- const raw = String(path4 ?? "").split("?")[0].split("#")[0];
2118
+ function pathnameOf(path5) {
2119
+ const raw = String(path5 ?? "").split("?")[0].split("#")[0];
2113
2120
  const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
2114
2121
  return noTrailing || "/";
2115
2122
  }
2116
- function isVaultLessEndpoint(method, path4) {
2123
+ function isVaultLessEndpoint(method, path5) {
2117
2124
  const m = String(method || "GET").toUpperCase();
2118
- const pathname = pathnameOf(path4);
2125
+ const pathname = pathnameOf(path5);
2119
2126
  return VAULT_LESS_ENDPOINTS.some((row) => {
2120
2127
  const [rowMethod, rowPath] = row.split(" ");
2121
2128
  return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
@@ -2135,7 +2142,9 @@ import {
2135
2142
  import { basename, join as join4, isAbsolute as isAbsolute2 } from "node:path";
2136
2143
 
2137
2144
  // scripts/lib/shared-path-helpers.mjs
2145
+ import { realpathSync } from "node:fs";
2138
2146
  import { isAbsolute, resolve as resolve2, sep } from "node:path";
2147
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
2139
2148
  function resolveCodeRoot({
2140
2149
  explicit,
2141
2150
  packageRoot,
@@ -2154,9 +2163,30 @@ function resolveCodeRoot({
2154
2163
 
2155
2164
  // vendor/heyspex-mcp/vault-resolve.mjs
2156
2165
  init_heyspex_home();
2157
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
2158
- import { homedir as homedir4, tmpdir } from "node:os";
2159
- import { dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
2166
+ import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
2167
+ import { homedir as homedir5, tmpdir } from "node:os";
2168
+
2169
+ // vendor/heyspex-mcp/usable-dir-env.mjs
2170
+ import { statSync } from "node:fs";
2171
+ import { isAbsolute as isAbsolute3 } from "node:path";
2172
+
2173
+ // vendor/heyspex-mcp/vault-resolve.mjs
2174
+ import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
2175
+
2176
+ // vendor/heyspex-mcp/vault-binding.mjs
2177
+ import {
2178
+ copyFileSync,
2179
+ existsSync as existsSync3,
2180
+ mkdirSync as mkdirSync3,
2181
+ readFileSync as readFileSync4,
2182
+ realpathSync as realpathSync2,
2183
+ renameSync as renameSync3,
2184
+ statSync as statSync2,
2185
+ unlinkSync,
2186
+ writeFileSync as writeFileSync3
2187
+ } from "node:fs";
2188
+ import { homedir as homedir4 } from "node:os";
2189
+ import path from "node:path";
2160
2190
 
2161
2191
  // vendor/heyspex-mcp/console-fetch.mjs
2162
2192
  var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMATCH", "VAULT_NOT_REGISTERED"]);
@@ -2165,17 +2195,17 @@ var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMAT
2165
2195
  init_credentials();
2166
2196
 
2167
2197
  // spex-console/src/lib/vault-registry/registry-file.ts
2168
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
2198
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
2169
2199
  import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
2170
2200
 
2171
2201
  // spex-console/src/lib/heyspex-home.ts
2172
- import { homedir as homedir6 } from "node:os";
2202
+ import { homedir as homedir7 } from "node:os";
2173
2203
  function heyspexHome2(env = process.env) {
2174
2204
  const override = env?.HEYSPEX_HOME;
2175
2205
  if (typeof override === "string" && override.trim().length > 0) {
2176
2206
  return override.trim();
2177
2207
  }
2178
- return homedir6();
2208
+ return homedir7();
2179
2209
  }
2180
2210
 
2181
2211
  // spex-console/src/lib/vault-registry/types.ts
@@ -2297,9 +2327,9 @@ function parseVaultRegistryFile(json) {
2297
2327
  }
2298
2328
  function readRegistryJson(opts) {
2299
2329
  const filePath = vaultRegistryFilePath(opts);
2300
- if (!existsSync4(filePath)) return { kind: "missing" };
2330
+ if (!existsSync5(filePath)) return { kind: "missing" };
2301
2331
  try {
2302
- const raw = readFileSync6(filePath, "utf8");
2332
+ const raw = readFileSync7(filePath, "utf8");
2303
2333
  const json = JSON.parse(raw);
2304
2334
  if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
2305
2335
  return { kind: "ok", raw, json };
@@ -2369,10 +2399,10 @@ function assertNotRealHomeRegistryUnderTest(filePath) {
2369
2399
  function writeVaultRegistry(registry, opts = {}) {
2370
2400
  const filePath = vaultRegistryFilePath(opts);
2371
2401
  assertNotRealHomeRegistryUnderTest(filePath);
2372
- mkdirSync3(dirname4(filePath), { recursive: true });
2402
+ mkdirSync4(dirname4(filePath), { recursive: true });
2373
2403
  const tmpPath = `${filePath}.tmp`;
2374
- writeFileSync3(tmpPath, serializeVaultRegistry(registry), "utf8");
2375
- renameSync3(tmpPath, filePath);
2404
+ writeFileSync4(tmpPath, serializeVaultRegistry(registry), "utf8");
2405
+ renameSync4(tmpPath, filePath);
2376
2406
  }
2377
2407
  function isValidId(id) {
2378
2408
  if (!isNonEmptyString(id)) return false;
@@ -2536,11 +2566,11 @@ function readPairedBearer(opts = {}) {
2536
2566
  function readPairedToken(opts = {}) {
2537
2567
  return readPairedBearer(opts).token ?? void 0;
2538
2568
  }
2539
- function scopeHeadersFor(scope, method, path4) {
2569
+ function scopeHeadersFor(scope, method, path5) {
2540
2570
  if (scope === "none") {
2541
- if (!isVaultLessEndpoint(method, path4)) {
2571
+ if (!isVaultLessEndpoint(method, path5)) {
2542
2572
  throw new Error(
2543
- `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 }.`
2573
+ `controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path5} is vault-scoped and must name { accountId, vaultId }.`
2544
2574
  );
2545
2575
  }
2546
2576
  return {};
@@ -2551,7 +2581,7 @@ function cloudRequestScope(scope) {
2551
2581
  const named = { accountId: scope.accountId, vaultId: scope.vaultId };
2552
2582
  return {
2553
2583
  ...named,
2554
- headersFor: (method, path4) => isVaultLessEndpoint(method, path4) ? {} : scopeHeadersFor(named, method, path4)
2584
+ headersFor: (method, path5) => isVaultLessEndpoint(method, path5) ? {} : scopeHeadersFor(named, method, path5)
2555
2585
  };
2556
2586
  }
2557
2587
  function withCloudScope(deps, accountId2, vaultId) {
@@ -2562,8 +2592,8 @@ function withCloudScope(deps, accountId2, vaultId) {
2562
2592
  // scripts/lib/console-url-resolver.mjs
2563
2593
  init_heyspex_home();
2564
2594
  init_dev_mode();
2565
- import { readFileSync as readFileSync8 } from "node:fs";
2566
- import { homedir as homedir8 } from "node:os";
2595
+ import { readFileSync as readFileSync9 } from "node:fs";
2596
+ import { homedir as homedir9 } from "node:os";
2567
2597
  import { join as join9 } from "node:path";
2568
2598
  var DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
2569
2599
  function trimTrailingSlash(url) {
@@ -2601,7 +2631,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile: readFile
2601
2631
  }
2602
2632
  }
2603
2633
  function defaultReadFile2(p) {
2604
- return readFileSync8(p, "utf8");
2634
+ return readFileSync9(p, "utf8");
2605
2635
  }
2606
2636
  function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery = defaultReadFile2 } = {}) {
2607
2637
  if (!repoRoot) return null;
@@ -2699,7 +2729,7 @@ var HELP = [
2699
2729
 
2700
2730
  // spex-console/src/lib/cli/install-identity.ts
2701
2731
  import { spawnSync } from "node:child_process";
2702
- import { existsSync as existsSync5 } from "node:fs";
2732
+ import { existsSync as existsSync6 } from "node:fs";
2703
2733
  import { join as join10 } from "node:path";
2704
2734
  import { createInterface } from "node:readline/promises";
2705
2735
  init_credentials();
@@ -2707,7 +2737,7 @@ init_credentials();
2707
2737
  // vendor/heyspex-mcp/whoami.mjs
2708
2738
  init_heyspex_home();
2709
2739
  init_credentials();
2710
- import { homedir as homedir9 } from "node:os";
2740
+ import { homedir as homedir10 } from "node:os";
2711
2741
  var SESSION_PATH = "/api/mcp/session";
2712
2742
  async function probeCredentialIdentity({ consoleUrl, token, fetchImpl = fetch } = {}) {
2713
2743
  if (!token) return { kind: "no_credential" };
@@ -2775,12 +2805,15 @@ var HELP2 = [
2775
2805
  "(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
2776
2806
  "copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
2777
2807
  "plus a generated CLAUDE.md, the kept-principles index, and the template_version",
2778
- "pin; with --git, one initial `git init` + commit. Rerunnable: a <dir> that already",
2779
- "holds heyspex.yaml and an empty/placeholder-only specs/ is accepted, existing",
2780
- "files are never overwritten (an existing CLAUDE.md gets a CLAUDE.md.template",
2781
- "beside it), and the result lists each manifest row as created or kept. Refuses a",
2782
- "non-empty <dir> without heyspex.yaml, or a specs/ holding managed specs.",
2783
- "See init.customer-vault.ts.",
2808
+ "pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
2809
+ "holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
2810
+ "vault-template row names: only missing shipped files are added, template_version",
2811
+ "is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
2812
+ "nothing existing is ever overwritten (an existing CLAUDE.md gets a",
2813
+ "CLAUDE.md.template beside it), the result lists each manifest row as created or",
2814
+ "kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
2815
+ "names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
2816
+ "non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
2784
2817
  "",
2785
2818
  "Flags:",
2786
2819
  " --console-url <url> console_url value (default: the mode-aware console --",
@@ -3048,7 +3081,7 @@ function isInteractiveTerminal() {
3048
3081
  }
3049
3082
  function defaultIdentityGateIo(opts) {
3050
3083
  const consoleUrl = opts.consoleUrl.trim().replace(/\/+$/, "");
3051
- const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join10(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) => existsSync5(p));
3084
+ const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join10(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) => existsSync6(p));
3052
3085
  return {
3053
3086
  consoleUrl,
3054
3087
  interactive: opts.interactive ?? isInteractiveTerminal(),
@@ -3096,7 +3129,7 @@ function defaultIdentityGateIo(opts) {
3096
3129
  init_sql_orm_inert_cli_shim();
3097
3130
 
3098
3131
  // spex-console/src/lib/cli/vault-workspace-shared.ts
3099
- import { readFileSync as readFileSync9 } from "node:fs";
3132
+ import { readFileSync as readFileSync10 } from "node:fs";
3100
3133
 
3101
3134
  // spex-console/src/lib/cli/default-workspace.ts
3102
3135
  function defaultWorkspaceId(accountId2) {
@@ -3109,7 +3142,7 @@ function resolveConfiguredAccountId(opts = {}) {
3109
3142
  return resolveSessionOwnerUncached({
3110
3143
  env: opts.env ?? process.env,
3111
3144
  homeDir: opts.homeDir ?? heyspexHome2(),
3112
- readFile: opts.readFile ?? ((p) => readFileSync9(p, "utf-8"))
3145
+ readFile: opts.readFile ?? ((p) => readFileSync10(p, "utf-8"))
3113
3146
  });
3114
3147
  }
3115
3148
  var CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE = "refusing -- no signed-in account (no HS_ACCOUNT_ID env var, and no account_id: in ~/.heyspex/config.yaml). Run `hs login` first; nothing was written.";
@@ -3130,10 +3163,10 @@ function classifyHttpFailureReason(res) {
3130
3163
  if (res.status === 401) return "unauthorized";
3131
3164
  return "server-error";
3132
3165
  }
3133
- async function cloudHttpRequest(http, method, path4, body) {
3134
- const scopeHeaders = http.scope ? http.scope.headersFor(method, path4) : {};
3166
+ async function cloudHttpRequest(http, method, path5, body) {
3167
+ const scopeHeaders = http.scope ? http.scope.headersFor(method, path5) : {};
3135
3168
  try {
3136
- const res = await http.fetchImpl(`${http.consoleUrl}${path4}`, {
3169
+ const res = await http.fetchImpl(`${http.consoleUrl}${path5}`, {
3137
3170
  method,
3138
3171
  headers: {
3139
3172
  "content-type": "application/json",
@@ -3186,8 +3219,8 @@ async function classifyWorkspaceConflictHttp(id, conflictJson, http) {
3186
3219
  if (!Array.isArray(rows)) return { written: false, reason: "server-error" };
3187
3220
  return rows.some((w) => w.id === id) ? { written: true } : { written: false, reason: "owned-by-other-account" };
3188
3221
  }
3189
- async function mutateHttp(http, method, path4, body, landed) {
3190
- const res = await cloudHttpRequest(http, method, path4, body);
3222
+ async function mutateHttp(http, method, path5, body, landed) {
3223
+ const res = await cloudHttpRequest(http, method, path5, body);
3191
3224
  if (res.ok) return landed;
3192
3225
  if (res.status === 404) return "not_found";
3193
3226
  if (res.status === 403 && isOwnedByOtherAccountCode(errorCodeOf(res.json))) {
@@ -3436,7 +3469,7 @@ init_postgres_client_cli_shim();
3436
3469
  init_postgres();
3437
3470
 
3438
3471
  // spex-console/src/lib/vault-registry/repo-identity.ts
3439
- import path2 from "node:path";
3472
+ import path3 from "node:path";
3440
3473
 
3441
3474
  // spex-console/src/lib/vault-registry/vault-workspace-store.ts
3442
3475
  function mapProbeFailureReason(reason) {
@@ -3778,7 +3811,7 @@ function buildCloseNotDestroyConfirm(kind, verb, displayName) {
3778
3811
  }
3779
3812
 
3780
3813
  // spex-console/src/lib/vault-registry/list.ts
3781
- import { existsSync as existsSync6 } from "node:fs";
3814
+ import { existsSync as existsSync7 } from "node:fs";
3782
3815
  function pointerDeps2(deps) {
3783
3816
  return deps.accountPointer ?? { registryOpts: deps.registryOpts };
3784
3817
  }
@@ -4299,11 +4332,11 @@ function readLocalReceiptSide(subject, registryOpts = {}) {
4299
4332
  return unreadSide(`registry.json could not be read: ${message(err)}`);
4300
4333
  }
4301
4334
  }
4302
- async function readList(http, path4, key) {
4303
- const res = await cloudHttpRequest(http, "GET", path4);
4335
+ async function readList(http, path5, key) {
4336
+ const res = await cloudHttpRequest(http, "GET", path5);
4304
4337
  const rows = res.json?.[key];
4305
4338
  if (!res.ok || !Array.isArray(rows)) {
4306
- return { reason: `GET ${path4.split("?")[0]} answered HTTP ${res.status}` };
4339
+ return { reason: `GET ${path5.split("?")[0]} answered HTTP ${res.status}` };
4307
4340
  }
4308
4341
  return { rows: rows.filter((r) => Boolean(r) && typeof r === "object") };
4309
4342
  }
@@ -4995,7 +5028,7 @@ function runEntry(name, main2, options = {}) {
4995
5028
  // spex-console/bin/hs-workspace.ts
4996
5029
  function readFile(p) {
4997
5030
  try {
4998
- return readFileSync10(p, "utf8");
5031
+ return readFileSync11(p, "utf8");
4999
5032
  } catch {
5000
5033
  return null;
5001
5034
  }
@@ -5032,14 +5065,14 @@ async function main() {
5032
5065
  process4.exitCode = parsed.error.code;
5033
5066
  return;
5034
5067
  }
5035
- const vaultRoot = process4.env.HS_VAULT_ROOT ? path3.resolve(process4.env.HS_VAULT_ROOT) : process4.cwd();
5068
+ const vaultRoot = process4.env.HS_VAULT_ROOT ? path4.resolve(process4.env.HS_VAULT_ROOT) : process4.cwd();
5036
5069
  const consoleUrl = resolveSpexConsoleUrl(vaultRoot, readFile);
5037
5070
  const gate = await gateWriteVerb({
5038
5071
  namespace: "hs workspace",
5039
5072
  sub: parsed.args.sub,
5040
5073
  writeVerbs: WORKSPACE_WRITE_VERBS,
5041
5074
  flags: identityArgv.flags,
5042
- io: defaultIdentityGateIo({ consoleUrl, platformRoot: path3.resolve(process4.cwd(), "..") })
5075
+ io: defaultIdentityGateIo({ consoleUrl, platformRoot: path4.resolve(process4.cwd(), "..") })
5043
5076
  });
5044
5077
  if (gate && !gate.ok) {
5045
5078
  process4.stderr.write(