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 (str3.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
  str3 = str3.slice(openLen);
1443
1443
  const len = str3.length;
1444
- const language = matter4.language(str3, opts);
1444
+ const language = matter5.language(str3, opts);
1445
1445
  if (language.name) {
1446
1446
  file.language = language.name;
1447
1447
  str3 = str3.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 str3 = fs11.readFileSync(filepath, "utf8");
1486
- const file = matter4(str3, options2);
1486
+ const file = matter5(str3, options2);
1487
1487
  file.path = filepath;
1488
1488
  return file;
1489
1489
  };
1490
- matter4.test = function(str3, options2) {
1490
+ matter5.test = function(str3, options2) {
1491
1491
  return utils.startsWith(str3, defaults(options2).delimiters[0]);
1492
1492
  };
1493
- matter4.language = function(str3, options2) {
1493
+ matter5.language = function(str3, options2) {
1494
1494
  const opts = defaults(options2);
1495
1495
  const open = opts.delimiters[0];
1496
- if (matter4.test(str3)) {
1496
+ if (matter5.test(str3)) {
1497
1497
  str3 = str3.slice(open.length);
1498
1498
  }
1499
1499
  const language = str3.slice(0, str3.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
 
@@ -2010,7 +2010,7 @@ var init_interfaces = __esm({
2010
2010
  });
2011
2011
 
2012
2012
  // spex-console/src/lib/db/schema/postgres.ts
2013
- 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;
2013
+ 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;
2014
2014
  var init_postgres = __esm({
2015
2015
  "spex-console/src/lib/db/schema/postgres.ts"() {
2016
2016
  "use strict";
@@ -2960,6 +2960,24 @@ var init_postgres = __esm({
2960
2960
  },
2961
2961
  (table) => [primaryKey({ columns: [table.userId, table.vaultId, table.namespace] })]
2962
2962
  );
2963
+ seatAssignments = pgTable(
2964
+ "seat_assignments",
2965
+ {
2966
+ accountId: text("account_id").notNull(),
2967
+ vaultId: text("vault_id").notNull(),
2968
+ harness: text("harness").notNull(),
2969
+ seatId: text("seat_id").notNull(),
2970
+ model: text("model").notNull(),
2971
+ // Set when a launch reported the harness REFUSED this model and the run fell
2972
+ // back to auto (Copilot's `heyspex.copilot_model_fallback` event, HS-F4UM);
2973
+ // cleared whenever the user sets a new model.
2974
+ overrideRejectedAt: timestamp("override_rejected_at", { withTimezone: true }),
2975
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().default(sql`now()`)
2976
+ },
2977
+ (table) => [
2978
+ primaryKey({ columns: [table.accountId, table.vaultId, table.harness, table.seatId] })
2979
+ ]
2980
+ );
2963
2981
  wipLedger = pgTable(
2964
2982
  "wip_ledger",
2965
2983
  {
@@ -3714,11 +3732,12 @@ function getPostgresAdminDb() {
3714
3732
  function inertClient() {
3715
3733
  return false;
3716
3734
  }
3717
- var isNeonPool;
3735
+ var isNeonPool, registerVaultScopedDbAccessor;
3718
3736
  var init_postgres_client_cli_shim = __esm({
3719
3737
  "dist/cli/._postgres-client.cli-shim.mjs"() {
3720
3738
  "use strict";
3721
3739
  isNeonPool = inertClient;
3740
+ registerVaultScopedDbAccessor = inertClient;
3722
3741
  }
3723
3742
  });
3724
3743
 
@@ -3975,6 +3994,141 @@ var init_clients = __esm({
3975
3994
  }
3976
3995
  });
3977
3996
 
3997
+ // spex-console/src/lib/rate-limits.ts
3998
+ var RATE_LIMIT_BUDGETS;
3999
+ var init_rate_limits = __esm({
4000
+ "spex-console/src/lib/rate-limits.ts"() {
4001
+ "use strict";
4002
+ RATE_LIMIT_BUDGETS = {
4003
+ mcp_tool_calls: { daily: 500, hourly: 125 },
4004
+ spec_writes: { daily: 100, hourly: 25 },
4005
+ capture_sessions: { daily: 20, hourly: 5 },
4006
+ // Paul's Advocate free-text chat ([[HS-4YYB-XEBI-TASK-paul-s-advocate-answers-real-questions]]):
4007
+ // a public YC reviewer can otherwise run up LLM spend on an unauthenticated-feeling
4008
+ // widget. 30/hour matches the builder brief's "e.g. 30 messages/hour" starting point;
4009
+ // daily is 4x hourly (looser than the 25%-of-daily pattern above — a reviewer chatting
4010
+ // steadily through a demo session should not get cut off mid-conversation).
4011
+ advocate_chat: { daily: 120, hourly: 30 }
4012
+ };
4013
+ }
4014
+ });
4015
+
4016
+ // spex-console/src/lib/plan-tier.ts
4017
+ function scaledBudgets(multiplier) {
4018
+ const out = {};
4019
+ for (const [bucket, budget] of Object.entries(RATE_LIMIT_BUDGETS)) {
4020
+ out[bucket] = {
4021
+ daily: Math.round(budget.daily * multiplier),
4022
+ hourly: Math.round(budget.hourly * multiplier)
4023
+ };
4024
+ }
4025
+ return Object.freeze(out);
4026
+ }
4027
+ var PLAN_TIER_RATE_LIMIT_MULTIPLIER, PLAN_TIER_LIMITS;
4028
+ var init_plan_tier = __esm({
4029
+ "spex-console/src/lib/plan-tier.ts"() {
4030
+ "use strict";
4031
+ init_sql_orm_inert_cli_shim();
4032
+ init_postgres_client_cli_shim();
4033
+ init_postgres();
4034
+ init_rate_limits();
4035
+ PLAN_TIER_RATE_LIMIT_MULTIPLIER = {
4036
+ free: 1,
4037
+ pro: 4,
4038
+ enterprise: 20
4039
+ };
4040
+ PLAN_TIER_LIMITS = {
4041
+ free: {
4042
+ statement_timeout_ms: 5e3,
4043
+ read_work_mem: "4MB",
4044
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.free)
4045
+ },
4046
+ pro: {
4047
+ statement_timeout_ms: 15e3,
4048
+ read_work_mem: "16MB",
4049
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.pro)
4050
+ },
4051
+ enterprise: {
4052
+ statement_timeout_ms: 3e4,
4053
+ read_work_mem: "64MB",
4054
+ rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.enterprise)
4055
+ }
4056
+ };
4057
+ }
4058
+ });
4059
+
4060
+ // spex-console/src/lib/db/clients/vault-router.ts
4061
+ import { existsSync as existsSync8, readFileSync as readFileSync13 } from "node:fs";
4062
+ import { homedir as homedir10 } from "node:os";
4063
+ import { join as join11 } from "node:path";
4064
+ var CREDENTIALS_PATH, ROUTER_KEY, globalRouter, router;
4065
+ var init_vault_router = __esm({
4066
+ "spex-console/src/lib/db/clients/vault-router.ts"() {
4067
+ "use strict";
4068
+ init_sql_orm_inert_cli_shim();
4069
+ init_sql_orm_inert_cli_shim();
4070
+ init_postgres();
4071
+ init_postgres_client_cli_shim();
4072
+ CREDENTIALS_PATH = join11(homedir10(), ".heyspex", "credentials.yaml");
4073
+ ROUTER_KEY = Symbol.for("heyspex.vaultRouter");
4074
+ globalRouter = globalThis;
4075
+ if (!globalRouter[ROUTER_KEY]) {
4076
+ globalRouter[ROUTER_KEY] = {
4077
+ dedicated: /* @__PURE__ */ new Map(),
4078
+ refCache: /* @__PURE__ */ new Map(),
4079
+ warnedSchemaMissing: false
4080
+ };
4081
+ }
4082
+ router = globalRouter[ROUTER_KEY];
4083
+ }
4084
+ });
4085
+
4086
+ // spex-console/src/lib/db/clients/vault-scope.ts
4087
+ import { AsyncLocalStorage } from "node:async_hooks";
4088
+ function warnScopeClosedOnce(ctx) {
4089
+ if (ctx.warnedClosed) return;
4090
+ ctx.warnedClosed = true;
4091
+ console.warn(
4092
+ `[vault-scope] db use after vault scope closed; falling back to the pool connection (unscoped) (vaultId: ${ctx.vaultId})`
4093
+ );
4094
+ }
4095
+ function handleScopeClosedAccess(ctx) {
4096
+ if (ctx.onStaleUse === "throw") {
4097
+ throw new VaultScopeClosedError(ctx.vaultId);
4098
+ }
4099
+ warnScopeClosedOnce(ctx);
4100
+ }
4101
+ var VaultScopeClosedError, vaultScopeStorage;
4102
+ var init_vault_scope = __esm({
4103
+ "spex-console/src/lib/db/clients/vault-scope.ts"() {
4104
+ "use strict";
4105
+ init_sql_orm_inert_cli_shim();
4106
+ init_plan_tier();
4107
+ init_postgres_client_cli_shim();
4108
+ init_vault_router();
4109
+ VaultScopeClosedError = class extends Error {
4110
+ vaultId;
4111
+ constructor(vaultId) {
4112
+ super(
4113
+ `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.`
4114
+ );
4115
+ this.name = "VaultScopeClosedError";
4116
+ this.vaultId = vaultId;
4117
+ }
4118
+ };
4119
+ vaultScopeStorage = new AsyncLocalStorage();
4120
+ registerVaultScopedDbAccessor(() => {
4121
+ const ctx = vaultScopeStorage.getStore();
4122
+ if (!ctx) return null;
4123
+ if (ctx.closed) {
4124
+ handleScopeClosedAccess(ctx);
4125
+ return null;
4126
+ }
4127
+ return ctx.proxy;
4128
+ });
4129
+ }
4130
+ });
4131
+
3978
4132
  // spex-console/src/lib/scheduler/types.ts
3979
4133
  var init_types2 = __esm({
3980
4134
  "spex-console/src/lib/scheduler/types.ts"() {
@@ -4502,10 +4656,45 @@ var init_config = __esm({
4502
4656
  }
4503
4657
  });
4504
4658
 
4505
- // spex-console/src/lib/vault-frontmatter.ts
4659
+ // spex-console/src/lib/safe-yaml.ts
4506
4660
  import yaml2 from "js-yaml";
4661
+ function dropUndefinedDeep(value) {
4662
+ if (Array.isArray(value)) {
4663
+ return value.filter((item) => item !== void 0).map((item) => dropUndefinedDeep(item));
4664
+ }
4665
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
4666
+ const out = {};
4667
+ for (const [k, v] of Object.entries(value)) {
4668
+ if (v === void 0) continue;
4669
+ out[k] = dropUndefinedDeep(v);
4670
+ }
4671
+ return out;
4672
+ }
4673
+ return value;
4674
+ }
4675
+ function safeYamlDump(value, options2 = {}) {
4676
+ const { schema: _ignoredSchema, ...rest } = options2;
4677
+ const data = dropUndefinedDeep(value);
4678
+ if (grayMatterYamlStringify) return grayMatterYamlStringify(data, rest);
4679
+ return yaml2.dump(data, { ...rest, schema: SAFE_YAML_SCHEMA });
4680
+ }
4681
+ var import_gray_matter2, SAFE_YAML_SCHEMA, grayMatterYamlStringify, YAML_FAILSAFE_SCHEMA;
4682
+ var init_safe_yaml = __esm({
4683
+ "spex-console/src/lib/safe-yaml.ts"() {
4684
+ "use strict";
4685
+ import_gray_matter2 = __toESM(require_gray_matter(), 1);
4686
+ SAFE_YAML_SCHEMA = yaml2.DEFAULT_SAFE_SCHEMA ?? yaml2.DEFAULT_SCHEMA;
4687
+ grayMatterYamlStringify = (() => {
4688
+ const engine = import_gray_matter2.default.engines?.yaml;
4689
+ return typeof engine?.stringify === "function" ? engine.stringify : void 0;
4690
+ })();
4691
+ YAML_FAILSAFE_SCHEMA = yaml2.FAILSAFE_SCHEMA;
4692
+ }
4693
+ });
4694
+
4695
+ // spex-console/src/lib/vault-frontmatter.ts
4507
4696
  function normalizeFrontmatterForYaml(frontmatter) {
4508
- const processed = { ...frontmatter };
4697
+ const processed = dropUndefinedDeep(frontmatter);
4509
4698
  for (const [key, value] of Object.entries(processed)) {
4510
4699
  if (value instanceof Date) {
4511
4700
  processed[key] = value.toISOString();
@@ -4514,17 +4703,18 @@ function normalizeFrontmatterForYaml(frontmatter) {
4514
4703
  return processed;
4515
4704
  }
4516
4705
  function stringifySpecMarkdown(body, frontmatter) {
4517
- const fm = yaml2.dump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
4706
+ const fm = safeYamlDump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
4518
4707
  return `---
4519
4708
  ${fm}
4520
4709
  ---
4521
4710
  ${body}`;
4522
4711
  }
4523
- var import_gray_matter2, YAML_STRINGIFY_OPTIONS;
4712
+ var import_gray_matter3, YAML_STRINGIFY_OPTIONS;
4524
4713
  var init_vault_frontmatter = __esm({
4525
4714
  "spex-console/src/lib/vault-frontmatter.ts"() {
4526
4715
  "use strict";
4527
- import_gray_matter2 = __toESM(require_gray_matter(), 1);
4716
+ import_gray_matter3 = __toESM(require_gray_matter(), 1);
4717
+ init_safe_yaml();
4528
4718
  YAML_STRINGIFY_OPTIONS = {
4529
4719
  quotingType: '"',
4530
4720
  skipInvalid: true,
@@ -5654,12 +5844,12 @@ var init_linkage_validation = __esm({
5654
5844
  });
5655
5845
 
5656
5846
  // spex-console/src/lib/perf-timing.ts
5657
- import { AsyncLocalStorage } from "node:async_hooks";
5847
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
5658
5848
  var storage;
5659
5849
  var init_perf_timing = __esm({
5660
5850
  "spex-console/src/lib/perf-timing.ts"() {
5661
5851
  "use strict";
5662
- storage = new AsyncLocalStorage();
5852
+ storage = new AsyncLocalStorage2();
5663
5853
  }
5664
5854
  });
5665
5855
 
@@ -5681,12 +5871,12 @@ var init_planning_flow_model = __esm({
5681
5871
  });
5682
5872
 
5683
5873
  // spex-console/src/lib/request-memo.ts
5684
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
5874
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
5685
5875
  var storage2;
5686
5876
  var init_request_memo = __esm({
5687
5877
  "spex-console/src/lib/request-memo.ts"() {
5688
5878
  "use strict";
5689
- storage2 = new AsyncLocalStorage2();
5879
+ storage2 = new AsyncLocalStorage3();
5690
5880
  }
5691
5881
  });
5692
5882
 
@@ -6015,10 +6205,9 @@ var init_search_body_hit = __esm({
6015
6205
 
6016
6206
  // spex-console/src/lib/vault.ts
6017
6207
  import { spawn } from "node:child_process";
6018
- import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
6208
+ import { readdirSync as readdirSync2, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
6019
6209
  import { promises as fs7 } from "node:fs";
6020
6210
  import path14 from "node:path";
6021
- import yaml3 from "js-yaml";
6022
6211
  function normalizeDateField(value) {
6023
6212
  let d = null;
6024
6213
  if (value instanceof Date) {
@@ -6267,7 +6456,7 @@ async function parseLoadedSpec(absPath, root) {
6267
6456
  }
6268
6457
  let parsed;
6269
6458
  try {
6270
- parsed = (0, import_gray_matter3.default)(raw);
6459
+ parsed = (0, import_gray_matter4.default)(raw);
6271
6460
  } catch {
6272
6461
  const summary2 = toSummary(absPath, root, {}, raw);
6273
6462
  return { ...summary2, frontmatter: {}, body: raw, absPath };
@@ -6281,11 +6470,11 @@ function extractWikilinks2(value) {
6281
6470
  const arr = Array.isArray(value) ? value : [value];
6282
6471
  return arr.map((v) => normalizeWikilink(String(v))).filter(Boolean);
6283
6472
  }
6284
- 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;
6473
+ 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;
6285
6474
  var init_vault = __esm({
6286
6475
  "spex-console/src/lib/vault.ts"() {
6287
6476
  "use strict";
6288
- import_gray_matter3 = __toESM(require_gray_matter(), 1);
6477
+ import_gray_matter4 = __toESM(require_gray_matter(), 1);
6289
6478
  init_diagnose_skeleton();
6290
6479
  init_auth();
6291
6480
  init_capture_vault();
@@ -6296,6 +6485,7 @@ var init_vault = __esm({
6296
6485
  init_skips();
6297
6486
  init_clients();
6298
6487
  init_postgres_client_cli_shim();
6488
+ init_vault_scope();
6299
6489
  init_helpers();
6300
6490
  init_index_write_service();
6301
6491
  init_spec_read_service();
@@ -6312,6 +6502,7 @@ var init_vault = __esm({
6312
6502
  init_planning_flow_model();
6313
6503
  init_request_memo();
6314
6504
  init_review_mode();
6505
+ init_safe_yaml();
6315
6506
  init_active_milestone();
6316
6507
  init_area();
6317
6508
  init_claim_eligibility();
@@ -6413,7 +6604,7 @@ var init_vault = __esm({
6413
6604
 
6414
6605
  // spex-console/bin/hs-index-reconcile.ts
6415
6606
  import { execFileSync } from "node:child_process";
6416
- import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
6607
+ import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "node:fs";
6417
6608
  import { promises as fs10 } from "node:fs";
6418
6609
  import path15 from "node:path";
6419
6610
  import process4 from "node:process";
@@ -7370,7 +7561,7 @@ async function postEdgesChunk(input, specs2) {
7370
7561
  }
7371
7562
 
7372
7563
  // spex-console/src/lib/vault-sync/pull-io.ts
7373
- import { promises as fs9, readFileSync as readFileSync14 } from "node:fs";
7564
+ import { promises as fs9, readFileSync as readFileSync15 } from "node:fs";
7374
7565
  import { EOL } from "node:os";
7375
7566
  import nodePath2 from "node:path";
7376
7567
 
@@ -7425,7 +7616,7 @@ var PullTargetNotVaultError = class extends Error {
7425
7616
  function assertPullTargetIsVault(repoRoot) {
7426
7617
  let text2;
7427
7618
  try {
7428
- text2 = readFileSync14(nodePath2.join(repoRoot, "heyspex.yaml"), "utf8");
7619
+ text2 = readFileSync15(nodePath2.join(repoRoot, "heyspex.yaml"), "utf8");
7429
7620
  } catch {
7430
7621
  text2 = null;
7431
7622
  }
@@ -7829,7 +8020,7 @@ function say(args, text2) {
7829
8020
  }
7830
8021
  function readDiscovery(p) {
7831
8022
  try {
7832
- return readFileSync15(p, "utf-8");
8023
+ return readFileSync16(p, "utf-8");
7833
8024
  } catch {
7834
8025
  return null;
7835
8026
  }
@@ -7841,7 +8032,7 @@ function resolveConsole(args) {
7841
8032
  }
7842
8033
  function readMarkerVaultId(vaultRoot2) {
7843
8034
  try {
7844
- return parseVaultId(readFileSync15(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
8035
+ return parseVaultId(readFileSync16(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
7845
8036
  } catch {
7846
8037
  return null;
7847
8038
  }
@@ -2837,7 +2837,7 @@ var COMMAND_SCOPES = Object.freeze({
2837
2837
  "hs-promote.md": { scope: "customer", why: "Capture \u2192 research \u2192 milestone promotion." },
2838
2838
  "hs-push.md": { scope: "customer", why: "Push an epic plan into the vault." },
2839
2839
  "hs-qc.md": { scope: "customer", why: "Post-plan QC gate." },
2840
- "hs-res.md": { scope: "customer", why: "Research method entrypoint (SpexCrawl/DeepSeek)." },
2840
+ "hs-res.md": { scope: "customer", why: "Research method entrypoint (local Docker Firecrawl search + SpexCrawl/Firecrawl fetch; DeepSeek on the platform)." },
2841
2841
  "hs-resolve.md": { scope: "customer", why: "Resolution lane." },
2842
2842
  "hs-review.md": { scope: "customer", why: "Review queue." },
2843
2843
  "hs-view.md": { scope: "customer", why: "Open a console surface." },
@@ -3166,7 +3166,7 @@ function githubHookNames() {
3166
3166
  }
3167
3167
  function githubHookCommand(row) {
3168
3168
  const tail = hookTail(row);
3169
- return `R=node_modules/heyspex/scripts/claude-hooks/_run.mjs; [ -f "$R" ] || R=./scripts/claude-hooks/_run.mjs; [ -f "$R" ] || exit 0; exec node "$R" ${tail}`;
3169
+ return `export HEYSPEX_HARNESS="\${HEYSPEX_HARNESS:-copilot}"; R=node_modules/heyspex/scripts/claude-hooks/_run.mjs; [ -f "$R" ] || R=./scripts/claude-hooks/_run.mjs; [ -f "$R" ] || exit 0; exec node "$R" ${tail}`;
3170
3170
  }
3171
3171
  function renderGithubHooksDocument() {
3172
3172
  const allowed = githubHookNames();
@@ -3233,6 +3233,25 @@ var fsDeps = {
3233
3233
  }
3234
3234
  }
3235
3235
  };
3236
+ function findProjectRoot(start) {
3237
+ let dir = path4.resolve(start);
3238
+ for (; ; ) {
3239
+ if (fs.existsSync(path4.join(dir, ".git")) || fs.existsSync(path4.join(dir, "heyspex.yaml"))) return dir;
3240
+ const up = path4.dirname(dir);
3241
+ if (up === dir) return path4.resolve(start);
3242
+ dir = up;
3243
+ }
3244
+ }
3245
+ function isInstalledPackageRoot(platformRoot, exists = fs.existsSync) {
3246
+ if (toPosixPath(path4.resolve(platformRoot)).includes("/node_modules/")) return true;
3247
+ return !exists(path4.join(platformRoot, "scripts", "heyspex-doctor.mjs"));
3248
+ }
3249
+ function resolveSetupTarget({ platformRoot, env = process.env, cwd = process.cwd(), exists = fs.existsSync }) {
3250
+ const explicit = env.HS_SETUP_TARGET?.trim();
3251
+ if (explicit) return path4.resolve(explicit);
3252
+ if (isInstalledPackageRoot(platformRoot, exists)) return findProjectRoot(cwd);
3253
+ return path4.resolve(platformRoot);
3254
+ }
3236
3255
  function invokedDirectly(importMetaUrl, selfBasename, argv1 = process.argv[1]) {
3237
3256
  try {
3238
3257
  if (!argv1) return false;
@@ -3281,7 +3300,7 @@ if (invokedDirectly(import.meta.url, "customer-kit.mjs")) {
3281
3300
 
3282
3301
  // scripts/install-cursor-hooks.mjs
3283
3302
  var PLATFORM_ROOT = path5.resolve(path5.dirname(fileURLToPath4(import.meta.url)), "..");
3284
- var REPO_ROOT = process.env.HS_SETUP_TARGET?.trim() ? path5.resolve(process.env.HS_SETUP_TARGET.trim()) : PLATFORM_ROOT;
3303
+ var REPO_ROOT = resolveSetupTarget({ platformRoot: PLATFORM_ROOT });
3285
3304
  var SETTINGS_PATH = path5.join(REPO_ROOT, ".cursor", "hooks.json");
3286
3305
  function cursorKitFor(targetRoot, platformRoot = PLATFORM_ROOT) {
3287
3306
  const customer = !samePlatformPath(targetRoot, platformRoot);
@@ -2342,7 +2342,7 @@ var COMMAND_SCOPES = Object.freeze({
2342
2342
  "hs-promote.md": { scope: "customer", why: "Capture \u2192 research \u2192 milestone promotion." },
2343
2343
  "hs-push.md": { scope: "customer", why: "Push an epic plan into the vault." },
2344
2344
  "hs-qc.md": { scope: "customer", why: "Post-plan QC gate." },
2345
- "hs-res.md": { scope: "customer", why: "Research method entrypoint (SpexCrawl/DeepSeek)." },
2345
+ "hs-res.md": { scope: "customer", why: "Research method entrypoint (local Docker Firecrawl search + SpexCrawl/Firecrawl fetch; DeepSeek on the platform)." },
2346
2346
  "hs-resolve.md": { scope: "customer", why: "Resolution lane." },
2347
2347
  "hs-review.md": { scope: "customer", why: "Review queue." },
2348
2348
  "hs-view.md": { scope: "customer", why: "Open a console surface." },
@@ -2752,7 +2752,7 @@ function githubHookNames() {
2752
2752
  }
2753
2753
  function githubHookCommand(row) {
2754
2754
  const tail = hookTail(row);
2755
- return `R=node_modules/heyspex/scripts/claude-hooks/_run.mjs; [ -f "$R" ] || R=./scripts/claude-hooks/_run.mjs; [ -f "$R" ] || exit 0; exec node "$R" ${tail}`;
2755
+ return `export HEYSPEX_HARNESS="\${HEYSPEX_HARNESS:-copilot}"; R=node_modules/heyspex/scripts/claude-hooks/_run.mjs; [ -f "$R" ] || R=./scripts/claude-hooks/_run.mjs; [ -f "$R" ] || exit 0; exec node "$R" ${tail}`;
2756
2756
  }
2757
2757
  function renderGithubHooksDocument() {
2758
2758
  const allowed = githubHookNames();
@@ -2891,6 +2891,25 @@ var fsDeps = {
2891
2891
  }
2892
2892
  }
2893
2893
  };
2894
+ function findProjectRoot(start) {
2895
+ let dir = path4.resolve(start);
2896
+ for (; ; ) {
2897
+ if (fs.existsSync(path4.join(dir, ".git")) || fs.existsSync(path4.join(dir, "heyspex.yaml"))) return dir;
2898
+ const up = path4.dirname(dir);
2899
+ if (up === dir) return path4.resolve(start);
2900
+ dir = up;
2901
+ }
2902
+ }
2903
+ function isInstalledPackageRoot(platformRoot, exists = fs.existsSync) {
2904
+ if (toPosixPath(path4.resolve(platformRoot)).includes("/node_modules/")) return true;
2905
+ return !exists(path4.join(platformRoot, "scripts", "heyspex-doctor.mjs"));
2906
+ }
2907
+ function resolveSetupTarget({ platformRoot, env = process.env, cwd = process.cwd(), exists = fs.existsSync }) {
2908
+ const explicit = env.HS_SETUP_TARGET?.trim();
2909
+ if (explicit) return path4.resolve(explicit);
2910
+ if (isInstalledPackageRoot(platformRoot, exists)) return findProjectRoot(cwd);
2911
+ return path4.resolve(platformRoot);
2912
+ }
2894
2913
  function invokedDirectly(importMetaUrl, selfBasename, argv1 = process.argv[1]) {
2895
2914
  try {
2896
2915
  if (!argv1) return false;
@@ -2939,7 +2958,7 @@ if (invokedDirectly(import.meta.url, "customer-kit.mjs")) {
2939
2958
 
2940
2959
  // scripts/install-cursor-hooks.mjs
2941
2960
  var PLATFORM_ROOT = path5.resolve(path5.dirname(fileURLToPath4(import.meta.url)), "..");
2942
- var REPO_ROOT = process.env.HS_SETUP_TARGET?.trim() ? path5.resolve(process.env.HS_SETUP_TARGET.trim()) : PLATFORM_ROOT;
2961
+ var REPO_ROOT = resolveSetupTarget({ platformRoot: PLATFORM_ROOT });
2943
2962
  var SETTINGS_PATH = path5.join(REPO_ROOT, ".cursor", "hooks.json");
2944
2963
  function cursorKitFor(targetRoot, platformRoot = PLATFORM_ROOT) {
2945
2964
  const customer = !samePlatformPath(targetRoot, platformRoot);
@@ -23909,6 +23909,24 @@ var consoleSettingsStore = pgTable(
23909
23909
  },
23910
23910
  (table) => [primaryKey({ columns: [table.userId, table.vaultId, table.namespace] })]
23911
23911
  );
23912
+ var seatAssignments = pgTable(
23913
+ "seat_assignments",
23914
+ {
23915
+ accountId: text("account_id").notNull(),
23916
+ vaultId: text("vault_id").notNull(),
23917
+ harness: text("harness").notNull(),
23918
+ seatId: text("seat_id").notNull(),
23919
+ model: text("model").notNull(),
23920
+ // Set when a launch reported the harness REFUSED this model and the run fell
23921
+ // back to auto (Copilot's `heyspex.copilot_model_fallback` event, HS-F4UM);
23922
+ // cleared whenever the user sets a new model.
23923
+ overrideRejectedAt: timestamp("override_rejected_at", { withTimezone: true }),
23924
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().default(sql`now()`)
23925
+ },
23926
+ (table) => [
23927
+ primaryKey({ columns: [table.accountId, table.vaultId, table.harness, table.seatId] })
23928
+ ]
23929
+ );
23912
23930
  var wipLedger = pgTable(
23913
23931
  "wip_ledger",
23914
23932
  {
@@ -26034,7 +26052,7 @@ var COMMAND_SCOPES = Object.freeze({
26034
26052
  "hs-promote.md": { scope: "customer", why: "Capture \u2192 research \u2192 milestone promotion." },
26035
26053
  "hs-push.md": { scope: "customer", why: "Push an epic plan into the vault." },
26036
26054
  "hs-qc.md": { scope: "customer", why: "Post-plan QC gate." },
26037
- "hs-res.md": { scope: "customer", why: "Research method entrypoint (SpexCrawl/DeepSeek)." },
26055
+ "hs-res.md": { scope: "customer", why: "Research method entrypoint (local Docker Firecrawl search + SpexCrawl/Firecrawl fetch; DeepSeek on the platform)." },
26038
26056
  "hs-resolve.md": { scope: "customer", why: "Resolution lane." },
26039
26057
  "hs-review.md": { scope: "customer", why: "Review queue." },
26040
26058
  "hs-view.md": { scope: "customer", why: "Open a console surface." },
@@ -26444,7 +26462,7 @@ function githubHookNames() {
26444
26462
  }
26445
26463
  function githubHookCommand(row2) {
26446
26464
  const tail = hookTail(row2);
26447
- return `R=node_modules/heyspex/scripts/claude-hooks/_run.mjs; [ -f "$R" ] || R=./scripts/claude-hooks/_run.mjs; [ -f "$R" ] || exit 0; exec node "$R" ${tail}`;
26465
+ return `export HEYSPEX_HARNESS="\${HEYSPEX_HARNESS:-copilot}"; R=node_modules/heyspex/scripts/claude-hooks/_run.mjs; [ -f "$R" ] || R=./scripts/claude-hooks/_run.mjs; [ -f "$R" ] || exit 0; exec node "$R" ${tail}`;
26448
26466
  }
26449
26467
  function renderGithubHooksDocument() {
26450
26468
  const allowed = githubHookNames();
@@ -26583,6 +26601,25 @@ var fsDeps = {
26583
26601
  }
26584
26602
  }
26585
26603
  };
26604
+ function findProjectRoot(start) {
26605
+ let dir = path2.resolve(start);
26606
+ for (; ; ) {
26607
+ if (fs.existsSync(path2.join(dir, ".git")) || fs.existsSync(path2.join(dir, "heyspex.yaml"))) return dir;
26608
+ const up = path2.dirname(dir);
26609
+ if (up === dir) return path2.resolve(start);
26610
+ dir = up;
26611
+ }
26612
+ }
26613
+ function isInstalledPackageRoot(platformRoot, exists2 = fs.existsSync) {
26614
+ if (toPosixPath(path2.resolve(platformRoot)).includes("/node_modules/")) return true;
26615
+ return !exists2(path2.join(platformRoot, "scripts", "heyspex-doctor.mjs"));
26616
+ }
26617
+ function resolveSetupTarget({ platformRoot, env = process.env, cwd = process.cwd(), exists: exists2 = fs.existsSync }) {
26618
+ const explicit = env.HS_SETUP_TARGET?.trim();
26619
+ if (explicit) return path2.resolve(explicit);
26620
+ if (isInstalledPackageRoot(platformRoot, exists2)) return findProjectRoot(cwd);
26621
+ return path2.resolve(platformRoot);
26622
+ }
26586
26623
  function invokedDirectly(importMetaUrl, selfBasename, argv1 = process.argv[1]) {
26587
26624
  try {
26588
26625
  if (!argv1) return false;
@@ -29537,7 +29574,7 @@ function timeoutFor(entry) {
29537
29574
 
29538
29575
  // scripts/install-cursor-hooks.mjs
29539
29576
  var PLATFORM_ROOT = path6.resolve(path6.dirname(fileURLToPath4(import.meta.url)), "..");
29540
- var REPO_ROOT = process.env.HS_SETUP_TARGET?.trim() ? path6.resolve(process.env.HS_SETUP_TARGET.trim()) : PLATFORM_ROOT;
29577
+ var REPO_ROOT = resolveSetupTarget({ platformRoot: PLATFORM_ROOT });
29541
29578
  var SETTINGS_PATH = path6.join(REPO_ROOT, ".cursor", "hooks.json");
29542
29579
  function cursorKitFor(targetRoot, platformRoot = PLATFORM_ROOT) {
29543
29580
  const customer = !samePlatformPath(targetRoot, platformRoot);