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
@@ -1077,10 +1077,10 @@ var require_stringify = __commonJS({
1077
1077
  data = Object.assign({}, file.data, data);
1078
1078
  const open2 = opts.delimiters[0];
1079
1079
  const close = opts.delimiters[1];
1080
- const matter4 = engine.stringify(data, options2).trim();
1080
+ const matter5 = engine.stringify(data, options2).trim();
1081
1081
  let buf = "";
1082
- if (matter4 !== "{}") {
1083
- buf = newline(open2) + newline(matter4) + newline(close);
1082
+ if (matter5 !== "{}") {
1083
+ buf = newline(open2) + newline(matter5) + newline(close);
1084
1084
  }
1085
1085
  if (typeof file.excerpt === "string" && file.excerpt !== "") {
1086
1086
  if (str2.indexOf(file.excerpt.trim()) === -1) {
@@ -1186,19 +1186,19 @@ var require_gray_matter = __commonJS({
1186
1186
  var toFile = require_to_file();
1187
1187
  var parse2 = require_parse();
1188
1188
  var utils = require_utils();
1189
- function matter4(input, options2) {
1189
+ function matter5(input, options2) {
1190
1190
  if (input === "") {
1191
1191
  return { data: {}, content: input, excerpt: "", orig: input };
1192
1192
  }
1193
1193
  let file = toFile(input);
1194
- const cached = matter4.cache[file.content];
1194
+ const cached = matter5.cache[file.content];
1195
1195
  if (!options2) {
1196
1196
  if (cached) {
1197
1197
  file = Object.assign({}, cached);
1198
1198
  file.orig = cached.orig;
1199
1199
  return file;
1200
1200
  }
1201
- matter4.cache[file.content] = file;
1201
+ matter5.cache[file.content] = file;
1202
1202
  }
1203
1203
  return parseMatter(file, options2);
1204
1204
  }
@@ -1220,7 +1220,7 @@ var require_gray_matter = __commonJS({
1220
1220
  }
1221
1221
  str2 = str2.slice(openLen);
1222
1222
  const len = str2.length;
1223
- const language = matter4.language(str2, opts);
1223
+ const language = matter5.language(str2, opts);
1224
1224
  if (language.name) {
1225
1225
  file.language = language.name;
1226
1226
  str2 = str2.slice(language.raw.length);
@@ -1255,24 +1255,24 @@ var require_gray_matter = __commonJS({
1255
1255
  }
1256
1256
  return file;
1257
1257
  }
1258
- matter4.engines = engines2;
1259
- matter4.stringify = function(file, data, options2) {
1260
- if (typeof file === "string") file = matter4(file, options2);
1258
+ matter5.engines = engines2;
1259
+ matter5.stringify = function(file, data, options2) {
1260
+ if (typeof file === "string") file = matter5(file, options2);
1261
1261
  return stringify(file, data, options2);
1262
1262
  };
1263
- matter4.read = function(filepath, options2) {
1263
+ matter5.read = function(filepath, options2) {
1264
1264
  const str2 = fs12.readFileSync(filepath, "utf8");
1265
- const file = matter4(str2, options2);
1265
+ const file = matter5(str2, options2);
1266
1266
  file.path = filepath;
1267
1267
  return file;
1268
1268
  };
1269
- matter4.test = function(str2, options2) {
1269
+ matter5.test = function(str2, options2) {
1270
1270
  return utils.startsWith(str2, defaults(options2).delimiters[0]);
1271
1271
  };
1272
- matter4.language = function(str2, options2) {
1272
+ matter5.language = function(str2, options2) {
1273
1273
  const opts = defaults(options2);
1274
1274
  const open2 = opts.delimiters[0];
1275
- if (matter4.test(str2)) {
1275
+ if (matter5.test(str2)) {
1276
1276
  str2 = str2.slice(open2.length);
1277
1277
  }
1278
1278
  const language = str2.slice(0, str2.search(/\r?\n/));
@@ -1281,11 +1281,11 @@ var require_gray_matter = __commonJS({
1281
1281
  name: language ? language.trim() : ""
1282
1282
  };
1283
1283
  };
1284
- matter4.cache = {};
1285
- matter4.clearCache = function() {
1286
- matter4.cache = {};
1284
+ matter5.cache = {};
1285
+ matter5.clearCache = function() {
1286
+ matter5.cache = {};
1287
1287
  };
1288
- module2.exports = matter4;
1288
+ module2.exports = matter5;
1289
1289
  }
1290
1290
  });
1291
1291
 
@@ -1368,7 +1368,7 @@ var init_interfaces = __esm({
1368
1368
  });
1369
1369
 
1370
1370
  // spex-console/src/lib/db/schema/postgres.ts
1371
- 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;
1371
+ 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;
1372
1372
  var init_postgres = __esm({
1373
1373
  "spex-console/src/lib/db/schema/postgres.ts"() {
1374
1374
  "use strict";
@@ -2318,6 +2318,24 @@ var init_postgres = __esm({
2318
2318
  },
2319
2319
  (table) => [primaryKey({ columns: [table.userId, table.vaultId, table.namespace] })]
2320
2320
  );
2321
+ seatAssignments = pgTable(
2322
+ "seat_assignments",
2323
+ {
2324
+ accountId: text("account_id").notNull(),
2325
+ vaultId: text("vault_id").notNull(),
2326
+ harness: text("harness").notNull(),
2327
+ seatId: text("seat_id").notNull(),
2328
+ model: text("model").notNull(),
2329
+ // Set when a launch reported the harness REFUSED this model and the run fell
2330
+ // back to auto (Copilot's `heyspex.copilot_model_fallback` event, HS-F4UM);
2331
+ // cleared whenever the user sets a new model.
2332
+ overrideRejectedAt: timestamp("override_rejected_at", { withTimezone: true }),
2333
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().default(sql`now()`)
2334
+ },
2335
+ (table) => [
2336
+ primaryKey({ columns: [table.accountId, table.vaultId, table.harness, table.seatId] })
2337
+ ]
2338
+ );
2321
2339
  wipLedger = pgTable(
2322
2340
  "wip_ledger",
2323
2341
  {
@@ -3072,11 +3090,12 @@ function getPostgresAdminDb() {
3072
3090
  function inertClient() {
3073
3091
  return false;
3074
3092
  }
3075
- var isNeonPool;
3093
+ var isNeonPool, registerVaultScopedDbAccessor;
3076
3094
  var init_postgres_client_cli_shim = __esm({
3077
3095
  "dist/cli/._postgres-client.cli-shim.mjs"() {
3078
3096
  "use strict";
3079
3097
  isNeonPool = inertClient;
3098
+ registerVaultScopedDbAccessor = inertClient;
3080
3099
  }
3081
3100
  });
3082
3101
 
@@ -3333,6 +3352,141 @@ var init_clients = __esm({
3333
3352
  }
3334
3353
  });
3335
3354
 
3355
+ // spex-console/src/lib/rate-limits.ts
3356
+ var RATE_LIMIT_BUDGETS;
3357
+ var init_rate_limits = __esm({
3358
+ "spex-console/src/lib/rate-limits.ts"() {
3359
+ "use strict";
3360
+ RATE_LIMIT_BUDGETS = {
3361
+ mcp_tool_calls: { daily: 500, hourly: 125 },
3362
+ spec_writes: { daily: 100, hourly: 25 },
3363
+ capture_sessions: { daily: 20, hourly: 5 },
3364
+ // Paul's Advocate free-text chat ([[HS-4YYB-XEBI-TASK-paul-s-advocate-answers-real-questions]]):
3365
+ // a public YC reviewer can otherwise run up LLM spend on an unauthenticated-feeling
3366
+ // widget. 30/hour matches the builder brief's "e.g. 30 messages/hour" starting point;
3367
+ // daily is 4x hourly (looser than the 25%-of-daily pattern above — a reviewer chatting
3368
+ // steadily through a demo session should not get cut off mid-conversation).
3369
+ advocate_chat: { daily: 120, hourly: 30 }
3370
+ };
3371
+ }
3372
+ });
3373
+
3374
+ // spex-console/src/lib/plan-tier.ts
3375
+ function scaledBudgets(multiplier) {
3376
+ const out = {};
3377
+ for (const [bucket, budget] of Object.entries(RATE_LIMIT_BUDGETS)) {
3378
+ out[bucket] = {
3379
+ daily: Math.round(budget.daily * multiplier),
3380
+ hourly: Math.round(budget.hourly * multiplier)
3381
+ };
3382
+ }
3383
+ return Object.freeze(out);
3384
+ }
3385
+ var PLAN_TIER_RATE_LIMIT_MULTIPLIER, PLAN_TIER_LIMITS;
3386
+ var init_plan_tier = __esm({
3387
+ "spex-console/src/lib/plan-tier.ts"() {
3388
+ "use strict";
3389
+ init_sql_orm_inert_cli_shim();
3390
+ init_postgres_client_cli_shim();
3391
+ init_postgres();
3392
+ init_rate_limits();
3393
+ PLAN_TIER_RATE_LIMIT_MULTIPLIER = {
3394
+ free: 1,
3395
+ pro: 4,
3396
+ enterprise: 20
3397
+ };
3398
+ PLAN_TIER_LIMITS = {
3399
+ free: {
3400
+ statement_timeout_ms: 5e3,
3401
+ read_work_mem: "4MB",
3402
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.free)
3403
+ },
3404
+ pro: {
3405
+ statement_timeout_ms: 15e3,
3406
+ read_work_mem: "16MB",
3407
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.pro)
3408
+ },
3409
+ enterprise: {
3410
+ statement_timeout_ms: 3e4,
3411
+ read_work_mem: "64MB",
3412
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.enterprise)
3413
+ }
3414
+ };
3415
+ }
3416
+ });
3417
+
3418
+ // spex-console/src/lib/db/clients/vault-router.ts
3419
+ import { existsSync as existsSync8, readFileSync as readFileSync13 } from "node:fs";
3420
+ import { homedir as homedir10 } from "node:os";
3421
+ import { join as join11 } from "node:path";
3422
+ var CREDENTIALS_PATH, ROUTER_KEY, globalRouter, router;
3423
+ var init_vault_router = __esm({
3424
+ "spex-console/src/lib/db/clients/vault-router.ts"() {
3425
+ "use strict";
3426
+ init_sql_orm_inert_cli_shim();
3427
+ init_sql_orm_inert_cli_shim();
3428
+ init_postgres();
3429
+ init_postgres_client_cli_shim();
3430
+ CREDENTIALS_PATH = join11(homedir10(), ".heyspex", "credentials.yaml");
3431
+ ROUTER_KEY = Symbol.for("heyspex.vaultRouter");
3432
+ globalRouter = globalThis;
3433
+ if (!globalRouter[ROUTER_KEY]) {
3434
+ globalRouter[ROUTER_KEY] = {
3435
+ dedicated: /* @__PURE__ */ new Map(),
3436
+ refCache: /* @__PURE__ */ new Map(),
3437
+ warnedSchemaMissing: false
3438
+ };
3439
+ }
3440
+ router = globalRouter[ROUTER_KEY];
3441
+ }
3442
+ });
3443
+
3444
+ // spex-console/src/lib/db/clients/vault-scope.ts
3445
+ import { AsyncLocalStorage } from "node:async_hooks";
3446
+ function warnScopeClosedOnce(ctx) {
3447
+ if (ctx.warnedClosed) return;
3448
+ ctx.warnedClosed = true;
3449
+ console.warn(
3450
+ `[vault-scope] db use after vault scope closed; falling back to the pool connection (unscoped) (vaultId: ${ctx.vaultId})`
3451
+ );
3452
+ }
3453
+ function handleScopeClosedAccess(ctx) {
3454
+ if (ctx.onStaleUse === "throw") {
3455
+ throw new VaultScopeClosedError(ctx.vaultId);
3456
+ }
3457
+ warnScopeClosedOnce(ctx);
3458
+ }
3459
+ var VaultScopeClosedError, vaultScopeStorage;
3460
+ var init_vault_scope = __esm({
3461
+ "spex-console/src/lib/db/clients/vault-scope.ts"() {
3462
+ "use strict";
3463
+ init_sql_orm_inert_cli_shim();
3464
+ init_plan_tier();
3465
+ init_postgres_client_cli_shim();
3466
+ init_vault_router();
3467
+ VaultScopeClosedError = class extends Error {
3468
+ vaultId;
3469
+ constructor(vaultId) {
3470
+ super(
3471
+ `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.`
3472
+ );
3473
+ this.name = "VaultScopeClosedError";
3474
+ this.vaultId = vaultId;
3475
+ }
3476
+ };
3477
+ vaultScopeStorage = new AsyncLocalStorage();
3478
+ registerVaultScopedDbAccessor(() => {
3479
+ const ctx = vaultScopeStorage.getStore();
3480
+ if (!ctx) return null;
3481
+ if (ctx.closed) {
3482
+ handleScopeClosedAccess(ctx);
3483
+ return null;
3484
+ }
3485
+ return ctx.proxy;
3486
+ });
3487
+ }
3488
+ });
3489
+
3336
3490
  // spex-console/src/lib/scheduler/types.ts
3337
3491
  var init_types2 = __esm({
3338
3492
  "spex-console/src/lib/scheduler/types.ts"() {
@@ -3992,10 +4146,45 @@ var init_config = __esm({
3992
4146
  }
3993
4147
  });
3994
4148
 
3995
- // spex-console/src/lib/vault-frontmatter.ts
4149
+ // spex-console/src/lib/safe-yaml.ts
3996
4150
  import yaml2 from "js-yaml";
4151
+ function dropUndefinedDeep(value) {
4152
+ if (Array.isArray(value)) {
4153
+ return value.filter((item) => item !== void 0).map((item) => dropUndefinedDeep(item));
4154
+ }
4155
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
4156
+ const out = {};
4157
+ for (const [k, v] of Object.entries(value)) {
4158
+ if (v === void 0) continue;
4159
+ out[k] = dropUndefinedDeep(v);
4160
+ }
4161
+ return out;
4162
+ }
4163
+ return value;
4164
+ }
4165
+ function safeYamlDump(value, options2 = {}) {
4166
+ const { schema: _ignoredSchema, ...rest } = options2;
4167
+ const data = dropUndefinedDeep(value);
4168
+ if (grayMatterYamlStringify) return grayMatterYamlStringify(data, rest);
4169
+ return yaml2.dump(data, { ...rest, schema: SAFE_YAML_SCHEMA });
4170
+ }
4171
+ var import_gray_matter, SAFE_YAML_SCHEMA, grayMatterYamlStringify, YAML_FAILSAFE_SCHEMA;
4172
+ var init_safe_yaml = __esm({
4173
+ "spex-console/src/lib/safe-yaml.ts"() {
4174
+ "use strict";
4175
+ import_gray_matter = __toESM(require_gray_matter(), 1);
4176
+ SAFE_YAML_SCHEMA = yaml2.DEFAULT_SAFE_SCHEMA ?? yaml2.DEFAULT_SCHEMA;
4177
+ grayMatterYamlStringify = (() => {
4178
+ const engine = import_gray_matter.default.engines?.yaml;
4179
+ return typeof engine?.stringify === "function" ? engine.stringify : void 0;
4180
+ })();
4181
+ YAML_FAILSAFE_SCHEMA = yaml2.FAILSAFE_SCHEMA;
4182
+ }
4183
+ });
4184
+
4185
+ // spex-console/src/lib/vault-frontmatter.ts
3997
4186
  function normalizeFrontmatterForYaml(frontmatter) {
3998
- const processed = { ...frontmatter };
4187
+ const processed = dropUndefinedDeep(frontmatter);
3999
4188
  for (const [key, value] of Object.entries(processed)) {
4000
4189
  if (value instanceof Date) {
4001
4190
  processed[key] = value.toISOString();
@@ -4004,20 +4193,21 @@ function normalizeFrontmatterForYaml(frontmatter) {
4004
4193
  return processed;
4005
4194
  }
4006
4195
  function stringifySpecMarkdown(body, frontmatter) {
4007
- const fm = yaml2.dump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
4196
+ const fm = safeYamlDump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
4008
4197
  return `---
4009
4198
  ${fm}
4010
4199
  ---
4011
4200
  ${body}`;
4012
4201
  }
4013
4202
  function parseSpecMarkdown(raw) {
4014
- return (0, import_gray_matter.default)(raw);
4203
+ return (0, import_gray_matter2.default)(raw);
4015
4204
  }
4016
- var import_gray_matter, YAML_STRINGIFY_OPTIONS;
4205
+ var import_gray_matter2, YAML_STRINGIFY_OPTIONS;
4017
4206
  var init_vault_frontmatter = __esm({
4018
4207
  "spex-console/src/lib/vault-frontmatter.ts"() {
4019
4208
  "use strict";
4020
- import_gray_matter = __toESM(require_gray_matter(), 1);
4209
+ import_gray_matter2 = __toESM(require_gray_matter(), 1);
4210
+ init_safe_yaml();
4021
4211
  YAML_STRINGIFY_OPTIONS = {
4022
4212
  quotingType: '"',
4023
4213
  skipInvalid: true,
@@ -5053,11 +5243,11 @@ var init_guardrail_naming = __esm({
5053
5243
  import { createHash as createHash3 } from "node:crypto";
5054
5244
  import { promises as fs6 } from "node:fs";
5055
5245
  import path9 from "node:path";
5056
- var import_gray_matter2;
5246
+ var import_gray_matter3;
5057
5247
  var init_parser = __esm({
5058
5248
  "spex-console/src/lib/indexer/parser.ts"() {
5059
5249
  "use strict";
5060
- import_gray_matter2 = __toESM(require_gray_matter(), 1);
5250
+ import_gray_matter3 = __toESM(require_gray_matter(), 1);
5061
5251
  init_vault_id();
5062
5252
  init_vault_shared();
5063
5253
  }
@@ -5196,12 +5386,12 @@ var init_linkage_validation = __esm({
5196
5386
  });
5197
5387
 
5198
5388
  // spex-console/src/lib/perf-timing.ts
5199
- import { AsyncLocalStorage } from "node:async_hooks";
5389
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
5200
5390
  var storage;
5201
5391
  var init_perf_timing = __esm({
5202
5392
  "spex-console/src/lib/perf-timing.ts"() {
5203
5393
  "use strict";
5204
- storage = new AsyncLocalStorage();
5394
+ storage = new AsyncLocalStorage2();
5205
5395
  }
5206
5396
  });
5207
5397
 
@@ -5223,12 +5413,12 @@ var init_planning_flow_model = __esm({
5223
5413
  });
5224
5414
 
5225
5415
  // spex-console/src/lib/request-memo.ts
5226
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
5416
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
5227
5417
  var storage2;
5228
5418
  var init_request_memo = __esm({
5229
5419
  "spex-console/src/lib/request-memo.ts"() {
5230
5420
  "use strict";
5231
- storage2 = new AsyncLocalStorage2();
5421
+ storage2 = new AsyncLocalStorage3();
5232
5422
  }
5233
5423
  });
5234
5424
 
@@ -5557,10 +5747,9 @@ var init_search_body_hit = __esm({
5557
5747
 
5558
5748
  // spex-console/src/lib/vault.ts
5559
5749
  import { spawn } from "node:child_process";
5560
- import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
5750
+ import { readdirSync as readdirSync2, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
5561
5751
  import { promises as fs9 } from "node:fs";
5562
5752
  import path14 from "node:path";
5563
- import yaml3 from "js-yaml";
5564
5753
  function normalizeDateField(value) {
5565
5754
  let d = null;
5566
5755
  if (value instanceof Date) {
@@ -5809,7 +5998,7 @@ async function parseLoadedSpec(absPath, root) {
5809
5998
  }
5810
5999
  let parsed;
5811
6000
  try {
5812
- parsed = (0, import_gray_matter3.default)(raw);
6001
+ parsed = (0, import_gray_matter4.default)(raw);
5813
6002
  } catch {
5814
6003
  const summary2 = toSummary(absPath, root, {}, raw);
5815
6004
  return { ...summary2, frontmatter: {}, body: raw, absPath };
@@ -5823,11 +6012,11 @@ function extractWikilinks(value) {
5823
6012
  const arr = Array.isArray(value) ? value : [value];
5824
6013
  return arr.map((v) => normalizeWikilink(String(v))).filter(Boolean);
5825
6014
  }
5826
- var import_gray_matter3, DIAGNOSE_SEVERITY_TIER, VAULT_CACHE_TTL_MS, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore, VaultDiskWriteError;
6015
+ var import_gray_matter4, DIAGNOSE_SEVERITY_TIER, VAULT_CACHE_TTL_MS, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore, VaultDiskWriteError;
5827
6016
  var init_vault = __esm({
5828
6017
  "spex-console/src/lib/vault.ts"() {
5829
6018
  "use strict";
5830
- import_gray_matter3 = __toESM(require_gray_matter(), 1);
6019
+ import_gray_matter4 = __toESM(require_gray_matter(), 1);
5831
6020
  init_diagnose_skeleton();
5832
6021
  init_auth();
5833
6022
  init_capture_vault();
@@ -5838,6 +6027,7 @@ var init_vault = __esm({
5838
6027
  init_skips();
5839
6028
  init_clients();
5840
6029
  init_postgres_client_cli_shim();
6030
+ init_vault_scope();
5841
6031
  init_helpers();
5842
6032
  init_index_write_service();
5843
6033
  init_spec_read_service();
@@ -5854,6 +6044,7 @@ var init_vault = __esm({
5854
6044
  init_planning_flow_model();
5855
6045
  init_request_memo();
5856
6046
  init_review_mode();
6047
+ init_safe_yaml();
5857
6048
  init_active_milestone();
5858
6049
  init_area();
5859
6050
  init_claim_eligibility();
@@ -7670,7 +7861,7 @@ var init_esm2 = __esm({
7670
7861
  });
7671
7862
 
7672
7863
  // spex-console/bin/hs-vault-pull.ts
7673
- import { readFileSync as readFileSync15 } from "node:fs";
7864
+ import { readFileSync as readFileSync16 } from "node:fs";
7674
7865
  import path15 from "node:path";
7675
7866
  import process4 from "node:process";
7676
7867
 
@@ -8495,7 +8686,7 @@ function formatPullSummary(result, { detail = false, sampleSize = 5 } = {}) {
8495
8686
  }
8496
8687
 
8497
8688
  // spex-console/src/lib/vault-sync/pull-io.ts
8498
- import { promises as fs10, readFileSync as readFileSync14 } from "node:fs";
8689
+ import { promises as fs10, readFileSync as readFileSync15 } from "node:fs";
8499
8690
  import { EOL } from "node:os";
8500
8691
  import nodePath3 from "node:path";
8501
8692
 
@@ -8540,7 +8731,7 @@ var PullTargetNotVaultError = class extends Error {
8540
8731
  function assertPullTargetIsVault(repoRoot) {
8541
8732
  let text2;
8542
8733
  try {
8543
- text2 = readFileSync14(nodePath3.join(repoRoot, "heyspex.yaml"), "utf8");
8734
+ text2 = readFileSync15(nodePath3.join(repoRoot, "heyspex.yaml"), "utf8");
8544
8735
  } catch {
8545
8736
  text2 = null;
8546
8737
  }
@@ -9442,7 +9633,7 @@ function makeSleeper() {
9442
9633
  }
9443
9634
  function readMarkerVaultId(vaultRoot2) {
9444
9635
  try {
9445
- return parseVaultId(readFileSync15(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
9636
+ return parseVaultId(readFileSync16(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
9446
9637
  } catch {
9447
9638
  return null;
9448
9639
  }