heyspex 0.2.0 → 0.2.2
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.
- package/.claude-plugin/marketplace.json +14 -0
- package/bin/hs.mjs +181 -15
- package/dist/cli/hs-check.mjs +88 -46
- package/dist/cli/hs-claim.mjs +84 -39
- package/dist/cli/hs-connect.mjs +583 -129
- package/dist/cli/hs-defer.mjs +251 -30
- package/dist/cli/hs-dev.mjs +20 -8
- package/dist/cli/hs-env.mjs +62 -34
- package/dist/cli/hs-index-rebuild.mjs +288 -83
- package/dist/cli/hs-index-reconcile.mjs +397 -111
- package/dist/cli/hs-init.mjs +2143 -91
- package/dist/cli/hs-install.mjs +628 -106
- package/dist/cli/hs-join.mjs +686 -236
- package/dist/cli/hs-vault-pull.mjs +3189 -179
- package/dist/cli/hs-vault-push.mjs +6433 -0
- package/dist/cli/hs-vault.mjs +396 -178
- package/dist/cli/hs-workspace.mjs +289 -151
- package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -28
- package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -62
- package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -29
- package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -28
- package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -29
- package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -70
- package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -35
- package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -27
- package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -23
- package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -23
- package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -26
- package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -32
- package/dist/cli-templates/harness-commands/claude/prototype.md +91 -91
- package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +22 -22
- package/dist/cli-templates/harness-commands/cursor/hs-capture.md +56 -56
- package/dist/cli-templates/harness-commands/cursor/hs-defer.md +23 -23
- package/dist/cli-templates/harness-commands/cursor/hs-ground.md +24 -22
- package/dist/cli-templates/harness-commands/cursor/hs-next.md +23 -23
- package/dist/cli-templates/harness-commands/cursor/hs-promote.md +64 -64
- package/dist/cli-templates/harness-commands/cursor/hs-push.md +29 -29
- package/dist/cli-templates/harness-commands/cursor/hs-qc.md +21 -21
- package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +17 -17
- package/dist/cli-templates/harness-commands/cursor/hs-review.md +17 -17
- package/dist/cli-templates/harness-commands/cursor/hs-view.md +24 -22
- package/dist/cli-templates/harness-commands/cursor/kill-ports.md +26 -26
- package/dist/cli-templates/harness-commands/cursor/prototype.md +86 -86
- package/docs/copilot-plugin-install.md +177 -0
- package/docs/driving-heyspex-from-copilot.md +165 -0
- package/package.json +21 -2
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -12
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -15
- package/plugins/heyspex/README.md +312 -0
- package/plugins/heyspex/bin/run-hook.mjs +20 -2
- package/plugins/heyspex/hooks/cursor-hooks.json +81 -0
- package/plugins/heyspex/hooks/hooks.json +94 -37
- package/scripts/claude-hooks/_intent-classifier-router.mjs +456 -410
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +5 -0
- package/scripts/claude-hooks/_run.mjs +316 -23
- package/scripts/claude-hooks/capture-execution-gate.mjs +51 -0
- package/scripts/claude-hooks/claim-liveness-shared.mjs +2 -0
- package/scripts/claude-hooks/diagnose-capture-reminder.mjs +11 -0
- package/scripts/claude-hooks/directive-obligation-gate.mjs +11 -0
- package/scripts/claude-hooks/inject-communication-style.mjs +20 -0
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +279 -247
- package/scripts/claude-hooks/session-end.mjs +2 -0
- package/scripts/claude-hooks/vault-mirror-catchup.mjs +11 -0
- package/scripts/claude-hooks/vault-write-tier-guard.mjs +11 -0
- package/scripts/claude-hooks/wip-checkpoint.mjs +5 -1
- package/scripts/cursor-hooks/manifest.mjs +46 -0
- package/scripts/heyspex-mode.mjs +20 -0
- package/scripts/hs-mode.mjs +1 -1
- package/scripts/hs-view.mjs +180 -17
- package/scripts/install-claude-code-hooks.mjs +3 -1
- package/scripts/install-copilot-hooks.mjs +288 -0
- package/scripts/lib/console-url-resolver.mjs +3 -2
- package/scripts/lib/customer-kit.mjs +327 -10
- package/scripts/lib/dev-mode.mjs +7 -6
- package/scripts/lib/directive-obligations.mjs +349 -0
- package/scripts/lib/hook-dispositions.mjs +210 -0
- package/scripts/lib/hook-vault-header.mjs +30 -0
- package/scripts/lib/hs-source-branch.mjs +196 -0
- package/scripts/lib/hs-source-log.mjs +2 -1
- package/scripts/lib/hs-source-preflight.mjs +18 -4
- package/scripts/lib/hs-source-push.mjs +386 -356
- package/scripts/lib/window-placement.mjs +109 -0
- package/spexharness/src/adapters/claude-code.mjs +11 -3
- package/spexharness/src/adapters/copilot.mjs +236 -0
- package/spexharness/src/adapters/detect-harness.mjs +34 -0
- package/spexharness/src/gates/_shell-write-targets.mjs +77 -0
- package/spexharness/src/gates/deep-research-cost-gate.mjs +4 -3
- package/spexharness/src/gates/diagnose-capture-reminder.mjs +112 -0
- package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -0
- package/spexharness/src/gates/directive-obligation-gate.mjs +183 -0
- package/spexharness/src/gates/vault-mirror-catchup.mjs +124 -0
- package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +125 -0
- package/spexharness/src/gates/vault-write-tier-guard.mjs +42 -0
- package/spexharness/src/intent-classifier.mjs +4 -2
- package/spexharness/src/intent-directive.mjs +626 -541
- package/spexharness/src/rule-table.mjs +31 -1
- package/vendor/heyspex-mcp/account-switch.mjs +6 -5
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +31 -3
- package/vendor/heyspex-mcp/capture-console-client.mjs +37 -2
- package/vendor/heyspex-mcp/capture-focus.mjs +26 -2
- package/vendor/heyspex-mcp/capture-outbox.mjs +10 -4
- package/vendor/heyspex-mcp/capture-sources.mjs +195 -0
- package/vendor/heyspex-mcp/capture-tools.mjs +56 -11
- package/vendor/heyspex-mcp/claim-tools.mjs +25 -2
- package/vendor/heyspex-mcp/cli-verbs.mjs +19 -21
- package/vendor/heyspex-mcp/cli.mjs +7 -1
- package/vendor/heyspex-mcp/close-branch-report.mjs +102 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +197 -7
- package/vendor/heyspex-mcp/console-fetch.mjs +192 -28
- package/vendor/heyspex-mcp/console-transport.mjs +7 -1
- package/vendor/heyspex-mcp/credentials.mjs +9 -8
- package/vendor/heyspex-mcp/dev-tools.mjs +7 -0
- package/vendor/heyspex-mcp/filing-tools.mjs +35 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +2 -1
- package/vendor/heyspex-mcp/folder-trust-store.mjs +2 -1
- package/vendor/heyspex-mcp/http-transport.mjs +2 -0
- package/vendor/heyspex-mcp/list-tools.mjs +1 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +14 -1
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -523
- package/vendor/heyspex-mcp/obligation-backstop.mjs +103 -0
- package/vendor/heyspex-mcp/occupied-keys.mjs +1 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +19 -2
- package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -1708
- package/vendor/heyspex-mcp/plan-push.mjs +3 -2
- package/vendor/heyspex-mcp/preflight-gate.mjs +1 -0
- package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -325
- package/vendor/heyspex-mcp/procedure-tools.mjs +1181 -1159
- package/vendor/heyspex-mcp/promote-capture.mjs +2 -0
- package/vendor/heyspex-mcp/promote-research.mjs +6 -0
- package/vendor/heyspex-mcp/read-path.mjs +162 -6
- package/vendor/heyspex-mcp/request-context.mjs +25 -1
- package/vendor/heyspex-mcp/research-handlers.mjs +6 -4
- package/vendor/heyspex-mcp/roadmap-default.mjs +70 -0
- package/vendor/heyspex-mcp/session-owner.mjs +3 -2
- package/vendor/heyspex-mcp/session-tools.mjs +10 -0
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +8 -1
- package/vendor/heyspex-mcp/split-research.mjs +3 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +18 -1
- package/vendor/heyspex-mcp/summary-write-tools.mjs +5 -1
- package/vendor/heyspex-mcp/test-helpers.mjs +18 -3
- package/vendor/heyspex-mcp/tool-catalog.mjs +7 -1
- package/vendor/heyspex-mcp/tool-dispatch.mjs +18 -0
- package/vendor/heyspex-mcp/vault-io.mjs +720 -724
- package/vendor/heyspex-mcp/vault-resolve.mjs +197 -9
- package/vendor/heyspex-mcp/whoami.mjs +2 -1
- package/vendor/heyspex-mcp/write-outbox.mjs +512 -0
|
@@ -59,11 +59,26 @@ var init_hs_command = __esm({
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
// scripts/lib/heyspex-home.mjs
|
|
63
|
+
import { homedir } from "node:os";
|
|
64
|
+
function heyspexHome(env = process.env) {
|
|
65
|
+
const override = env?.HEYSPEX_HOME;
|
|
66
|
+
if (typeof override === "string" && override.trim().length > 0) {
|
|
67
|
+
return override.trim();
|
|
68
|
+
}
|
|
69
|
+
return homedir();
|
|
70
|
+
}
|
|
71
|
+
var init_heyspex_home = __esm({
|
|
72
|
+
"scripts/lib/heyspex-home.mjs"() {
|
|
73
|
+
"use strict";
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
62
77
|
// vendor/heyspex-mcp/credentials.mjs
|
|
63
78
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
64
|
-
import os, { homedir } from "node:os";
|
|
79
|
+
import os, { homedir as homedir2 } from "node:os";
|
|
65
80
|
import { dirname as dirname2, join as join2, resolve } from "node:path";
|
|
66
|
-
function credentialsFilePath(homeDir =
|
|
81
|
+
function credentialsFilePath(homeDir = heyspexHome()) {
|
|
67
82
|
return join2(homeDir, ".heyspex", "credentials.yaml");
|
|
68
83
|
}
|
|
69
84
|
function parseMcpToken(raw) {
|
|
@@ -159,7 +174,7 @@ function readLegacyConfigAccountId(read, homeDir) {
|
|
|
159
174
|
const value = raw?.match(CONFIG_ACCOUNT_ID_RE)?.[1]?.trim() ?? "";
|
|
160
175
|
return value || null;
|
|
161
176
|
}
|
|
162
|
-
function readCurrentAccountId({ homeDir =
|
|
177
|
+
function readCurrentAccountId({ homeDir = heyspexHome(), readFile } = {}) {
|
|
163
178
|
const read = readFile ?? ((p) => readFileSync(p, "utf8"));
|
|
164
179
|
const store = parseCredentialsStore(readTextOrNull(read, credentialsFilePath(homeDir)));
|
|
165
180
|
if (store.schemaVersion === 2 && store.currentAccountId && store.currentAccountId !== LEGACY_ACCOUNT_ID) {
|
|
@@ -172,6 +187,7 @@ var MCP_TOKEN_LINE_RE, CREDENTIALS_SCHEMA_VERSION, LEGACY_ACCOUNT_ID, ACCOUNT_FI
|
|
|
172
187
|
var init_credentials = __esm({
|
|
173
188
|
"vendor/heyspex-mcp/credentials.mjs"() {
|
|
174
189
|
"use strict";
|
|
190
|
+
init_heyspex_home();
|
|
175
191
|
MCP_TOKEN_LINE_RE = /^mcp_token:[ \t]*["']?([^"'\r\n#]+)/m;
|
|
176
192
|
CREDENTIALS_SCHEMA_VERSION = 2;
|
|
177
193
|
LEGACY_ACCOUNT_ID = "legacy";
|
|
@@ -183,9 +199,9 @@ var init_credentials = __esm({
|
|
|
183
199
|
|
|
184
200
|
// vendor/heyspex-mcp/session-owner.mjs
|
|
185
201
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
186
|
-
import { homedir as
|
|
202
|
+
import { homedir as homedir3 } from "node:os";
|
|
187
203
|
import { join as join3 } from "node:path";
|
|
188
|
-
function resolveSessionOwnerUncached({ env = process.env, homeDir =
|
|
204
|
+
function resolveSessionOwnerUncached({ env = process.env, homeDir = heyspexHome(), readFile } = {}) {
|
|
189
205
|
const fromEnv = typeof env?.HS_ACCOUNT_ID === "string" ? env.HS_ACCOUNT_ID.trim() : "";
|
|
190
206
|
if (fromEnv) return fromEnv;
|
|
191
207
|
return readCurrentAccountId({ homeDir, readFile });
|
|
@@ -193,6 +209,7 @@ function resolveSessionOwnerUncached({ env = process.env, homeDir = homedir2(),
|
|
|
193
209
|
var init_session_owner = __esm({
|
|
194
210
|
"vendor/heyspex-mcp/session-owner.mjs"() {
|
|
195
211
|
"use strict";
|
|
212
|
+
init_heyspex_home();
|
|
196
213
|
init_credentials();
|
|
197
214
|
}
|
|
198
215
|
});
|
|
@@ -227,20 +244,47 @@ var init_vault_less_endpoints = __esm({
|
|
|
227
244
|
}
|
|
228
245
|
});
|
|
229
246
|
|
|
247
|
+
// scripts/lib/shared-path-helpers.mjs
|
|
248
|
+
import { isAbsolute, resolve as resolve2, sep } from "node:path";
|
|
249
|
+
var init_shared_path_helpers = __esm({
|
|
250
|
+
"scripts/lib/shared-path-helpers.mjs"() {
|
|
251
|
+
"use strict";
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// vendor/heyspex-mcp/write-outbox.mjs
|
|
256
|
+
import {
|
|
257
|
+
readdirSync,
|
|
258
|
+
readFileSync as readFileSync3,
|
|
259
|
+
writeFileSync as writeFileSync2,
|
|
260
|
+
mkdirSync as mkdirSync2,
|
|
261
|
+
existsSync as existsSync2,
|
|
262
|
+
renameSync as renameSync2,
|
|
263
|
+
rmSync
|
|
264
|
+
} from "node:fs";
|
|
265
|
+
import { basename, join as join4, isAbsolute as isAbsolute2 } from "node:path";
|
|
266
|
+
var init_write_outbox = __esm({
|
|
267
|
+
"vendor/heyspex-mcp/write-outbox.mjs"() {
|
|
268
|
+
"use strict";
|
|
269
|
+
init_shared_path_helpers();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
230
273
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
231
|
-
import { existsSync as
|
|
232
|
-
import { homedir as
|
|
233
|
-
import { dirname as dirname3, join as
|
|
274
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
275
|
+
import { homedir as homedir4, tmpdir } from "node:os";
|
|
276
|
+
import { dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
|
|
234
277
|
var init_vault_resolve = __esm({
|
|
235
278
|
"vendor/heyspex-mcp/vault-resolve.mjs"() {
|
|
236
279
|
"use strict";
|
|
280
|
+
init_heyspex_home();
|
|
237
281
|
}
|
|
238
282
|
});
|
|
239
283
|
|
|
240
284
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
241
|
-
import { readFileSync as
|
|
242
|
-
import { homedir as
|
|
243
|
-
import { join as
|
|
285
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
286
|
+
import { homedir as homedir5 } from "node:os";
|
|
287
|
+
import { join as join6 } from "node:path";
|
|
244
288
|
function isCloudVaultId(value) {
|
|
245
289
|
return typeof value === "string" && CLOUD_VAULT_ID_RE.test(value.trim());
|
|
246
290
|
}
|
|
@@ -248,16 +292,33 @@ var CLOUD_VAULT_ID_RE, SCOPE_REFUSAL_CODES;
|
|
|
248
292
|
var init_console_fetch = __esm({
|
|
249
293
|
"vendor/heyspex-mcp/console-fetch.mjs"() {
|
|
250
294
|
"use strict";
|
|
295
|
+
init_heyspex_home();
|
|
251
296
|
init_credentials();
|
|
252
297
|
init_session_owner();
|
|
253
298
|
init_vault_less_endpoints();
|
|
254
299
|
init_hs_command();
|
|
300
|
+
init_write_outbox();
|
|
255
301
|
init_vault_resolve();
|
|
256
302
|
CLOUD_VAULT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
257
303
|
SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMATCH", "VAULT_NOT_REGISTERED"]);
|
|
258
304
|
}
|
|
259
305
|
});
|
|
260
306
|
|
|
307
|
+
// spex-console/src/lib/heyspex-home.ts
|
|
308
|
+
import { homedir as homedir6 } from "node:os";
|
|
309
|
+
function heyspexHome2(env = process.env) {
|
|
310
|
+
const override = env?.HEYSPEX_HOME;
|
|
311
|
+
if (typeof override === "string" && override.trim().length > 0) {
|
|
312
|
+
return override.trim();
|
|
313
|
+
}
|
|
314
|
+
return homedir6();
|
|
315
|
+
}
|
|
316
|
+
var init_heyspex_home2 = __esm({
|
|
317
|
+
"spex-console/src/lib/heyspex-home.ts"() {
|
|
318
|
+
"use strict";
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
261
322
|
// spex-console/src/lib/vault-registry/types.ts
|
|
262
323
|
function emptyVaultRegistry() {
|
|
263
324
|
return {
|
|
@@ -278,14 +339,13 @@ var init_types = __esm({
|
|
|
278
339
|
});
|
|
279
340
|
|
|
280
341
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
281
|
-
import { existsSync as
|
|
282
|
-
import {
|
|
283
|
-
import { dirname as dirname4, join as join6, resolve as resolve3 } from "node:path";
|
|
342
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
343
|
+
import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
|
|
284
344
|
function vaultRegistryDir(opts = {}) {
|
|
285
|
-
return opts.registryDir ??
|
|
345
|
+
return opts.registryDir ?? join7(heyspexHome2(), ".heyspex");
|
|
286
346
|
}
|
|
287
347
|
function vaultRegistryFilePath(opts = {}) {
|
|
288
|
-
return
|
|
348
|
+
return join7(vaultRegistryDir(opts), "registry.json");
|
|
289
349
|
}
|
|
290
350
|
function isNonEmptyString(v) {
|
|
291
351
|
return typeof v === "string" && v.trim().length > 0;
|
|
@@ -386,9 +446,9 @@ function parseVaultRegistryFile(json) {
|
|
|
386
446
|
}
|
|
387
447
|
function readRegistryJson(opts) {
|
|
388
448
|
const filePath = vaultRegistryFilePath(opts);
|
|
389
|
-
if (!
|
|
449
|
+
if (!existsSync4(filePath)) return { kind: "missing" };
|
|
390
450
|
try {
|
|
391
|
-
const raw =
|
|
451
|
+
const raw = readFileSync6(filePath, "utf8");
|
|
392
452
|
const json = JSON.parse(raw);
|
|
393
453
|
if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
|
|
394
454
|
return { kind: "ok", raw, json };
|
|
@@ -417,17 +477,18 @@ function loadVaultRegistry(opts = {}) {
|
|
|
417
477
|
var init_registry_file = __esm({
|
|
418
478
|
"spex-console/src/lib/vault-registry/registry-file.ts"() {
|
|
419
479
|
"use strict";
|
|
480
|
+
init_heyspex_home2();
|
|
420
481
|
init_types();
|
|
421
482
|
init_types();
|
|
422
483
|
}
|
|
423
484
|
});
|
|
424
485
|
|
|
425
486
|
// scripts/lib/dev-mode.mjs
|
|
426
|
-
import { mkdirSync as
|
|
427
|
-
import { homedir as
|
|
428
|
-
import { join as
|
|
429
|
-
function devModeFilePath(homeDir =
|
|
430
|
-
return
|
|
487
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
488
|
+
import { homedir as homedir7 } from "node:os";
|
|
489
|
+
import { join as join8 } from "node:path";
|
|
490
|
+
function devModeFilePath(homeDir = heyspexHome()) {
|
|
491
|
+
return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
|
|
431
492
|
}
|
|
432
493
|
function normalizeMode(raw) {
|
|
433
494
|
const v = String(raw ?? "").trim().toLowerCase();
|
|
@@ -451,7 +512,7 @@ function deriveSemantics(mode, reliance) {
|
|
|
451
512
|
vaultWriteMode: local ? "local" : "cloud"
|
|
452
513
|
};
|
|
453
514
|
}
|
|
454
|
-
function readLocalDevModeRecord({ homeDir =
|
|
515
|
+
function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultReadFile } = {}) {
|
|
455
516
|
let raw;
|
|
456
517
|
try {
|
|
457
518
|
raw = readFile(devModeFilePath(homeDir));
|
|
@@ -481,7 +542,7 @@ function readLocalDevModeRecord({ homeDir = homedir6(), readFile = defaultReadFi
|
|
|
481
542
|
}
|
|
482
543
|
function resolveDevMode({
|
|
483
544
|
env = process.env,
|
|
484
|
-
homeDir =
|
|
545
|
+
homeDir = heyspexHome(),
|
|
485
546
|
readFile = defaultReadFile,
|
|
486
547
|
cloudSetting = void 0
|
|
487
548
|
} = {}) {
|
|
@@ -544,18 +605,19 @@ var CLOUD_CONSOLE_URL, LOCAL_CONSOLE_URL, DEV_MODE_FILENAME, defaultReadFile;
|
|
|
544
605
|
var init_dev_mode = __esm({
|
|
545
606
|
"scripts/lib/dev-mode.mjs"() {
|
|
546
607
|
"use strict";
|
|
608
|
+
init_heyspex_home();
|
|
547
609
|
init_credentials();
|
|
548
610
|
CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
549
611
|
LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
550
612
|
DEV_MODE_FILENAME = "dev-mode.json";
|
|
551
|
-
defaultReadFile = (p) =>
|
|
613
|
+
defaultReadFile = (p) => readFileSync7(p, "utf8");
|
|
552
614
|
}
|
|
553
615
|
});
|
|
554
616
|
|
|
555
617
|
// scripts/lib/console-url-resolver.mjs
|
|
556
|
-
import { readFileSync as
|
|
557
|
-
import { homedir as
|
|
558
|
-
import { join as
|
|
618
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
619
|
+
import { homedir as homedir8 } from "node:os";
|
|
620
|
+
import { join as join9 } from "node:path";
|
|
559
621
|
function trimTrailingSlash(url) {
|
|
560
622
|
return url.replace(/\/+$/, "");
|
|
561
623
|
}
|
|
@@ -578,9 +640,9 @@ function warnStaleLoopbackCredentialsOnce(credentialsUrl, derivedUrl, { log }) {
|
|
|
578
640
|
} catch {
|
|
579
641
|
}
|
|
580
642
|
}
|
|
581
|
-
function readCredentialsConsoleUrl({ homeDir =
|
|
643
|
+
function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile = defaultReadFile2 } = {}) {
|
|
582
644
|
try {
|
|
583
|
-
const raw = readFile(
|
|
645
|
+
const raw = readFile(join9(homeDir, ".heyspex", "credentials.yaml"));
|
|
584
646
|
const match = String(raw ?? "").match(/^console_url:\s*["']?([^"'\r\n#]+)/m);
|
|
585
647
|
const value = match?.[1]?.trim();
|
|
586
648
|
return value && value.length > 0 ? trimTrailingSlash(value) : null;
|
|
@@ -589,12 +651,12 @@ function readCredentialsConsoleUrl({ homeDir = homedir7(), readFile = defaultRea
|
|
|
589
651
|
}
|
|
590
652
|
}
|
|
591
653
|
function defaultReadFile2(p) {
|
|
592
|
-
return
|
|
654
|
+
return readFileSync8(p, "utf8");
|
|
593
655
|
}
|
|
594
656
|
function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery: readDiscovery2 = defaultReadFile2 } = {}) {
|
|
595
657
|
if (!repoRoot) return null;
|
|
596
658
|
try {
|
|
597
|
-
const discovery = String(readDiscovery2(
|
|
659
|
+
const discovery = String(readDiscovery2(join9(repoRoot, ".heyspex", "console-url")) ?? "").trim();
|
|
598
660
|
if (discovery.length > 0) return trimTrailingSlash(discovery);
|
|
599
661
|
} catch {
|
|
600
662
|
}
|
|
@@ -614,7 +676,7 @@ function resolveModeEnvelope({ env, homeDir }) {
|
|
|
614
676
|
function resolveConsoleUrl({
|
|
615
677
|
repoRoot,
|
|
616
678
|
env = process.env,
|
|
617
|
-
homeDir =
|
|
679
|
+
homeDir = heyspexHome(),
|
|
618
680
|
strict = false,
|
|
619
681
|
devMode,
|
|
620
682
|
log = (line) => process.stderr.write(line),
|
|
@@ -647,6 +709,7 @@ var DEFAULT_CONSOLE_URL, LOOPBACK_HOSTS, staleLoopbackCredentialsWarned, Console
|
|
|
647
709
|
var init_console_url_resolver = __esm({
|
|
648
710
|
"scripts/lib/console-url-resolver.mjs"() {
|
|
649
711
|
"use strict";
|
|
712
|
+
init_heyspex_home();
|
|
650
713
|
init_dev_mode();
|
|
651
714
|
DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
|
|
652
715
|
LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]", "0.0.0.0"]);
|
|
@@ -700,13 +763,12 @@ var init_default_workspace = __esm({
|
|
|
700
763
|
});
|
|
701
764
|
|
|
702
765
|
// spex-console/src/lib/cli/vault-workspace-shared.ts
|
|
703
|
-
import { readFileSync as
|
|
704
|
-
import os2 from "node:os";
|
|
766
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
705
767
|
function resolveConfiguredAccountId(opts = {}) {
|
|
706
768
|
return resolveSessionOwnerUncached({
|
|
707
769
|
env: opts.env ?? process.env,
|
|
708
|
-
homeDir: opts.homeDir ??
|
|
709
|
-
readFile: opts.readFile ?? ((p) =>
|
|
770
|
+
homeDir: opts.homeDir ?? heyspexHome2(),
|
|
771
|
+
readFile: opts.readFile ?? ((p) => readFileSync9(p, "utf-8"))
|
|
710
772
|
});
|
|
711
773
|
}
|
|
712
774
|
function resolveCloudAccountIdOrRefuse(opts = {}) {
|
|
@@ -739,6 +801,7 @@ var CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE;
|
|
|
739
801
|
var init_vault_workspace_shared = __esm({
|
|
740
802
|
"spex-console/src/lib/cli/vault-workspace-shared.ts"() {
|
|
741
803
|
"use strict";
|
|
804
|
+
init_heyspex_home2();
|
|
742
805
|
init_session_owner();
|
|
743
806
|
init_default_workspace();
|
|
744
807
|
CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE = "refusing -- no signed-in account (no HS_ACCOUNT_ID env var, and no account_id: in ~/.heyspex/config.yaml). Run `hs login` first; nothing was written.";
|
|
@@ -1225,11 +1288,11 @@ var require_excerpt = __commonJS({
|
|
|
1225
1288
|
if (typeof opts.excerpt === "function") {
|
|
1226
1289
|
return opts.excerpt(file, opts);
|
|
1227
1290
|
}
|
|
1228
|
-
const
|
|
1229
|
-
if (
|
|
1291
|
+
const sep2 = file.data.excerpt_separator || opts.excerpt_separator;
|
|
1292
|
+
if (sep2 == null && (opts.excerpt === false || opts.excerpt == null)) {
|
|
1230
1293
|
return file;
|
|
1231
1294
|
}
|
|
1232
|
-
const delimiter2 = typeof opts.excerpt === "string" ? opts.excerpt :
|
|
1295
|
+
const delimiter2 = typeof opts.excerpt === "string" ? opts.excerpt : sep2 || opts.delimiters[0];
|
|
1233
1296
|
const idx = file.content.indexOf(delimiter2);
|
|
1234
1297
|
if (idx !== -1) {
|
|
1235
1298
|
file.excerpt = file.content.slice(0, idx);
|
|
@@ -1294,7 +1357,7 @@ var require_parse = __commonJS({
|
|
|
1294
1357
|
var require_gray_matter = __commonJS({
|
|
1295
1358
|
"node_modules/gray-matter/index.js"(exports2, module2) {
|
|
1296
1359
|
"use strict";
|
|
1297
|
-
var
|
|
1360
|
+
var fs11 = __require("fs");
|
|
1298
1361
|
var sections = require_section_matter();
|
|
1299
1362
|
var defaults = require_defaults();
|
|
1300
1363
|
var stringify = require_stringify();
|
|
@@ -1378,7 +1441,7 @@ var require_gray_matter = __commonJS({
|
|
|
1378
1441
|
return stringify(file, data, options2);
|
|
1379
1442
|
};
|
|
1380
1443
|
matter4.read = function(filepath, options2) {
|
|
1381
|
-
const str2 =
|
|
1444
|
+
const str2 = fs11.readFileSync(filepath, "utf8");
|
|
1382
1445
|
const file = matter4(str2, options2);
|
|
1383
1446
|
file.path = filepath;
|
|
1384
1447
|
return file;
|
|
@@ -1526,13 +1589,13 @@ function bareFileKeyOf(input) {
|
|
|
1526
1589
|
if (KNOWN_TYPE_LABELS.has(token)) return null;
|
|
1527
1590
|
return FILE_KEY_RE.test(token) ? token : null;
|
|
1528
1591
|
}
|
|
1529
|
-
function isEra3SpecId(id,
|
|
1592
|
+
function isEra3SpecId(id, basename2) {
|
|
1530
1593
|
const raw = String(id ?? "").trim();
|
|
1531
1594
|
if (!raw) return false;
|
|
1532
1595
|
if (parseSpecId(raw)?.era === 3) return true;
|
|
1533
1596
|
const fileKey = bareFileKeyOf(raw);
|
|
1534
1597
|
if (!fileKey) return false;
|
|
1535
|
-
const parsed = parseSpecId(
|
|
1598
|
+
const parsed = parseSpecId(basename2 ?? "");
|
|
1536
1599
|
return parsed?.era === 3 && (parsed.fileKey ?? "").toUpperCase() === fileKey;
|
|
1537
1600
|
}
|
|
1538
1601
|
var FILE_KEY_RE, KNOWN_TYPE_LABELS, RULED_FAMILY_SURNAMES, FAMILY_KEYED_TYPE_LABELS, FAMILY_KEY_SHAPE_RE;
|
|
@@ -1858,7 +1921,7 @@ var init_parser = __esm({
|
|
|
1858
1921
|
});
|
|
1859
1922
|
|
|
1860
1923
|
// dist/cli/._sql-orm-inert.cli-shim.mjs
|
|
1861
|
-
var inert, customType, sql, and, eq, isNull, inArray, bigint, bigserial, boolean, foreignKey, index, integer, jsonb, pgEnum, pgTable, primaryKey, text, timestamp, uniqueIndex;
|
|
1924
|
+
var inert, customType, sql, and, eq, isNull, desc, gt, inArray, bigint, bigserial, boolean, foreignKey, index, integer, jsonb, pgEnum, pgTable, primaryKey, text, timestamp, uniqueIndex;
|
|
1862
1925
|
var init_sql_orm_inert_cli_shim = __esm({
|
|
1863
1926
|
"dist/cli/._sql-orm-inert.cli-shim.mjs"() {
|
|
1864
1927
|
"use strict";
|
|
@@ -1881,6 +1944,8 @@ var init_sql_orm_inert_cli_shim = __esm({
|
|
|
1881
1944
|
and = inert;
|
|
1882
1945
|
eq = inert;
|
|
1883
1946
|
isNull = inert;
|
|
1947
|
+
desc = inert;
|
|
1948
|
+
gt = inert;
|
|
1884
1949
|
inArray = inert;
|
|
1885
1950
|
bigint = inert;
|
|
1886
1951
|
bigserial = inert;
|
|
@@ -1909,7 +1974,7 @@ var init_interfaces = __esm({
|
|
|
1909
1974
|
});
|
|
1910
1975
|
|
|
1911
1976
|
// spex-console/src/lib/db/schema/postgres.ts
|
|
1912
|
-
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;
|
|
1977
|
+
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;
|
|
1913
1978
|
var init_postgres = __esm({
|
|
1914
1979
|
"spex-console/src/lib/db/schema/postgres.ts"() {
|
|
1915
1980
|
"use strict";
|
|
@@ -2351,7 +2416,13 @@ var init_postgres = __esm({
|
|
|
2351
2416
|
"priority_change",
|
|
2352
2417
|
"parent_change",
|
|
2353
2418
|
"claim_acquired",
|
|
2354
|
-
"claim_released"
|
|
2419
|
+
"claim_released",
|
|
2420
|
+
// Collaborator write audit (HS-FWB6, migration 0077).
|
|
2421
|
+
"spec_created",
|
|
2422
|
+
"spec_edited",
|
|
2423
|
+
"spec_deleted",
|
|
2424
|
+
"capture_written",
|
|
2425
|
+
"wip_recorded"
|
|
2355
2426
|
]);
|
|
2356
2427
|
focusPin = pgTable(
|
|
2357
2428
|
"focus_pin",
|
|
@@ -3544,11 +3615,43 @@ var init_postgres = __esm({
|
|
|
3544
3615
|
// `owner/name`
|
|
3545
3616
|
branch: text("branch").notNull(),
|
|
3546
3617
|
commitSha: text("commit_sha").notNull(),
|
|
3547
|
-
settledAt: timestamp("settled_at", { withTimezone: true }).notNull().default(sql`now()`)
|
|
3618
|
+
settledAt: timestamp("settled_at", { withTimezone: true }).notNull().default(sql`now()`),
|
|
3619
|
+
// The pull request that carries this settlement
|
|
3620
|
+
// ([[HS-9D25-CWPX-EPIC-branch-lifecycle-open-a-pull-request]]). NULL until
|
|
3621
|
+
// the branch has gone quiet and a PR was opened or found; a write made
|
|
3622
|
+
// after its PR merged stays NULL, which is what triggers the next PR.
|
|
3623
|
+
pullRequestNumber: integer("pull_request_number"),
|
|
3624
|
+
pullRequestUrl: text("pull_request_url")
|
|
3548
3625
|
},
|
|
3549
3626
|
(table) => [
|
|
3550
3627
|
// "latest settlement for this spec" — the read path's only query.
|
|
3551
|
-
index("spec_settlements_spec_settled_idx").on(table.specId, table.settledAt.desc())
|
|
3628
|
+
index("spec_settlements_spec_settled_idx").on(table.specId, table.settledAt.desc()),
|
|
3629
|
+
// The PR sweep's only query: branches with settlements not yet in a PR.
|
|
3630
|
+
index("spec_settlements_unproposed_idx").on(table.vaultId, table.repo, table.branch).where(sql`${table.pullRequestNumber} IS NULL`)
|
|
3631
|
+
]
|
|
3632
|
+
);
|
|
3633
|
+
sessionBranchDeclarations = pgTable(
|
|
3634
|
+
"session_branch_declarations",
|
|
3635
|
+
{
|
|
3636
|
+
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
|
|
3637
|
+
vaultId: text("vault_id").notNull().default(DEFAULT_VAULT_ID),
|
|
3638
|
+
actorKey: text("actor_key").notNull(),
|
|
3639
|
+
sessionId: text("session_id").notNull(),
|
|
3640
|
+
branch: text("branch").notNull(),
|
|
3641
|
+
declaredAt: timestamp("declared_at", { withTimezone: true }).notNull().default(sql`now()`),
|
|
3642
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull()
|
|
3643
|
+
},
|
|
3644
|
+
(table) => [
|
|
3645
|
+
uniqueIndex("session_branch_declarations_uq").on(
|
|
3646
|
+
table.vaultId,
|
|
3647
|
+
table.actorKey,
|
|
3648
|
+
table.sessionId
|
|
3649
|
+
),
|
|
3650
|
+
index("session_branch_declarations_lookup_idx").on(
|
|
3651
|
+
table.vaultId,
|
|
3652
|
+
table.actorKey,
|
|
3653
|
+
table.declaredAt.desc()
|
|
3654
|
+
)
|
|
3552
3655
|
]
|
|
3553
3656
|
);
|
|
3554
3657
|
}
|
|
@@ -4465,7 +4568,7 @@ var init_satellite = __esm({
|
|
|
4465
4568
|
});
|
|
4466
4569
|
|
|
4467
4570
|
// spex-console/src/lib/vault-resolve/index.ts
|
|
4468
|
-
import { existsSync as
|
|
4571
|
+
import { existsSync as existsSync5, readFileSync as readFileSync11 } from "node:fs";
|
|
4469
4572
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4470
4573
|
import path5 from "node:path";
|
|
4471
4574
|
var init_vault_resolve2 = __esm({
|
|
@@ -4504,7 +4607,7 @@ var init_resolve_status = __esm({
|
|
|
4504
4607
|
});
|
|
4505
4608
|
|
|
4506
4609
|
// spex-console/src/lib/vault-resolve/active.ts
|
|
4507
|
-
import { existsSync as
|
|
4610
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
4508
4611
|
import path6 from "node:path";
|
|
4509
4612
|
var ACTIVE_VAULT_CACHE_KEY, cacheStore;
|
|
4510
4613
|
var init_active = __esm({
|
|
@@ -4714,8 +4817,8 @@ function singleFlightDebounce(fn, delayMs) {
|
|
|
4714
4817
|
})();
|
|
4715
4818
|
}
|
|
4716
4819
|
return function debounced() {
|
|
4717
|
-
return new Promise((
|
|
4718
|
-
const resolver = { resolve:
|
|
4820
|
+
return new Promise((resolve5, reject) => {
|
|
4821
|
+
const resolver = { resolve: resolve5, reject };
|
|
4719
4822
|
if (inFlight) {
|
|
4720
4823
|
if (queuedResolvers) {
|
|
4721
4824
|
queuedResolvers.push(resolver);
|
|
@@ -5008,6 +5111,55 @@ var init_branch_policy = __esm({
|
|
|
5008
5111
|
}
|
|
5009
5112
|
});
|
|
5010
5113
|
|
|
5114
|
+
// spex-console/src/lib/git-export/branch-resolver.ts
|
|
5115
|
+
function isValidSessionBranch(name, defaultBranch) {
|
|
5116
|
+
const b = name.trim();
|
|
5117
|
+
if (!b) return { ok: false, reason: "branch name is empty" };
|
|
5118
|
+
if (b === defaultBranch) {
|
|
5119
|
+
return { ok: false, reason: `\`${b}\` is the default branch; the drain never writes it` };
|
|
5120
|
+
}
|
|
5121
|
+
if (PROTECTED_SESSION_BRANCHES.includes(b)) {
|
|
5122
|
+
return { ok: false, reason: `\`${b}\` is a protected ref` };
|
|
5123
|
+
}
|
|
5124
|
+
const hasControlChar = [...b].some((c) => c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127);
|
|
5125
|
+
if (hasControlChar || /[\s~^:?*[\\]/.test(b)) {
|
|
5126
|
+
return { ok: false, reason: "branch name contains characters git refs do not allow" };
|
|
5127
|
+
}
|
|
5128
|
+
if (b.includes("..") || b.includes("@{") || b.includes("//")) {
|
|
5129
|
+
return { ok: false, reason: "branch name contains a forbidden sequence (.. @{ //)" };
|
|
5130
|
+
}
|
|
5131
|
+
if (b.startsWith("/") || b.endsWith("/") || b.startsWith("-") || b.endsWith(".") || b === "@") {
|
|
5132
|
+
return { ok: false, reason: "branch name has a forbidden leading or trailing character" };
|
|
5133
|
+
}
|
|
5134
|
+
if (b.split("/").some((seg) => seg.startsWith(".") || seg.endsWith(".lock"))) {
|
|
5135
|
+
return {
|
|
5136
|
+
ok: false,
|
|
5137
|
+
reason: "branch name has a path segment starting with . or ending in .lock"
|
|
5138
|
+
};
|
|
5139
|
+
}
|
|
5140
|
+
return { ok: true };
|
|
5141
|
+
}
|
|
5142
|
+
function resolveSessionTargetBranch(input) {
|
|
5143
|
+
const dated = resolveTargetBranch({
|
|
5144
|
+
user: input.user,
|
|
5145
|
+
date: input.date,
|
|
5146
|
+
defaultBranch: input.defaultBranch
|
|
5147
|
+
});
|
|
5148
|
+
const declared = input.sessionBranch?.trim();
|
|
5149
|
+
if (!declared) return { branch: dated, source: "dated" };
|
|
5150
|
+
const check = isValidSessionBranch(declared, input.defaultBranch);
|
|
5151
|
+
if (!check.ok) return { branch: dated, source: "dated", reason: check.reason };
|
|
5152
|
+
return { branch: declared, source: "session" };
|
|
5153
|
+
}
|
|
5154
|
+
var PROTECTED_SESSION_BRANCHES;
|
|
5155
|
+
var init_branch_resolver = __esm({
|
|
5156
|
+
"spex-console/src/lib/git-export/branch-resolver.ts"() {
|
|
5157
|
+
"use strict";
|
|
5158
|
+
init_branch_policy();
|
|
5159
|
+
PROTECTED_SESSION_BRANCHES = ["main", "master", "HEAD", "trunk"];
|
|
5160
|
+
}
|
|
5161
|
+
});
|
|
5162
|
+
|
|
5011
5163
|
// spex-console/src/lib/git-export/config.ts
|
|
5012
5164
|
function parseGithubRemote(githubRemote) {
|
|
5013
5165
|
const match = githubRemote.match(/^([^/\s]+)\/([^/\s]+)$/);
|
|
@@ -5095,10 +5247,10 @@ function trashPathFor(relPath, now) {
|
|
|
5095
5247
|
const normalized = relPath.split("\\").join("/");
|
|
5096
5248
|
const slashIdx = normalized.lastIndexOf("/");
|
|
5097
5249
|
const folder = slashIdx === -1 ? "" : normalized.slice(0, slashIdx);
|
|
5098
|
-
const
|
|
5250
|
+
const basename2 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
|
|
5099
5251
|
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
5100
5252
|
const trashDir = folder ? `${folder}/.trash` : ".trash";
|
|
5101
|
-
return `${trashDir}/${stamp}-${
|
|
5253
|
+
return `${trashDir}/${stamp}-${basename2}`;
|
|
5102
5254
|
}
|
|
5103
5255
|
function materializeSpecMarkdown(bundle) {
|
|
5104
5256
|
return stringifySpecMarkdown(bundle.bodyText, bundle.frontmatter);
|
|
@@ -5407,6 +5559,39 @@ var init_github_client = __esm({
|
|
|
5407
5559
|
}
|
|
5408
5560
|
});
|
|
5409
5561
|
|
|
5562
|
+
// spex-console/src/lib/git-export/session-branch-store.ts
|
|
5563
|
+
async function readActiveDeclaration(db, vaultId, actorKey, now) {
|
|
5564
|
+
const rows = await db.select().from(sessionBranchDeclarations).where(
|
|
5565
|
+
and(
|
|
5566
|
+
eq(sessionBranchDeclarations.vaultId, vaultId),
|
|
5567
|
+
eq(sessionBranchDeclarations.actorKey, actorKey),
|
|
5568
|
+
gt(sessionBranchDeclarations.expiresAt, now)
|
|
5569
|
+
)
|
|
5570
|
+
).orderBy(desc(sessionBranchDeclarations.declaredAt)).limit(1);
|
|
5571
|
+
const row = rows[0];
|
|
5572
|
+
if (!row) return null;
|
|
5573
|
+
return {
|
|
5574
|
+
vaultId: row.vaultId,
|
|
5575
|
+
actorKey: row.actorKey,
|
|
5576
|
+
sessionId: row.sessionId,
|
|
5577
|
+
branch: row.branch,
|
|
5578
|
+
declaredAt: row.declaredAt.toISOString(),
|
|
5579
|
+
expiresAt: row.expiresAt.toISOString()
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
async function readActiveSessionBranch(db, vaultId, actorKey, now) {
|
|
5583
|
+
return (await readActiveDeclaration(db, vaultId, actorKey, now))?.branch ?? null;
|
|
5584
|
+
}
|
|
5585
|
+
var SESSION_BRANCH_TTL_MS;
|
|
5586
|
+
var init_session_branch_store = __esm({
|
|
5587
|
+
"spex-console/src/lib/git-export/session-branch-store.ts"() {
|
|
5588
|
+
"use strict";
|
|
5589
|
+
init_sql_orm_inert_cli_shim();
|
|
5590
|
+
init_postgres();
|
|
5591
|
+
SESSION_BRANCH_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
5592
|
+
}
|
|
5593
|
+
});
|
|
5594
|
+
|
|
5410
5595
|
// spex-console/src/lib/git-export/settlement-store.ts
|
|
5411
5596
|
async function recordSettlement(tx, input) {
|
|
5412
5597
|
await tx.insert(specSettlements).values({
|
|
@@ -5637,6 +5822,7 @@ async function processVault(db, vaultId, env, now, deps, batchLimit, jobTimeoutM
|
|
|
5637
5822
|
result: { status: "error", message: target.message }
|
|
5638
5823
|
};
|
|
5639
5824
|
}
|
|
5825
|
+
const lookupSessionBranch = deps.lookupSessionBranch ?? ((vId, actor) => readActiveSessionBranch(tx, vId, actor, now()));
|
|
5640
5826
|
const { config, tokenSource, installationId } = target;
|
|
5641
5827
|
const provider = createPatCommitProvider(() => config.token);
|
|
5642
5828
|
let pendingSettlement = null;
|
|
@@ -5650,11 +5836,18 @@ async function processVault(db, vaultId, env, now, deps, batchLimit, jobTimeoutM
|
|
|
5650
5836
|
fetchBundles: (specIds) => fetchDrainBundles(tx, vaultId, specIds),
|
|
5651
5837
|
resolveActorIdentity,
|
|
5652
5838
|
commit: async (plan, message, coAuthor) => {
|
|
5653
|
-
|
|
5839
|
+
let sessionBranch = null;
|
|
5840
|
+
try {
|
|
5841
|
+
sessionBranch = await lookupSessionBranch(vaultId, coAuthor.name);
|
|
5842
|
+
} catch {
|
|
5843
|
+
sessionBranch = null;
|
|
5844
|
+
}
|
|
5845
|
+
const targetBranch = resolveSessionTargetBranch({
|
|
5654
5846
|
user: coAuthor.name,
|
|
5655
5847
|
date: now(),
|
|
5656
|
-
defaultBranch: config.branch
|
|
5657
|
-
|
|
5848
|
+
defaultBranch: config.branch,
|
|
5849
|
+
sessionBranch
|
|
5850
|
+
}).branch;
|
|
5658
5851
|
const outcome = await commitPlanToGitHub(
|
|
5659
5852
|
provider,
|
|
5660
5853
|
config,
|
|
@@ -5795,12 +5988,13 @@ var init_drain_runner = __esm({
|
|
|
5795
5988
|
init_postgres();
|
|
5796
5989
|
init_github_app();
|
|
5797
5990
|
init_advisory_lock();
|
|
5798
|
-
|
|
5991
|
+
init_branch_resolver();
|
|
5799
5992
|
init_config();
|
|
5800
5993
|
init_drain();
|
|
5801
5994
|
init_drain_skip();
|
|
5802
5995
|
init_export_status();
|
|
5803
5996
|
init_github_client();
|
|
5997
|
+
init_session_branch_store();
|
|
5804
5998
|
init_settlement_store();
|
|
5805
5999
|
BOT_NAME = "heyspex-bot";
|
|
5806
6000
|
BOT_EMAIL = "bot@heyspex.app";
|
|
@@ -6237,7 +6431,7 @@ var init_search_body_hit = __esm({
|
|
|
6237
6431
|
|
|
6238
6432
|
// spex-console/src/lib/vault.ts
|
|
6239
6433
|
import { spawn } from "node:child_process";
|
|
6240
|
-
import { readdirSync, readFileSync as
|
|
6434
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync } from "node:fs";
|
|
6241
6435
|
import { promises as fs7 } from "node:fs";
|
|
6242
6436
|
import path13 from "node:path";
|
|
6243
6437
|
import yaml3 from "js-yaml";
|
|
@@ -6357,8 +6551,8 @@ var init_vault = __esm({
|
|
|
6357
6551
|
|
|
6358
6552
|
// spex-console/bin/hs-index-rebuild.ts
|
|
6359
6553
|
import { execSync } from "node:child_process";
|
|
6360
|
-
import { appendFileSync, mkdirSync as
|
|
6361
|
-
import { promises as
|
|
6554
|
+
import { appendFileSync, mkdirSync as mkdirSync6, readFileSync as readFileSync13 } from "node:fs";
|
|
6555
|
+
import { promises as fs10 } from "node:fs";
|
|
6362
6556
|
import path14 from "node:path";
|
|
6363
6557
|
import process4 from "node:process";
|
|
6364
6558
|
|
|
@@ -6527,7 +6721,7 @@ function resolveSyncVaultTarget(input) {
|
|
|
6527
6721
|
detail: `heyspex.yaml vault_id${drift}`
|
|
6528
6722
|
};
|
|
6529
6723
|
}
|
|
6530
|
-
if (input.pointer.status === "read" && input.pointer.vaultId) {
|
|
6724
|
+
if (input.allowPointer && input.pointer.status === "read" && input.pointer.vaultId) {
|
|
6531
6725
|
return {
|
|
6532
6726
|
ok: true,
|
|
6533
6727
|
vaultId: input.pointer.vaultId,
|
|
@@ -6535,9 +6729,9 @@ function resolveSyncVaultTarget(input) {
|
|
|
6535
6729
|
detail: "this account's active vault, read from the console (heyspex.yaml names none)"
|
|
6536
6730
|
};
|
|
6537
6731
|
}
|
|
6538
|
-
const why = input.pointer.status === "unreadable" ? `the console could not answer for the active vault (${input.pointer.detail})` : "the console reports no active vault for this account";
|
|
6732
|
+
const why = !input.allowPointer ? "the account-wide active vault is not a binding and was not consulted (set HEYSPEX_ALLOW_POINTER_VAULT=1 to follow it on purpose)" : input.pointer.status === "unreadable" ? `the console could not answer for the active vault (${input.pointer.detail})` : "the console reports no active vault for this account";
|
|
6539
6733
|
return refuse(
|
|
6540
|
-
`cannot tell which vault to ${verb === "pull" ? "pull into" : verb === "index reconcile" ? "reconcile" : "rebuild"}: heyspex.yaml names no vault_id and ${why}. Nothing was changed. The registry.json pointer copy was deliberately not used (it can be stale and target the wrong vault). Name the vault: \`hs --vault <id> ${verb}\`; check this repo's wiring with \`hs connect --check\`; or
|
|
6734
|
+
`cannot tell which vault to ${verb === "pull" ? "pull into" : verb === "push" ? "push from" : verb === "import" ? "import into" : verb === "index reconcile" ? "reconcile" : "rebuild"}: heyspex.yaml names no vault_id and ${why}. Nothing was changed. The registry.json pointer copy was deliberately not used (it can be stale and target the wrong vault). Name the vault: \`hs --vault <id> ${verb}\`; check this repo's wiring with \`hs connect --check\`; or bind this repo with a heyspex.yaml vault_id (a committed satellite marker is portable).`
|
|
6541
6735
|
);
|
|
6542
6736
|
}
|
|
6543
6737
|
async function resolveSyncVaultTargetFromEnv(deps) {
|
|
@@ -6559,8 +6753,11 @@ async function resolveSyncVaultTargetFromEnv(deps) {
|
|
|
6559
6753
|
} catch {
|
|
6560
6754
|
registryRowId = null;
|
|
6561
6755
|
}
|
|
6756
|
+
const allowPointer = ["1", "true", "yes"].includes(
|
|
6757
|
+
(deps.env.HEYSPEX_ALLOW_POINTER_VAULT ?? "").trim().toLowerCase()
|
|
6758
|
+
);
|
|
6562
6759
|
let pointer = { status: "unreadable", detail: "not read" };
|
|
6563
|
-
if (!override && !markerVaultId) {
|
|
6760
|
+
if (!override && !markerVaultId && allowPointer) {
|
|
6564
6761
|
const http = {
|
|
6565
6762
|
fetchImpl: deps.fetchImpl ?? fetch,
|
|
6566
6763
|
consoleUrl: deps.consoleUrl,
|
|
@@ -6582,7 +6779,8 @@ async function resolveSyncVaultTargetFromEnv(deps) {
|
|
|
6582
6779
|
markerVaultId,
|
|
6583
6780
|
registryRowId,
|
|
6584
6781
|
pointer,
|
|
6585
|
-
verb: deps.verb
|
|
6782
|
+
verb: deps.verb,
|
|
6783
|
+
allowPointer
|
|
6586
6784
|
});
|
|
6587
6785
|
}
|
|
6588
6786
|
|
|
@@ -6743,23 +6941,22 @@ async function runReindex(input) {
|
|
|
6743
6941
|
init_validator();
|
|
6744
6942
|
|
|
6745
6943
|
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
6746
|
-
import { promises as
|
|
6747
|
-
import
|
|
6748
|
-
import nodePath from "node:path";
|
|
6944
|
+
import { promises as fs9 } from "node:fs";
|
|
6945
|
+
import nodePath2 from "node:path";
|
|
6749
6946
|
|
|
6750
6947
|
// spex-console/src/lib/dev-mode.ts
|
|
6948
|
+
init_heyspex_home2();
|
|
6751
6949
|
init_credentials();
|
|
6752
|
-
import { mkdirSync as
|
|
6753
|
-
import {
|
|
6754
|
-
import { join as join9 } from "node:path";
|
|
6950
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6951
|
+
import { join as join10 } from "node:path";
|
|
6755
6952
|
var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
|
|
6756
|
-
var defaultReadFile3 = (p) =>
|
|
6953
|
+
var defaultReadFile3 = (p) => readFileSync10(p, "utf8");
|
|
6757
6954
|
function readCredentialsMcpToken({
|
|
6758
|
-
homeDir =
|
|
6955
|
+
homeDir = heyspexHome2(),
|
|
6759
6956
|
readFile = defaultReadFile3
|
|
6760
6957
|
} = {}) {
|
|
6761
6958
|
try {
|
|
6762
|
-
const raw = readFile(
|
|
6959
|
+
const raw = readFile(join10(homeDir, ".heyspex", "credentials.yaml"));
|
|
6763
6960
|
const match = raw.match(/^mcp_token:\s*["']?([^"'\r\n#]+)/m);
|
|
6764
6961
|
return match?.[1]?.trim() || null;
|
|
6765
6962
|
} catch {
|
|
@@ -6768,11 +6965,19 @@ function readCredentialsMcpToken({
|
|
|
6768
6965
|
}
|
|
6769
6966
|
|
|
6770
6967
|
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
6968
|
+
init_heyspex_home2();
|
|
6771
6969
|
init_vault();
|
|
6772
6970
|
init_vault_frontmatter();
|
|
6971
|
+
|
|
6972
|
+
// spex-console/src/lib/vault-sync/ledger.ts
|
|
6973
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
6974
|
+
import { promises as fs8 } from "node:fs";
|
|
6975
|
+
import nodePath from "node:path";
|
|
6976
|
+
|
|
6977
|
+
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
6773
6978
|
function resolvePullToken({
|
|
6774
6979
|
env = process.env,
|
|
6775
|
-
homeDir =
|
|
6980
|
+
homeDir = heyspexHome2()
|
|
6776
6981
|
} = {}) {
|
|
6777
6982
|
const fromEnv = env.HEYSPEX_MCP_TOKEN?.trim();
|
|
6778
6983
|
if (fromEnv) return fromEnv;
|
|
@@ -6896,7 +7101,7 @@ function currentGitHead(vaultRoot2) {
|
|
|
6896
7101
|
}
|
|
6897
7102
|
function appendJournal(vaultRoot2, message) {
|
|
6898
7103
|
const heyspexDir = path14.join(vaultRoot2, ".heyspex");
|
|
6899
|
-
|
|
7104
|
+
mkdirSync6(heyspexDir, { recursive: true });
|
|
6900
7105
|
const logPath = path14.join(heyspexDir, "index-rebuild.log");
|
|
6901
7106
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
6902
7107
|
appendFileSync(logPath, `[${ts}] ${message}
|
|
@@ -6904,7 +7109,7 @@ function appendJournal(vaultRoot2, message) {
|
|
|
6904
7109
|
}
|
|
6905
7110
|
function readDiscovery(p) {
|
|
6906
7111
|
try {
|
|
6907
|
-
return
|
|
7112
|
+
return readFileSync13(p, "utf-8");
|
|
6908
7113
|
} catch {
|
|
6909
7114
|
return null;
|
|
6910
7115
|
}
|
|
@@ -6916,7 +7121,7 @@ function resolveConsole(args) {
|
|
|
6916
7121
|
}
|
|
6917
7122
|
async function loadCrosswalk(vaultRoot2) {
|
|
6918
7123
|
try {
|
|
6919
|
-
const raw = await
|
|
7124
|
+
const raw = await fs10.readFile(path14.join(vaultRoot2, ".heyspex", "id-crosswalk.json"), "utf8");
|
|
6920
7125
|
const parsed = JSON.parse(raw);
|
|
6921
7126
|
return Array.isArray(parsed) ? parsed : [];
|
|
6922
7127
|
} catch {
|
|
@@ -6926,7 +7131,7 @@ async function loadCrosswalk(vaultRoot2) {
|
|
|
6926
7131
|
async function walkLocal(args) {
|
|
6927
7132
|
const specsDir = path14.join(args.vaultRoot, "specs");
|
|
6928
7133
|
const files = [];
|
|
6929
|
-
for await (const file of
|
|
7134
|
+
for await (const file of fs10.glob("**/*.md", { cwd: specsDir })) {
|
|
6930
7135
|
files.push(path14.join(specsDir, file));
|
|
6931
7136
|
}
|
|
6932
7137
|
files.sort();
|
|
@@ -6934,7 +7139,7 @@ async function walkLocal(args) {
|
|
|
6934
7139
|
let failed = 0;
|
|
6935
7140
|
for (const abs of files) {
|
|
6936
7141
|
try {
|
|
6937
|
-
const content = await
|
|
7142
|
+
const content = await fs10.readFile(abs, "utf8");
|
|
6938
7143
|
const relPath = path14.relative(args.vaultRoot, abs).split(path14.sep).join("/");
|
|
6939
7144
|
const parsed = parseSpecFromContent(content, {
|
|
6940
7145
|
relPath,
|