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
@@ -161,9 +161,9 @@ function parseCredentialsStore(raw) {
161
161
  accounts
162
162
  };
163
163
  }
164
- function readTextOrNull(read, path15) {
164
+ function readTextOrNull(read, path16) {
165
165
  try {
166
- const raw = read(path15);
166
+ const raw = read(path16);
167
167
  return typeof raw === "string" ? raw : null;
168
168
  } catch {
169
169
  return null;
@@ -215,14 +215,14 @@ var init_session_owner = __esm({
215
215
  });
216
216
 
217
217
  // vendor/heyspex-mcp/vault-less-endpoints.mjs
218
- function pathnameOf(path15) {
219
- const raw = String(path15 ?? "").split("?")[0].split("#")[0];
218
+ function pathnameOf(path16) {
219
+ const raw = String(path16 ?? "").split("?")[0].split("#")[0];
220
220
  const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
221
221
  return noTrailing || "/";
222
222
  }
223
- function isVaultLessEndpoint(method, path15) {
223
+ function isVaultLessEndpoint(method, path16) {
224
224
  const m = String(method || "GET").toUpperCase();
225
- const pathname = pathnameOf(path15);
225
+ const pathname = pathnameOf(path16);
226
226
  return VAULT_LESS_ENDPOINTS.some((row) => {
227
227
  const [rowMethod, rowPath] = row.split(" ");
228
228
  return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
@@ -245,7 +245,9 @@ var init_vault_less_endpoints = __esm({
245
245
  });
246
246
 
247
247
  // scripts/lib/shared-path-helpers.mjs
248
+ import { realpathSync } from "node:fs";
248
249
  import { isAbsolute, resolve as resolve2, sep } from "node:path";
250
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
249
251
  var init_shared_path_helpers = __esm({
250
252
  "scripts/lib/shared-path-helpers.mjs"() {
251
253
  "use strict";
@@ -270,20 +272,52 @@ var init_write_outbox = __esm({
270
272
  }
271
273
  });
272
274
 
275
+ // vendor/heyspex-mcp/usable-dir-env.mjs
276
+ import { statSync } from "node:fs";
277
+ import { isAbsolute as isAbsolute3 } from "node:path";
278
+ var init_usable_dir_env = __esm({
279
+ "vendor/heyspex-mcp/usable-dir-env.mjs"() {
280
+ "use strict";
281
+ }
282
+ });
283
+
284
+ // vendor/heyspex-mcp/vault-binding.mjs
285
+ import {
286
+ copyFileSync,
287
+ existsSync as existsSync3,
288
+ mkdirSync as mkdirSync3,
289
+ readFileSync as readFileSync4,
290
+ realpathSync as realpathSync2,
291
+ renameSync as renameSync3,
292
+ statSync as statSync2,
293
+ unlinkSync,
294
+ writeFileSync as writeFileSync3
295
+ } from "node:fs";
296
+ import { homedir as homedir4 } from "node:os";
297
+ import path from "node:path";
298
+ var init_vault_binding = __esm({
299
+ "vendor/heyspex-mcp/vault-binding.mjs"() {
300
+ "use strict";
301
+ }
302
+ });
303
+
273
304
  // vendor/heyspex-mcp/vault-resolve.mjs
274
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
275
- import { homedir as homedir4, tmpdir } from "node:os";
276
- import { dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
305
+ import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
306
+ import { homedir as homedir5, tmpdir } from "node:os";
307
+ import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
277
308
  var init_vault_resolve = __esm({
278
309
  "vendor/heyspex-mcp/vault-resolve.mjs"() {
279
310
  "use strict";
280
311
  init_heyspex_home();
312
+ init_usable_dir_env();
313
+ init_vault_binding();
314
+ init_vault_binding();
281
315
  }
282
316
  });
283
317
 
284
318
  // vendor/heyspex-mcp/console-fetch.mjs
285
- import { readFileSync as readFileSync5 } from "node:fs";
286
- import { homedir as homedir5 } from "node:os";
319
+ import { readFileSync as readFileSync6 } from "node:fs";
320
+ import { homedir as homedir6 } from "node:os";
287
321
  import { join as join6 } from "node:path";
288
322
  function isCloudVaultId(value) {
289
323
  return typeof value === "string" && CLOUD_VAULT_ID_RE.test(value.trim());
@@ -305,13 +339,13 @@ var init_console_fetch = __esm({
305
339
  });
306
340
 
307
341
  // spex-console/src/lib/heyspex-home.ts
308
- import { homedir as homedir6 } from "node:os";
342
+ import { homedir as homedir7 } from "node:os";
309
343
  function heyspexHome2(env = process.env) {
310
344
  const override = env?.HEYSPEX_HOME;
311
345
  if (typeof override === "string" && override.trim().length > 0) {
312
346
  return override.trim();
313
347
  }
314
- return homedir6();
348
+ return homedir7();
315
349
  }
316
350
  var init_heyspex_home2 = __esm({
317
351
  "spex-console/src/lib/heyspex-home.ts"() {
@@ -339,7 +373,7 @@ var init_types = __esm({
339
373
  });
340
374
 
341
375
  // spex-console/src/lib/vault-registry/registry-file.ts
342
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
376
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
343
377
  import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
344
378
  function vaultRegistryDir(opts = {}) {
345
379
  return opts.registryDir ?? join7(heyspexHome2(), ".heyspex");
@@ -446,9 +480,9 @@ function parseVaultRegistryFile(json) {
446
480
  }
447
481
  function readRegistryJson(opts) {
448
482
  const filePath = vaultRegistryFilePath(opts);
449
- if (!existsSync4(filePath)) return { kind: "missing" };
483
+ if (!existsSync5(filePath)) return { kind: "missing" };
450
484
  try {
451
- const raw = readFileSync6(filePath, "utf8");
485
+ const raw = readFileSync7(filePath, "utf8");
452
486
  const json = JSON.parse(raw);
453
487
  if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
454
488
  return { kind: "ok", raw, json };
@@ -484,8 +518,8 @@ var init_registry_file = __esm({
484
518
  });
485
519
 
486
520
  // scripts/lib/dev-mode.mjs
487
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
488
- import { homedir as homedir7 } from "node:os";
521
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
522
+ import { homedir as homedir8 } from "node:os";
489
523
  import { join as join8 } from "node:path";
490
524
  function devModeFilePath(homeDir = heyspexHome()) {
491
525
  return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
@@ -540,6 +574,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultRea
540
574
  return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
541
575
  }
542
576
  }
577
+ function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
578
+ if (source !== "local-override") return "not-applicable";
579
+ if (!cloudKnown) return "unverified";
580
+ return staleAgainstCloud ? "stale" : "in-sync";
581
+ }
543
582
  function resolveDevMode({
544
583
  env = process.env,
545
584
  homeDir = heyspexHome(),
@@ -579,6 +618,7 @@ function resolveDevMode({
579
618
  const semantics = deriveSemantics(mode, reliance);
580
619
  const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
581
620
  const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
621
+ const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
582
622
  return {
583
623
  mode,
584
624
  reliance: semantics.reliance,
@@ -596,7 +636,8 @@ function resolveDevMode({
596
636
  present: local.record !== null,
597
637
  record: local.record,
598
638
  parseError: local.parseError,
599
- staleAgainstCloud
639
+ staleAgainstCloud,
640
+ cloudCheck
600
641
  },
601
642
  cloudSetting: { known: cloudKnown, value: cloudValue }
602
643
  };
@@ -610,13 +651,13 @@ var init_dev_mode = __esm({
610
651
  CLOUD_CONSOLE_URL = "https://app.heyspex.com";
611
652
  LOCAL_CONSOLE_URL = "http://localhost:3000";
612
653
  DEV_MODE_FILENAME = "dev-mode.json";
613
- defaultReadFile = (p) => readFileSync7(p, "utf8");
654
+ defaultReadFile = (p) => readFileSync8(p, "utf8");
614
655
  }
615
656
  });
616
657
 
617
658
  // scripts/lib/console-url-resolver.mjs
618
- import { readFileSync as readFileSync8 } from "node:fs";
619
- import { homedir as homedir8 } from "node:os";
659
+ import { readFileSync as readFileSync9 } from "node:fs";
660
+ import { homedir as homedir9 } from "node:os";
620
661
  import { join as join9 } from "node:path";
621
662
  function trimTrailingSlash(url) {
622
663
  return url.replace(/\/+$/, "");
@@ -651,7 +692,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile = default
651
692
  }
652
693
  }
653
694
  function defaultReadFile2(p) {
654
- return readFileSync8(p, "utf8");
695
+ return readFileSync9(p, "utf8");
655
696
  }
656
697
  function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery: readDiscovery2 = defaultReadFile2 } = {}) {
657
698
  if (!repoRoot) return null;
@@ -726,9 +767,9 @@ var init_console_url_resolver = __esm({
726
767
  });
727
768
 
728
769
  // spex-console/src/lib/vault-registry/repo-identity.ts
729
- import path2 from "node:path";
770
+ import path3 from "node:path";
730
771
  function normalizeRepoRoot(p) {
731
- let resolved = path2.resolve(p);
772
+ let resolved = path3.resolve(p);
732
773
  while (resolved.length > 1 && (resolved.endsWith("/") || resolved.endsWith("\\"))) {
733
774
  resolved = resolved.slice(0, -1);
734
775
  }
@@ -737,8 +778,8 @@ function normalizeRepoRoot(p) {
737
778
  function repoRootCandidates(p) {
738
779
  const norm = normalizeRepoRoot(p);
739
780
  const candidates = [norm];
740
- if (path2.basename(norm) === "specs") {
741
- const parent = path2.dirname(norm);
781
+ if (path3.basename(norm) === "specs") {
782
+ const parent = path3.dirname(norm);
742
783
  if (parent !== norm) candidates.push(parent);
743
784
  }
744
785
  return candidates;
@@ -763,22 +804,22 @@ var init_default_workspace = __esm({
763
804
  });
764
805
 
765
806
  // spex-console/src/lib/cli/vault-workspace-shared.ts
766
- import { readFileSync as readFileSync9 } from "node:fs";
807
+ import { readFileSync as readFileSync10 } from "node:fs";
767
808
  function resolveConfiguredAccountId(opts = {}) {
768
809
  return resolveSessionOwnerUncached({
769
810
  env: opts.env ?? process.env,
770
811
  homeDir: opts.homeDir ?? heyspexHome2(),
771
- readFile: opts.readFile ?? ((p) => readFileSync9(p, "utf-8"))
812
+ readFile: opts.readFile ?? ((p) => readFileSync10(p, "utf-8"))
772
813
  });
773
814
  }
774
815
  function resolveCloudAccountIdOrRefuse(opts = {}) {
775
816
  const accountId = resolveConfiguredAccountId(opts);
776
817
  return accountId ? { ok: true, accountId } : { ok: false, message: CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE };
777
818
  }
778
- async function cloudHttpRequest(http, method, path15, body) {
779
- const scopeHeaders = http.scope ? http.scope.headersFor(method, path15) : {};
819
+ async function cloudHttpRequest(http, method, path16, body) {
820
+ const scopeHeaders = http.scope ? http.scope.headersFor(method, path16) : {};
780
821
  try {
781
- const res = await http.fetchImpl(`${http.consoleUrl}${path15}`, {
822
+ const res = await http.fetchImpl(`${http.consoleUrl}${path16}`, {
782
823
  method,
783
824
  headers: {
784
825
  "content-type": "application/json",
@@ -1589,13 +1630,13 @@ function bareFileKeyOf(input) {
1589
1630
  if (KNOWN_TYPE_LABELS.has(token)) return null;
1590
1631
  return FILE_KEY_RE.test(token) ? token : null;
1591
1632
  }
1592
- function isEra3SpecId(id, basename2) {
1633
+ function isEra3SpecId(id, basename3) {
1593
1634
  const raw = String(id ?? "").trim();
1594
1635
  if (!raw) return false;
1595
1636
  if (parseSpecId(raw)?.era === 3) return true;
1596
1637
  const fileKey = bareFileKeyOf(raw);
1597
1638
  if (!fileKey) return false;
1598
- const parsed = parseSpecId(basename2 ?? "");
1639
+ const parsed = parseSpecId(basename3 ?? "");
1599
1640
  return parsed?.era === 3 && (parsed.fileKey ?? "").toUpperCase() === fileKey;
1600
1641
  }
1601
1642
  var FILE_KEY_RE, KNOWN_TYPE_LABELS, RULED_FAMILY_SURNAMES, FAMILY_KEYED_TYPE_LABELS, FAMILY_KEY_SHAPE_RE;
@@ -1669,7 +1710,7 @@ var init_vault_shared = __esm({
1669
1710
  // spex-console/src/lib/indexer/parser.ts
1670
1711
  import { createHash } from "node:crypto";
1671
1712
  import { promises as fs2 } from "node:fs";
1672
- import path4 from "node:path";
1713
+ import path5 from "node:path";
1673
1714
  function normalizeWikilink2(raw) {
1674
1715
  let t = raw.trim();
1675
1716
  if (t.startsWith("[[") && t.endsWith("]]")) {
@@ -4568,9 +4609,9 @@ var init_satellite = __esm({
4568
4609
  });
4569
4610
 
4570
4611
  // spex-console/src/lib/vault-resolve/index.ts
4571
- import { existsSync as existsSync5, readFileSync as readFileSync11 } from "node:fs";
4612
+ import { existsSync as existsSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
4572
4613
  import { tmpdir as tmpdir2 } from "node:os";
4573
- import path5 from "node:path";
4614
+ import path6 from "node:path";
4574
4615
  var init_vault_resolve2 = __esm({
4575
4616
  "spex-console/src/lib/vault-resolve/index.ts"() {
4576
4617
  "use strict";
@@ -4607,8 +4648,8 @@ var init_resolve_status = __esm({
4607
4648
  });
4608
4649
 
4609
4650
  // spex-console/src/lib/vault-resolve/active.ts
4610
- import { existsSync as existsSync6 } from "node:fs";
4611
- import path6 from "node:path";
4651
+ import { existsSync as existsSync7 } from "node:fs";
4652
+ import path7 from "node:path";
4612
4653
  var ACTIVE_VAULT_CACHE_KEY, cacheStore;
4613
4654
  var init_active = __esm({
4614
4655
  "spex-console/src/lib/vault-resolve/active.ts"() {
@@ -4630,7 +4671,7 @@ var init_active = __esm({
4630
4671
 
4631
4672
  // spex-console/src/lib/control-plane/statusline-focus-cache.ts
4632
4673
  import fs3 from "node:fs";
4633
- import path7 from "node:path";
4674
+ import path8 from "node:path";
4634
4675
  var init_statusline_focus_cache = __esm({
4635
4676
  "spex-console/src/lib/control-plane/statusline-focus-cache.ts"() {
4636
4677
  "use strict";
@@ -5247,10 +5288,10 @@ function trashPathFor(relPath, now) {
5247
5288
  const normalized = relPath.split("\\").join("/");
5248
5289
  const slashIdx = normalized.lastIndexOf("/");
5249
5290
  const folder = slashIdx === -1 ? "" : normalized.slice(0, slashIdx);
5250
- const basename2 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
5291
+ const basename3 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
5251
5292
  const stamp = now.toISOString().replace(/[:.]/g, "-");
5252
5293
  const trashDir = folder ? `${folder}/.trash` : ".trash";
5253
- return `${trashDir}/${stamp}-${basename2}`;
5294
+ return `${trashDir}/${stamp}-${basename3}`;
5254
5295
  }
5255
5296
  function materializeSpecMarkdown(bundle) {
5256
5297
  return stringifySpecMarkdown(bundle.bodyText, bundle.frontmatter);
@@ -5372,10 +5413,10 @@ var init_export_status = __esm({
5372
5413
  function isGitHubFailure(value) {
5373
5414
  return typeof value === "object" && value !== null && "status" in value;
5374
5415
  }
5375
- async function githubJson(path15, target, fetchImpl, init) {
5416
+ async function githubJson(path16, target, fetchImpl, init) {
5376
5417
  let response;
5377
5418
  try {
5378
- response = await fetchImpl(`${GITHUB_API_BASE2}${path15}`, {
5419
+ response = await fetchImpl(`${GITHUB_API_BASE2}${path16}`, {
5379
5420
  ...init,
5380
5421
  headers: {
5381
5422
  ...githubHeaders(target.token),
@@ -6104,7 +6145,7 @@ var init_diagnose_spawned = __esm({
6104
6145
 
6105
6146
  // spex-console/src/lib/guardrail-naming.ts
6106
6147
  import fs4 from "node:fs";
6107
- import path8 from "node:path";
6148
+ import path9 from "node:path";
6108
6149
  var init_guardrail_naming = __esm({
6109
6150
  "spex-console/src/lib/guardrail-naming.ts"() {
6110
6151
  "use strict";
@@ -6114,7 +6155,7 @@ var init_guardrail_naming = __esm({
6114
6155
  // spex-console/src/lib/indexer/indexer-service.ts
6115
6156
  import { spawnSync } from "node:child_process";
6116
6157
  import { promises as fs5 } from "node:fs";
6117
- import path9 from "node:path";
6158
+ import path10 from "node:path";
6118
6159
  var init_indexer_service = __esm({
6119
6160
  "spex-console/src/lib/indexer/indexer-service.ts"() {
6120
6161
  "use strict";
@@ -6133,7 +6174,7 @@ var init_indexer_service = __esm({
6133
6174
 
6134
6175
  // spex-console/src/lib/indexer/outbox-index-feeder.ts
6135
6176
  import { promises as fs6 } from "node:fs";
6136
- import path10 from "node:path";
6177
+ import path11 from "node:path";
6137
6178
  var init_outbox_index_feeder = __esm({
6138
6179
  "spex-console/src/lib/indexer/outbox-index-feeder.ts"() {
6139
6180
  "use strict";
@@ -6331,7 +6372,7 @@ var init_list_filter = __esm({
6331
6372
  });
6332
6373
 
6333
6374
  // spex-console/src/lib/specs/list-query.ts
6334
- import path11 from "node:path";
6375
+ import path12 from "node:path";
6335
6376
  var init_list_query = __esm({
6336
6377
  "spex-console/src/lib/specs/list-query.ts"() {
6337
6378
  "use strict";
@@ -6374,7 +6415,7 @@ var init_pipeline_neighborhood = __esm({
6374
6415
  });
6375
6416
 
6376
6417
  // spex-console/src/lib/specs/spec-by-id.ts
6377
- import path12 from "node:path";
6418
+ import path13 from "node:path";
6378
6419
  var init_spec_by_id = __esm({
6379
6420
  "spex-console/src/lib/specs/spec-by-id.ts"() {
6380
6421
  "use strict";
@@ -6431,9 +6472,9 @@ var init_search_body_hit = __esm({
6431
6472
 
6432
6473
  // spex-console/src/lib/vault.ts
6433
6474
  import { spawn } from "node:child_process";
6434
- import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync } from "node:fs";
6475
+ import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
6435
6476
  import { promises as fs7 } from "node:fs";
6436
- import path13 from "node:path";
6477
+ import path14 from "node:path";
6437
6478
  import yaml3 from "js-yaml";
6438
6479
  var import_gray_matter3, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore;
6439
6480
  var init_vault = __esm({
@@ -6551,9 +6592,9 @@ var init_vault = __esm({
6551
6592
 
6552
6593
  // spex-console/bin/hs-index-rebuild.ts
6553
6594
  import { execSync } from "node:child_process";
6554
- import { appendFileSync, mkdirSync as mkdirSync6, readFileSync as readFileSync13 } from "node:fs";
6595
+ import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync15 } from "node:fs";
6555
6596
  import { promises as fs10 } from "node:fs";
6556
- import path14 from "node:path";
6597
+ import path15 from "node:path";
6557
6598
  import process4 from "node:process";
6558
6599
 
6559
6600
  // spex-console/src/lib/cli/control-shared.ts
@@ -6563,14 +6604,14 @@ init_credentials();
6563
6604
  init_session_owner();
6564
6605
  init_vault_less_endpoints();
6565
6606
  init_registry_file();
6566
- import path from "node:path";
6607
+ import path2 from "node:path";
6567
6608
  var VAULT_ID_HEADER = "x-heyspex-vault-id";
6568
6609
  var ACCOUNT_ID_HEADER = "x-heyspex-account-id";
6569
- function scopeHeadersFor(scope, method, path15) {
6610
+ function scopeHeadersFor(scope, method, path16) {
6570
6611
  if (scope === "none") {
6571
- if (!isVaultLessEndpoint(method, path15)) {
6612
+ if (!isVaultLessEndpoint(method, path16)) {
6572
6613
  throw new Error(
6573
- `controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path15} is vault-scoped and must name { accountId, vaultId }.`
6614
+ `controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path16} is vault-scoped and must name { accountId, vaultId }.`
6574
6615
  );
6575
6616
  }
6576
6617
  return {};
@@ -6581,7 +6622,7 @@ function cloudRequestScope(scope) {
6581
6622
  const named = { accountId: scope.accountId, vaultId: scope.vaultId };
6582
6623
  return {
6583
6624
  ...named,
6584
- headersFor: (method, path15) => isVaultLessEndpoint(method, path15) ? {} : scopeHeadersFor(named, method, path15)
6625
+ headersFor: (method, path16) => isVaultLessEndpoint(method, path16) ? {} : scopeHeadersFor(named, method, path16)
6585
6626
  };
6586
6627
  }
6587
6628
 
@@ -6624,7 +6665,7 @@ init_console_fetch();
6624
6665
  init_registry_file();
6625
6666
  init_repo_identity();
6626
6667
  import { promises as fs } from "node:fs";
6627
- import path3 from "node:path";
6668
+ import path4 from "node:path";
6628
6669
 
6629
6670
  // spex-console/src/lib/cli/init.ts
6630
6671
  init_console_url_resolver();
@@ -6661,12 +6702,15 @@ var HELP2 = [
6661
6702
  "(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
6662
6703
  "copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
6663
6704
  "plus a generated CLAUDE.md, the kept-principles index, and the template_version",
6664
- "pin; with --git, one initial `git init` + commit. Rerunnable: a <dir> that already",
6665
- "holds heyspex.yaml and an empty/placeholder-only specs/ is accepted, existing",
6666
- "files are never overwritten (an existing CLAUDE.md gets a CLAUDE.md.template",
6667
- "beside it), and the result lists each manifest row as created or kept. Refuses a",
6668
- "non-empty <dir> without heyspex.yaml, or a specs/ holding managed specs.",
6669
- "See init.customer-vault.ts.",
6705
+ "pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
6706
+ "holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
6707
+ "vault-template row names: only missing shipped files are added, template_version",
6708
+ "is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
6709
+ "nothing existing is ever overwritten (an existing CLAUDE.md gets a",
6710
+ "CLAUDE.md.template beside it), the result lists each manifest row as created or",
6711
+ "kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
6712
+ "names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
6713
+ "non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
6670
6714
  "",
6671
6715
  "Flags:",
6672
6716
  " --console-url <url> console_url value (default: the mode-aware console --",
@@ -6739,7 +6783,7 @@ async function resolveSyncVaultTargetFromEnv(deps) {
6739
6783
  let markerVaultId = null;
6740
6784
  try {
6741
6785
  markerVaultId = parseVaultId(
6742
- await fs.readFile(path3.join(deps.vaultRoot, "heyspex.yaml"), "utf8")
6786
+ await fs.readFile(path4.join(deps.vaultRoot, "heyspex.yaml"), "utf8")
6743
6787
  );
6744
6788
  } catch {
6745
6789
  markerVaultId = null;
@@ -6941,16 +6985,17 @@ async function runReindex(input) {
6941
6985
  init_validator();
6942
6986
 
6943
6987
  // spex-console/src/lib/vault-sync/pull-io.ts
6944
- import { promises as fs9 } from "node:fs";
6988
+ import { promises as fs9, readFileSync as readFileSync14 } from "node:fs";
6989
+ import { EOL } from "node:os";
6945
6990
  import nodePath2 from "node:path";
6946
6991
 
6947
6992
  // spex-console/src/lib/dev-mode.ts
6948
6993
  init_heyspex_home2();
6949
6994
  init_credentials();
6950
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
6995
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
6951
6996
  import { join as join10 } from "node:path";
6952
6997
  var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
6953
- var defaultReadFile3 = (p) => readFileSync10(p, "utf8");
6998
+ var defaultReadFile3 = (p) => readFileSync11(p, "utf8");
6954
6999
  function readCredentialsMcpToken({
6955
7000
  homeDir = heyspexHome2(),
6956
7001
  readFile = defaultReadFile3
@@ -7100,16 +7145,16 @@ function currentGitHead(vaultRoot2) {
7100
7145
  }
7101
7146
  }
7102
7147
  function appendJournal(vaultRoot2, message) {
7103
- const heyspexDir = path14.join(vaultRoot2, ".heyspex");
7104
- mkdirSync6(heyspexDir, { recursive: true });
7105
- const logPath = path14.join(heyspexDir, "index-rebuild.log");
7148
+ const heyspexDir = path15.join(vaultRoot2, ".heyspex");
7149
+ mkdirSync7(heyspexDir, { recursive: true });
7150
+ const logPath = path15.join(heyspexDir, "index-rebuild.log");
7106
7151
  const ts = (/* @__PURE__ */ new Date()).toISOString();
7107
7152
  appendFileSync(logPath, `[${ts}] ${message}
7108
7153
  `, "utf-8");
7109
7154
  }
7110
7155
  function readDiscovery(p) {
7111
7156
  try {
7112
- return readFileSync13(p, "utf-8");
7157
+ return readFileSync15(p, "utf-8");
7113
7158
  } catch {
7114
7159
  return null;
7115
7160
  }
@@ -7121,7 +7166,7 @@ function resolveConsole(args) {
7121
7166
  }
7122
7167
  async function loadCrosswalk(vaultRoot2) {
7123
7168
  try {
7124
- const raw = await fs10.readFile(path14.join(vaultRoot2, ".heyspex", "id-crosswalk.json"), "utf8");
7169
+ const raw = await fs10.readFile(path15.join(vaultRoot2, ".heyspex", "id-crosswalk.json"), "utf8");
7125
7170
  const parsed = JSON.parse(raw);
7126
7171
  return Array.isArray(parsed) ? parsed : [];
7127
7172
  } catch {
@@ -7129,10 +7174,10 @@ async function loadCrosswalk(vaultRoot2) {
7129
7174
  }
7130
7175
  }
7131
7176
  async function walkLocal(args) {
7132
- const specsDir = path14.join(args.vaultRoot, "specs");
7177
+ const specsDir = path15.join(args.vaultRoot, "specs");
7133
7178
  const files = [];
7134
7179
  for await (const file of fs10.glob("**/*.md", { cwd: specsDir })) {
7135
- files.push(path14.join(specsDir, file));
7180
+ files.push(path15.join(specsDir, file));
7136
7181
  }
7137
7182
  files.sort();
7138
7183
  const specs2 = [];
@@ -7140,10 +7185,10 @@ async function walkLocal(args) {
7140
7185
  for (const abs of files) {
7141
7186
  try {
7142
7187
  const content = await fs10.readFile(abs, "utf8");
7143
- const relPath = path14.relative(args.vaultRoot, abs).split(path14.sep).join("/");
7188
+ const relPath = path15.relative(args.vaultRoot, abs).split(path15.sep).join("/");
7144
7189
  const parsed = parseSpecFromContent(content, {
7145
7190
  relPath,
7146
- basenameFallback: path14.basename(abs).replace(/\.md$/i, "")
7191
+ basenameFallback: path15.basename(abs).replace(/\.md$/i, "")
7147
7192
  });
7148
7193
  specs2.push({ relPath, content, parsed });
7149
7194
  if (args.verbose) say(args, ` parsed ${relPath}