heyspex 0.2.5 → 0.2.7

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 (110) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/bin/hs.mjs +349 -38
  3. package/bin/spexcode.mjs +31 -0
  4. package/dev-factory/lib/dispatch-guard.mjs +75 -0
  5. package/dev-factory/lib/local-firecrawl.mjs +294 -0
  6. package/dev-factory/lib/runner-path.mjs +51 -0
  7. package/dev-factory/lib/seat-context.mjs +20 -4
  8. package/dev-factory/model-catalog.json +222 -222
  9. package/dev-factory/spexcrawl/firecrawl-cli.mjs +755 -0
  10. package/dev-factory/spexcrawl/spexcrawl-cli.mjs +734 -0
  11. package/dist/cli/hs-connect.mjs +40 -3
  12. package/dist/cli/hs-defer.mjs +3817 -3741
  13. package/dist/cli/hs-events.mjs +1296 -0
  14. package/dist/cli/hs-export-events.mjs +1252 -0
  15. package/dist/cli/hs-handoff.mjs +2576 -0
  16. package/dist/cli/hs-index-rebuild.mjs +228 -37
  17. package/dist/cli/hs-index-reconcile.mjs +231 -40
  18. package/dist/cli/hs-init.mjs +22 -3
  19. package/dist/cli/hs-install.mjs +22 -3
  20. package/dist/cli/hs-join.mjs +40 -3
  21. package/dist/cli/hs-next-launch.mjs +2777 -0
  22. package/dist/cli/hs-pin.mjs +1336 -0
  23. package/dist/cli/hs-prereq.mjs +1380 -0
  24. package/dist/cli/hs-revalidate.mjs +3257 -0
  25. package/dist/cli/hs-spawn.mjs +12403 -0
  26. package/dist/cli/hs-test.mjs +1046 -0
  27. package/dist/cli/hs-trash.mjs +362 -0
  28. package/dist/cli/hs-trust.mjs +197 -0
  29. package/dist/cli/hs-vault-pull.mjs +233 -42
  30. package/dist/cli/hs-vault-push.mjs +231 -40
  31. package/dist/cli/hs-vault.mjs +21 -7
  32. package/dist/cli/hs-workspace.mjs +19 -1
  33. package/dist/cli-templates/customer-vault/specs/entity-model.md +1728 -1728
  34. package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +464 -464
  35. package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -122
  36. package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +46 -46
  37. package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +530 -530
  38. package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -83
  39. package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -28
  40. package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -62
  41. package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -29
  42. package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -30
  43. package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -29
  44. package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -70
  45. package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -35
  46. package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -27
  47. package/dist/cli-templates/harness-commands/claude/hs-res.md +83 -63
  48. package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -23
  49. package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -28
  50. package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -32
  51. package/dist/cli-templates/harness-commands/claude/prototype.md +91 -91
  52. package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +22 -22
  53. package/dist/cli-templates/harness-commands/cursor/hs-capture.md +56 -56
  54. package/dist/cli-templates/harness-commands/cursor/hs-defer.md +23 -23
  55. package/dist/cli-templates/harness-commands/cursor/hs-ground.md +24 -24
  56. package/dist/cli-templates/harness-commands/cursor/hs-next.md +23 -23
  57. package/dist/cli-templates/harness-commands/cursor/hs-promote.md +64 -64
  58. package/dist/cli-templates/harness-commands/cursor/hs-push.md +29 -29
  59. package/dist/cli-templates/harness-commands/cursor/hs-qc.md +21 -21
  60. package/dist/cli-templates/harness-commands/cursor/hs-res.md +78 -58
  61. package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +17 -17
  62. package/dist/cli-templates/harness-commands/cursor/hs-view.md +23 -23
  63. package/dist/cli-templates/harness-commands/cursor/kill-ports.md +26 -26
  64. package/dist/cli-templates/harness-commands/cursor/prototype.md +86 -86
  65. package/docs/copilot-plugin-install.md +9 -2
  66. package/docs/mcp-client-config.md +6 -0
  67. package/package.json +266 -257
  68. package/plugins/heyspex/.claude-plugin/plugin.json +1 -1
  69. package/plugins/heyspex/.cursor-plugin/plugin.json +1 -1
  70. package/plugins/heyspex/README.md +6 -0
  71. package/plugins/heyspex/bin/launch-mcp.mjs +17 -0
  72. package/plugins/heyspex/hooks/hooks.json +418 -418
  73. package/scripts/claude-hooks/_intent-classifier-semantic.mjs +2 -2
  74. package/scripts/claude-hooks/wip-checkpoint.mjs +17 -2
  75. package/scripts/claude-statusline/statusline-command.mjs +287 -27
  76. package/scripts/hs-companion.mjs +41 -3
  77. package/scripts/install-claude-code-hooks.mjs +104 -3
  78. package/scripts/install-copilot-hooks.mjs +21 -1
  79. package/scripts/install-cursor-hooks.mjs +4 -3
  80. package/scripts/lib/capture-execution-guard.mjs +221 -0
  81. package/scripts/lib/companion-install.mjs +768 -0
  82. package/scripts/lib/customer-kit.mjs +53 -3
  83. package/scripts/lib/hs-companion-settings.mjs +26 -0
  84. package/scripts/lib/hs-source-push.mjs +386 -386
  85. package/scripts/record-grounding.mjs +38 -0
  86. package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -29
  87. package/spexharness/src/adapters/copilot-models.mjs +179 -0
  88. package/spexharness/src/adapters/harness-detect.mjs +148 -0
  89. package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -132
  90. package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +153 -125
  91. package/spexharness/src/seat-context.mjs +10 -5
  92. package/spexharness/src/session-bootstrap.mjs +6 -3
  93. package/vendor/heyspex-mcp/cli.mjs +6 -1
  94. package/vendor/heyspex-mcp/config-seed.yaml +15 -15
  95. package/vendor/heyspex-mcp/dev-factory-overlay.mjs +142 -0
  96. package/vendor/heyspex-mcp/http-transport.mjs +1 -1
  97. package/vendor/heyspex-mcp/memory-tools.mjs +523 -523
  98. package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -1708
  99. package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -327
  100. package/vendor/heyspex-mcp/procedure-tools.mjs +1209 -1181
  101. package/vendor/heyspex-mcp/research-tools.mjs +22 -2
  102. package/vendor/heyspex-mcp/seat-assign-cli.mjs +242 -0
  103. package/vendor/heyspex-mcp/seat-assignment-rules.mjs +96 -0
  104. package/vendor/heyspex-mcp/seat-assignments-client.mjs +265 -0
  105. package/vendor/heyspex-mcp/seat-defaults-cli.mjs +45 -0
  106. package/vendor/heyspex-mcp/seat-defaults.mjs +232 -0
  107. package/vendor/heyspex-mcp/seat-identity.mjs +171 -0
  108. package/vendor/heyspex-mcp/test-helpers.mjs +37 -23
  109. package/vendor/heyspex-mcp/vault-io.mjs +720 -720
  110. package/bin/postinstall.mjs +0 -88
@@ -1298,10 +1298,10 @@ var require_stringify = __commonJS({
1298
1298
  data = Object.assign({}, file.data, data);
1299
1299
  const open = opts.delimiters[0];
1300
1300
  const close = opts.delimiters[1];
1301
- const matter4 = engine.stringify(data, options2).trim();
1301
+ const matter5 = engine.stringify(data, options2).trim();
1302
1302
  let buf = "";
1303
- if (matter4 !== "{}") {
1304
- buf = newline(open) + newline(matter4) + newline(close);
1303
+ if (matter5 !== "{}") {
1304
+ buf = newline(open) + newline(matter5) + newline(close);
1305
1305
  }
1306
1306
  if (typeof file.excerpt === "string" && file.excerpt !== "") {
1307
1307
  if (str2.indexOf(file.excerpt.trim()) === -1) {
@@ -1407,19 +1407,19 @@ var require_gray_matter = __commonJS({
1407
1407
  var toFile = require_to_file();
1408
1408
  var parse2 = require_parse();
1409
1409
  var utils = require_utils();
1410
- function matter4(input, options2) {
1410
+ function matter5(input, options2) {
1411
1411
  if (input === "") {
1412
1412
  return { data: {}, content: input, excerpt: "", orig: input };
1413
1413
  }
1414
1414
  let file = toFile(input);
1415
- const cached = matter4.cache[file.content];
1415
+ const cached = matter5.cache[file.content];
1416
1416
  if (!options2) {
1417
1417
  if (cached) {
1418
1418
  file = Object.assign({}, cached);
1419
1419
  file.orig = cached.orig;
1420
1420
  return file;
1421
1421
  }
1422
- matter4.cache[file.content] = file;
1422
+ matter5.cache[file.content] = file;
1423
1423
  }
1424
1424
  return parseMatter(file, options2);
1425
1425
  }
@@ -1441,7 +1441,7 @@ var require_gray_matter = __commonJS({
1441
1441
  }
1442
1442
  str2 = str2.slice(openLen);
1443
1443
  const len = str2.length;
1444
- const language = matter4.language(str2, opts);
1444
+ const language = matter5.language(str2, opts);
1445
1445
  if (language.name) {
1446
1446
  file.language = language.name;
1447
1447
  str2 = str2.slice(language.raw.length);
@@ -1476,24 +1476,24 @@ var require_gray_matter = __commonJS({
1476
1476
  }
1477
1477
  return file;
1478
1478
  }
1479
- matter4.engines = engines2;
1480
- matter4.stringify = function(file, data, options2) {
1481
- if (typeof file === "string") file = matter4(file, options2);
1479
+ matter5.engines = engines2;
1480
+ matter5.stringify = function(file, data, options2) {
1481
+ if (typeof file === "string") file = matter5(file, options2);
1482
1482
  return stringify(file, data, options2);
1483
1483
  };
1484
- matter4.read = function(filepath, options2) {
1484
+ matter5.read = function(filepath, options2) {
1485
1485
  const str2 = fs11.readFileSync(filepath, "utf8");
1486
- const file = matter4(str2, options2);
1486
+ const file = matter5(str2, options2);
1487
1487
  file.path = filepath;
1488
1488
  return file;
1489
1489
  };
1490
- matter4.test = function(str2, options2) {
1490
+ matter5.test = function(str2, options2) {
1491
1491
  return utils.startsWith(str2, defaults(options2).delimiters[0]);
1492
1492
  };
1493
- matter4.language = function(str2, options2) {
1493
+ matter5.language = function(str2, options2) {
1494
1494
  const opts = defaults(options2);
1495
1495
  const open = opts.delimiters[0];
1496
- if (matter4.test(str2)) {
1496
+ if (matter5.test(str2)) {
1497
1497
  str2 = str2.slice(open.length);
1498
1498
  }
1499
1499
  const language = str2.slice(0, str2.search(/\r?\n/));
@@ -1502,11 +1502,11 @@ var require_gray_matter = __commonJS({
1502
1502
  name: language ? language.trim() : ""
1503
1503
  };
1504
1504
  };
1505
- matter4.cache = {};
1506
- matter4.clearCache = function() {
1507
- matter4.cache = {};
1505
+ matter5.cache = {};
1506
+ matter5.clearCache = function() {
1507
+ matter5.cache = {};
1508
1508
  };
1509
- module2.exports = matter4;
1509
+ module2.exports = matter5;
1510
1510
  }
1511
1511
  });
1512
1512
 
@@ -2015,7 +2015,7 @@ var init_interfaces = __esm({
2015
2015
  });
2016
2016
 
2017
2017
  // spex-console/src/lib/db/schema/postgres.ts
2018
- var edgeTypeEnum, validationSeverityEnum, outboxOpEnum, tsvector, specs, tags, edges, wikilinks, specLinks, aliases, validationIssues, indexMeta, frontmatterFields, eventOutbox, captureSessions, handoffContext, claimEventKindEnum, activeClaimStatusEnum, pendingReasonEnum, focusEventKindEnum, specEventKindEnum, focusPin, captureFocusPin, captureFocusSelection, focusSelection, focusSkips, activeClaims, claimWaiters, fileClaims, claimEvents, focusEvents, focusAreas, focusAreasEvents, specEvents, rateLimitBuckets, userVaults, milestoneSnapshots, modelRegistryEntries, launchIntentStatusEnum, launchIntents, companionRelayPresence, companionSettingsStore, consoleSettingsStore, wipLedger, wipLedgerArchive, gatherResults, agentSessions, usageEvents, streamEvents, jobStreams, resolutionStatusEnum, resolutionFocusPin, resolutionLedger, workspaces, vaults, accountSettings, vaultGrants, pendingVaultGrants, githubInstallations, fleetMachines, fleetTasks, fleetResults, userApiKeys, apiKeys, mcpSessions, pairingCodes, companionSessions, inviteRequests, notifications, webhookIngestions, specSettlements, sessionBranchDeclarations;
2018
+ var edgeTypeEnum, validationSeverityEnum, outboxOpEnum, tsvector, specs, tags, edges, wikilinks, specLinks, aliases, validationIssues, indexMeta, frontmatterFields, eventOutbox, captureSessions, handoffContext, claimEventKindEnum, activeClaimStatusEnum, pendingReasonEnum, focusEventKindEnum, specEventKindEnum, focusPin, captureFocusPin, captureFocusSelection, focusSelection, focusSkips, activeClaims, claimWaiters, fileClaims, claimEvents, focusEvents, focusAreas, focusAreasEvents, specEvents, rateLimitBuckets, userVaults, milestoneSnapshots, modelRegistryEntries, launchIntentStatusEnum, launchIntents, companionRelayPresence, companionSettingsStore, consoleSettingsStore, seatAssignments, wipLedger, wipLedgerArchive, gatherResults, agentSessions, usageEvents, streamEvents, jobStreams, resolutionStatusEnum, resolutionFocusPin, resolutionLedger, workspaces, vaults, accountSettings, vaultGrants, pendingVaultGrants, githubInstallations, fleetMachines, fleetTasks, fleetResults, userApiKeys, apiKeys, mcpSessions, pairingCodes, companionSessions, inviteRequests, notifications, webhookIngestions, specSettlements, sessionBranchDeclarations;
2019
2019
  var init_postgres = __esm({
2020
2020
  "spex-console/src/lib/db/schema/postgres.ts"() {
2021
2021
  "use strict";
@@ -2965,6 +2965,24 @@ var init_postgres = __esm({
2965
2965
  },
2966
2966
  (table) => [primaryKey({ columns: [table.userId, table.vaultId, table.namespace] })]
2967
2967
  );
2968
+ seatAssignments = pgTable(
2969
+ "seat_assignments",
2970
+ {
2971
+ accountId: text("account_id").notNull(),
2972
+ vaultId: text("vault_id").notNull(),
2973
+ harness: text("harness").notNull(),
2974
+ seatId: text("seat_id").notNull(),
2975
+ model: text("model").notNull(),
2976
+ // Set when a launch reported the harness REFUSED this model and the run fell
2977
+ // back to auto (Copilot's `heyspex.copilot_model_fallback` event, HS-F4UM);
2978
+ // cleared whenever the user sets a new model.
2979
+ overrideRejectedAt: timestamp("override_rejected_at", { withTimezone: true }),
2980
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().default(sql`now()`)
2981
+ },
2982
+ (table) => [
2983
+ primaryKey({ columns: [table.accountId, table.vaultId, table.harness, table.seatId] })
2984
+ ]
2985
+ );
2968
2986
  wipLedger = pgTable(
2969
2987
  "wip_ledger",
2970
2988
  {
@@ -4473,11 +4491,12 @@ function getPostgresAdminDb() {
4473
4491
  function inertClient() {
4474
4492
  return false;
4475
4493
  }
4476
- var isNeonPool;
4494
+ var isNeonPool, registerVaultScopedDbAccessor;
4477
4495
  var init_postgres_client_cli_shim = __esm({
4478
4496
  "dist/cli/._postgres-client.cli-shim.mjs"() {
4479
4497
  "use strict";
4480
4498
  isNeonPool = inertClient;
4499
+ registerVaultScopedDbAccessor = inertClient;
4481
4500
  }
4482
4501
  });
4483
4502
 
@@ -4720,6 +4739,141 @@ var init_clients = __esm({
4720
4739
  }
4721
4740
  });
4722
4741
 
4742
+ // spex-console/src/lib/rate-limits.ts
4743
+ var RATE_LIMIT_BUDGETS;
4744
+ var init_rate_limits = __esm({
4745
+ "spex-console/src/lib/rate-limits.ts"() {
4746
+ "use strict";
4747
+ RATE_LIMIT_BUDGETS = {
4748
+ mcp_tool_calls: { daily: 500, hourly: 125 },
4749
+ spec_writes: { daily: 100, hourly: 25 },
4750
+ capture_sessions: { daily: 20, hourly: 5 },
4751
+ // Paul's Advocate free-text chat ([[HS-4YYB-XEBI-TASK-paul-s-advocate-answers-real-questions]]):
4752
+ // a public YC reviewer can otherwise run up LLM spend on an unauthenticated-feeling
4753
+ // widget. 30/hour matches the builder brief's "e.g. 30 messages/hour" starting point;
4754
+ // daily is 4x hourly (looser than the 25%-of-daily pattern above — a reviewer chatting
4755
+ // steadily through a demo session should not get cut off mid-conversation).
4756
+ advocate_chat: { daily: 120, hourly: 30 }
4757
+ };
4758
+ }
4759
+ });
4760
+
4761
+ // spex-console/src/lib/plan-tier.ts
4762
+ function scaledBudgets(multiplier) {
4763
+ const out = {};
4764
+ for (const [bucket, budget] of Object.entries(RATE_LIMIT_BUDGETS)) {
4765
+ out[bucket] = {
4766
+ daily: Math.round(budget.daily * multiplier),
4767
+ hourly: Math.round(budget.hourly * multiplier)
4768
+ };
4769
+ }
4770
+ return Object.freeze(out);
4771
+ }
4772
+ var PLAN_TIER_RATE_LIMIT_MULTIPLIER, PLAN_TIER_LIMITS;
4773
+ var init_plan_tier = __esm({
4774
+ "spex-console/src/lib/plan-tier.ts"() {
4775
+ "use strict";
4776
+ init_sql_orm_inert_cli_shim();
4777
+ init_postgres_client_cli_shim();
4778
+ init_postgres();
4779
+ init_rate_limits();
4780
+ PLAN_TIER_RATE_LIMIT_MULTIPLIER = {
4781
+ free: 1,
4782
+ pro: 4,
4783
+ enterprise: 20
4784
+ };
4785
+ PLAN_TIER_LIMITS = {
4786
+ free: {
4787
+ statement_timeout_ms: 5e3,
4788
+ read_work_mem: "4MB",
4789
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.free)
4790
+ },
4791
+ pro: {
4792
+ statement_timeout_ms: 15e3,
4793
+ read_work_mem: "16MB",
4794
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.pro)
4795
+ },
4796
+ enterprise: {
4797
+ statement_timeout_ms: 3e4,
4798
+ read_work_mem: "64MB",
4799
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.enterprise)
4800
+ }
4801
+ };
4802
+ }
4803
+ });
4804
+
4805
+ // spex-console/src/lib/db/clients/vault-router.ts
4806
+ import { existsSync as existsSync8, readFileSync as readFileSync13 } from "node:fs";
4807
+ import { homedir as homedir10 } from "node:os";
4808
+ import { join as join11 } from "node:path";
4809
+ var CREDENTIALS_PATH, ROUTER_KEY, globalRouter, router;
4810
+ var init_vault_router = __esm({
4811
+ "spex-console/src/lib/db/clients/vault-router.ts"() {
4812
+ "use strict";
4813
+ init_sql_orm_inert_cli_shim();
4814
+ init_sql_orm_inert_cli_shim();
4815
+ init_postgres();
4816
+ init_postgres_client_cli_shim();
4817
+ CREDENTIALS_PATH = join11(homedir10(), ".heyspex", "credentials.yaml");
4818
+ ROUTER_KEY = Symbol.for("heyspex.vaultRouter");
4819
+ globalRouter = globalThis;
4820
+ if (!globalRouter[ROUTER_KEY]) {
4821
+ globalRouter[ROUTER_KEY] = {
4822
+ dedicated: /* @__PURE__ */ new Map(),
4823
+ refCache: /* @__PURE__ */ new Map(),
4824
+ warnedSchemaMissing: false
4825
+ };
4826
+ }
4827
+ router = globalRouter[ROUTER_KEY];
4828
+ }
4829
+ });
4830
+
4831
+ // spex-console/src/lib/db/clients/vault-scope.ts
4832
+ import { AsyncLocalStorage } from "node:async_hooks";
4833
+ function warnScopeClosedOnce(ctx) {
4834
+ if (ctx.warnedClosed) return;
4835
+ ctx.warnedClosed = true;
4836
+ console.warn(
4837
+ `[vault-scope] db use after vault scope closed; falling back to the pool connection (unscoped) (vaultId: ${ctx.vaultId})`
4838
+ );
4839
+ }
4840
+ function handleScopeClosedAccess(ctx) {
4841
+ if (ctx.onStaleUse === "throw") {
4842
+ throw new VaultScopeClosedError(ctx.vaultId);
4843
+ }
4844
+ warnScopeClosedOnce(ctx);
4845
+ }
4846
+ var VaultScopeClosedError, vaultScopeStorage;
4847
+ var init_vault_scope = __esm({
4848
+ "spex-console/src/lib/db/clients/vault-scope.ts"() {
4849
+ "use strict";
4850
+ init_sql_orm_inert_cli_shim();
4851
+ init_plan_tier();
4852
+ init_postgres_client_cli_shim();
4853
+ init_vault_router();
4854
+ VaultScopeClosedError = class extends Error {
4855
+ vaultId;
4856
+ constructor(vaultId) {
4857
+ super(
4858
+ `Vault scope for "${vaultId}" is closed; this path opted into onStaleUseAfterClose: "throw", so a db handle used after the scope's transaction settled fails loudly instead of silently falling back to the unscoped pool connection.`
4859
+ );
4860
+ this.name = "VaultScopeClosedError";
4861
+ this.vaultId = vaultId;
4862
+ }
4863
+ };
4864
+ vaultScopeStorage = new AsyncLocalStorage();
4865
+ registerVaultScopedDbAccessor(() => {
4866
+ const ctx = vaultScopeStorage.getStore();
4867
+ if (!ctx) return null;
4868
+ if (ctx.closed) {
4869
+ handleScopeClosedAccess(ctx);
4870
+ return null;
4871
+ }
4872
+ return ctx.proxy;
4873
+ });
4874
+ }
4875
+ });
4876
+
4723
4877
  // spex-console/src/lib/scheduler/types.ts
4724
4878
  var init_types2 = __esm({
4725
4879
  "spex-console/src/lib/scheduler/types.ts"() {
@@ -5247,10 +5401,45 @@ var init_config = __esm({
5247
5401
  }
5248
5402
  });
5249
5403
 
5250
- // spex-console/src/lib/vault-frontmatter.ts
5404
+ // spex-console/src/lib/safe-yaml.ts
5251
5405
  import yaml2 from "js-yaml";
5406
+ function dropUndefinedDeep(value) {
5407
+ if (Array.isArray(value)) {
5408
+ return value.filter((item) => item !== void 0).map((item) => dropUndefinedDeep(item));
5409
+ }
5410
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
5411
+ const out = {};
5412
+ for (const [k, v] of Object.entries(value)) {
5413
+ if (v === void 0) continue;
5414
+ out[k] = dropUndefinedDeep(v);
5415
+ }
5416
+ return out;
5417
+ }
5418
+ return value;
5419
+ }
5420
+ function safeYamlDump(value, options2 = {}) {
5421
+ const { schema: _ignoredSchema, ...rest } = options2;
5422
+ const data = dropUndefinedDeep(value);
5423
+ if (grayMatterYamlStringify) return grayMatterYamlStringify(data, rest);
5424
+ return yaml2.dump(data, { ...rest, schema: SAFE_YAML_SCHEMA });
5425
+ }
5426
+ var import_gray_matter2, SAFE_YAML_SCHEMA, grayMatterYamlStringify, YAML_FAILSAFE_SCHEMA;
5427
+ var init_safe_yaml = __esm({
5428
+ "spex-console/src/lib/safe-yaml.ts"() {
5429
+ "use strict";
5430
+ import_gray_matter2 = __toESM(require_gray_matter(), 1);
5431
+ SAFE_YAML_SCHEMA = yaml2.DEFAULT_SAFE_SCHEMA ?? yaml2.DEFAULT_SCHEMA;
5432
+ grayMatterYamlStringify = (() => {
5433
+ const engine = import_gray_matter2.default.engines?.yaml;
5434
+ return typeof engine?.stringify === "function" ? engine.stringify : void 0;
5435
+ })();
5436
+ YAML_FAILSAFE_SCHEMA = yaml2.FAILSAFE_SCHEMA;
5437
+ }
5438
+ });
5439
+
5440
+ // spex-console/src/lib/vault-frontmatter.ts
5252
5441
  function normalizeFrontmatterForYaml(frontmatter) {
5253
- const processed = { ...frontmatter };
5442
+ const processed = dropUndefinedDeep(frontmatter);
5254
5443
  for (const [key, value] of Object.entries(processed)) {
5255
5444
  if (value instanceof Date) {
5256
5445
  processed[key] = value.toISOString();
@@ -5259,17 +5448,18 @@ function normalizeFrontmatterForYaml(frontmatter) {
5259
5448
  return processed;
5260
5449
  }
5261
5450
  function stringifySpecMarkdown(body, frontmatter) {
5262
- const fm = yaml2.dump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
5451
+ const fm = safeYamlDump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
5263
5452
  return `---
5264
5453
  ${fm}
5265
5454
  ---
5266
5455
  ${body}`;
5267
5456
  }
5268
- var import_gray_matter2, YAML_STRINGIFY_OPTIONS;
5457
+ var import_gray_matter3, YAML_STRINGIFY_OPTIONS;
5269
5458
  var init_vault_frontmatter = __esm({
5270
5459
  "spex-console/src/lib/vault-frontmatter.ts"() {
5271
5460
  "use strict";
5272
- import_gray_matter2 = __toESM(require_gray_matter(), 1);
5461
+ import_gray_matter3 = __toESM(require_gray_matter(), 1);
5462
+ init_safe_yaml();
5273
5463
  YAML_STRINGIFY_OPTIONS = {
5274
5464
  quotingType: '"',
5275
5465
  skipInvalid: true,
@@ -6192,12 +6382,12 @@ var init_linkage_validation = __esm({
6192
6382
  });
6193
6383
 
6194
6384
  // spex-console/src/lib/perf-timing.ts
6195
- import { AsyncLocalStorage } from "node:async_hooks";
6385
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6196
6386
  var storage;
6197
6387
  var init_perf_timing = __esm({
6198
6388
  "spex-console/src/lib/perf-timing.ts"() {
6199
6389
  "use strict";
6200
- storage = new AsyncLocalStorage();
6390
+ storage = new AsyncLocalStorage2();
6201
6391
  }
6202
6392
  });
6203
6393
 
@@ -6219,12 +6409,12 @@ var init_planning_flow_model = __esm({
6219
6409
  });
6220
6410
 
6221
6411
  // spex-console/src/lib/request-memo.ts
6222
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6412
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
6223
6413
  var storage2;
6224
6414
  var init_request_memo = __esm({
6225
6415
  "spex-console/src/lib/request-memo.ts"() {
6226
6416
  "use strict";
6227
- storage2 = new AsyncLocalStorage2();
6417
+ storage2 = new AsyncLocalStorage3();
6228
6418
  }
6229
6419
  });
6230
6420
 
@@ -6472,15 +6662,14 @@ var init_search_body_hit = __esm({
6472
6662
 
6473
6663
  // spex-console/src/lib/vault.ts
6474
6664
  import { spawn } from "node:child_process";
6475
- import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
6665
+ import { readdirSync as readdirSync2, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
6476
6666
  import { promises as fs7 } from "node:fs";
6477
6667
  import path14 from "node:path";
6478
- import yaml3 from "js-yaml";
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;
6668
+ var import_gray_matter4, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore;
6480
6669
  var init_vault = __esm({
6481
6670
  "spex-console/src/lib/vault.ts"() {
6482
6671
  "use strict";
6483
- import_gray_matter3 = __toESM(require_gray_matter(), 1);
6672
+ import_gray_matter4 = __toESM(require_gray_matter(), 1);
6484
6673
  init_diagnose_skeleton();
6485
6674
  init_auth();
6486
6675
  init_capture_vault();
@@ -6491,6 +6680,7 @@ var init_vault = __esm({
6491
6680
  init_skips();
6492
6681
  init_clients();
6493
6682
  init_postgres_client_cli_shim();
6683
+ init_vault_scope();
6494
6684
  init_helpers();
6495
6685
  init_index_write_service();
6496
6686
  init_spec_read_service();
@@ -6507,6 +6697,7 @@ var init_vault = __esm({
6507
6697
  init_planning_flow_model();
6508
6698
  init_request_memo();
6509
6699
  init_review_mode();
6700
+ init_safe_yaml();
6510
6701
  init_active_milestone();
6511
6702
  init_area();
6512
6703
  init_claim_eligibility();
@@ -6592,7 +6783,7 @@ var init_vault = __esm({
6592
6783
 
6593
6784
  // spex-console/bin/hs-index-rebuild.ts
6594
6785
  import { execSync } from "node:child_process";
6595
- import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync15 } from "node:fs";
6786
+ import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "node:fs";
6596
6787
  import { promises as fs10 } from "node:fs";
6597
6788
  import path15 from "node:path";
6598
6789
  import process4 from "node:process";
@@ -6985,7 +7176,7 @@ async function runReindex(input) {
6985
7176
  init_validator();
6986
7177
 
6987
7178
  // spex-console/src/lib/vault-sync/pull-io.ts
6988
- import { promises as fs9, readFileSync as readFileSync14 } from "node:fs";
7179
+ import { promises as fs9, readFileSync as readFileSync15 } from "node:fs";
6989
7180
  import { EOL } from "node:os";
6990
7181
  import nodePath2 from "node:path";
6991
7182
 
@@ -7154,7 +7345,7 @@ function appendJournal(vaultRoot2, message) {
7154
7345
  }
7155
7346
  function readDiscovery(p) {
7156
7347
  try {
7157
- return readFileSync15(p, "utf-8");
7348
+ return readFileSync16(p, "utf-8");
7158
7349
  } catch {
7159
7350
  return null;
7160
7351
  }