heyspex 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +10 -0
  2. package/bin/hs-vault-binding.mjs +157 -0
  3. package/bin/hs.mjs +99 -81
  4. package/bin/spexcode.mjs +91 -0
  5. package/dev-factory/engine/worktree.mjs +36 -7
  6. package/dev-factory/lib/epic-detect.mjs +2 -2
  7. package/dev-factory/lib/fleet-readiness.mjs +3 -5
  8. package/dev-factory/lib/fleet-state.mjs +2 -2
  9. package/dev-factory/lib/sufficiency-score.mjs +3 -5
  10. package/dist/cli/hs-check.mjs +76 -46
  11. package/dist/cli/hs-claim.mjs +63 -33
  12. package/dist/cli/hs-connect.mjs +520 -214
  13. package/dist/cli/hs-defer.mjs +161 -19
  14. package/dist/cli/hs-dev.mjs +11 -5
  15. package/dist/cli/hs-env.mjs +187 -161
  16. package/dist/cli/hs-index-rebuild.mjs +123 -78
  17. package/dist/cli/hs-index-reconcile.mjs +203 -107
  18. package/dist/cli/hs-init.mjs +426 -179
  19. package/dist/cli/hs-install.mjs +759 -437
  20. package/dist/cli/hs-join.mjs +590 -383
  21. package/dist/cli/hs-vault-pull.mjs +359 -254
  22. package/dist/cli/hs-vault-push.mjs +151 -113
  23. package/dist/cli/hs-vault.mjs +271 -104
  24. package/dist/cli/hs-workspace.mjs +90 -57
  25. package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +178 -91
  26. package/docs/copilot-plugin-install.md +189 -162
  27. package/docs/driving-heyspex-from-copilot.md +174 -165
  28. package/docs/mcp-client-config.md +93 -25
  29. package/package.json +255 -249
  30. package/plugins/heyspex/.mcp.json +3 -4
  31. package/plugins/heyspex/README.md +323 -309
  32. package/plugins/heyspex/bin/launch-mcp.mjs +21 -7
  33. package/plugins/heyspex/mcp.json +12 -13
  34. package/scripts/check-mcp-freshness.mjs +6 -4
  35. package/scripts/claude-hooks/_intent-classifier-router.mjs +518 -456
  36. package/scripts/claude-hooks/intent-classifier-trigger.mjs +305 -279
  37. package/scripts/claude-hooks/research-method-guard.mjs +2 -2
  38. package/scripts/claude-hooks/wip-checkpoint.mjs +33 -9
  39. package/scripts/claude-statusline/fonts/HeySpexIcons.ttf +0 -0
  40. package/scripts/claude-statusline/statusline-command.mjs +1058 -0
  41. package/scripts/dev-server-status.mjs +3 -2
  42. package/scripts/dev-server-stop.mjs +3 -2
  43. package/scripts/hs-capture-cli.mjs +2 -2
  44. package/scripts/hs-settings.mjs +2 -2
  45. package/scripts/hs-view.mjs +3 -2
  46. package/scripts/install-copilot-hooks.mjs +318 -0
  47. package/scripts/lib/copilot-cli.mjs +108 -0
  48. package/scripts/lib/customer-kit.mjs +223 -30
  49. package/scripts/lib/dev-mode.mjs +28 -2
  50. package/scripts/lib/shared-path-helpers.mjs +32 -0
  51. package/scripts/lib/standalone-doctor.mjs +131 -32
  52. package/scripts/wip-ledger-health.mjs +3 -2
  53. package/spexharness/src/gates/vault-mirror-catchup.mjs +113 -24
  54. package/spexharness/src/gates/vault-mirror-catchup.runner.mjs +52 -0
  55. package/spexharness/src/intent-directive.mjs +839 -626
  56. package/spexharness/src/rule-table.mjs +1 -1
  57. package/vendor/heyspex-mcp/cli-verbs.mjs +16 -2
  58. package/vendor/heyspex-mcp/cli.mjs +20 -4
  59. package/vendor/heyspex-mcp/console-fetch.mjs +15 -3
  60. package/vendor/heyspex-mcp/dev-tools.mjs +11 -4
  61. package/vendor/heyspex-mcp/events-tools.mjs +1 -1
  62. package/vendor/heyspex-mcp/factory-run-status.mjs +42 -0
  63. package/vendor/heyspex-mcp/mcp-config.mjs +4 -2
  64. package/vendor/heyspex-mcp/spec-edit-tools.mjs +26 -1
  65. package/vendor/heyspex-mcp/test-helpers.mjs +5 -2
  66. package/vendor/heyspex-mcp/tool-catalog.mjs +12 -0
  67. package/vendor/heyspex-mcp/tool-dispatch.mjs +59 -23
  68. package/vendor/heyspex-mcp/usable-dir-env.mjs +82 -0
  69. package/vendor/heyspex-mcp/vault-binding.mjs +839 -0
  70. package/vendor/heyspex-mcp/vault-resolve.mjs +353 -245
@@ -124,9 +124,9 @@ function resolveBearer({ envToken, credentialsText, accountId, legacyAccountId }
124
124
  if (entry?.mcp_token) return { token: entry.mcp_token, source: "credentials-file" };
125
125
  return { token: null, source: null };
126
126
  }
127
- function readTextOrNull(read, path2) {
127
+ function readTextOrNull(read, path3) {
128
128
  try {
129
- const raw = read(path2);
129
+ const raw = read(path3);
130
130
  return typeof raw === "string" ? raw : null;
131
131
  } catch {
132
132
  return null;
@@ -169,8 +169,8 @@ var init_credentials = __esm({
169
169
  });
170
170
 
171
171
  // scripts/lib/dev-mode.mjs
172
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
173
- import { homedir as homedir7 } from "node:os";
172
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
173
+ import { homedir as homedir8 } from "node:os";
174
174
  import { join as join8 } from "node:path";
175
175
  function devModeFilePath(homeDir = heyspexHome()) {
176
176
  return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
@@ -225,6 +225,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultRea
225
225
  return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
226
226
  }
227
227
  }
228
+ function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
229
+ if (source !== "local-override") return "not-applicable";
230
+ if (!cloudKnown) return "unverified";
231
+ return staleAgainstCloud ? "stale" : "in-sync";
232
+ }
228
233
  function resolveDevMode({
229
234
  env = process.env,
230
235
  homeDir = heyspexHome(),
@@ -264,6 +269,7 @@ function resolveDevMode({
264
269
  const semantics = deriveSemantics(mode, reliance);
265
270
  const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
266
271
  const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
272
+ const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
267
273
  return {
268
274
  mode,
269
275
  reliance: semantics.reliance,
@@ -281,7 +287,8 @@ function resolveDevMode({
281
287
  present: local.record !== null,
282
288
  record: local.record,
283
289
  parseError: local.parseError,
284
- staleAgainstCloud
290
+ staleAgainstCloud,
291
+ cloudCheck
285
292
  },
286
293
  cloudSetting: { known: cloudKnown, value: cloudValue }
287
294
  };
@@ -295,12 +302,12 @@ var init_dev_mode = __esm({
295
302
  CLOUD_CONSOLE_URL = "https://app.heyspex.com";
296
303
  LOCAL_CONSOLE_URL = "http://localhost:3000";
297
304
  DEV_MODE_FILENAME = "dev-mode.json";
298
- defaultReadFile = (p) => readFileSync7(p, "utf8");
305
+ defaultReadFile = (p) => readFileSync8(p, "utf8");
299
306
  }
300
307
  });
301
308
 
302
309
  // spex-console/bin/hs-claim.ts
303
- import { readFileSync as readFileSync9 } from "node:fs";
310
+ import { readFileSync as readFileSync10 } from "node:fs";
304
311
  import process3 from "node:process";
305
312
 
306
313
  // spex-console/src/lib/work-stages.ts
@@ -344,7 +351,7 @@ function resolveAgentIdentity(argAgent, argSession) {
344
351
  }
345
352
 
346
353
  // spex-console/src/lib/cli/control-shared.ts
347
- import path from "node:path";
354
+ import path2 from "node:path";
348
355
 
349
356
  // scripts/lib/hs-command.mjs
350
357
  import { existsSync } from "node:fs";
@@ -434,8 +441,8 @@ function hsCommand(args = "", options = {}) {
434
441
  // vendor/heyspex-mcp/console-fetch.mjs
435
442
  init_heyspex_home();
436
443
  init_credentials();
437
- import { readFileSync as readFileSync5 } from "node:fs";
438
- import { homedir as homedir5 } from "node:os";
444
+ import { readFileSync as readFileSync6 } from "node:fs";
445
+ import { homedir as homedir6 } from "node:os";
439
446
  import { join as join6 } from "node:path";
440
447
 
441
448
  // vendor/heyspex-mcp/session-owner.mjs
@@ -460,14 +467,14 @@ var VAULT_LESS_ENDPOINTS = Object.freeze([
460
467
  "GET /api/workspaces",
461
468
  "* /api/admin/dev-mode"
462
469
  ]);
463
- function pathnameOf(path2) {
464
- const raw = String(path2 ?? "").split("?")[0].split("#")[0];
470
+ function pathnameOf(path3) {
471
+ const raw = String(path3 ?? "").split("?")[0].split("#")[0];
465
472
  const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
466
473
  return noTrailing || "/";
467
474
  }
468
- function isVaultLessEndpoint(method, path2) {
475
+ function isVaultLessEndpoint(method, path3) {
469
476
  const m = String(method || "GET").toUpperCase();
470
- const pathname = pathnameOf(path2);
477
+ const pathname = pathnameOf(path3);
471
478
  return VAULT_LESS_ENDPOINTS.some((row) => {
472
479
  const [rowMethod, rowPath] = row.split(" ");
473
480
  return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
@@ -487,13 +494,36 @@ import {
487
494
  import { basename, join as join4, isAbsolute as isAbsolute2 } from "node:path";
488
495
 
489
496
  // scripts/lib/shared-path-helpers.mjs
497
+ import { realpathSync } from "node:fs";
490
498
  import { isAbsolute, resolve as resolve2, sep } from "node:path";
499
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
491
500
 
492
501
  // vendor/heyspex-mcp/vault-resolve.mjs
493
502
  init_heyspex_home();
494
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
495
- import { homedir as homedir4, tmpdir } from "node:os";
496
- import { dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
503
+ import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
504
+ import { homedir as homedir5, tmpdir } from "node:os";
505
+
506
+ // vendor/heyspex-mcp/usable-dir-env.mjs
507
+ import { statSync } from "node:fs";
508
+ import { isAbsolute as isAbsolute3 } from "node:path";
509
+
510
+ // vendor/heyspex-mcp/vault-resolve.mjs
511
+ import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
512
+
513
+ // vendor/heyspex-mcp/vault-binding.mjs
514
+ import {
515
+ copyFileSync,
516
+ existsSync as existsSync3,
517
+ mkdirSync as mkdirSync3,
518
+ readFileSync as readFileSync4,
519
+ realpathSync as realpathSync2,
520
+ renameSync as renameSync3,
521
+ statSync as statSync2,
522
+ unlinkSync,
523
+ writeFileSync as writeFileSync3
524
+ } from "node:fs";
525
+ import { homedir as homedir4 } from "node:os";
526
+ import path from "node:path";
497
527
 
498
528
  // vendor/heyspex-mcp/console-fetch.mjs
499
529
  var VAULT_NOT_REGISTERED = "VAULT_NOT_REGISTERED";
@@ -525,7 +555,7 @@ function isCloudVaultId(value) {
525
555
  var MARKER_VAULT_ID_RE = /^vault_id:[ \t]*["']?([^"'\r\n#]+)/m;
526
556
  function readMarkerVaultId(repoRootAbs, { readFile } = {}) {
527
557
  if (typeof repoRootAbs !== "string" || !repoRootAbs.trim()) return null;
528
- const read = readFile ?? ((p) => readFileSync5(p, "utf8"));
558
+ const read = readFile ?? ((p) => readFileSync6(p, "utf8"));
529
559
  try {
530
560
  const raw = read(join6(repoRootAbs, "heyspex.yaml"));
531
561
  const value = String(raw ?? "").match(MARKER_VAULT_ID_RE)?.[1]?.trim() ?? "";
@@ -547,17 +577,17 @@ var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMAT
547
577
  init_credentials();
548
578
 
549
579
  // spex-console/src/lib/vault-registry/registry-file.ts
550
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
580
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
551
581
  import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
552
582
 
553
583
  // spex-console/src/lib/heyspex-home.ts
554
- import { homedir as homedir6 } from "node:os";
584
+ import { homedir as homedir7 } from "node:os";
555
585
  function heyspexHome2(env = process.env) {
556
586
  const override = env?.HEYSPEX_HOME;
557
587
  if (typeof override === "string" && override.trim().length > 0) {
558
588
  return override.trim();
559
589
  }
560
- return homedir6();
590
+ return homedir7();
561
591
  }
562
592
 
563
593
  // spex-console/src/lib/vault-registry/types.ts
@@ -679,9 +709,9 @@ function parseVaultRegistryFile(json) {
679
709
  }
680
710
  function readRegistryJson(opts) {
681
711
  const filePath = vaultRegistryFilePath(opts);
682
- if (!existsSync4(filePath)) return { kind: "missing" };
712
+ if (!existsSync5(filePath)) return { kind: "missing" };
683
713
  try {
684
- const raw = readFileSync6(filePath, "utf8");
714
+ const raw = readFileSync7(filePath, "utf8");
685
715
  const json = JSON.parse(raw);
686
716
  if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
687
717
  return { kind: "ok", raw, json };
@@ -816,11 +846,11 @@ function bearerHeaderFor(consoleUrl, opts = {}) {
816
846
  return {};
817
847
  }
818
848
  }
819
- function scopeHeadersFor(scope, method, path2) {
849
+ function scopeHeadersFor(scope, method, path3) {
820
850
  if (scope === "none") {
821
- if (!isVaultLessEndpoint(method, path2)) {
851
+ if (!isVaultLessEndpoint(method, path3)) {
822
852
  throw new Error(
823
- `controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path2} is vault-scoped and must name { accountId, vaultId }.`
853
+ `controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path3} is vault-scoped and must name { accountId, vaultId }.`
824
854
  );
825
855
  }
826
856
  return {};
@@ -914,7 +944,7 @@ function parseTtl(token) {
914
944
  return { value: n };
915
945
  }
916
946
  function normalizeRoot(p) {
917
- return path.resolve(p).replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
947
+ return path2.resolve(p).replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
918
948
  }
919
949
  function resolveControlScope(input) {
920
950
  const target = normalizeRoot(input.vaultRoot);
@@ -1184,8 +1214,8 @@ ${lines}`,
1184
1214
  return writeFailed(res2.status, res2.body);
1185
1215
  }
1186
1216
  const releaser = resolveAgentIdentity(args.agent, args.session).agent;
1187
- const path2 = `/api/control/active-claims/${encodeURIComponent(args.specId)}?agent=${encodeURIComponent(releaser)}`;
1188
- const res = await controlRequest(deps, { method: "DELETE", path: path2 });
1217
+ const path3 = `/api/control/active-claims/${encodeURIComponent(args.specId)}?agent=${encodeURIComponent(releaser)}`;
1218
+ const res = await controlRequest(deps, { method: "DELETE", path: path3 });
1189
1219
  if (!res.ok) return res.error;
1190
1220
  if (res.status === 200) {
1191
1221
  return {
@@ -1245,8 +1275,8 @@ var HELP = [
1245
1275
  // scripts/lib/console-url-resolver.mjs
1246
1276
  init_heyspex_home();
1247
1277
  init_dev_mode();
1248
- import { readFileSync as readFileSync8 } from "node:fs";
1249
- import { homedir as homedir8 } from "node:os";
1278
+ import { readFileSync as readFileSync9 } from "node:fs";
1279
+ import { homedir as homedir9 } from "node:os";
1250
1280
  import { join as join9 } from "node:path";
1251
1281
  var DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
1252
1282
  function trimTrailingSlash(url) {
@@ -1284,7 +1314,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile = default
1284
1314
  }
1285
1315
  }
1286
1316
  function defaultReadFile2(p) {
1287
- return readFileSync8(p, "utf8");
1317
+ return readFileSync9(p, "utf8");
1288
1318
  }
1289
1319
  function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery: readDiscovery2 = defaultReadFile2 } = {}) {
1290
1320
  if (!repoRoot) return null;
@@ -1411,7 +1441,7 @@ function runEntry(name, main2, options = {}) {
1411
1441
  // spex-console/bin/hs-claim.ts
1412
1442
  function readDiscovery(p) {
1413
1443
  try {
1414
- return readFileSync9(p, "utf-8");
1444
+ return readFileSync10(p, "utf-8");
1415
1445
  } catch {
1416
1446
  return null;
1417
1447
  }