heyspex 0.2.1 → 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 +99 -81
  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 -162
  27. package/docs/driving-heyspex-from-copilot.md +174 -165
  28. package/docs/mcp-client-config.md +93 -25
  29. package/package.json +255 -249
  30. package/plugins/heyspex/.mcp.json +3 -4
  31. package/plugins/heyspex/README.md +323 -309
  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 +318 -0
  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
@@ -165,9 +165,9 @@ function parseCredentialsStore(raw) {
165
165
  accounts
166
166
  };
167
167
  }
168
- function readTextOrNull(read, path15) {
168
+ function readTextOrNull(read, path16) {
169
169
  try {
170
- const raw = read(path15);
170
+ const raw = read(path16);
171
171
  return typeof raw === "string" ? raw : null;
172
172
  } catch {
173
173
  return null;
@@ -219,14 +219,14 @@ var init_session_owner = __esm({
219
219
  });
220
220
 
221
221
  // vendor/heyspex-mcp/vault-less-endpoints.mjs
222
- function pathnameOf(path15) {
223
- const raw = String(path15 ?? "").split("?")[0].split("#")[0];
222
+ function pathnameOf(path16) {
223
+ const raw = String(path16 ?? "").split("?")[0].split("#")[0];
224
224
  const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
225
225
  return noTrailing || "/";
226
226
  }
227
- function isVaultLessEndpoint(method, path15) {
227
+ function isVaultLessEndpoint(method, path16) {
228
228
  const m = String(method || "GET").toUpperCase();
229
- const pathname = pathnameOf(path15);
229
+ const pathname = pathnameOf(path16);
230
230
  return VAULT_LESS_ENDPOINTS.some((row) => {
231
231
  const [rowMethod, rowPath] = row.split(" ");
232
232
  return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
@@ -249,7 +249,9 @@ var init_vault_less_endpoints = __esm({
249
249
  });
250
250
 
251
251
  // scripts/lib/shared-path-helpers.mjs
252
+ import { realpathSync } from "node:fs";
252
253
  import { isAbsolute, resolve as resolve2, sep } from "node:path";
254
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
253
255
  var init_shared_path_helpers = __esm({
254
256
  "scripts/lib/shared-path-helpers.mjs"() {
255
257
  "use strict";
@@ -274,20 +276,52 @@ var init_write_outbox = __esm({
274
276
  }
275
277
  });
276
278
 
279
+ // vendor/heyspex-mcp/usable-dir-env.mjs
280
+ import { statSync } from "node:fs";
281
+ import { isAbsolute as isAbsolute3 } from "node:path";
282
+ var init_usable_dir_env = __esm({
283
+ "vendor/heyspex-mcp/usable-dir-env.mjs"() {
284
+ "use strict";
285
+ }
286
+ });
287
+
288
+ // vendor/heyspex-mcp/vault-binding.mjs
289
+ import {
290
+ copyFileSync,
291
+ existsSync as existsSync3,
292
+ mkdirSync as mkdirSync3,
293
+ readFileSync as readFileSync4,
294
+ realpathSync as realpathSync2,
295
+ renameSync as renameSync3,
296
+ statSync as statSync2,
297
+ unlinkSync,
298
+ writeFileSync as writeFileSync3
299
+ } from "node:fs";
300
+ import { homedir as homedir4 } from "node:os";
301
+ import path from "node:path";
302
+ var init_vault_binding = __esm({
303
+ "vendor/heyspex-mcp/vault-binding.mjs"() {
304
+ "use strict";
305
+ }
306
+ });
307
+
277
308
  // vendor/heyspex-mcp/vault-resolve.mjs
278
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
279
- import { homedir as homedir4, tmpdir } from "node:os";
280
- import { dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
309
+ import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
310
+ import { homedir as homedir5, tmpdir } from "node:os";
311
+ import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
281
312
  var init_vault_resolve = __esm({
282
313
  "vendor/heyspex-mcp/vault-resolve.mjs"() {
283
314
  "use strict";
284
315
  init_heyspex_home();
316
+ init_usable_dir_env();
317
+ init_vault_binding();
318
+ init_vault_binding();
285
319
  }
286
320
  });
287
321
 
288
322
  // vendor/heyspex-mcp/console-fetch.mjs
289
- import { readFileSync as readFileSync5 } from "node:fs";
290
- import { homedir as homedir5 } from "node:os";
323
+ import { readFileSync as readFileSync6 } from "node:fs";
324
+ import { homedir as homedir6 } from "node:os";
291
325
  import { join as join6 } from "node:path";
292
326
  function isCloudVaultId(value) {
293
327
  return typeof value === "string" && CLOUD_VAULT_ID_RE.test(value.trim());
@@ -309,13 +343,13 @@ var init_console_fetch = __esm({
309
343
  });
310
344
 
311
345
  // spex-console/src/lib/heyspex-home.ts
312
- import { homedir as homedir6 } from "node:os";
346
+ import { homedir as homedir7 } from "node:os";
313
347
  function heyspexHome2(env = process.env) {
314
348
  const override = env?.HEYSPEX_HOME;
315
349
  if (typeof override === "string" && override.trim().length > 0) {
316
350
  return override.trim();
317
351
  }
318
- return homedir6();
352
+ return homedir7();
319
353
  }
320
354
  var init_heyspex_home2 = __esm({
321
355
  "spex-console/src/lib/heyspex-home.ts"() {
@@ -343,7 +377,7 @@ var init_types = __esm({
343
377
  });
344
378
 
345
379
  // spex-console/src/lib/vault-registry/registry-file.ts
346
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
380
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
347
381
  import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
348
382
  function vaultRegistryDir(opts = {}) {
349
383
  return opts.registryDir ?? join7(heyspexHome2(), ".heyspex");
@@ -450,9 +484,9 @@ function parseVaultRegistryFile(json) {
450
484
  }
451
485
  function readRegistryJson(opts) {
452
486
  const filePath = vaultRegistryFilePath(opts);
453
- if (!existsSync4(filePath)) return { kind: "missing" };
487
+ if (!existsSync5(filePath)) return { kind: "missing" };
454
488
  try {
455
- const raw = readFileSync6(filePath, "utf8");
489
+ const raw = readFileSync7(filePath, "utf8");
456
490
  const json = JSON.parse(raw);
457
491
  if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
458
492
  return { kind: "ok", raw, json };
@@ -488,8 +522,8 @@ var init_registry_file = __esm({
488
522
  });
489
523
 
490
524
  // scripts/lib/dev-mode.mjs
491
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
492
- import { homedir as homedir7 } from "node:os";
525
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
526
+ import { homedir as homedir8 } from "node:os";
493
527
  import { join as join8 } from "node:path";
494
528
  var init_dev_mode = __esm({
495
529
  "scripts/lib/dev-mode.mjs"() {
@@ -500,8 +534,8 @@ var init_dev_mode = __esm({
500
534
  });
501
535
 
502
536
  // scripts/lib/console-url-resolver.mjs
503
- import { readFileSync as readFileSync8 } from "node:fs";
504
- import { homedir as homedir8 } from "node:os";
537
+ import { readFileSync as readFileSync9 } from "node:fs";
538
+ import { homedir as homedir9 } from "node:os";
505
539
  import { join as join9 } from "node:path";
506
540
  var init_console_url_resolver = __esm({
507
541
  "scripts/lib/console-url-resolver.mjs"() {
@@ -512,9 +546,9 @@ var init_console_url_resolver = __esm({
512
546
  });
513
547
 
514
548
  // spex-console/src/lib/vault-registry/repo-identity.ts
515
- import path2 from "node:path";
549
+ import path3 from "node:path";
516
550
  function normalizeRepoRoot(p) {
517
- let resolved = path2.resolve(p);
551
+ let resolved = path3.resolve(p);
518
552
  while (resolved.length > 1 && (resolved.endsWith("/") || resolved.endsWith("\\"))) {
519
553
  resolved = resolved.slice(0, -1);
520
554
  }
@@ -523,8 +557,8 @@ function normalizeRepoRoot(p) {
523
557
  function repoRootCandidates(p) {
524
558
  const norm = normalizeRepoRoot(p);
525
559
  const candidates = [norm];
526
- if (path2.basename(norm) === "specs") {
527
- const parent = path2.dirname(norm);
560
+ if (path3.basename(norm) === "specs") {
561
+ const parent = path3.dirname(norm);
528
562
  if (parent !== norm) candidates.push(parent);
529
563
  }
530
564
  return candidates;
@@ -549,22 +583,22 @@ var init_default_workspace = __esm({
549
583
  });
550
584
 
551
585
  // spex-console/src/lib/cli/vault-workspace-shared.ts
552
- import { readFileSync as readFileSync9 } from "node:fs";
586
+ import { readFileSync as readFileSync10 } from "node:fs";
553
587
  function resolveConfiguredAccountId(opts = {}) {
554
588
  return resolveSessionOwnerUncached({
555
589
  env: opts.env ?? process.env,
556
590
  homeDir: opts.homeDir ?? heyspexHome2(),
557
- readFile: opts.readFile ?? ((p) => readFileSync9(p, "utf-8"))
591
+ readFile: opts.readFile ?? ((p) => readFileSync10(p, "utf-8"))
558
592
  });
559
593
  }
560
594
  function resolveCloudAccountIdOrRefuse(opts = {}) {
561
595
  const accountId = resolveConfiguredAccountId(opts);
562
596
  return accountId ? { ok: true, accountId } : { ok: false, message: CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE };
563
597
  }
564
- async function cloudHttpRequest(http, method, path15, body) {
565
- const scopeHeaders = http.scope ? http.scope.headersFor(method, path15) : {};
598
+ async function cloudHttpRequest(http, method, path16, body) {
599
+ const scopeHeaders = http.scope ? http.scope.headersFor(method, path16) : {};
566
600
  try {
567
- const res = await http.fetchImpl(`${http.consoleUrl}${path15}`, {
601
+ const res = await http.fetchImpl(`${http.consoleUrl}${path16}`, {
568
602
  method,
569
603
  headers: {
570
604
  "content-type": "application/json",
@@ -3177,9 +3211,9 @@ var init_satellite = __esm({
3177
3211
  });
3178
3212
 
3179
3213
  // spex-console/src/lib/vault-resolve/index.ts
3180
- import { existsSync as existsSync5, readFileSync as readFileSync11 } from "node:fs";
3214
+ import { existsSync as existsSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
3181
3215
  import { tmpdir as tmpdir2 } from "node:os";
3182
- import path4 from "node:path";
3216
+ import path5 from "node:path";
3183
3217
  var init_vault_resolve2 = __esm({
3184
3218
  "spex-console/src/lib/vault-resolve/index.ts"() {
3185
3219
  "use strict";
@@ -3216,12 +3250,12 @@ var init_resolve_status = __esm({
3216
3250
  });
3217
3251
 
3218
3252
  // spex-console/src/lib/vault-resolve/active.ts
3219
- import { existsSync as existsSync6 } from "node:fs";
3220
- import path5 from "node:path";
3253
+ import { existsSync as existsSync7 } from "node:fs";
3254
+ import path6 from "node:path";
3221
3255
  function fallbackSpecsDir() {
3222
3256
  const override = process.env.HEYSPEX_VAULT_DIR;
3223
- if (override) return path5.resolve(override);
3224
- return path5.resolve(process.cwd(), "..", "specs");
3257
+ if (override) return path6.resolve(override);
3258
+ return path6.resolve(process.cwd(), "..", "specs");
3225
3259
  }
3226
3260
  function getActiveSpecsDir() {
3227
3261
  return cacheStore.value ? cacheStore.value.specsDirAbs : fallbackSpecsDir();
@@ -3250,7 +3284,7 @@ var init_active = __esm({
3250
3284
 
3251
3285
  // spex-console/src/lib/control-plane/statusline-focus-cache.ts
3252
3286
  import fs4 from "node:fs";
3253
- import path6 from "node:path";
3287
+ import path7 from "node:path";
3254
3288
  var init_statusline_focus_cache = __esm({
3255
3289
  "spex-console/src/lib/control-plane/statusline-focus-cache.ts"() {
3256
3290
  "use strict";
@@ -4002,10 +4036,10 @@ function trashPathFor(relPath, now) {
4002
4036
  const normalized = relPath.split("\\").join("/");
4003
4037
  const slashIdx = normalized.lastIndexOf("/");
4004
4038
  const folder = slashIdx === -1 ? "" : normalized.slice(0, slashIdx);
4005
- const basename4 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
4039
+ const basename5 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
4006
4040
  const stamp = now.toISOString().replace(/[:.]/g, "-");
4007
4041
  const trashDir = folder ? `${folder}/.trash` : ".trash";
4008
- return `${trashDir}/${stamp}-${basename4}`;
4042
+ return `${trashDir}/${stamp}-${basename5}`;
4009
4043
  }
4010
4044
  function materializeSpecMarkdown(bundle) {
4011
4045
  return stringifySpecMarkdown(bundle.bodyText, bundle.frontmatter);
@@ -4127,10 +4161,10 @@ var init_export_status = __esm({
4127
4161
  function isGitHubFailure(value) {
4128
4162
  return typeof value === "object" && value !== null && "status" in value;
4129
4163
  }
4130
- async function githubJson(path15, target, fetchImpl, init) {
4164
+ async function githubJson(path16, target, fetchImpl, init) {
4131
4165
  let response;
4132
4166
  try {
4133
- response = await fetchImpl(`${GITHUB_API_BASE2}${path15}`, {
4167
+ response = await fetchImpl(`${GITHUB_API_BASE2}${path16}`, {
4134
4168
  ...init,
4135
4169
  headers: {
4136
4170
  ...githubHeaders(target.token),
@@ -5008,7 +5042,7 @@ var init_diagnose_spawned = __esm({
5008
5042
 
5009
5043
  // spex-console/src/lib/guardrail-naming.ts
5010
5044
  import fs5 from "node:fs";
5011
- import path7 from "node:path";
5045
+ import path8 from "node:path";
5012
5046
  var init_guardrail_naming = __esm({
5013
5047
  "spex-console/src/lib/guardrail-naming.ts"() {
5014
5048
  "use strict";
@@ -5018,7 +5052,7 @@ var init_guardrail_naming = __esm({
5018
5052
  // spex-console/src/lib/indexer/parser.ts
5019
5053
  import { createHash as createHash3 } from "node:crypto";
5020
5054
  import { promises as fs6 } from "node:fs";
5021
- import path8 from "node:path";
5055
+ import path9 from "node:path";
5022
5056
  var import_gray_matter2;
5023
5057
  var init_parser = __esm({
5024
5058
  "spex-console/src/lib/indexer/parser.ts"() {
@@ -5125,7 +5159,7 @@ var init_scoped_context = __esm({
5125
5159
  // spex-console/src/lib/indexer/indexer-service.ts
5126
5160
  import { spawnSync } from "node:child_process";
5127
5161
  import { promises as fs7 } from "node:fs";
5128
- import path9 from "node:path";
5162
+ import path10 from "node:path";
5129
5163
  var init_indexer_service = __esm({
5130
5164
  "spex-console/src/lib/indexer/indexer-service.ts"() {
5131
5165
  "use strict";
@@ -5144,7 +5178,7 @@ var init_indexer_service = __esm({
5144
5178
 
5145
5179
  // spex-console/src/lib/indexer/outbox-index-feeder.ts
5146
5180
  import { promises as fs8 } from "node:fs";
5147
- import path10 from "node:path";
5181
+ import path11 from "node:path";
5148
5182
  var init_outbox_index_feeder = __esm({
5149
5183
  "spex-console/src/lib/indexer/outbox-index-feeder.ts"() {
5150
5184
  "use strict";
@@ -5423,7 +5457,7 @@ var init_list_filter = __esm({
5423
5457
  });
5424
5458
 
5425
5459
  // spex-console/src/lib/specs/list-query.ts
5426
- import path11 from "node:path";
5460
+ import path12 from "node:path";
5427
5461
  var init_list_query = __esm({
5428
5462
  "spex-console/src/lib/specs/list-query.ts"() {
5429
5463
  "use strict";
@@ -5466,7 +5500,7 @@ var init_pipeline_neighborhood = __esm({
5466
5500
  });
5467
5501
 
5468
5502
  // spex-console/src/lib/specs/spec-by-id.ts
5469
- import path12 from "node:path";
5503
+ import path13 from "node:path";
5470
5504
  var init_spec_by_id = __esm({
5471
5505
  "spex-console/src/lib/specs/spec-by-id.ts"() {
5472
5506
  "use strict";
@@ -5523,9 +5557,9 @@ var init_search_body_hit = __esm({
5523
5557
 
5524
5558
  // spex-console/src/lib/vault.ts
5525
5559
  import { spawn } from "node:child_process";
5526
- import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync } from "node:fs";
5560
+ import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
5527
5561
  import { promises as fs9 } from "node:fs";
5528
- import path13 from "node:path";
5562
+ import path14 from "node:path";
5529
5563
  import yaml3 from "js-yaml";
5530
5564
  function normalizeDateField(value) {
5531
5565
  let d = null;
@@ -5566,18 +5600,18 @@ function extractBodyLinks(body) {
5566
5600
  return links;
5567
5601
  }
5568
5602
  function toSummary(absPath, root, fm, body) {
5569
- const basename4 = path13.basename(absPath).replace(/\.md$/i, "");
5570
- const relPath = path13.relative(root, absPath).split(path13.sep).join("/");
5603
+ const basename5 = path14.basename(absPath).replace(/\.md$/i, "");
5604
+ const relPath = path14.relative(root, absPath).split(path14.sep).join("/");
5571
5605
  return {
5572
- id: typeof fm.id === "string" && fm.id ? fm.id : basename4,
5573
- name: typeof fm.name === "string" && fm.name ? fm.name : basename4,
5606
+ id: typeof fm.id === "string" && fm.id ? fm.id : basename5,
5607
+ name: typeof fm.name === "string" && fm.name ? fm.name : basename5,
5574
5608
  type: typeof fm.type === "string" ? fm.type : "unknown",
5575
5609
  status: typeof fm.status === "string" ? fm.status : "unknown",
5576
5610
  effort: typeof fm.effort === "number" ? fm.effort : null,
5577
5611
  owner: typeof fm.owner === "string" ? fm.owner : null,
5578
5612
  tags: Array.isArray(fm.tags) ? fm.tags.map(String) : [],
5579
5613
  archived: fm.archived === true,
5580
- basename: basename4,
5614
+ basename: basename5,
5581
5615
  relPath,
5582
5616
  links: extractBodyLinks(body),
5583
5617
  created: normalizeDateField(fm.created),
@@ -5663,9 +5697,9 @@ function invalidateVaultCacheForFile(absPath) {
5663
5697
  else evictProcessVaultHotRows();
5664
5698
  }
5665
5699
  function specIdFromAbsPath(absPath) {
5666
- const basename4 = path13.basename(absPath).replace(/\.md$/i, "");
5667
- if (!basename4) return null;
5668
- const parsed = parseSpecId(basename4);
5700
+ const basename5 = path14.basename(absPath).replace(/\.md$/i, "");
5701
+ if (!basename5) return null;
5702
+ const parsed = parseSpecId(basename5);
5669
5703
  if (parsed?.era === 3 && parsed.fileKey) return `HS-${parsed.fileKey.toUpperCase()}`;
5670
5704
  return null;
5671
5705
  }
@@ -5754,8 +5788,8 @@ function isLocalVaultMode() {
5754
5788
  }
5755
5789
  async function writeSpecBytesToVault(relPath, content) {
5756
5790
  try {
5757
- const absPath = path13.join(vaultRoot(), relPath);
5758
- const dir = path13.dirname(absPath);
5791
+ const absPath = path14.join(vaultRoot(), relPath);
5792
+ const dir = path14.dirname(absPath);
5759
5793
  await fs9.mkdir(dir, { recursive: true });
5760
5794
  await fs9.writeFile(absPath, content, "utf8");
5761
5795
  } catch (err) {
@@ -6009,7 +6043,7 @@ var init_esm = __esm({
6009
6043
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
6010
6044
  const statMethod = opts.lstat ? lstat : stat;
6011
6045
  if (wantBigintFsStats) {
6012
- this._stat = (path15) => statMethod(path15, { bigint: true });
6046
+ this._stat = (path16) => statMethod(path16, { bigint: true });
6013
6047
  } else {
6014
6048
  this._stat = statMethod;
6015
6049
  }
@@ -6034,8 +6068,8 @@ var init_esm = __esm({
6034
6068
  const par = this.parent;
6035
6069
  const fil = par && par.files;
6036
6070
  if (fil && fil.length > 0) {
6037
- const { path: path15, depth } = par;
6038
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path15));
6071
+ const { path: path16, depth } = par;
6072
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path16));
6039
6073
  const awaited = await Promise.all(slice);
6040
6074
  for (const entry of awaited) {
6041
6075
  if (!entry)
@@ -6075,21 +6109,21 @@ var init_esm = __esm({
6075
6109
  this.reading = false;
6076
6110
  }
6077
6111
  }
6078
- async _exploreDir(path15, depth) {
6112
+ async _exploreDir(path16, depth) {
6079
6113
  let files;
6080
6114
  try {
6081
- files = await readdir(path15, this._rdOptions);
6115
+ files = await readdir(path16, this._rdOptions);
6082
6116
  } catch (error) {
6083
6117
  this._onError(error);
6084
6118
  }
6085
- return { files, depth, path: path15 };
6119
+ return { files, depth, path: path16 };
6086
6120
  }
6087
- async _formatEntry(dirent, path15) {
6121
+ async _formatEntry(dirent, path16) {
6088
6122
  let entry;
6089
- const basename4 = this._isDirent ? dirent.name : dirent;
6123
+ const basename5 = this._isDirent ? dirent.name : dirent;
6090
6124
  try {
6091
- const fullPath = presolve(pjoin(path15, basename4));
6092
- entry = { path: prelative(this._root, fullPath), fullPath, basename: basename4 };
6125
+ const fullPath = presolve(pjoin(path16, basename5));
6126
+ entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
6093
6127
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
6094
6128
  } catch (err) {
6095
6129
  this._onError(err);
@@ -6149,16 +6183,16 @@ import { watchFile, unwatchFile, watch as fs_watch } from "fs";
6149
6183
  import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
6150
6184
  import * as sysPath from "path";
6151
6185
  import { type as osType } from "os";
6152
- function createFsWatchInstance(path15, options2, listener, errHandler, emitRaw) {
6186
+ function createFsWatchInstance(path16, options2, listener, errHandler, emitRaw) {
6153
6187
  const handleEvent = (rawEvent, evPath) => {
6154
- listener(path15);
6155
- emitRaw(rawEvent, evPath, { watchedPath: path15 });
6156
- if (evPath && path15 !== evPath) {
6157
- fsWatchBroadcast(sysPath.resolve(path15, evPath), KEY_LISTENERS, sysPath.join(path15, evPath));
6188
+ listener(path16);
6189
+ emitRaw(rawEvent, evPath, { watchedPath: path16 });
6190
+ if (evPath && path16 !== evPath) {
6191
+ fsWatchBroadcast(sysPath.resolve(path16, evPath), KEY_LISTENERS, sysPath.join(path16, evPath));
6158
6192
  }
6159
6193
  };
6160
6194
  try {
6161
- return fs_watch(path15, {
6195
+ return fs_watch(path16, {
6162
6196
  persistent: options2.persistent
6163
6197
  }, handleEvent);
6164
6198
  } catch (error) {
@@ -6503,12 +6537,12 @@ var init_handler = __esm({
6503
6537
  listener(val1, val2, val3);
6504
6538
  });
6505
6539
  };
6506
- setFsWatchListener = (path15, fullPath, options2, handlers) => {
6540
+ setFsWatchListener = (path16, fullPath, options2, handlers) => {
6507
6541
  const { listener, errHandler, rawEmitter } = handlers;
6508
6542
  let cont = FsWatchInstances.get(fullPath);
6509
6543
  let watcher;
6510
6544
  if (!options2.persistent) {
6511
- watcher = createFsWatchInstance(path15, options2, listener, errHandler, rawEmitter);
6545
+ watcher = createFsWatchInstance(path16, options2, listener, errHandler, rawEmitter);
6512
6546
  if (!watcher)
6513
6547
  return;
6514
6548
  return watcher.close.bind(watcher);
@@ -6519,7 +6553,7 @@ var init_handler = __esm({
6519
6553
  addAndConvert(cont, KEY_RAW, rawEmitter);
6520
6554
  } else {
6521
6555
  watcher = createFsWatchInstance(
6522
- path15,
6556
+ path16,
6523
6557
  options2,
6524
6558
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
6525
6559
  errHandler,
@@ -6534,7 +6568,7 @@ var init_handler = __esm({
6534
6568
  cont.watcherUnusable = true;
6535
6569
  if (isWindows && error.code === "EPERM") {
6536
6570
  try {
6537
- const fd = await open(path15, "r");
6571
+ const fd = await open(path16, "r");
6538
6572
  await fd.close();
6539
6573
  broadcastErr(error);
6540
6574
  } catch (err) {
@@ -6565,7 +6599,7 @@ var init_handler = __esm({
6565
6599
  };
6566
6600
  };
6567
6601
  FsWatchFileInstances = /* @__PURE__ */ new Map();
6568
- setFsWatchFileListener = (path15, fullPath, options2, handlers) => {
6602
+ setFsWatchFileListener = (path16, fullPath, options2, handlers) => {
6569
6603
  const { listener, rawEmitter } = handlers;
6570
6604
  let cont = FsWatchFileInstances.get(fullPath);
6571
6605
  const copts = cont && cont.options;
@@ -6587,7 +6621,7 @@ var init_handler = __esm({
6587
6621
  });
6588
6622
  const currmtime = curr.mtimeMs;
6589
6623
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
6590
- foreach(cont.listeners, (listener2) => listener2(path15, curr));
6624
+ foreach(cont.listeners, (listener2) => listener2(path16, curr));
6591
6625
  }
6592
6626
  })
6593
6627
  };
@@ -6615,13 +6649,13 @@ var init_handler = __esm({
6615
6649
  * @param listener on fs change
6616
6650
  * @returns closer for the watcher instance
6617
6651
  */
6618
- _watchWithNodeFs(path15, listener) {
6652
+ _watchWithNodeFs(path16, listener) {
6619
6653
  const opts = this.fsw.options;
6620
- const directory = sysPath.dirname(path15);
6621
- const basename4 = sysPath.basename(path15);
6654
+ const directory = sysPath.dirname(path16);
6655
+ const basename5 = sysPath.basename(path16);
6622
6656
  const parent = this.fsw._getWatchedDir(directory);
6623
- parent.add(basename4);
6624
- const absolutePath = sysPath.resolve(path15);
6657
+ parent.add(basename5);
6658
+ const absolutePath = sysPath.resolve(path16);
6625
6659
  const options2 = {
6626
6660
  persistent: opts.persistent
6627
6661
  };
@@ -6630,13 +6664,13 @@ var init_handler = __esm({
6630
6664
  let closer;
6631
6665
  if (opts.usePolling) {
6632
6666
  const enableBin = opts.interval !== opts.binaryInterval;
6633
- options2.interval = enableBin && isBinaryPath(basename4) ? opts.binaryInterval : opts.interval;
6634
- closer = setFsWatchFileListener(path15, absolutePath, options2, {
6667
+ options2.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
6668
+ closer = setFsWatchFileListener(path16, absolutePath, options2, {
6635
6669
  listener,
6636
6670
  rawEmitter: this.fsw._emitRaw
6637
6671
  });
6638
6672
  } else {
6639
- closer = setFsWatchListener(path15, absolutePath, options2, {
6673
+ closer = setFsWatchListener(path16, absolutePath, options2, {
6640
6674
  listener,
6641
6675
  errHandler: this._boundHandleError,
6642
6676
  rawEmitter: this.fsw._emitRaw
@@ -6653,12 +6687,12 @@ var init_handler = __esm({
6653
6687
  return;
6654
6688
  }
6655
6689
  const dirname7 = sysPath.dirname(file);
6656
- const basename4 = sysPath.basename(file);
6690
+ const basename5 = sysPath.basename(file);
6657
6691
  const parent = this.fsw._getWatchedDir(dirname7);
6658
6692
  let prevStats = stats;
6659
- if (parent.has(basename4))
6693
+ if (parent.has(basename5))
6660
6694
  return;
6661
- const listener = async (path15, newStats) => {
6695
+ const listener = async (path16, newStats) => {
6662
6696
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
6663
6697
  return;
6664
6698
  if (!newStats || newStats.mtimeMs === 0) {
@@ -6672,18 +6706,18 @@ var init_handler = __esm({
6672
6706
  this.fsw._emit(EV.CHANGE, file, newStats2);
6673
6707
  }
6674
6708
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
6675
- this.fsw._closeFile(path15);
6709
+ this.fsw._closeFile(path16);
6676
6710
  prevStats = newStats2;
6677
6711
  const closer2 = this._watchWithNodeFs(file, listener);
6678
6712
  if (closer2)
6679
- this.fsw._addPathCloser(path15, closer2);
6713
+ this.fsw._addPathCloser(path16, closer2);
6680
6714
  } else {
6681
6715
  prevStats = newStats2;
6682
6716
  }
6683
6717
  } catch (error) {
6684
- this.fsw._remove(dirname7, basename4);
6718
+ this.fsw._remove(dirname7, basename5);
6685
6719
  }
6686
- } else if (parent.has(basename4)) {
6720
+ } else if (parent.has(basename5)) {
6687
6721
  const at = newStats.atimeMs;
6688
6722
  const mt = newStats.mtimeMs;
6689
6723
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -6708,7 +6742,7 @@ var init_handler = __esm({
6708
6742
  * @param item basename of this item
6709
6743
  * @returns true if no more processing is needed for this entry.
6710
6744
  */
6711
- async _handleSymlink(entry, directory, path15, item) {
6745
+ async _handleSymlink(entry, directory, path16, item) {
6712
6746
  if (this.fsw.closed) {
6713
6747
  return;
6714
6748
  }
@@ -6718,7 +6752,7 @@ var init_handler = __esm({
6718
6752
  this.fsw._incrReadyCount();
6719
6753
  let linkPath;
6720
6754
  try {
6721
- linkPath = await fsrealpath(path15);
6755
+ linkPath = await fsrealpath(path16);
6722
6756
  } catch (e) {
6723
6757
  this.fsw._emitReady();
6724
6758
  return true;
@@ -6728,12 +6762,12 @@ var init_handler = __esm({
6728
6762
  if (dir.has(item)) {
6729
6763
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
6730
6764
  this.fsw._symlinkPaths.set(full, linkPath);
6731
- this.fsw._emit(EV.CHANGE, path15, entry.stats);
6765
+ this.fsw._emit(EV.CHANGE, path16, entry.stats);
6732
6766
  }
6733
6767
  } else {
6734
6768
  dir.add(item);
6735
6769
  this.fsw._symlinkPaths.set(full, linkPath);
6736
- this.fsw._emit(EV.ADD, path15, entry.stats);
6770
+ this.fsw._emit(EV.ADD, path16, entry.stats);
6737
6771
  }
6738
6772
  this.fsw._emitReady();
6739
6773
  return true;
@@ -6762,9 +6796,9 @@ var init_handler = __esm({
6762
6796
  return;
6763
6797
  }
6764
6798
  const item = entry.path;
6765
- let path15 = sysPath.join(directory, item);
6799
+ let path16 = sysPath.join(directory, item);
6766
6800
  current.add(item);
6767
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path15, item)) {
6801
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path16, item)) {
6768
6802
  return;
6769
6803
  }
6770
6804
  if (this.fsw.closed) {
@@ -6773,8 +6807,8 @@ var init_handler = __esm({
6773
6807
  }
6774
6808
  if (item === target || !target && !previous.has(item)) {
6775
6809
  this.fsw._incrReadyCount();
6776
- path15 = sysPath.join(dir, sysPath.relative(dir, path15));
6777
- this._addToNodeFs(path15, initialAdd, wh, depth + 1);
6810
+ path16 = sysPath.join(dir, sysPath.relative(dir, path16));
6811
+ this._addToNodeFs(path16, initialAdd, wh, depth + 1);
6778
6812
  }
6779
6813
  }).on(EV.ERROR, this._boundHandleError);
6780
6814
  return new Promise((resolve7, reject) => {
@@ -6843,13 +6877,13 @@ var init_handler = __esm({
6843
6877
  * @param depth Child path actually targeted for watch
6844
6878
  * @param target Child path actually targeted for watch
6845
6879
  */
6846
- async _addToNodeFs(path15, initialAdd, priorWh, depth, target) {
6880
+ async _addToNodeFs(path16, initialAdd, priorWh, depth, target) {
6847
6881
  const ready = this.fsw._emitReady;
6848
- if (this.fsw._isIgnored(path15) || this.fsw.closed) {
6882
+ if (this.fsw._isIgnored(path16) || this.fsw.closed) {
6849
6883
  ready();
6850
6884
  return false;
6851
6885
  }
6852
- const wh = this.fsw._getWatchHelpers(path15);
6886
+ const wh = this.fsw._getWatchHelpers(path16);
6853
6887
  if (priorWh) {
6854
6888
  wh.filterPath = (entry) => priorWh.filterPath(entry);
6855
6889
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -6865,8 +6899,8 @@ var init_handler = __esm({
6865
6899
  const follow = this.fsw.options.followSymlinks;
6866
6900
  let closer;
6867
6901
  if (stats.isDirectory()) {
6868
- const absPath = sysPath.resolve(path15);
6869
- const targetPath = follow ? await fsrealpath(path15) : path15;
6902
+ const absPath = sysPath.resolve(path16);
6903
+ const targetPath = follow ? await fsrealpath(path16) : path16;
6870
6904
  if (this.fsw.closed)
6871
6905
  return;
6872
6906
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -6876,29 +6910,29 @@ var init_handler = __esm({
6876
6910
  this.fsw._symlinkPaths.set(absPath, targetPath);
6877
6911
  }
6878
6912
  } else if (stats.isSymbolicLink()) {
6879
- const targetPath = follow ? await fsrealpath(path15) : path15;
6913
+ const targetPath = follow ? await fsrealpath(path16) : path16;
6880
6914
  if (this.fsw.closed)
6881
6915
  return;
6882
6916
  const parent = sysPath.dirname(wh.watchPath);
6883
6917
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
6884
6918
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
6885
- closer = await this._handleDir(parent, stats, initialAdd, depth, path15, wh, targetPath);
6919
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path16, wh, targetPath);
6886
6920
  if (this.fsw.closed)
6887
6921
  return;
6888
6922
  if (targetPath !== void 0) {
6889
- this.fsw._symlinkPaths.set(sysPath.resolve(path15), targetPath);
6923
+ this.fsw._symlinkPaths.set(sysPath.resolve(path16), targetPath);
6890
6924
  }
6891
6925
  } else {
6892
6926
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
6893
6927
  }
6894
6928
  ready();
6895
6929
  if (closer)
6896
- this.fsw._addPathCloser(path15, closer);
6930
+ this.fsw._addPathCloser(path16, closer);
6897
6931
  return false;
6898
6932
  } catch (error) {
6899
6933
  if (this.fsw._handleError(error)) {
6900
6934
  ready();
6901
- return path15;
6935
+ return path16;
6902
6936
  }
6903
6937
  }
6904
6938
  }
@@ -6944,26 +6978,26 @@ function createPattern(matcher) {
6944
6978
  }
6945
6979
  return () => false;
6946
6980
  }
6947
- function normalizePath(path15) {
6948
- if (typeof path15 !== "string")
6981
+ function normalizePath(path16) {
6982
+ if (typeof path16 !== "string")
6949
6983
  throw new Error("string expected");
6950
- path15 = sysPath2.normalize(path15);
6951
- path15 = path15.replace(/\\/g, "/");
6984
+ path16 = sysPath2.normalize(path16);
6985
+ path16 = path16.replace(/\\/g, "/");
6952
6986
  let prepend = false;
6953
- if (path15.startsWith("//"))
6987
+ if (path16.startsWith("//"))
6954
6988
  prepend = true;
6955
6989
  const DOUBLE_SLASH_RE2 = /\/\//;
6956
- while (path15.match(DOUBLE_SLASH_RE2))
6957
- path15 = path15.replace(DOUBLE_SLASH_RE2, "/");
6990
+ while (path16.match(DOUBLE_SLASH_RE2))
6991
+ path16 = path16.replace(DOUBLE_SLASH_RE2, "/");
6958
6992
  if (prepend)
6959
- path15 = "/" + path15;
6960
- return path15;
6993
+ path16 = "/" + path16;
6994
+ return path16;
6961
6995
  }
6962
6996
  function matchPatterns(patterns, testString, stats) {
6963
- const path15 = normalizePath(testString);
6997
+ const path16 = normalizePath(testString);
6964
6998
  for (let index2 = 0; index2 < patterns.length; index2++) {
6965
6999
  const pattern = patterns[index2];
6966
- if (pattern(path15, stats)) {
7000
+ if (pattern(path16, stats)) {
6967
7001
  return true;
6968
7002
  }
6969
7003
  }
@@ -7024,19 +7058,19 @@ var init_esm2 = __esm({
7024
7058
  }
7025
7059
  return str2;
7026
7060
  };
7027
- normalizePathToUnix = (path15) => toUnix(sysPath2.normalize(toUnix(path15)));
7028
- normalizeIgnored = (cwd = "") => (path15) => {
7029
- if (typeof path15 === "string") {
7030
- return normalizePathToUnix(sysPath2.isAbsolute(path15) ? path15 : sysPath2.join(cwd, path15));
7061
+ normalizePathToUnix = (path16) => toUnix(sysPath2.normalize(toUnix(path16)));
7062
+ normalizeIgnored = (cwd = "") => (path16) => {
7063
+ if (typeof path16 === "string") {
7064
+ return normalizePathToUnix(sysPath2.isAbsolute(path16) ? path16 : sysPath2.join(cwd, path16));
7031
7065
  } else {
7032
- return path15;
7066
+ return path16;
7033
7067
  }
7034
7068
  };
7035
- getAbsolutePath = (path15, cwd) => {
7036
- if (sysPath2.isAbsolute(path15)) {
7037
- return path15;
7069
+ getAbsolutePath = (path16, cwd) => {
7070
+ if (sysPath2.isAbsolute(path16)) {
7071
+ return path16;
7038
7072
  }
7039
- return sysPath2.join(cwd, path15);
7073
+ return sysPath2.join(cwd, path16);
7040
7074
  };
7041
7075
  EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
7042
7076
  DirEntry = class {
@@ -7091,10 +7125,10 @@ var init_esm2 = __esm({
7091
7125
  STAT_METHOD_F = "stat";
7092
7126
  STAT_METHOD_L = "lstat";
7093
7127
  WatchHelper = class {
7094
- constructor(path15, follow, fsw) {
7128
+ constructor(path16, follow, fsw) {
7095
7129
  this.fsw = fsw;
7096
- const watchPath = path15;
7097
- this.path = path15 = path15.replace(REPLACER_RE, "");
7130
+ const watchPath = path16;
7131
+ this.path = path16 = path16.replace(REPLACER_RE, "");
7098
7132
  this.watchPath = watchPath;
7099
7133
  this.fullWatchPath = sysPath2.resolve(watchPath);
7100
7134
  this.dirParts = [];
@@ -7216,20 +7250,20 @@ var init_esm2 = __esm({
7216
7250
  this._closePromise = void 0;
7217
7251
  let paths = unifyPaths(paths_);
7218
7252
  if (cwd) {
7219
- paths = paths.map((path15) => {
7220
- const absPath = getAbsolutePath(path15, cwd);
7253
+ paths = paths.map((path16) => {
7254
+ const absPath = getAbsolutePath(path16, cwd);
7221
7255
  return absPath;
7222
7256
  });
7223
7257
  }
7224
- paths.forEach((path15) => {
7225
- this._removeIgnoredPath(path15);
7258
+ paths.forEach((path16) => {
7259
+ this._removeIgnoredPath(path16);
7226
7260
  });
7227
7261
  this._userIgnored = void 0;
7228
7262
  if (!this._readyCount)
7229
7263
  this._readyCount = 0;
7230
7264
  this._readyCount += paths.length;
7231
- Promise.all(paths.map(async (path15) => {
7232
- const res = await this._nodeFsHandler._addToNodeFs(path15, !_internal, void 0, 0, _origAdd);
7265
+ Promise.all(paths.map(async (path16) => {
7266
+ const res = await this._nodeFsHandler._addToNodeFs(path16, !_internal, void 0, 0, _origAdd);
7233
7267
  if (res)
7234
7268
  this._emitReady();
7235
7269
  return res;
@@ -7251,17 +7285,17 @@ var init_esm2 = __esm({
7251
7285
  return this;
7252
7286
  const paths = unifyPaths(paths_);
7253
7287
  const { cwd } = this.options;
7254
- paths.forEach((path15) => {
7255
- if (!sysPath2.isAbsolute(path15) && !this._closers.has(path15)) {
7288
+ paths.forEach((path16) => {
7289
+ if (!sysPath2.isAbsolute(path16) && !this._closers.has(path16)) {
7256
7290
  if (cwd)
7257
- path15 = sysPath2.join(cwd, path15);
7258
- path15 = sysPath2.resolve(path15);
7291
+ path16 = sysPath2.join(cwd, path16);
7292
+ path16 = sysPath2.resolve(path16);
7259
7293
  }
7260
- this._closePath(path15);
7261
- this._addIgnoredPath(path15);
7262
- if (this._watched.has(path15)) {
7294
+ this._closePath(path16);
7295
+ this._addIgnoredPath(path16);
7296
+ if (this._watched.has(path16)) {
7263
7297
  this._addIgnoredPath({
7264
- path: path15,
7298
+ path: path16,
7265
7299
  recursive: true
7266
7300
  });
7267
7301
  }
@@ -7325,38 +7359,38 @@ var init_esm2 = __esm({
7325
7359
  * @param stats arguments to be passed with event
7326
7360
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
7327
7361
  */
7328
- async _emit(event, path15, stats) {
7362
+ async _emit(event, path16, stats) {
7329
7363
  if (this.closed)
7330
7364
  return;
7331
7365
  const opts = this.options;
7332
7366
  if (isWindows)
7333
- path15 = sysPath2.normalize(path15);
7367
+ path16 = sysPath2.normalize(path16);
7334
7368
  if (opts.cwd)
7335
- path15 = sysPath2.relative(opts.cwd, path15);
7336
- const args = [path15];
7369
+ path16 = sysPath2.relative(opts.cwd, path16);
7370
+ const args = [path16];
7337
7371
  if (stats != null)
7338
7372
  args.push(stats);
7339
7373
  const awf = opts.awaitWriteFinish;
7340
7374
  let pw;
7341
- if (awf && (pw = this._pendingWrites.get(path15))) {
7375
+ if (awf && (pw = this._pendingWrites.get(path16))) {
7342
7376
  pw.lastChange = /* @__PURE__ */ new Date();
7343
7377
  return this;
7344
7378
  }
7345
7379
  if (opts.atomic) {
7346
7380
  if (event === EVENTS.UNLINK) {
7347
- this._pendingUnlinks.set(path15, [event, ...args]);
7381
+ this._pendingUnlinks.set(path16, [event, ...args]);
7348
7382
  setTimeout(() => {
7349
- this._pendingUnlinks.forEach((entry, path16) => {
7383
+ this._pendingUnlinks.forEach((entry, path17) => {
7350
7384
  this.emit(...entry);
7351
7385
  this.emit(EVENTS.ALL, ...entry);
7352
- this._pendingUnlinks.delete(path16);
7386
+ this._pendingUnlinks.delete(path17);
7353
7387
  });
7354
7388
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
7355
7389
  return this;
7356
7390
  }
7357
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path15)) {
7391
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path16)) {
7358
7392
  event = EVENTS.CHANGE;
7359
- this._pendingUnlinks.delete(path15);
7393
+ this._pendingUnlinks.delete(path16);
7360
7394
  }
7361
7395
  }
7362
7396
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -7374,16 +7408,16 @@ var init_esm2 = __esm({
7374
7408
  this.emitWithAll(event, args);
7375
7409
  }
7376
7410
  };
7377
- this._awaitWriteFinish(path15, awf.stabilityThreshold, event, awfEmit);
7411
+ this._awaitWriteFinish(path16, awf.stabilityThreshold, event, awfEmit);
7378
7412
  return this;
7379
7413
  }
7380
7414
  if (event === EVENTS.CHANGE) {
7381
- const isThrottled = !this._throttle(EVENTS.CHANGE, path15, 50);
7415
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path16, 50);
7382
7416
  if (isThrottled)
7383
7417
  return this;
7384
7418
  }
7385
7419
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
7386
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path15) : path15;
7420
+ const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path16) : path16;
7387
7421
  let stats2;
7388
7422
  try {
7389
7423
  stats2 = await stat3(fullPath);
@@ -7414,23 +7448,23 @@ var init_esm2 = __esm({
7414
7448
  * @param timeout duration of time to suppress duplicate actions
7415
7449
  * @returns tracking object or false if action should be suppressed
7416
7450
  */
7417
- _throttle(actionType, path15, timeout) {
7451
+ _throttle(actionType, path16, timeout) {
7418
7452
  if (!this._throttled.has(actionType)) {
7419
7453
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
7420
7454
  }
7421
7455
  const action = this._throttled.get(actionType);
7422
7456
  if (!action)
7423
7457
  throw new Error("invalid throttle");
7424
- const actionPath = action.get(path15);
7458
+ const actionPath = action.get(path16);
7425
7459
  if (actionPath) {
7426
7460
  actionPath.count++;
7427
7461
  return false;
7428
7462
  }
7429
7463
  let timeoutObject;
7430
7464
  const clear = () => {
7431
- const item = action.get(path15);
7465
+ const item = action.get(path16);
7432
7466
  const count2 = item ? item.count : 0;
7433
- action.delete(path15);
7467
+ action.delete(path16);
7434
7468
  clearTimeout(timeoutObject);
7435
7469
  if (item)
7436
7470
  clearTimeout(item.timeoutObject);
@@ -7438,7 +7472,7 @@ var init_esm2 = __esm({
7438
7472
  };
7439
7473
  timeoutObject = setTimeout(clear, timeout);
7440
7474
  const thr = { timeoutObject, clear, count: 0 };
7441
- action.set(path15, thr);
7475
+ action.set(path16, thr);
7442
7476
  return thr;
7443
7477
  }
7444
7478
  _incrReadyCount() {
@@ -7452,44 +7486,44 @@ var init_esm2 = __esm({
7452
7486
  * @param event
7453
7487
  * @param awfEmit Callback to be called when ready for event to be emitted.
7454
7488
  */
7455
- _awaitWriteFinish(path15, threshold, event, awfEmit) {
7489
+ _awaitWriteFinish(path16, threshold, event, awfEmit) {
7456
7490
  const awf = this.options.awaitWriteFinish;
7457
7491
  if (typeof awf !== "object")
7458
7492
  return;
7459
7493
  const pollInterval = awf.pollInterval;
7460
7494
  let timeoutHandler;
7461
- let fullPath = path15;
7462
- if (this.options.cwd && !sysPath2.isAbsolute(path15)) {
7463
- fullPath = sysPath2.join(this.options.cwd, path15);
7495
+ let fullPath = path16;
7496
+ if (this.options.cwd && !sysPath2.isAbsolute(path16)) {
7497
+ fullPath = sysPath2.join(this.options.cwd, path16);
7464
7498
  }
7465
7499
  const now = /* @__PURE__ */ new Date();
7466
7500
  const writes = this._pendingWrites;
7467
7501
  function awaitWriteFinishFn(prevStat) {
7468
7502
  statcb(fullPath, (err, curStat) => {
7469
- if (err || !writes.has(path15)) {
7503
+ if (err || !writes.has(path16)) {
7470
7504
  if (err && err.code !== "ENOENT")
7471
7505
  awfEmit(err);
7472
7506
  return;
7473
7507
  }
7474
7508
  const now2 = Number(/* @__PURE__ */ new Date());
7475
7509
  if (prevStat && curStat.size !== prevStat.size) {
7476
- writes.get(path15).lastChange = now2;
7510
+ writes.get(path16).lastChange = now2;
7477
7511
  }
7478
- const pw = writes.get(path15);
7512
+ const pw = writes.get(path16);
7479
7513
  const df = now2 - pw.lastChange;
7480
7514
  if (df >= threshold) {
7481
- writes.delete(path15);
7515
+ writes.delete(path16);
7482
7516
  awfEmit(void 0, curStat);
7483
7517
  } else {
7484
7518
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
7485
7519
  }
7486
7520
  });
7487
7521
  }
7488
- if (!writes.has(path15)) {
7489
- writes.set(path15, {
7522
+ if (!writes.has(path16)) {
7523
+ writes.set(path16, {
7490
7524
  lastChange: now,
7491
7525
  cancelWait: () => {
7492
- writes.delete(path15);
7526
+ writes.delete(path16);
7493
7527
  clearTimeout(timeoutHandler);
7494
7528
  return event;
7495
7529
  }
@@ -7500,8 +7534,8 @@ var init_esm2 = __esm({
7500
7534
  /**
7501
7535
  * Determines whether user has asked to ignore this path.
7502
7536
  */
7503
- _isIgnored(path15, stats) {
7504
- if (this.options.atomic && DOT_RE.test(path15))
7537
+ _isIgnored(path16, stats) {
7538
+ if (this.options.atomic && DOT_RE.test(path16))
7505
7539
  return true;
7506
7540
  if (!this._userIgnored) {
7507
7541
  const { cwd } = this.options;
@@ -7511,17 +7545,17 @@ var init_esm2 = __esm({
7511
7545
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
7512
7546
  this._userIgnored = anymatch(list, void 0);
7513
7547
  }
7514
- return this._userIgnored(path15, stats);
7548
+ return this._userIgnored(path16, stats);
7515
7549
  }
7516
- _isntIgnored(path15, stat4) {
7517
- return !this._isIgnored(path15, stat4);
7550
+ _isntIgnored(path16, stat4) {
7551
+ return !this._isIgnored(path16, stat4);
7518
7552
  }
7519
7553
  /**
7520
7554
  * Provides a set of common helpers and properties relating to symlink handling.
7521
7555
  * @param path file or directory pattern being watched
7522
7556
  */
7523
- _getWatchHelpers(path15) {
7524
- return new WatchHelper(path15, this.options.followSymlinks, this);
7557
+ _getWatchHelpers(path16) {
7558
+ return new WatchHelper(path16, this.options.followSymlinks, this);
7525
7559
  }
7526
7560
  // Directory helpers
7527
7561
  // -----------------
@@ -7553,63 +7587,63 @@ var init_esm2 = __esm({
7553
7587
  * @param item base path of item/directory
7554
7588
  */
7555
7589
  _remove(directory, item, isDirectory) {
7556
- const path15 = sysPath2.join(directory, item);
7557
- const fullPath = sysPath2.resolve(path15);
7558
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path15) || this._watched.has(fullPath);
7559
- if (!this._throttle("remove", path15, 100))
7590
+ const path16 = sysPath2.join(directory, item);
7591
+ const fullPath = sysPath2.resolve(path16);
7592
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path16) || this._watched.has(fullPath);
7593
+ if (!this._throttle("remove", path16, 100))
7560
7594
  return;
7561
7595
  if (!isDirectory && this._watched.size === 1) {
7562
7596
  this.add(directory, item, true);
7563
7597
  }
7564
- const wp = this._getWatchedDir(path15);
7598
+ const wp = this._getWatchedDir(path16);
7565
7599
  const nestedDirectoryChildren = wp.getChildren();
7566
- nestedDirectoryChildren.forEach((nested) => this._remove(path15, nested));
7600
+ nestedDirectoryChildren.forEach((nested) => this._remove(path16, nested));
7567
7601
  const parent = this._getWatchedDir(directory);
7568
7602
  const wasTracked = parent.has(item);
7569
7603
  parent.remove(item);
7570
7604
  if (this._symlinkPaths.has(fullPath)) {
7571
7605
  this._symlinkPaths.delete(fullPath);
7572
7606
  }
7573
- let relPath = path15;
7607
+ let relPath = path16;
7574
7608
  if (this.options.cwd)
7575
- relPath = sysPath2.relative(this.options.cwd, path15);
7609
+ relPath = sysPath2.relative(this.options.cwd, path16);
7576
7610
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
7577
7611
  const event = this._pendingWrites.get(relPath).cancelWait();
7578
7612
  if (event === EVENTS.ADD)
7579
7613
  return;
7580
7614
  }
7581
- this._watched.delete(path15);
7615
+ this._watched.delete(path16);
7582
7616
  this._watched.delete(fullPath);
7583
7617
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
7584
- if (wasTracked && !this._isIgnored(path15))
7585
- this._emit(eventName, path15);
7586
- this._closePath(path15);
7618
+ if (wasTracked && !this._isIgnored(path16))
7619
+ this._emit(eventName, path16);
7620
+ this._closePath(path16);
7587
7621
  }
7588
7622
  /**
7589
7623
  * Closes all watchers for a path
7590
7624
  */
7591
- _closePath(path15) {
7592
- this._closeFile(path15);
7593
- const dir = sysPath2.dirname(path15);
7594
- this._getWatchedDir(dir).remove(sysPath2.basename(path15));
7625
+ _closePath(path16) {
7626
+ this._closeFile(path16);
7627
+ const dir = sysPath2.dirname(path16);
7628
+ this._getWatchedDir(dir).remove(sysPath2.basename(path16));
7595
7629
  }
7596
7630
  /**
7597
7631
  * Closes only file-specific watchers
7598
7632
  */
7599
- _closeFile(path15) {
7600
- const closers = this._closers.get(path15);
7633
+ _closeFile(path16) {
7634
+ const closers = this._closers.get(path16);
7601
7635
  if (!closers)
7602
7636
  return;
7603
7637
  closers.forEach((closer) => closer());
7604
- this._closers.delete(path15);
7638
+ this._closers.delete(path16);
7605
7639
  }
7606
- _addPathCloser(path15, closer) {
7640
+ _addPathCloser(path16, closer) {
7607
7641
  if (!closer)
7608
7642
  return;
7609
- let list = this._closers.get(path15);
7643
+ let list = this._closers.get(path16);
7610
7644
  if (!list) {
7611
7645
  list = [];
7612
- this._closers.set(path15, list);
7646
+ this._closers.set(path16, list);
7613
7647
  }
7614
7648
  list.push(closer);
7615
7649
  }
@@ -7636,8 +7670,8 @@ var init_esm2 = __esm({
7636
7670
  });
7637
7671
 
7638
7672
  // spex-console/bin/hs-vault-pull.ts
7639
- import { readFileSync as readFileSync13 } from "node:fs";
7640
- import path14 from "node:path";
7673
+ import { readFileSync as readFileSync15 } from "node:fs";
7674
+ import path15 from "node:path";
7641
7675
  import process4 from "node:process";
7642
7676
 
7643
7677
  // spex-console/src/lib/cli/control-shared.ts
@@ -7647,14 +7681,14 @@ init_credentials();
7647
7681
  init_session_owner();
7648
7682
  init_vault_less_endpoints();
7649
7683
  init_registry_file();
7650
- import path from "node:path";
7684
+ import path2 from "node:path";
7651
7685
  var VAULT_ID_HEADER = "x-heyspex-vault-id";
7652
7686
  var ACCOUNT_ID_HEADER = "x-heyspex-account-id";
7653
- function scopeHeadersFor(scope, method, path15) {
7687
+ function scopeHeadersFor(scope, method, path16) {
7654
7688
  if (scope === "none") {
7655
- if (!isVaultLessEndpoint(method, path15)) {
7689
+ if (!isVaultLessEndpoint(method, path16)) {
7656
7690
  throw new Error(
7657
- `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 }.`
7691
+ `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 }.`
7658
7692
  );
7659
7693
  }
7660
7694
  return {};
@@ -7665,7 +7699,7 @@ function cloudRequestScope(scope) {
7665
7699
  const named = { accountId: scope.accountId, vaultId: scope.vaultId };
7666
7700
  return {
7667
7701
  ...named,
7668
- headersFor: (method, path15) => isVaultLessEndpoint(method, path15) ? {} : scopeHeadersFor(named, method, path15)
7702
+ headersFor: (method, path16) => isVaultLessEndpoint(method, path16) ? {} : scopeHeadersFor(named, method, path16)
7669
7703
  };
7670
7704
  }
7671
7705
 
@@ -7704,12 +7738,15 @@ var HELP = [
7704
7738
  "(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
7705
7739
  "copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
7706
7740
  "plus a generated CLAUDE.md, the kept-principles index, and the template_version",
7707
- "pin; with --git, one initial `git init` + commit. Rerunnable: a <dir> that already",
7708
- "holds heyspex.yaml and an empty/placeholder-only specs/ is accepted, existing",
7709
- "files are never overwritten (an existing CLAUDE.md gets a CLAUDE.md.template",
7710
- "beside it), and the result lists each manifest row as created or kept. Refuses a",
7711
- "non-empty <dir> without heyspex.yaml, or a specs/ holding managed specs.",
7712
- "See init.customer-vault.ts.",
7741
+ "pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
7742
+ "holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
7743
+ "vault-template row names: only missing shipped files are added, template_version",
7744
+ "is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
7745
+ "nothing existing is ever overwritten (an existing CLAUDE.md gets a",
7746
+ "CLAUDE.md.template beside it), the result lists each manifest row as created or",
7747
+ "kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
7748
+ "names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
7749
+ "non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
7713
7750
  "",
7714
7751
  "Flags:",
7715
7752
  " --console-url <url> console_url value (default: the mode-aware console --",
@@ -7769,7 +7806,8 @@ var PULL_HELP = [
7769
7806
  " 0 Done (or plan reported)",
7770
7807
  " 1 Runtime failure (unreadable source, a spec failed to write)",
7771
7808
  " 2 Usage error",
7772
- " 11 No vault could be resolved, or --apply would write another vault into this repo",
7809
+ " 11 No vault could be resolved, or --apply would write another vault into this repo,",
7810
+ " or this repo is a satellite / repo-binding code repo (its specs/ live in the vault)",
7773
7811
  "",
7774
7812
  "Without --push: cloud \u2192 disk only; never deletes. A spec already on disk is",
7775
7813
  "skipped and reported, except one the last sync wrote and nobody edited since",
@@ -7883,7 +7921,7 @@ init_console_fetch();
7883
7921
  init_registry_file();
7884
7922
  init_repo_identity();
7885
7923
  import { promises as fs } from "node:fs";
7886
- import path3 from "node:path";
7924
+ import path4 from "node:path";
7887
7925
  init_vault_workspace_shared();
7888
7926
  var SYNC_VAULT_EXIT_UNRESOLVED = 11;
7889
7927
  function refuse(message) {
@@ -7934,7 +7972,7 @@ async function resolveSyncVaultTargetFromEnv(deps) {
7934
7972
  let markerVaultId = null;
7935
7973
  try {
7936
7974
  markerVaultId = parseVaultId(
7937
- await fs.readFile(path3.join(deps.vaultRoot, "heyspex.yaml"), "utf8")
7975
+ await fs.readFile(path4.join(deps.vaultRoot, "heyspex.yaml"), "utf8")
7938
7976
  );
7939
7977
  } catch {
7940
7978
  markerVaultId = null;
@@ -8126,6 +8164,28 @@ function watchDirtyMarker(vaultRoot2, onDirty, { debounceMs = 100 } = {}) {
8126
8164
  }
8127
8165
 
8128
8166
  // spex-console/src/lib/vault-sync/pull.ts
8167
+ function detectEol(text2) {
8168
+ const crlf = (text2.match(/\r\n/g) ?? []).length;
8169
+ const all = (text2.match(/\n/g) ?? []).length;
8170
+ if (all === 0) return null;
8171
+ return crlf * 2 >= all ? "\r\n" : "\n";
8172
+ }
8173
+ function normalizeEol(text2, eol) {
8174
+ return text2.replace(/\r\n|\r|\n/g, eol);
8175
+ }
8176
+ function classifyHeyspexYaml(text2) {
8177
+ if (text2 === null) return "none";
8178
+ if (/^vault_path:[ \t]*[^\s#]/m.test(text2)) return "satellite";
8179
+ if (/^kind:[ \t]*["']?repo-binding["']?[ \t]*$/m.test(text2)) return "repo-binding";
8180
+ const markerKey = /^(vault_id|vault_root):/m.test(text2);
8181
+ if (!markerKey && /^vault:/m.test(text2)) return "repo-binding";
8182
+ return "vault";
8183
+ }
8184
+ function pullTargetRefusal(repoRoot, markerText) {
8185
+ const kind = classifyHeyspexYaml(markerText);
8186
+ if (kind !== "satellite" && kind !== "repo-binding") return null;
8187
+ return `refusing to write specs/ into ${repoRoot}: its heyspex.yaml is a ${kind} marker (a code repo that names its vault elsewhere), not a vault checkout. Nothing was written. Run \`hs pull\` inside the vault's own checkout (\`hs vault resolve\` shows which one this repo is bound to).`;
8188
+ }
8129
8189
  function normalizeSpecRelPath(raw) {
8130
8190
  if (typeof raw !== "string") return null;
8131
8191
  let p = raw.trim();
@@ -8157,11 +8217,11 @@ function selectRemoteRefs(remote, only) {
8157
8217
  const selected = [];
8158
8218
  for (const ref of remote) {
8159
8219
  const id = (ref.id ?? "").toLowerCase();
8160
- const basename4 = (ref.basename ?? "").toLowerCase();
8220
+ const basename5 = (ref.basename ?? "").toLowerCase();
8161
8221
  let hit = null;
8162
8222
  for (const sel of selectors) {
8163
8223
  const s = sel.toLowerCase();
8164
- if (id === s || basename4 === s || id.startsWith(`${s}-`) || basename4.startsWith(`${s}-`)) {
8224
+ if (id === s || basename5 === s || id.startsWith(`${s}-`) || basename5.startsWith(`${s}-`)) {
8165
8225
  hit = sel;
8166
8226
  break;
8167
8227
  }
@@ -8212,7 +8272,7 @@ function decidePullPlan({
8212
8272
  });
8213
8273
  continue;
8214
8274
  }
8215
- const basename4 = basenameOfRelPath(relPath);
8275
+ const basename5 = basenameOfRelPath(relPath);
8216
8276
  if (hasDotSegment(relPath)) {
8217
8277
  skip2.push({
8218
8278
  id: ref.id,
@@ -8228,7 +8288,7 @@ function decidePullPlan({
8228
8288
  pull.push({
8229
8289
  id: ref.id,
8230
8290
  relPath,
8231
- basename: basename4,
8291
+ basename: basename5,
8232
8292
  overwrites: true,
8233
8293
  remoteUpdatedAt: ref.updatedAt ?? void 0
8234
8294
  });
@@ -8236,7 +8296,7 @@ function decidePullPlan({
8236
8296
  pull.push({
8237
8297
  id: ref.id,
8238
8298
  relPath,
8239
- basename: basename4,
8299
+ basename: basename5,
8240
8300
  overwrites: true,
8241
8301
  remoteUpdatedAt: ref.updatedAt ?? void 0,
8242
8302
  fastForwardFromHash: ffHash
@@ -8246,7 +8306,7 @@ function decidePullPlan({
8246
8306
  }
8247
8307
  continue;
8248
8308
  }
8249
- const elsewhere = localByBasename.get(basename4.toLowerCase());
8309
+ const elsewhere = localByBasename.get(basename5.toLowerCase());
8250
8310
  if (elsewhere) {
8251
8311
  skip2.push({
8252
8312
  id: ref.id,
@@ -8259,7 +8319,7 @@ function decidePullPlan({
8259
8319
  pull.push({
8260
8320
  id: ref.id,
8261
8321
  relPath,
8262
- basename: basename4,
8322
+ basename: basename5,
8263
8323
  overwrites: false,
8264
8324
  remoteUpdatedAt: ref.updatedAt ?? void 0
8265
8325
  });
@@ -8320,7 +8380,8 @@ async function runVaultPull(deps, options2 = {}) {
8320
8380
  });
8321
8381
  continue;
8322
8382
  }
8323
- const bytes = deps.serialize(content.body, content.frontmatter);
8383
+ const serialized = deps.serialize(content.body, content.frontmatter);
8384
+ const bytes = deps.eolFor ? normalizeEol(serialized, await deps.eolFor(item.relPath)) : serialized;
8324
8385
  if (item.fastForwardFromHash && deps.hashLocal) {
8325
8386
  const nowHash = await deps.hashLocal(item.relPath);
8326
8387
  if (nowHash !== item.fastForwardFromHash) {
@@ -8434,16 +8495,17 @@ function formatPullSummary(result, { detail = false, sampleSize = 5 } = {}) {
8434
8495
  }
8435
8496
 
8436
8497
  // spex-console/src/lib/vault-sync/pull-io.ts
8437
- import { promises as fs10 } from "node:fs";
8498
+ import { promises as fs10, readFileSync as readFileSync14 } from "node:fs";
8499
+ import { EOL } from "node:os";
8438
8500
  import nodePath3 from "node:path";
8439
8501
 
8440
8502
  // spex-console/src/lib/dev-mode.ts
8441
8503
  init_heyspex_home2();
8442
8504
  init_credentials();
8443
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
8505
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
8444
8506
  import { join as join10 } from "node:path";
8445
8507
  var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
8446
- var defaultReadFile = (p) => readFileSync10(p, "utf8");
8508
+ var defaultReadFile = (p) => readFileSync11(p, "utf8");
8447
8509
  function readCredentialsMcpToken({
8448
8510
  homeDir = heyspexHome2(),
8449
8511
  readFile = defaultReadFile
@@ -8461,6 +8523,30 @@ function readCredentialsMcpToken({
8461
8523
  init_heyspex_home2();
8462
8524
  init_vault();
8463
8525
  init_vault_frontmatter();
8526
+ var DEFAULT_PULL_EOL = EOL === "\r\n" ? "\r\n" : "\n";
8527
+ async function eolForPath(abs, fallback = DEFAULT_PULL_EOL) {
8528
+ try {
8529
+ return detectEol(await fs10.readFile(abs, "utf8")) ?? fallback;
8530
+ } catch {
8531
+ return fallback;
8532
+ }
8533
+ }
8534
+ var PullTargetNotVaultError = class extends Error {
8535
+ constructor(message) {
8536
+ super(message);
8537
+ this.name = "PullTargetNotVaultError";
8538
+ }
8539
+ };
8540
+ function assertPullTargetIsVault(repoRoot) {
8541
+ let text2;
8542
+ try {
8543
+ text2 = readFileSync14(nodePath3.join(repoRoot, "heyspex.yaml"), "utf8");
8544
+ } catch {
8545
+ text2 = null;
8546
+ }
8547
+ const refusal = pullTargetRefusal(repoRoot, text2);
8548
+ if (refusal) throw new PullTargetNotVaultError(refusal);
8549
+ }
8464
8550
  var LIST_FIELDS = "id,basename,relPath,type,status,updated,updatedAt";
8465
8551
  var PULL_LIST_PAGE_LIMIT = 500;
8466
8552
  var PULL_LIST_MAX_PAGES = 2e3;
@@ -8484,10 +8570,10 @@ function resolvePullSourceUrl(explicit) {
8484
8570
  const raw = explicit?.trim() || process.env.HEYSPEX_PULL_SOURCE_URL?.trim() || CLOUD_CONSOLE_URL2;
8485
8571
  return raw.replace(/\/+$/, "");
8486
8572
  }
8487
- async function getJson({ baseUrl, token, fetchImpl = fetch, scopeHeaders = {} }, path15) {
8573
+ async function getJson({ baseUrl, token, fetchImpl = fetch, scopeHeaders = {} }, path16) {
8488
8574
  const headers = { Accept: "application/json", ...scopeHeaders };
8489
8575
  if (token) headers.Authorization = `Bearer ${token}`;
8490
- const res = await fetchImpl(`${baseUrl}/${path15.replace(/^\/+/, "")}`, { headers });
8576
+ const res = await fetchImpl(`${baseUrl}/${path16.replace(/^\/+/, "")}`, { headers });
8491
8577
  const text2 = await res.text();
8492
8578
  let json = null;
8493
8579
  try {
@@ -8652,6 +8738,12 @@ function createVaultPullDeps({
8652
8738
  }) {
8653
8739
  const http = { baseUrl, token, fetchImpl, scopeHeaders };
8654
8740
  const prefetched = /* @__PURE__ */ new Map();
8741
+ let targetChecked = false;
8742
+ const guardTarget = () => {
8743
+ if (!localRoot || targetChecked) return;
8744
+ assertPullTargetIsVault(nodePath3.dirname(localRoot));
8745
+ targetChecked = true;
8746
+ };
8655
8747
  return {
8656
8748
  listRemote: () => listRemoteSpecs(http),
8657
8749
  getRemoteRef: async (selector) => {
@@ -8673,7 +8765,9 @@ function createVaultPullDeps({
8673
8765
  finish: ledger ? () => ledger.flush() : void 0,
8674
8766
  listLocal: () => listLocalSpecRelPaths(localRoot ?? vaultRoot()),
8675
8767
  serialize: stringifySpecMarkdown,
8768
+ eolFor: (relPath) => eolForPath(nodePath3.join(localRoot ?? vaultRoot(), relPath)),
8676
8769
  writeSpec: localRoot ? async (relPath, content, opts) => {
8770
+ guardTarget();
8677
8771
  const abs = nodePath3.join(localRoot, relPath);
8678
8772
  await fs10.mkdir(nodePath3.dirname(abs), { recursive: true });
8679
8773
  onLocalWrite?.(relPath);
@@ -8923,9 +9017,9 @@ async function runVaultPush(deps, options2 = {}) {
8923
9017
  result.skipped.push(item(pre.reason, pre.detail));
8924
9018
  continue;
8925
9019
  }
8926
- const basename4 = basenameOfRelPath(relPath);
9020
+ const basename5 = basenameOfRelPath(relPath);
8927
9021
  if (remoteIndex) {
8928
- const stamp = remoteIndex.get(basename4.toLowerCase());
9022
+ const stamp = remoteIndex.get(basename5.toLowerCase());
8929
9023
  if (stamp === void 0) {
8930
9024
  result.skipped.push(
8931
9025
  item("new-on-disk", "no remote row - hs never creates cloud specs from a disk file")
@@ -8944,7 +9038,7 @@ async function runVaultPush(deps, options2 = {}) {
8944
9038
  }
8945
9039
  let remote;
8946
9040
  try {
8947
- remote = await deps.fetchRemote(basename4);
9041
+ remote = await deps.fetchRemote(basename5);
8948
9042
  } catch (err) {
8949
9043
  result.failed.push({
8950
9044
  relPath,
@@ -8992,7 +9086,7 @@ async function runVaultPush(deps, options2 = {}) {
8992
9086
  puts += 1;
8993
9087
  let put;
8994
9088
  try {
8995
- put = await deps.putRemote(basename4, {
9089
+ put = await deps.putRemote(basename5, {
8996
9090
  frontmatter: payload.frontmatter,
8997
9091
  body: payload.body
8998
9092
  });
@@ -9046,7 +9140,7 @@ import nodePath4 from "node:path";
9046
9140
  init_vault_frontmatter();
9047
9141
  async function putRemoteSpec(http, id, payload) {
9048
9142
  const { baseUrl, token, fetchImpl = fetch } = http;
9049
- const path15 = `/api/specs/${encodeURIComponent(id)}`;
9143
+ const path16 = `/api/specs/${encodeURIComponent(id)}`;
9050
9144
  const scope = http.accountId && http.vaultId ? cloudRequestScope({ accountId: http.accountId, vaultId: http.vaultId }).headersFor(
9051
9145
  "PUT",
9052
9146
  "/api/specs/:id"
@@ -9057,7 +9151,7 @@ async function putRemoteSpec(http, id, payload) {
9057
9151
  ...scope
9058
9152
  };
9059
9153
  if (token) headers.Authorization = `Bearer ${token}`;
9060
- const res = await fetchImpl(`${baseUrl}${path15}`, {
9154
+ const res = await fetchImpl(`${baseUrl}${path16}`, {
9061
9155
  method: "PUT",
9062
9156
  headers,
9063
9157
  body: JSON.stringify({ frontmatter: payload.frontmatter, body: payload.body })
@@ -9073,7 +9167,7 @@ async function putRemoteSpec(http, id, payload) {
9073
9167
  return {
9074
9168
  ok: false,
9075
9169
  status: res.status,
9076
- error: `PUT ${path15} returned ${res.status}: ${text2.slice(0, 200)}`,
9170
+ error: `PUT ${path16} returned ${res.status}: ${text2.slice(0, 200)}`,
9077
9171
  ...res.status === 429 && Number.isFinite(retry) && retry > 0 ? { retryAfterSec: retry } : {}
9078
9172
  };
9079
9173
  }
@@ -9085,19 +9179,19 @@ async function putRemoteSpec(http, id, payload) {
9085
9179
  statusIgnored: json?.status_ignored === true
9086
9180
  };
9087
9181
  }
9088
- async function fetchPushRemote(http, basename4) {
9089
- const { status, json, text: text2 } = await getJson(http, `api/specs/${encodeURIComponent(basename4)}`);
9182
+ async function fetchPushRemote(http, basename5) {
9183
+ const { status, json, text: text2 } = await getJson(http, `api/specs/${encodeURIComponent(basename5)}`);
9090
9184
  if (status === 404) return null;
9091
9185
  if (status !== 200) {
9092
9186
  throw new VaultPullSourceError(
9093
- `GET /api/specs/${basename4} returned ${status}: ${text2.slice(0, 200)}`,
9187
+ `GET /api/specs/${basename5} returned ${status}: ${text2.slice(0, 200)}`,
9094
9188
  status
9095
9189
  );
9096
9190
  }
9097
9191
  const spec = json?.spec;
9098
9192
  const fm = spec?.frontmatter && typeof spec.frontmatter === "object" ? spec.frontmatter : null;
9099
9193
  if (!spec || typeof spec.body !== "string" || !fm) {
9100
- throw new VaultPullSourceError(`GET /api/specs/${basename4} returned no usable spec`, status);
9194
+ throw new VaultPullSourceError(`GET /api/specs/${basename5} returned no usable spec`, status);
9101
9195
  }
9102
9196
  return {
9103
9197
  id: String(spec.id ?? fm.id ?? ""),
@@ -9139,10 +9233,10 @@ function createVaultPushDeps({
9139
9233
  }
9140
9234
  },
9141
9235
  readLedger: () => ledger.snapshot(),
9142
- fetchRemote: (basename4) => fetchPushRemote(http, basename4),
9236
+ fetchRemote: (basename5) => fetchPushRemote(http, basename5),
9143
9237
  listRemote: withListIndex ? () => listRemoteSpecs(http) : void 0,
9144
9238
  serialize: stringifySpecMarkdown,
9145
- putRemote: (basename4, payload) => putRemoteSpec(http, basename4, payload),
9239
+ putRemote: (basename5, payload) => putRemoteSpec(http, basename5, payload),
9146
9240
  recordLedger: (relPath, entry) => ledger.record(relPath, entry),
9147
9241
  flushLedger: () => ledger.flush(),
9148
9242
  markDirty
@@ -9348,7 +9442,7 @@ function makeSleeper() {
9348
9442
  }
9349
9443
  function readMarkerVaultId(vaultRoot2) {
9350
9444
  try {
9351
- return parseVaultId(readFileSync13(path14.join(vaultRoot2, "heyspex.yaml"), "utf8"));
9445
+ return parseVaultId(readFileSync15(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
9352
9446
  } catch {
9353
9447
  return null;
9354
9448
  }
@@ -9386,6 +9480,17 @@ ${PULL_HELP}
9386
9480
  process4.exitCode = PULL_EXIT.RUNTIME;
9387
9481
  return;
9388
9482
  }
9483
+ if (args.apply) {
9484
+ try {
9485
+ assertPullTargetIsVault(vaultRoot2);
9486
+ } catch (err) {
9487
+ if (!(err instanceof PullTargetNotVaultError)) throw err;
9488
+ process4.stderr.write(`hs pull: ${err.message}
9489
+ `);
9490
+ process4.exitCode = PULL_EXIT.VAULT_UNRESOLVED;
9491
+ return;
9492
+ }
9493
+ }
9389
9494
  const baseUrl = resolvePullSourceUrl(args.from);
9390
9495
  const token = resolvePullToken();
9391
9496
  if (!token && /^https:/i.test(baseUrl)) {
@@ -9414,7 +9519,7 @@ ${PULL_HELP}
9414
9519
  const markerVaultId = readMarkerVaultId(vaultRoot2);
9415
9520
  if (args.apply && markerVaultId && markerVaultId !== target.vaultId) {
9416
9521
  process4.stderr.write(
9417
- `hs pull: refusing --apply: it would write vault ${target.vaultId} into ${path14.join(vaultRoot2, "specs")}, but that repo's heyspex.yaml names vault ${markerVaultId}. Nothing was written.
9522
+ `hs pull: refusing --apply: it would write vault ${target.vaultId} into ${path15.join(vaultRoot2, "specs")}, but that repo's heyspex.yaml names vault ${markerVaultId}. Nothing was written.
9418
9523
  Drop --vault to pull ${markerVaultId}, or run from the repo that holds ${target.vaultId}.
9419
9524
  `
9420
9525
  );
@@ -9431,7 +9536,7 @@ ${PULL_HELP}
9431
9536
  baseUrl,
9432
9537
  token,
9433
9538
  scopeHeaders,
9434
- localRoot: path14.join(vaultRoot2, "specs"),
9539
+ localRoot: path15.join(vaultRoot2, "specs"),
9435
9540
  ledger,
9436
9541
  onLocalWrite: (relPath) => pushScheduler?.notePullWrite(relPath)
9437
9542
  });
@@ -9499,7 +9604,7 @@ watch: every ${args.intervalSec}s while active, backing off to ${Math.round(Math
9499
9604
  `relay: push startup sweep failed \u2014 ${err instanceof Error ? err.message : String(err)}`
9500
9605
  );
9501
9606
  }
9502
- closePushWatch = await startPushFileWatch(path14.join(vaultRoot2, "specs"), pushScheduler);
9607
+ closePushWatch = await startPushFileWatch(path15.join(vaultRoot2, "specs"), pushScheduler);
9503
9608
  }
9504
9609
  const summary = await runPullWatch(
9505
9610
  {