heyspex 0.2.0 → 0.2.1
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 +163 -11
- 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 +162 -0
- package/docs/driving-heyspex-from-copilot.md +165 -0
- package/package.json +20 -2
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -12
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -15
- package/plugins/heyspex/README.md +309 -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/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
package/dist/cli/hs-join.mjs
CHANGED
|
@@ -39,11 +39,26 @@ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__
|
|
|
39
39
|
mod2
|
|
40
40
|
));
|
|
41
41
|
|
|
42
|
+
// scripts/lib/heyspex-home.mjs
|
|
43
|
+
import { homedir as homedir2 } from "node:os";
|
|
44
|
+
function heyspexHome2(env = process.env) {
|
|
45
|
+
const override = env?.HEYSPEX_HOME;
|
|
46
|
+
if (typeof override === "string" && override.trim().length > 0) {
|
|
47
|
+
return override.trim();
|
|
48
|
+
}
|
|
49
|
+
return homedir2();
|
|
50
|
+
}
|
|
51
|
+
var init_heyspex_home = __esm({
|
|
52
|
+
"scripts/lib/heyspex-home.mjs"() {
|
|
53
|
+
"use strict";
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
42
57
|
// vendor/heyspex-mcp/credentials.mjs
|
|
43
58
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
44
|
-
import os, { homedir } from "node:os";
|
|
59
|
+
import os, { homedir as homedir3 } from "node:os";
|
|
45
60
|
import { dirname, join, resolve } from "node:path";
|
|
46
|
-
function credentialsFilePath(homeDir =
|
|
61
|
+
function credentialsFilePath(homeDir = heyspexHome2()) {
|
|
47
62
|
return join(homeDir, ".heyspex", "credentials.yaml");
|
|
48
63
|
}
|
|
49
64
|
function parseMcpToken(raw) {
|
|
@@ -156,14 +171,14 @@ function readLegacyConfigAccountId(read2, homeDir) {
|
|
|
156
171
|
const value = raw?.match(CONFIG_ACCOUNT_ID_RE)?.[1]?.trim() ?? "";
|
|
157
172
|
return value || null;
|
|
158
173
|
}
|
|
159
|
-
function readBearer({ env = process.env, homeDir =
|
|
174
|
+
function readBearer({ env = process.env, homeDir = heyspexHome2(), readFile: readFile3, accountId } = {}) {
|
|
160
175
|
const read2 = readFile3 ?? ((p) => readFileSync(p, "utf8"));
|
|
161
176
|
const credentialsText = readTextOrNull(read2, credentialsFilePath(homeDir));
|
|
162
177
|
const named = typeof accountId === "string" && accountId.trim() ? accountId.trim() : null;
|
|
163
178
|
const legacyAccountId = named && parseCredentialsStore(credentialsText).schemaVersion === 1 ? readLegacyConfigAccountId(read2, homeDir) : null;
|
|
164
179
|
return resolveBearer({ envToken: env?.[MCP_TOKEN_ENV], credentialsText, accountId: named, legacyAccountId });
|
|
165
180
|
}
|
|
166
|
-
function readCurrentAccountId({ homeDir =
|
|
181
|
+
function readCurrentAccountId({ homeDir = heyspexHome2(), readFile: readFile3 } = {}) {
|
|
167
182
|
const read2 = readFile3 ?? ((p) => readFileSync(p, "utf8"));
|
|
168
183
|
const store = parseCredentialsStore(readTextOrNull(read2, credentialsFilePath(homeDir)));
|
|
169
184
|
if (store.schemaVersion === 2 && store.currentAccountId && store.currentAccountId !== LEGACY_ACCOUNT_ID) {
|
|
@@ -181,6 +196,7 @@ var MCP_TOKEN_ENV, MCP_TOKEN_LINE_RE, CREDENTIALS_SCHEMA_VERSION, LEGACY_ACCOUNT
|
|
|
181
196
|
var init_credentials = __esm({
|
|
182
197
|
"vendor/heyspex-mcp/credentials.mjs"() {
|
|
183
198
|
"use strict";
|
|
199
|
+
init_heyspex_home();
|
|
184
200
|
MCP_TOKEN_ENV = "HEYSPEX_MCP_TOKEN";
|
|
185
201
|
MCP_TOKEN_LINE_RE = /^mcp_token:[ \t]*["']?([^"'\r\n#]+)/m;
|
|
186
202
|
CREDENTIALS_SCHEMA_VERSION = 2;
|
|
@@ -193,9 +209,9 @@ var init_credentials = __esm({
|
|
|
193
209
|
|
|
194
210
|
// scripts/lib/dev-mode.mjs
|
|
195
211
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
196
|
-
import { homedir as
|
|
212
|
+
import { homedir as homedir5 } from "node:os";
|
|
197
213
|
import { join as join4 } from "node:path";
|
|
198
|
-
function devModeFilePath(homeDir =
|
|
214
|
+
function devModeFilePath(homeDir = heyspexHome2()) {
|
|
199
215
|
return join4(homeDir, ".heyspex", DEV_MODE_FILENAME);
|
|
200
216
|
}
|
|
201
217
|
function normalizeMode(raw) {
|
|
@@ -220,7 +236,7 @@ function deriveSemantics(mode, reliance) {
|
|
|
220
236
|
vaultWriteMode: local ? "local" : "cloud"
|
|
221
237
|
};
|
|
222
238
|
}
|
|
223
|
-
function readLocalDevModeRecord({ homeDir =
|
|
239
|
+
function readLocalDevModeRecord({ homeDir = heyspexHome2(), readFile: readFile3 = defaultReadFile } = {}) {
|
|
224
240
|
let raw;
|
|
225
241
|
try {
|
|
226
242
|
raw = readFile3(devModeFilePath(homeDir));
|
|
@@ -250,7 +266,7 @@ function readLocalDevModeRecord({ homeDir = homedir4(), readFile: readFile3 = de
|
|
|
250
266
|
}
|
|
251
267
|
function resolveDevMode({
|
|
252
268
|
env = process.env,
|
|
253
|
-
homeDir =
|
|
269
|
+
homeDir = heyspexHome2(),
|
|
254
270
|
readFile: readFile3 = defaultReadFile,
|
|
255
271
|
cloudSetting = void 0
|
|
256
272
|
} = {}) {
|
|
@@ -313,6 +329,7 @@ var CLOUD_CONSOLE_URL, LOCAL_CONSOLE_URL, DEV_MODE_FILENAME, defaultReadFile;
|
|
|
313
329
|
var init_dev_mode = __esm({
|
|
314
330
|
"scripts/lib/dev-mode.mjs"() {
|
|
315
331
|
"use strict";
|
|
332
|
+
init_heyspex_home();
|
|
316
333
|
init_credentials();
|
|
317
334
|
CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
318
335
|
LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
@@ -22722,14 +22739,14 @@ var require_lib2 = __commonJS({
|
|
|
22722
22739
|
|
|
22723
22740
|
// spex-console/bin/hs-join.ts
|
|
22724
22741
|
import { spawn, spawnSync as spawnSync5 } from "node:child_process";
|
|
22725
|
-
import { existsSync as
|
|
22742
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
|
|
22726
22743
|
import { userInfo } from "node:os";
|
|
22727
|
-
import { basename as
|
|
22744
|
+
import { basename as basename4, dirname as dirname6, join as join16, resolve as resolve5 } from "node:path";
|
|
22728
22745
|
import process4 from "node:process";
|
|
22729
22746
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
22730
22747
|
|
|
22731
22748
|
// spex-console/src/lib/cli/connect.ts
|
|
22732
|
-
import { readdirSync } from "node:fs";
|
|
22749
|
+
import { readdirSync as readdirSync2 } from "node:fs";
|
|
22733
22750
|
|
|
22734
22751
|
// dist/cli/._sql-orm-inert.cli-shim.mjs
|
|
22735
22752
|
var inert = new Proxy(function inert2() {
|
|
@@ -22767,14 +22784,24 @@ var uniqueIndex = inert;
|
|
|
22767
22784
|
|
|
22768
22785
|
// spex-console/src/lib/cli/vault-workspace-shared.ts
|
|
22769
22786
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
22770
|
-
|
|
22787
|
+
|
|
22788
|
+
// spex-console/src/lib/heyspex-home.ts
|
|
22789
|
+
import { homedir } from "node:os";
|
|
22790
|
+
function heyspexHome(env = process.env) {
|
|
22791
|
+
const override = env?.HEYSPEX_HOME;
|
|
22792
|
+
if (typeof override === "string" && override.trim().length > 0) {
|
|
22793
|
+
return override.trim();
|
|
22794
|
+
}
|
|
22795
|
+
return homedir();
|
|
22796
|
+
}
|
|
22771
22797
|
|
|
22772
22798
|
// vendor/heyspex-mcp/session-owner.mjs
|
|
22799
|
+
init_heyspex_home();
|
|
22773
22800
|
init_credentials();
|
|
22774
22801
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
22775
|
-
import { homedir as
|
|
22802
|
+
import { homedir as homedir4 } from "node:os";
|
|
22776
22803
|
import { join as join2 } from "node:path";
|
|
22777
|
-
function resolveSessionOwnerUncached({ env = process.env, homeDir =
|
|
22804
|
+
function resolveSessionOwnerUncached({ env = process.env, homeDir = heyspexHome2(), readFile: readFile3 } = {}) {
|
|
22778
22805
|
const fromEnv = typeof env?.HS_ACCOUNT_ID === "string" ? env.HS_ACCOUNT_ID.trim() : "";
|
|
22779
22806
|
if (fromEnv) return fromEnv;
|
|
22780
22807
|
return readCurrentAccountId({ homeDir, readFile: readFile3 });
|
|
@@ -22803,7 +22830,7 @@ function resolveOfflineDefaultWorkspace(accountId, localWorkspaceIds) {
|
|
|
22803
22830
|
function resolveConfiguredAccountId(opts = {}) {
|
|
22804
22831
|
return resolveSessionOwnerUncached({
|
|
22805
22832
|
env: opts.env ?? process.env,
|
|
22806
|
-
homeDir: opts.homeDir ??
|
|
22833
|
+
homeDir: opts.homeDir ?? heyspexHome(),
|
|
22807
22834
|
readFile: opts.readFile ?? ((p) => readFileSync3(p, "utf-8"))
|
|
22808
22835
|
});
|
|
22809
22836
|
}
|
|
@@ -23367,7 +23394,13 @@ var specEventKindEnum = pgEnum("spec_event_kind", [
|
|
|
23367
23394
|
"priority_change",
|
|
23368
23395
|
"parent_change",
|
|
23369
23396
|
"claim_acquired",
|
|
23370
|
-
"claim_released"
|
|
23397
|
+
"claim_released",
|
|
23398
|
+
// Collaborator write audit (HS-FWB6, migration 0077).
|
|
23399
|
+
"spec_created",
|
|
23400
|
+
"spec_edited",
|
|
23401
|
+
"spec_deleted",
|
|
23402
|
+
"capture_written",
|
|
23403
|
+
"wip_recorded"
|
|
23371
23404
|
]);
|
|
23372
23405
|
var focusPin = pgTable(
|
|
23373
23406
|
"focus_pin",
|
|
@@ -24560,11 +24593,43 @@ var specSettlements = pgTable(
|
|
|
24560
24593
|
// `owner/name`
|
|
24561
24594
|
branch: text("branch").notNull(),
|
|
24562
24595
|
commitSha: text("commit_sha").notNull(),
|
|
24563
|
-
settledAt: timestamp("settled_at", { withTimezone: true }).notNull().default(sql`now()`)
|
|
24596
|
+
settledAt: timestamp("settled_at", { withTimezone: true }).notNull().default(sql`now()`),
|
|
24597
|
+
// The pull request that carries this settlement
|
|
24598
|
+
// ([[HS-9D25-CWPX-EPIC-branch-lifecycle-open-a-pull-request]]). NULL until
|
|
24599
|
+
// the branch has gone quiet and a PR was opened or found; a write made
|
|
24600
|
+
// after its PR merged stays NULL, which is what triggers the next PR.
|
|
24601
|
+
pullRequestNumber: integer("pull_request_number"),
|
|
24602
|
+
pullRequestUrl: text("pull_request_url")
|
|
24564
24603
|
},
|
|
24565
24604
|
(table) => [
|
|
24566
24605
|
// "latest settlement for this spec" — the read path's only query.
|
|
24567
|
-
index("spec_settlements_spec_settled_idx").on(table.specId, table.settledAt.desc())
|
|
24606
|
+
index("spec_settlements_spec_settled_idx").on(table.specId, table.settledAt.desc()),
|
|
24607
|
+
// The PR sweep's only query: branches with settlements not yet in a PR.
|
|
24608
|
+
index("spec_settlements_unproposed_idx").on(table.vaultId, table.repo, table.branch).where(sql`${table.pullRequestNumber} IS NULL`)
|
|
24609
|
+
]
|
|
24610
|
+
);
|
|
24611
|
+
var sessionBranchDeclarations = pgTable(
|
|
24612
|
+
"session_branch_declarations",
|
|
24613
|
+
{
|
|
24614
|
+
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
|
|
24615
|
+
vaultId: text("vault_id").notNull().default(DEFAULT_VAULT_ID),
|
|
24616
|
+
actorKey: text("actor_key").notNull(),
|
|
24617
|
+
sessionId: text("session_id").notNull(),
|
|
24618
|
+
branch: text("branch").notNull(),
|
|
24619
|
+
declaredAt: timestamp("declared_at", { withTimezone: true }).notNull().default(sql`now()`),
|
|
24620
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull()
|
|
24621
|
+
},
|
|
24622
|
+
(table) => [
|
|
24623
|
+
uniqueIndex("session_branch_declarations_uq").on(
|
|
24624
|
+
table.vaultId,
|
|
24625
|
+
table.actorKey,
|
|
24626
|
+
table.sessionId
|
|
24627
|
+
),
|
|
24628
|
+
index("session_branch_declarations_lookup_idx").on(
|
|
24629
|
+
table.vaultId,
|
|
24630
|
+
table.actorKey,
|
|
24631
|
+
table.declaredAt.desc()
|
|
24632
|
+
)
|
|
24568
24633
|
]
|
|
24569
24634
|
);
|
|
24570
24635
|
|
|
@@ -24657,7 +24722,6 @@ async function runNeonReachabilityProbe(attempt, options2 = {}, label = "Neon re
|
|
|
24657
24722
|
|
|
24658
24723
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
24659
24724
|
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
24660
|
-
import { homedir as homedir3 } from "node:os";
|
|
24661
24725
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
24662
24726
|
|
|
24663
24727
|
// spex-console/src/lib/vault-registry/types.ts
|
|
@@ -24675,7 +24739,7 @@ function emptyVaultRegistry() {
|
|
|
24675
24739
|
|
|
24676
24740
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
24677
24741
|
function vaultRegistryDir(opts = {}) {
|
|
24678
|
-
return opts.registryDir ?? join3(
|
|
24742
|
+
return opts.registryDir ?? join3(heyspexHome(), ".heyspex");
|
|
24679
24743
|
}
|
|
24680
24744
|
function vaultRegistryFilePath(opts = {}) {
|
|
24681
24745
|
return join3(vaultRegistryDir(opts), "registry.json");
|
|
@@ -24845,7 +24909,7 @@ function samePathLoose(a, b) {
|
|
|
24845
24909
|
}
|
|
24846
24910
|
function assertNotRealHomeRegistryUnderTest(filePath) {
|
|
24847
24911
|
if (!underTestRunner()) return;
|
|
24848
|
-
if (samePathLoose(dirname2(filePath), join3(
|
|
24912
|
+
if (samePathLoose(dirname2(filePath), join3(heyspexHome(), ".heyspex"))) {
|
|
24849
24913
|
throw new Error(
|
|
24850
24914
|
`registry-file: refusing to write ${filePath} under a test runner \u2014 that is the real ~/.heyspex registry. Pass opts.registryDir (a temp dir).`
|
|
24851
24915
|
);
|
|
@@ -25312,12 +25376,17 @@ async function listWorkspacesNeon(accountId, deps = {}) {
|
|
|
25312
25376
|
id: workspaces.id,
|
|
25313
25377
|
displayName: workspaces.displayName,
|
|
25314
25378
|
githubOrg: workspaces.githubOrg,
|
|
25315
|
-
createdAt: workspaces.createdAt
|
|
25379
|
+
createdAt: workspaces.createdAt,
|
|
25380
|
+
archived: workspaces.archived
|
|
25316
25381
|
}).from(workspaces).where(eq(workspaces.accountId, accountId));
|
|
25317
25382
|
return rows.map((r) => ({
|
|
25318
25383
|
id: r.id,
|
|
25319
25384
|
display_name: r.displayName,
|
|
25320
25385
|
created_at: (r.createdAt instanceof Date ? r.createdAt : /* @__PURE__ */ new Date()).toISOString(),
|
|
25386
|
+
// The flag `setWorkspaceArchivedNeon` writes; `list.ts` filters on it.
|
|
25387
|
+
// On a cloud host this row IS the roster (no registry.json to carry the
|
|
25388
|
+
// flag), so leaving it out made an archived workspace list forever.
|
|
25389
|
+
archived: r.archived,
|
|
25321
25390
|
...r.githubOrg ? { github_org: r.githubOrg } : {}
|
|
25322
25391
|
}));
|
|
25323
25392
|
} catch {
|
|
@@ -25601,9 +25670,10 @@ async function createWorkspace(input, deps = {}) {
|
|
|
25601
25670
|
}
|
|
25602
25671
|
|
|
25603
25672
|
// scripts/lib/console-url-resolver.mjs
|
|
25673
|
+
init_heyspex_home();
|
|
25604
25674
|
init_dev_mode();
|
|
25605
25675
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
25606
|
-
import { homedir as
|
|
25676
|
+
import { homedir as homedir6 } from "node:os";
|
|
25607
25677
|
import { join as join5 } from "node:path";
|
|
25608
25678
|
var DEFAULT_CONSOLE_URL = LOCAL_CONSOLE_URL;
|
|
25609
25679
|
function trimTrailingSlash(url) {
|
|
@@ -25630,7 +25700,7 @@ function warnStaleLoopbackCredentialsOnce(credentialsUrl, derivedUrl, { log }) {
|
|
|
25630
25700
|
} catch {
|
|
25631
25701
|
}
|
|
25632
25702
|
}
|
|
25633
|
-
function readCredentialsConsoleUrl({ homeDir =
|
|
25703
|
+
function readCredentialsConsoleUrl({ homeDir = heyspexHome2(), readFile: readFile3 = defaultReadFile2 } = {}) {
|
|
25634
25704
|
try {
|
|
25635
25705
|
const raw = readFile3(join5(homeDir, ".heyspex", "credentials.yaml"));
|
|
25636
25706
|
const match = String(raw ?? "").match(/^console_url:\s*["']?([^"'\r\n#]+)/m);
|
|
@@ -25674,7 +25744,7 @@ function resolveModeEnvelope({ env, homeDir }) {
|
|
|
25674
25744
|
function resolveConsoleUrl({
|
|
25675
25745
|
repoRoot,
|
|
25676
25746
|
env = process.env,
|
|
25677
|
-
homeDir =
|
|
25747
|
+
homeDir = heyspexHome2(),
|
|
25678
25748
|
strict = false,
|
|
25679
25749
|
devMode,
|
|
25680
25750
|
log = (line) => process.stderr.write(line),
|
|
@@ -25708,7 +25778,134 @@ function resolveConsoleUrl({
|
|
|
25708
25778
|
import { createHash } from "node:crypto";
|
|
25709
25779
|
import fs from "node:fs";
|
|
25710
25780
|
import path2 from "node:path";
|
|
25781
|
+
import os2 from "node:os";
|
|
25711
25782
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
25783
|
+
|
|
25784
|
+
// scripts/lib/hook-dispositions.mjs
|
|
25785
|
+
var HOSTS = Object.freeze(["claude-code", "cursor", "copilot"]);
|
|
25786
|
+
var DISPOSITIONS = Object.freeze(["mapped", "advisory-degraded", "unportable", "not-shipped"]);
|
|
25787
|
+
var mapped = (note) => Object.freeze({ disposition: "mapped", ...note ? { note } : {} });
|
|
25788
|
+
var degraded = (note) => Object.freeze({ disposition: "advisory-degraded", note });
|
|
25789
|
+
var unportable = (control) => Object.freeze({ disposition: "unportable", control });
|
|
25790
|
+
var notShipped = Object.freeze({ disposition: "not-shipped" });
|
|
25791
|
+
var CC = mapped();
|
|
25792
|
+
var COPILOT_PROMPT_DROPPED = "Copilot drops command-hook output on userPromptSubmitted (no prompt-time context channel); the text moves to sessionStart / .github/copilot-instructions.md.";
|
|
25793
|
+
var CURSOR_PROMPT_USER_MESSAGE = "Cursor beforeSubmitPrompt honors user_message only (no additional_context): the text surfaces in the transcript instead of as invisible context.";
|
|
25794
|
+
var COPILOT_NO_PLAN_TOOL = "MCP preflight refusals (heyspex_claim_spec / heyspex_submit_for_review / heyspex_save_research) + .github/copilot-instructions.md plan rule (state the plan in chat, claim the spec before editing) + HS-20SW directive-obligation-gate claim-before-build deny";
|
|
25795
|
+
var HOOK_DISPOSITIONS = Object.freeze({
|
|
25796
|
+
// ── customer: session start / compaction ──────────────────────────────────
|
|
25797
|
+
"wip-checkpoint": {
|
|
25798
|
+
"claude-code": CC,
|
|
25799
|
+
cursor: mapped("preCompact is relayed as SessionStart(compact)."),
|
|
25800
|
+
copilot: mapped("sessionStart additionalContext is delivered; the preCompact leg writes WIP only (no output channel), re-injected at the next sessionStart.")
|
|
25801
|
+
},
|
|
25802
|
+
"inject-doctrine": { "claude-code": CC, cursor: mapped(), copilot: mapped("sessionStart additionalContext.") },
|
|
25803
|
+
"check-auto-memory-budget": { "claude-code": CC, cursor: mapped(), copilot: mapped("sessionStart additionalContext.") },
|
|
25804
|
+
"check-claude-md-budget": { "claude-code": CC, cursor: mapped(), copilot: mapped("sessionStart additionalContext.") },
|
|
25805
|
+
"git-reflog-audit": { "claude-code": CC, cursor: mapped(), copilot: mapped("sessionStart additionalContext and agentStop.") },
|
|
25806
|
+
"vault-mirror-catchup": { "claude-code": CC, cursor: mapped(), copilot: mapped("Detached side effect on sessionStart.") },
|
|
25807
|
+
// ── customer: every user turn ─────────────────────────────────────────────
|
|
25808
|
+
"preflight-reminder": {
|
|
25809
|
+
"claude-code": CC,
|
|
25810
|
+
cursor: degraded(CURSOR_PROMPT_USER_MESSAGE),
|
|
25811
|
+
copilot: degraded(`${COPILOT_PROMPT_DROPPED} Real enforcement is the MCP preflight refusal.`)
|
|
25812
|
+
},
|
|
25813
|
+
"intent-classifier-trigger": {
|
|
25814
|
+
"claude-code": CC,
|
|
25815
|
+
cursor: degraded(CURSOR_PROMPT_USER_MESSAGE),
|
|
25816
|
+
copilot: degraded(
|
|
25817
|
+
`${COPILOT_PROMPT_DROPPED} The hook still runs and arms obligation state; the HS-20SW directive-obligation-gate turns that state into a hard deny.`
|
|
25818
|
+
)
|
|
25819
|
+
},
|
|
25820
|
+
"inject-communication-style": {
|
|
25821
|
+
"claude-code": CC,
|
|
25822
|
+
cursor: degraded(CURSOR_PROMPT_USER_MESSAGE),
|
|
25823
|
+
copilot: degraded(COPILOT_PROMPT_DROPPED)
|
|
25824
|
+
},
|
|
25825
|
+
"inject-vault-context": {
|
|
25826
|
+
"claude-code": CC,
|
|
25827
|
+
cursor: degraded(CURSOR_PROMPT_USER_MESSAGE),
|
|
25828
|
+
copilot: degraded(`${COPILOT_PROMPT_DROPPED} Per-prompt vault retrieval has no Copilot channel at all.`)
|
|
25829
|
+
},
|
|
25830
|
+
// ── customer: before a tool call (hard gates) ─────────────────────────────
|
|
25831
|
+
"git-identity-guard": { "claude-code": CC, cursor: mapped("beforeShellExecution. Always on."), copilot: mapped("preToolUse on bash/powershell. Always on.") },
|
|
25832
|
+
"vault-write-tier-guard": { "claude-code": CC, cursor: mapped(), copilot: mapped("preToolUse on create/edit/bash/powershell.") },
|
|
25833
|
+
"settings-write-gate": { "claude-code": CC, cursor: mapped(), copilot: mapped("preToolUse on create/edit/bash/powershell and the MCP settings tool.") },
|
|
25834
|
+
"emit-claim-status": { "claude-code": CC, cursor: mapped(), copilot: mapped("Telemetry on preToolUse/postToolUse/agentStop; the cloud agent needs a network allow rule for the console.") },
|
|
25835
|
+
"enforce-vault-naming": { "claude-code": CC, cursor: mapped(), copilot: mapped("preToolUse on create/bash/powershell.") },
|
|
25836
|
+
"guard-auto-memory-writes": { "claude-code": CC, cursor: mapped(), copilot: mapped("preToolUse on create/bash/powershell.") },
|
|
25837
|
+
"directive-obligation-gate": {
|
|
25838
|
+
"claude-code": CC,
|
|
25839
|
+
cursor: mapped(),
|
|
25840
|
+
copilot: mapped("HS-20SW: hard deny on task/create/edit/write-shaped shell while a capture obligation is unmet; the only hard capture-on-idea guarantee on Copilot.")
|
|
25841
|
+
},
|
|
25842
|
+
"claim-before-code-gate": {
|
|
25843
|
+
"claude-code": CC,
|
|
25844
|
+
cursor: degraded("Armed only by plan-exit-ingest-gate, which has no Cursor trigger, so it stays dormant; covered by the HS-20SW claim-before-build rule."),
|
|
25845
|
+
copilot: degraded("Armed only by plan-exit-ingest-gate, which has no Copilot trigger (no ExitPlanMode tool), so it stays dormant; covered by the HS-20SW claim-before-build rule.")
|
|
25846
|
+
},
|
|
25847
|
+
"capture-workflow-gate": {
|
|
25848
|
+
"claude-code": CC,
|
|
25849
|
+
cursor: mapped(),
|
|
25850
|
+
copilot: mapped("preToolUse on the MCP heyspex_file_spec tool; MCP tool naming in hook payloads is unverified live (matched by suffix).")
|
|
25851
|
+
},
|
|
25852
|
+
"diagnose-capture-reminder": {
|
|
25853
|
+
"claude-code": CC,
|
|
25854
|
+
cursor: mapped("preToolUse agent_message delivers the reminder."),
|
|
25855
|
+
copilot: degraded("Copilot preToolUse has no context channel for an inject, so this advisory is silent (lost).")
|
|
25856
|
+
},
|
|
25857
|
+
"git-stash-gate": {
|
|
25858
|
+
"claude-code": CC,
|
|
25859
|
+
cursor: mapped(),
|
|
25860
|
+
copilot: mapped('The Claude "ask" becomes a deny where no user is present (cloud agent, CLI autopilot).')
|
|
25861
|
+
},
|
|
25862
|
+
"research-protocol-gate": { "claude-code": CC, cursor: mapped("Always on."), copilot: mapped("preToolUse on web_fetch/web_search and the MCP save_research tool. Always on; the MCP refusal duplicates it.") },
|
|
25863
|
+
"research-method-guard": {
|
|
25864
|
+
"claude-code": CC,
|
|
25865
|
+
cursor: mapped("preToolUse with a Skill|Workflow tool filter. Always on."),
|
|
25866
|
+
copilot: unportable("MCP preflight refusal on heyspex_save_research (hard floor, hook-independent) + .github/copilot-instructions.md research rule; Copilot has no Skill or Workflow tool to hook.")
|
|
25867
|
+
},
|
|
25868
|
+
"deep-research-cost-gate": {
|
|
25869
|
+
"claude-code": CC,
|
|
25870
|
+
cursor: mapped("preToolUse with a Workflow|Skill tool filter."),
|
|
25871
|
+
copilot: unportable("MCP preflight refusal on heyspex_save_research + .github/copilot-instructions.md cost-confirmation rule; Copilot has no Skill or Workflow tool to hook.")
|
|
25872
|
+
},
|
|
25873
|
+
"plan-review-gate": {
|
|
25874
|
+
"claude-code": CC,
|
|
25875
|
+
cursor: unportable(".cursor/rules/plan-review-required.mdc (always-on rule)"),
|
|
25876
|
+
copilot: unportable(COPILOT_NO_PLAN_TOOL)
|
|
25877
|
+
},
|
|
25878
|
+
"plan-exit-ingest-gate": {
|
|
25879
|
+
"claude-code": CC,
|
|
25880
|
+
cursor: unportable(".cursor/rules/plan-review-required.mdc + spec-creation-linkage.mdc"),
|
|
25881
|
+
copilot: unportable(COPILOT_NO_PLAN_TOOL)
|
|
25882
|
+
},
|
|
25883
|
+
// ── customer: end of turn / session ───────────────────────────────────────
|
|
25884
|
+
"naughty-list-gate": { "claude-code": CC, cursor: mapped("stop followup_message re-drive."), copilot: mapped('agentStop {decision:"block"} re-drive, capped at 8 consecutive blocks.') },
|
|
25885
|
+
"id-display-format-gate": { "claude-code": CC, cursor: mapped("stop followup_message re-drive."), copilot: mapped("agentStop re-drive; reads the transcript (format is not a stable API).") },
|
|
25886
|
+
"epic-milestone-phrasing-gate": { "claude-code": CC, cursor: mapped("stop followup_message re-drive."), copilot: mapped("agentStop re-drive; reads the transcript (format is not a stable API).") },
|
|
25887
|
+
"enforce-session-sequence": { "claude-code": CC, cursor: mapped("stop followup_message re-drive."), copilot: mapped("agentStop re-drive, capped at 8 consecutive blocks.") },
|
|
25888
|
+
"notify-status-flip": { "claude-code": CC, cursor: mapped(), copilot: mapped("Side effect on postToolUse.") },
|
|
25889
|
+
"close-terminal-on-ship": { "claude-code": CC, cursor: mapped(), copilot: mapped("Side effect on postToolUse / sessionEnd.") },
|
|
25890
|
+
"scripts/stamp-provenance-on-edit.mjs": { "claude-code": CC, cursor: mapped(), copilot: mapped("Side effect on postToolUse.") },
|
|
25891
|
+
"session-end": { "claude-code": CC, cursor: mapped(), copilot: mapped("sessionEnd.") },
|
|
25892
|
+
// ── platform: heyspex-app development machinery (never installed in a customer repo) ──
|
|
25893
|
+
"inject-dev-server-awareness": { "claude-code": CC, cursor: mapped(), copilot: notShipped },
|
|
25894
|
+
"inject-coo-seat": { "claude-code": CC, cursor: mapped(), copilot: notShipped },
|
|
25895
|
+
"coo-guard-check": { "claude-code": CC, cursor: mapped(), copilot: notShipped },
|
|
25896
|
+
"coo-denylist-gate": { "claude-code": CC, cursor: mapped(), copilot: notShipped },
|
|
25897
|
+
"agent-spawn-delegation-gate": { "claude-code": CC, cursor: mapped("subagentStart."), copilot: notShipped },
|
|
25898
|
+
"capture-execution-gate": { "claude-code": CC, cursor: mapped("beforeShellExecution + subagentStart."), copilot: notShipped },
|
|
25899
|
+
"check-mcp-freshness-hook": { "claude-code": CC, cursor: mapped(), copilot: notShipped },
|
|
25900
|
+
"dev-server-task-reminder": { "claude-code": CC, cursor: mapped(), copilot: notShipped },
|
|
25901
|
+
"env-vault-guard": { "claude-code": CC, cursor: mapped("Always on."), copilot: notShipped },
|
|
25902
|
+
"log-session-activity": { "claude-code": CC, cursor: mapped(), copilot: notShipped }
|
|
25903
|
+
});
|
|
25904
|
+
function dispositionFor(hook, host) {
|
|
25905
|
+
return HOOK_DISPOSITIONS[hook]?.[host] ?? null;
|
|
25906
|
+
}
|
|
25907
|
+
|
|
25908
|
+
// scripts/lib/customer-kit.mjs
|
|
25712
25909
|
var HOOK_SCOPES = Object.freeze({
|
|
25713
25910
|
// ── customer: vault / workflow / safety gates ─────────────────────────────
|
|
25714
25911
|
"wip-checkpoint": { scope: "customer", why: "Previous-session WIP handoff, read from the active vault." },
|
|
@@ -25724,6 +25921,7 @@ var HOOK_SCOPES = Object.freeze({
|
|
|
25724
25921
|
"inject-communication-style": { scope: "customer", why: "Plain-language communication rules each turn." },
|
|
25725
25922
|
"inject-vault-context": { scope: "customer", why: "Lexical vault retrieval for the prompt." },
|
|
25726
25923
|
"git-identity-guard": { scope: "customer", why: "Agents never write git identity (owner ruling 2026-09-19)." },
|
|
25924
|
+
"vault-write-tier-guard": { scope: "customer", why: "Cloud write mode: disk edits to vault specs are a mirror the console never reads back (HS-FCNH)." },
|
|
25727
25925
|
"settings-write-gate": { scope: "customer", why: "Approval before the vault's .heyspex/config.yaml changes." },
|
|
25728
25926
|
"emit-claim-status": { scope: "customer", why: "Claim liveness heartbeat to the console." },
|
|
25729
25927
|
"enforce-vault-naming": { scope: "customer", why: "Naming V3 filename guard (HS-RY2N)." },
|
|
@@ -25731,7 +25929,10 @@ var HOOK_SCOPES = Object.freeze({
|
|
|
25731
25929
|
"plan-review-gate": { scope: "customer", why: "Plan review + QC markers before ExitPlanMode." },
|
|
25732
25930
|
"plan-exit-ingest-gate": { scope: "customer", why: "Plan ingest on ExitPlanMode; arms claim-before-code." },
|
|
25733
25931
|
"claim-before-code-gate": { scope: "customer", why: "First code edit after plan exit needs a claim." },
|
|
25932
|
+
"directive-obligation-gate": { scope: "customer", why: "A capture_on_idea directive leaves a durable obligation; an unmet one denies sub-agent spawns, product-code writes and builds, and re-drives Stop once. Also claim-before-build (HS-20SW)." },
|
|
25734
25933
|
"capture-workflow-gate": { scope: "customer", why: "No implementation spec straight out of an open capture." },
|
|
25934
|
+
"vault-mirror-catchup": { scope: "customer", why: "Cloud write mode: a SessionStart detached `hs pull --apply` closes a stale disk mirror before the first tool call (HS-XJFQ)." },
|
|
25935
|
+
"diagnose-capture-reminder": { scope: "customer", why: "Warn when a diagnose is filed with no capture holding the owner's words (HS-IK2D)." },
|
|
25735
25936
|
"git-stash-gate": { scope: "customer", why: "Always ask before a mutating git stash." },
|
|
25736
25937
|
"research-method-guard": { scope: "customer", why: "Research method guard (HS-EN4F)." },
|
|
25737
25938
|
"deep-research-cost-gate": { scope: "customer", why: "Cost confirmation before a paid deep-research launch." },
|
|
@@ -25750,6 +25951,7 @@ var HOOK_SCOPES = Object.freeze({
|
|
|
25750
25951
|
"coo-guard-check": { scope: "platform", why: "dev-factory breakdown-delegation guard." },
|
|
25751
25952
|
"coo-denylist-gate": { scope: "platform", why: "dev-factory COO window deny list." },
|
|
25752
25953
|
"agent-spawn-delegation-gate": { scope: "platform", why: "dev-factory seat routing for subagents." },
|
|
25954
|
+
"capture-execution-gate": { scope: "platform", why: "Blocks dev-factory research/paid-model/routing surfaces past an ungrounded capture (HS-KNJ8)." },
|
|
25753
25955
|
"check-mcp-freshness-hook": { scope: "platform", why: "Freshness of the vendored MCP build \u2014 a platform-dev concern." },
|
|
25754
25956
|
"dev-server-task-reminder": { scope: "platform", why: "spex-console dev server + Neon DB safety." },
|
|
25755
25957
|
"env-vault-guard": { scope: "platform", why: "Guards heyspex-app's encrypted env vault paths." },
|
|
@@ -25768,6 +25970,7 @@ var CLAUDE_HOOK_ROWS = Object.freeze([
|
|
|
25768
25970
|
{ event: "SessionStart", hook: "check-claude-md-budget", timeout: 5 },
|
|
25769
25971
|
{ event: "SessionStart", hook: "check-mcp-freshness-hook", timeout: 5 },
|
|
25770
25972
|
{ event: "SessionStart", hook: "git-reflog-audit", timeout: 5 },
|
|
25973
|
+
{ event: "SessionStart", hook: "vault-mirror-catchup", timeout: 5 },
|
|
25771
25974
|
{ event: "PreCompact", hook: "wip-checkpoint", timeout: 5 },
|
|
25772
25975
|
{ event: "UserPromptSubmit", hook: "preflight-reminder", timeout: 5 },
|
|
25773
25976
|
{ event: "UserPromptSubmit", hook: "intent-classifier-trigger", timeout: 5 },
|
|
@@ -25775,10 +25978,13 @@ var CLAUDE_HOOK_ROWS = Object.freeze([
|
|
|
25775
25978
|
{ event: "UserPromptSubmit", hook: "inject-vault-context", timeout: 10 },
|
|
25776
25979
|
{ event: "PreToolUse", matcher: "Write|Edit|MultiEdit|NotebookEdit|Bash|PowerShell", hook: "env-vault-guard", timeout: 5 },
|
|
25777
25980
|
{ event: "PreToolUse", matcher: "Bash|PowerShell", hook: "git-identity-guard", timeout: 5 },
|
|
25981
|
+
{ event: "PreToolUse", matcher: "Write|Edit|MultiEdit|NotebookEdit|Bash|PowerShell", hook: "vault-write-tier-guard", timeout: 5 },
|
|
25778
25982
|
{ event: "PreToolUse", matcher: "Write|Edit|NotebookEdit|Bash|PowerShell|mcp__heyspex__heyspex_settings_set", hook: "settings-write-gate", timeout: 5 },
|
|
25779
25983
|
{ event: "PreToolUse", matcher: ".*", hook: "emit-claim-status", args: ["--event=PreToolUse"], timeout: 5 },
|
|
25780
25984
|
{ event: "PreToolUse", matcher: ".*", hook: "coo-denylist-gate", timeout: 5 },
|
|
25781
25985
|
{ event: "PreToolUse", matcher: "Agent|Task", hook: "agent-spawn-delegation-gate", timeout: 5 },
|
|
25986
|
+
{ event: "PreToolUse", matcher: "Bash|PowerShell|Agent|Task", hook: "capture-execution-gate", timeout: 5 },
|
|
25987
|
+
{ event: "PreToolUse", matcher: "Agent|Task|Edit|Write|MultiEdit|NotebookEdit|Bash|PowerShell", hook: "directive-obligation-gate", timeout: 5 },
|
|
25782
25988
|
{ event: "PreToolUse", matcher: "mcp__heyspex__heyspex_claim_spec", hook: "coo-guard-check", timeout: 5 },
|
|
25783
25989
|
{ event: "PreToolUse", matcher: "Write|NotebookEdit|Bash|PowerShell", hook: "enforce-vault-naming", timeout: 5 },
|
|
25784
25990
|
{ event: "PreToolUse", matcher: "Write|NotebookEdit|Bash|PowerShell", hook: "guard-auto-memory-writes", timeout: 5 },
|
|
@@ -25787,6 +25993,7 @@ var CLAUDE_HOOK_ROWS = Object.freeze([
|
|
|
25787
25993
|
{ event: "PreToolUse", matcher: "ExitPlanMode", hook: "plan-exit-ingest-gate", timeout: 5 },
|
|
25788
25994
|
{ event: "PreToolUse", matcher: "Edit|MultiEdit|Write", hook: "claim-before-code-gate", timeout: 5 },
|
|
25789
25995
|
{ event: "PreToolUse", matcher: "mcp__heyspex__heyspex_file_spec", hook: "capture-workflow-gate", timeout: 8 },
|
|
25996
|
+
{ event: "PreToolUse", matcher: "mcp__heyspex__heyspex_file_spec", hook: "diagnose-capture-reminder", timeout: 5 },
|
|
25790
25997
|
{ event: "PreToolUse", matcher: "Bash|PowerShell|mcp__neon__prepare_database_migration|mcp__neon__complete_database_migration|mcp__neon__reset_from_parent|mcp__neon__delete_branch|mcp__Neon__prepare_database_migration|mcp__Neon__complete_database_migration|mcp__Neon__reset_from_parent|mcp__Neon__delete_branch", hook: "dev-server-task-reminder", timeout: 10 },
|
|
25791
25998
|
{ event: "PreToolUse", matcher: "Bash|PowerShell", hook: "git-stash-gate", timeout: 10 },
|
|
25792
25999
|
{ event: "PreToolUse", matcher: "Skill|Workflow", hook: "research-method-guard", timeout: 5 },
|
|
@@ -25799,7 +26006,9 @@ var CLAUDE_HOOK_ROWS = Object.freeze([
|
|
|
25799
26006
|
{ event: "Stop", hook: "epic-milestone-phrasing-gate", timeout: 5 },
|
|
25800
26007
|
{ event: "Stop", hook: "git-reflog-audit", timeout: 5 },
|
|
25801
26008
|
{ event: "Stop", hook: "enforce-session-sequence", timeout: 5 },
|
|
26009
|
+
{ event: "Stop", hook: "directive-obligation-gate", timeout: 5 },
|
|
25802
26010
|
{ event: "PostToolUse", matcher: ".*", hook: "emit-claim-status", args: ["--event=PostToolUse"], timeout: 5 },
|
|
26011
|
+
{ event: "PostToolUse", matcher: "Skill|mcp__heyspex__heyspex_update_capture|mcp__heyspex__heyspex_promote_capture.*|mcp__heyspex__heyspex_file_spec|mcp__heyspex__heyspex_claim_spec|mcp__heyspex__heyspex_release_spec|mcp__heyspex__heyspex_defer_spec", hook: "directive-obligation-gate", timeout: 5 },
|
|
25803
26012
|
{ event: "PostToolUse", matcher: "Edit|Write|MultiEdit", hook: "notify-status-flip", timeout: 5 },
|
|
25804
26013
|
{ event: "PostToolUse", matcher: "Edit|Write|MultiEdit", hook: "close-terminal-on-ship", timeout: 5 },
|
|
25805
26014
|
{ event: "PostToolUse", matcher: "Edit|Write|MultiEdit", hook: "scripts/stamp-provenance-on-edit.mjs", timeout: 5 },
|
|
@@ -25884,9 +26093,22 @@ var CUSTOMER_LAUNCHERS = Object.freeze({
|
|
|
25884
26093
|
});
|
|
25885
26094
|
function customerRunnerPrefix(_platformRoot, surface = "claude") {
|
|
25886
26095
|
const l = CUSTOMER_LAUNCHERS[surface];
|
|
26096
|
+
if (surface === "claude") return CUSTOMER_RUNNER_PREFIX;
|
|
25887
26097
|
return `node "$${l.projectDirEnv}/${l.rel.join("/")}"`;
|
|
25888
26098
|
}
|
|
25889
|
-
var
|
|
26099
|
+
var BOOTSTRAP_WALK = "var d=process.cwd(),r=e.CLAUDE_PROJECT_DIR;if(!r||!f.existsSync(p.join(r,...n))){r='';for(;;){if(f.existsSync(p.join(d,...n))){r=d;break}var q=p.dirname(d);if(q===d)break;d=q}}";
|
|
26100
|
+
function buildBootstrap({ plugin = false } = {}) {
|
|
26101
|
+
const walk = BOOTSTRAP_WALK;
|
|
26102
|
+
const body = plugin ? "var f=require('fs'),p=require('path'),u=require('url'),e=process.env,n=['scripts','claude-hooks','_run.mjs'],s='',m=e.CLAUDE_PLUGIN_ROOT||e.COPILOT_PLUGIN_ROOT||e.PLUGIN_ROOT;if(m&&f.existsSync(p.join(m,'bin','run-hook.mjs')))s=p.join(m,'bin','run-hook.mjs');if(!s){" + walk + "if(r){e.CLAUDE_PROJECT_DIR=r;s=p.join(r,...n)}}" : "var f=require('fs'),p=require('path'),u=require('url'),e=process.env,n=['scripts','claude-hooks','_run.mjs'],s='';" + walk + "if(r){e.CLAUDE_PROJECT_DIR=r;s=p.join(r,...n)}";
|
|
26103
|
+
return body + "if(!s)process.exit(0);process.argv=[process.argv[0],s].concat(process.argv.slice(1));import(u.pathToFileURL(s).href).catch(function(){process.exit(0)});" + (plugin ? "//bin/run-hook.mjs" : "//scripts/claude-hooks/_run.mjs");
|
|
26104
|
+
}
|
|
26105
|
+
var PLATFORM_RUNNER_PREFIX = 'node -e "' + buildBootstrap() + '"';
|
|
26106
|
+
var PLUGIN_RUNNER_PREFIX = 'node -e "' + buildBootstrap({ plugin: true }) + '"';
|
|
26107
|
+
function buildCustomerBootstrap() {
|
|
26108
|
+
const rel = CUSTOMER_LAUNCHERS.claude.rel.map((s) => "'" + s + "'").join(",");
|
|
26109
|
+
return "var f=require('fs'),p=require('path'),u=require('url'),e=process.env,n=[" + rel + "],s='';" + BOOTSTRAP_WALK + "if(r){e.CLAUDE_PROJECT_DIR=r;s=p.join(r,...n)}if(!s)process.exit(0);process.argv=[process.argv[0],s].concat(process.argv.slice(1));import(u.pathToFileURL(s).href).catch(function(){process.exit(0)});//" + CUSTOMER_LAUNCHERS.claude.rel.join("/");
|
|
26110
|
+
}
|
|
26111
|
+
var CUSTOMER_RUNNER_PREFIX = 'node -e "' + buildCustomerBootstrap() + '"';
|
|
25890
26112
|
var LAUNCHER_TEMPLATE = [
|
|
25891
26113
|
"#!/usr/bin/env node",
|
|
25892
26114
|
"// HeySpex hook launcher -- GENERATED by hs install / hs connect. Commit it with this repo.",
|
|
@@ -25964,7 +26186,12 @@ var LAUNCHER_TEMPLATE = [
|
|
|
25964
26186
|
"// (the Cursor one) runs exactly as if Cursor had invoked it directly.",
|
|
25965
26187
|
"const shim = resolve(root, ...SHIM);",
|
|
25966
26188
|
"process.argv[1] = shim;",
|
|
25967
|
-
"
|
|
26189
|
+
"try {",
|
|
26190
|
+
" await import(pathToFileURL(shim).href);",
|
|
26191
|
+
"} catch (err) {",
|
|
26192
|
+
" process.stderr.write('[heyspex] hook ' + (process.argv[2] ?? '?') + ' skipped: ' + (err && err.message ? err.message : err) + '\\n');",
|
|
26193
|
+
" process.exit(0);",
|
|
26194
|
+
"}",
|
|
25968
26195
|
""
|
|
25969
26196
|
].join("\n");
|
|
25970
26197
|
function renderCustomerLauncher(surface = "claude") {
|
|
@@ -26071,7 +26298,6 @@ function renderPluginHooksDocument() {
|
|
|
26071
26298
|
});
|
|
26072
26299
|
}
|
|
26073
26300
|
return {
|
|
26074
|
-
_comment: "GENERATED from scripts/lib/customer-kit.mjs (customer-scoped rows only) \u2014 do not edit by hand; run `node scripts/lib/customer-kit.mjs --write-plugin-hooks`. Every command goes through bin/run-hook.mjs, which finds the hook runtime (HEYSPEX_PLATFORM_ROOT / HEYSPEX_MCP_ENTRY when set in the machine's own env, then the plugin's own location, node_modules/heyspex, $CLAUDE_PROJECT_DIR) \u2014 never $CLAUDE_PROJECT_DIR/scripts, which only exists in heyspex-app. WARNING: a repo whose .claude/settings.json already carries the hs install kit hooks runs every hook twice if this plugin is also enabled \u2014 use one or the other.",
|
|
26075
26301
|
hooks
|
|
26076
26302
|
};
|
|
26077
26303
|
}
|
|
@@ -26079,6 +26305,75 @@ function renderPluginHooksText() {
|
|
|
26079
26305
|
return `${JSON.stringify(renderPluginHooksDocument(), null, 2)}
|
|
26080
26306
|
`;
|
|
26081
26307
|
}
|
|
26308
|
+
var COPILOT_EVENT_NAMES = Object.freeze({
|
|
26309
|
+
SessionStart: "sessionStart",
|
|
26310
|
+
SessionEnd: "sessionEnd",
|
|
26311
|
+
UserPromptSubmit: "userPromptSubmitted",
|
|
26312
|
+
PreToolUse: "preToolUse",
|
|
26313
|
+
PostToolUse: "postToolUse",
|
|
26314
|
+
Stop: "agentStop",
|
|
26315
|
+
PreCompact: "preCompact"
|
|
26316
|
+
});
|
|
26317
|
+
var GITHUB_HOOKS_STATE_ARMING = Object.freeze(["intent-classifier-trigger"]);
|
|
26318
|
+
var COPILOT_TOOL_NAMES = Object.freeze({
|
|
26319
|
+
Write: "create",
|
|
26320
|
+
Edit: "edit",
|
|
26321
|
+
MultiEdit: "edit",
|
|
26322
|
+
NotebookEdit: "edit",
|
|
26323
|
+
Bash: "bash",
|
|
26324
|
+
PowerShell: "powershell",
|
|
26325
|
+
Agent: "task",
|
|
26326
|
+
Task: "task",
|
|
26327
|
+
WebFetch: "web_fetch",
|
|
26328
|
+
WebSearch: "web_search"
|
|
26329
|
+
});
|
|
26330
|
+
function toCopilotMatcher(matcher) {
|
|
26331
|
+
if (!matcher) return void 0;
|
|
26332
|
+
const out = [];
|
|
26333
|
+
for (const part of matcher.split("|")) {
|
|
26334
|
+
let t = null;
|
|
26335
|
+
if (part === ".*") t = ".*";
|
|
26336
|
+
else if (COPILOT_TOOL_NAMES[part]) t = COPILOT_TOOL_NAMES[part];
|
|
26337
|
+
else if (part.startsWith(MCP_PROJECT_PREFIX)) t = ".*" + part.slice(MCP_PROJECT_PREFIX.length);
|
|
26338
|
+
else if (part.startsWith(MCP_PLUGIN_PREFIX)) t = ".*" + part.slice(MCP_PLUGIN_PREFIX.length);
|
|
26339
|
+
if (t && !out.includes(t)) out.push(t);
|
|
26340
|
+
}
|
|
26341
|
+
if (out.includes(".*")) return ".*";
|
|
26342
|
+
return out.length ? out.join("|") : null;
|
|
26343
|
+
}
|
|
26344
|
+
function githubHookNames() {
|
|
26345
|
+
const names = /* @__PURE__ */ new Set();
|
|
26346
|
+
for (const r of customerHookRows()) {
|
|
26347
|
+
if (dispositionFor(r.hook, "copilot")?.disposition === "mapped" || GITHUB_HOOKS_STATE_ARMING.includes(r.hook)) names.add(r.hook);
|
|
26348
|
+
}
|
|
26349
|
+
return names;
|
|
26350
|
+
}
|
|
26351
|
+
function githubHookCommand(row2) {
|
|
26352
|
+
const tail = hookTail(row2);
|
|
26353
|
+
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}`;
|
|
26354
|
+
}
|
|
26355
|
+
function renderGithubHooksDocument() {
|
|
26356
|
+
const allowed = githubHookNames();
|
|
26357
|
+
const hooks = {};
|
|
26358
|
+
for (const row2 of customerHookRows()) {
|
|
26359
|
+
if (!allowed.has(row2.hook)) continue;
|
|
26360
|
+
const event = COPILOT_EVENT_NAMES[row2.event];
|
|
26361
|
+
if (!event) continue;
|
|
26362
|
+
const matcher = toCopilotMatcher(row2.matcher);
|
|
26363
|
+
if (matcher === null) continue;
|
|
26364
|
+
(hooks[event] ??= []).push({
|
|
26365
|
+
type: "command",
|
|
26366
|
+
...matcher ? { matcher } : {},
|
|
26367
|
+
bash: githubHookCommand(row2),
|
|
26368
|
+
timeoutSec: row2.timeout
|
|
26369
|
+
});
|
|
26370
|
+
}
|
|
26371
|
+
return { version: 1, hooks };
|
|
26372
|
+
}
|
|
26373
|
+
function renderGithubHooksText() {
|
|
26374
|
+
return `${JSON.stringify(renderGithubHooksDocument(), null, 2)}
|
|
26375
|
+
`;
|
|
26376
|
+
}
|
|
26082
26377
|
var HARNESS_COMMAND_SURFACES = Object.freeze([
|
|
26083
26378
|
{
|
|
26084
26379
|
installedDir: Object.freeze([".claude", "commands"]),
|
|
@@ -26211,21 +26506,35 @@ if (invokedDirectly(import.meta.url, "customer-kit.mjs")) {
|
|
|
26211
26506
|
process.exit(1);
|
|
26212
26507
|
}
|
|
26213
26508
|
console.log("customer-kit: plugins/heyspex/hooks/hooks.json up to date");
|
|
26509
|
+
} else if (process.argv.includes("--write-github-hooks") || process.argv.includes("--check-github-hooks")) {
|
|
26510
|
+
const ghTarget = path2.join(platformRoot, ".github", "hooks", "heyspex.json");
|
|
26511
|
+
const ghWant = renderGithubHooksText();
|
|
26512
|
+
const ghSame = (fsDeps.readFile(ghTarget) ?? "").replace(/\r\n/g, "\n") === ghWant;
|
|
26513
|
+
if (process.argv.includes("--write-github-hooks")) {
|
|
26514
|
+
if (!ghSame) {
|
|
26515
|
+
fs.mkdirSync(path2.dirname(ghTarget), { recursive: true });
|
|
26516
|
+
fs.writeFileSync(ghTarget, ghWant, "utf8");
|
|
26517
|
+
}
|
|
26518
|
+
console.log(`customer-kit: ${ghSame ? "unchanged" : "wrote"} ${path2.relative(platformRoot, ghTarget).replace(/\\/g, "/")}`);
|
|
26519
|
+
} else if (!ghSame) {
|
|
26520
|
+
console.error("customer-kit: .github/hooks/heyspex.json is stale - run `node scripts/lib/customer-kit.mjs --write-github-hooks`");
|
|
26521
|
+
process.exit(1);
|
|
26522
|
+
} else {
|
|
26523
|
+
console.log("customer-kit: .github/hooks/heyspex.json up to date");
|
|
26524
|
+
}
|
|
26214
26525
|
} else {
|
|
26215
26526
|
console.log("usage: node scripts/lib/customer-kit.mjs --write-plugin-hooks | --check-plugin-hooks");
|
|
26216
26527
|
}
|
|
26217
26528
|
}
|
|
26218
26529
|
|
|
26219
26530
|
// spex-console/src/lib/cli/connect-checks.ts
|
|
26220
|
-
import { homedir as homedir9 } from "node:os";
|
|
26221
26531
|
import { dirname as dirname5 } from "node:path";
|
|
26222
26532
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
26223
26533
|
|
|
26224
26534
|
// spex-console/src/lib/dev-mode.ts
|
|
26225
|
-
init_credentials();
|
|
26226
26535
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
26227
|
-
import { homedir as homedir6 } from "node:os";
|
|
26228
26536
|
import { join as join6 } from "node:path";
|
|
26537
|
+
init_credentials();
|
|
26229
26538
|
var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
|
|
26230
26539
|
var LOCAL_CONSOLE_URL2 = "http://localhost:3000";
|
|
26231
26540
|
function normalizeMode2(raw) {
|
|
@@ -26258,12 +26567,12 @@ function deriveSemantics2(mode, reliance) {
|
|
|
26258
26567
|
vaultWriteMode: local ? "local" : "cloud"
|
|
26259
26568
|
};
|
|
26260
26569
|
}
|
|
26261
|
-
function devModeFilePath2(homeDir =
|
|
26570
|
+
function devModeFilePath2(homeDir = heyspexHome()) {
|
|
26262
26571
|
return join6(homeDir, ".heyspex", "dev-mode.json");
|
|
26263
26572
|
}
|
|
26264
26573
|
var defaultReadFile3 = (p) => readFileSync7(p, "utf8");
|
|
26265
26574
|
function readLocalDevModeRecord2({
|
|
26266
|
-
homeDir =
|
|
26575
|
+
homeDir = heyspexHome(),
|
|
26267
26576
|
readFile: readFile3 = defaultReadFile3
|
|
26268
26577
|
} = {}) {
|
|
26269
26578
|
let raw;
|
|
@@ -26298,7 +26607,7 @@ function readLocalDevModeRecord2({
|
|
|
26298
26607
|
}
|
|
26299
26608
|
function resolveDevMode2({
|
|
26300
26609
|
env = process.env,
|
|
26301
|
-
homeDir =
|
|
26610
|
+
homeDir = heyspexHome(),
|
|
26302
26611
|
readFile: readFile3 = defaultReadFile3,
|
|
26303
26612
|
cloudSetting = void 0
|
|
26304
26613
|
} = {}) {
|
|
@@ -26409,10 +26718,11 @@ function shellQuote(p) {
|
|
|
26409
26718
|
var HS_DISPATCHER_PREFIX = HS_DISPATCHER_PATH ? `node ${shellQuote(HS_DISPATCHER_PATH)}` : HS_BIN_NAME;
|
|
26410
26719
|
|
|
26411
26720
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
26721
|
+
init_heyspex_home();
|
|
26412
26722
|
init_credentials();
|
|
26413
|
-
import { readFileSync as
|
|
26723
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
26414
26724
|
import { homedir as homedir8 } from "node:os";
|
|
26415
|
-
import { join as
|
|
26725
|
+
import { join as join10 } from "node:path";
|
|
26416
26726
|
|
|
26417
26727
|
// vendor/heyspex-mcp/vault-less-endpoints.mjs
|
|
26418
26728
|
var VAULT_LESS_ENDPOINTS = Object.freeze([
|
|
@@ -26438,10 +26748,41 @@ function isVaultLessEndpoint(method, path9) {
|
|
|
26438
26748
|
});
|
|
26439
26749
|
}
|
|
26440
26750
|
|
|
26751
|
+
// vendor/heyspex-mcp/write-outbox.mjs
|
|
26752
|
+
import {
|
|
26753
|
+
readdirSync,
|
|
26754
|
+
readFileSync as readFileSync8,
|
|
26755
|
+
writeFileSync as writeFileSync5,
|
|
26756
|
+
mkdirSync as mkdirSync5,
|
|
26757
|
+
existsSync as existsSync3,
|
|
26758
|
+
renameSync as renameSync3,
|
|
26759
|
+
rmSync
|
|
26760
|
+
} from "node:fs";
|
|
26761
|
+
import { basename, join as join8, isAbsolute as isAbsolute2 } from "node:path";
|
|
26762
|
+
|
|
26763
|
+
// scripts/lib/shared-path-helpers.mjs
|
|
26764
|
+
import { isAbsolute, resolve as resolve3, sep } from "node:path";
|
|
26765
|
+
function resolveCodeRoot({
|
|
26766
|
+
explicit,
|
|
26767
|
+
packageRoot,
|
|
26768
|
+
vaultRoot,
|
|
26769
|
+
env = process.env
|
|
26770
|
+
} = {}) {
|
|
26771
|
+
const fromExplicit = typeof explicit === "string" ? explicit.trim() : "";
|
|
26772
|
+
if (fromExplicit) return fromExplicit;
|
|
26773
|
+
const fromEnv = typeof env?.HS_PKG_ROOT === "string" ? env.HS_PKG_ROOT.trim() : "";
|
|
26774
|
+
if (fromEnv) return fromEnv;
|
|
26775
|
+
const fromPackage = typeof packageRoot === "string" ? packageRoot.trim() : "";
|
|
26776
|
+
if (fromPackage) return fromPackage;
|
|
26777
|
+
const fromVault = typeof vaultRoot === "string" ? vaultRoot.trim() : "";
|
|
26778
|
+
return fromVault;
|
|
26779
|
+
}
|
|
26780
|
+
|
|
26441
26781
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
26442
|
-
|
|
26782
|
+
init_heyspex_home();
|
|
26783
|
+
import { existsSync as existsSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
26443
26784
|
import { homedir as homedir7, tmpdir } from "node:os";
|
|
26444
|
-
import { dirname as dirname4, join as
|
|
26785
|
+
import { dirname as dirname4, join as join9, resolve as resolve4 } from "node:path";
|
|
26445
26786
|
|
|
26446
26787
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
26447
26788
|
var SCOPE_REFUSAL_CODES = Object.freeze(["VAULT_SCOPE_MISMATCH", "ACCOUNT_MISMATCH", "VAULT_NOT_REGISTERED"]);
|
|
@@ -26749,7 +27090,7 @@ function runInit(args, deps) {
|
|
|
26749
27090
|
};
|
|
26750
27091
|
}
|
|
26751
27092
|
const vaultId = existingVaultId ?? args.adoptedVaultId ?? mintVaultId();
|
|
26752
|
-
const accountId =
|
|
27093
|
+
const accountId = null;
|
|
26753
27094
|
const content = renderHeyspexYaml({ ...args, vaultId, accountId });
|
|
26754
27095
|
try {
|
|
26755
27096
|
deps.writeFile(markerPath, content);
|
|
@@ -27015,6 +27356,62 @@ function isAbsolutePathForCompare(p) {
|
|
|
27015
27356
|
const s = p.trim();
|
|
27016
27357
|
return /^[\\/]/.test(s) || /^[a-zA-Z]:[\\/]/.test(s);
|
|
27017
27358
|
}
|
|
27359
|
+
function expandPinPlaceholders(value, repoRoot) {
|
|
27360
|
+
return value.replace(/\$\{(?:CLAUDE_PROJECT_DIR|workspaceFolder)(?::-[^}]*)?\}/g, repoRoot);
|
|
27361
|
+
}
|
|
27362
|
+
function lexicalJoin(base, rel) {
|
|
27363
|
+
const parts = `${base}/${rel}`.replace(/\\/g, "/").split("/");
|
|
27364
|
+
const out = [];
|
|
27365
|
+
for (const seg of parts) {
|
|
27366
|
+
if (seg === "" && out.length > 0) continue;
|
|
27367
|
+
if (seg === ".") continue;
|
|
27368
|
+
if (seg === ".." && out.length > 1) {
|
|
27369
|
+
out.pop();
|
|
27370
|
+
continue;
|
|
27371
|
+
}
|
|
27372
|
+
out.push(seg);
|
|
27373
|
+
}
|
|
27374
|
+
return out.join("/");
|
|
27375
|
+
}
|
|
27376
|
+
function parseMarkerVaultPath(content) {
|
|
27377
|
+
const m = content.match(/^vault_path:[ \t]*(.+?)[ \t]*$/m);
|
|
27378
|
+
if (!m) return null;
|
|
27379
|
+
const v = m[1].trim().replace(/\s+#.*$/, "").replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1").trim();
|
|
27380
|
+
if (!v || v.includes("${") || /^[\\/]/.test(v) || /^[a-zA-Z]:[\\/]/.test(v)) return null;
|
|
27381
|
+
return v;
|
|
27382
|
+
}
|
|
27383
|
+
function markerVaultIdOf(content) {
|
|
27384
|
+
const m = content.match(/^vault_id:\s*(.+?)\s*$/m);
|
|
27385
|
+
if (!m) return null;
|
|
27386
|
+
const v = m[1].trim().replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
|
|
27387
|
+
return v.length > 0 ? v : null;
|
|
27388
|
+
}
|
|
27389
|
+
function detectMarkerAttachment(repoRoot, deps) {
|
|
27390
|
+
const markerPath = deps.joinPath(repoRoot, "heyspex.yaml");
|
|
27391
|
+
if (!deps.fileExists(markerPath)) return null;
|
|
27392
|
+
const raw = deps.readFile(markerPath);
|
|
27393
|
+
const vaultPath = raw ? parseMarkerVaultPath(raw) : null;
|
|
27394
|
+
if (!raw || !vaultPath) return null;
|
|
27395
|
+
const vaultRootAbs = lexicalJoin(repoRoot, vaultPath);
|
|
27396
|
+
const markerId = markerVaultIdOf(raw);
|
|
27397
|
+
let cloned = deps.fileExists(deps.joinPath(vaultRootAbs, "specs"));
|
|
27398
|
+
if (cloned && markerId) {
|
|
27399
|
+
const theirs = deps.readFile(deps.joinPath(vaultRootAbs, "heyspex.yaml"));
|
|
27400
|
+
const theirId = theirs ? markerVaultIdOf(theirs) : null;
|
|
27401
|
+
if (theirId && theirId !== markerId) cloned = false;
|
|
27402
|
+
}
|
|
27403
|
+
const registry = loadVaultRegistry(deps.registryOpts);
|
|
27404
|
+
const record = registry.vaults.find((v) => samePath(v.repo_root_abs, vaultRootAbs));
|
|
27405
|
+
return {
|
|
27406
|
+
vaultRootAbs,
|
|
27407
|
+
vaultId: markerId ?? record?.id ?? null,
|
|
27408
|
+
pins: [{ source: "heyspex.yaml", value: vaultRootAbs }],
|
|
27409
|
+
conflicting: false,
|
|
27410
|
+
viaMarker: true,
|
|
27411
|
+
cloned,
|
|
27412
|
+
vaultPath
|
|
27413
|
+
};
|
|
27414
|
+
}
|
|
27018
27415
|
function readPinnedVaultRoot(repoRoot, deps) {
|
|
27019
27416
|
const pins = [];
|
|
27020
27417
|
for (const rel of SATELLITE_MANIFEST_RELS) {
|
|
@@ -27031,13 +27428,19 @@ function readPinnedVaultRoot(repoRoot, deps) {
|
|
|
27031
27428
|
}
|
|
27032
27429
|
if (typeof value !== "string") continue;
|
|
27033
27430
|
const trimmed = value.trim();
|
|
27034
|
-
if (!trimmed
|
|
27035
|
-
|
|
27036
|
-
|
|
27431
|
+
if (!trimmed) continue;
|
|
27432
|
+
const expanded = expandPinPlaceholders(trimmed, repoRoot);
|
|
27433
|
+
if (expanded.includes("${")) continue;
|
|
27434
|
+
if (/^[a-zA-Z]:(?![\\/])/.test(expanded)) continue;
|
|
27435
|
+
const abs = isAbsolutePathForCompare(expanded) ? expanded.includes("..") ? lexicalJoin(expanded, ".") : expanded : lexicalJoin(repoRoot, expanded);
|
|
27436
|
+
if (expanded !== trimmed && isAtOrUnder(abs, repoRoot)) continue;
|
|
27437
|
+
pins.push({ source: rel.join("/"), value: abs });
|
|
27037
27438
|
}
|
|
27038
27439
|
return pins;
|
|
27039
27440
|
}
|
|
27040
27441
|
function detectSatelliteAttachment(repoRoot, deps) {
|
|
27442
|
+
const viaMarker = detectMarkerAttachment(repoRoot, deps);
|
|
27443
|
+
if (viaMarker) return viaMarker;
|
|
27041
27444
|
const pins = readPinnedVaultRoot(repoRoot, deps).filter(
|
|
27042
27445
|
(pin) => !isAtOrUnder(pin.value, repoRoot)
|
|
27043
27446
|
);
|
|
@@ -27153,7 +27556,7 @@ function workspaceAgreementRow(vaultRecord, cloudWorkspaceId) {
|
|
|
27153
27556
|
function checksHomeDir(deps) {
|
|
27154
27557
|
if (deps.homeDir) return deps.homeDir;
|
|
27155
27558
|
const registryDir = deps.registryOpts?.registryDir;
|
|
27156
|
-
return registryDir ? dirname5(registryDir) :
|
|
27559
|
+
return registryDir ? dirname5(registryDir) : heyspexHome();
|
|
27157
27560
|
}
|
|
27158
27561
|
function accountAgreementRow(yamlContent, vaultRecord, deps, opts) {
|
|
27159
27562
|
const id = "account_agreement";
|
|
@@ -27161,14 +27564,14 @@ function accountAgreementRow(yamlContent, vaultRecord, deps, opts) {
|
|
|
27161
27564
|
if (yamlContent === null) {
|
|
27162
27565
|
return { id, label, status: "not_applicable", detail: "no heyspex.yaml yet" };
|
|
27163
27566
|
}
|
|
27164
|
-
const
|
|
27567
|
+
const legacyMarkerAccount = parseAccountId(yamlContent);
|
|
27568
|
+
const recorded = vaultRecord && vaultRecord.account_id && vaultRecord.account_id !== LEGACY_REGISTRY_ACCOUNT_ID ? vaultRecord.account_id : null;
|
|
27165
27569
|
if (!recorded) {
|
|
27166
27570
|
return {
|
|
27167
27571
|
id,
|
|
27168
27572
|
label,
|
|
27169
|
-
status: "
|
|
27170
|
-
detail: "
|
|
27171
|
-
remedy: "hs connect"
|
|
27573
|
+
status: "not_applicable",
|
|
27574
|
+
detail: "the account binding is machine-local (the registry row), and none exists on this machine yet; the signed-in account is used" + (legacyMarkerAccount ? ` (legacy account: ${legacyMarkerAccount} in heyspex.yaml is ignored)` : "")
|
|
27172
27575
|
};
|
|
27173
27576
|
}
|
|
27174
27577
|
const cloud = opts.cloudVaultAccountId ?? null;
|
|
@@ -27196,28 +27599,25 @@ function accountAgreementRow(yamlContent, vaultRecord, deps, opts) {
|
|
|
27196
27599
|
remedy
|
|
27197
27600
|
};
|
|
27198
27601
|
}
|
|
27199
|
-
if (vaultRecord && vaultRecord.account_id !== recorded) {
|
|
27200
|
-
const legacy = vaultRecord.account_id === LEGACY_REGISTRY_ACCOUNT_ID;
|
|
27201
|
-
return {
|
|
27202
|
-
id,
|
|
27203
|
-
label,
|
|
27204
|
-
status: "stale",
|
|
27205
|
-
detail: `the registry row for this repo is keyed to ${legacy ? "no account (legacy)" : vaultRecord.account_id}, heyspex.yaml says ${recorded} -- run \`hs connect\` to backfill it`,
|
|
27206
|
-
remedy: "hs connect"
|
|
27207
|
-
};
|
|
27208
|
-
}
|
|
27209
27602
|
const current = resolveSessionOwnerUncached({
|
|
27210
27603
|
env,
|
|
27211
27604
|
homeDir,
|
|
27212
27605
|
readFile: (p) => readFile3(p) ?? ""
|
|
27213
27606
|
});
|
|
27214
27607
|
const signedIn = current && current !== recorded ? `; signed in as ${current} -- sessions here still use ${recorded}'s sign-in` : "";
|
|
27215
|
-
|
|
27608
|
+
const legacyNote = legacyMarkerAccount && legacyMarkerAccount !== recorded ? `; legacy account: ${legacyMarkerAccount} in heyspex.yaml is ignored` : "";
|
|
27609
|
+
return {
|
|
27610
|
+
id,
|
|
27611
|
+
label,
|
|
27612
|
+
status: "present",
|
|
27613
|
+
detail: `account: ${recorded} (machine-local)${signedIn}${legacyNote}`
|
|
27614
|
+
};
|
|
27216
27615
|
}
|
|
27217
27616
|
function runConnectChecks(repoRoot, deps, opts = {}) {
|
|
27218
27617
|
const checks = [];
|
|
27219
27618
|
const satellite = opts.satellite !== void 0 ? opts.satellite : detectSatelliteAttachment(repoRoot, deps);
|
|
27220
|
-
const
|
|
27619
|
+
const cloudOnly = Boolean(satellite?.viaMarker) && satellite?.cloned === false;
|
|
27620
|
+
const vaultSubject = satellite && !cloudOnly ? satellite.vaultRootAbs : repoRoot;
|
|
27221
27621
|
const yamlPath = deps.joinPath(vaultSubject, "heyspex.yaml");
|
|
27222
27622
|
const yamlContent = readIfExists(deps, yamlPath);
|
|
27223
27623
|
const vaultId = yamlContent ? parseVaultId(yamlContent) : null;
|
|
@@ -27245,11 +27645,12 @@ function runConnectChecks(repoRoot, deps, opts = {}) {
|
|
|
27245
27645
|
)
|
|
27246
27646
|
);
|
|
27247
27647
|
const workspaceRecord = vaultRecord ? registry.workspaces.find((w) => w.id === vaultRecord.workspace_id) : void 0;
|
|
27648
|
+
const ownVaultRegistered = Boolean(vaultId) && Boolean(vaultRecord) && vaultRecord?.id === vaultId;
|
|
27248
27649
|
checks.push(
|
|
27249
27650
|
row(
|
|
27250
27651
|
"workspace_registered",
|
|
27251
27652
|
"workspace registered in ~/.heyspex/registry.json",
|
|
27252
|
-
Boolean(workspaceRecord),
|
|
27653
|
+
Boolean(workspaceRecord) || ownVaultRegistered,
|
|
27253
27654
|
satelliteDetail(
|
|
27254
27655
|
satellite,
|
|
27255
27656
|
workspaceRecord ? `id: ${workspaceRecord.id}` : vaultRecord ? `vault's workspace "${vaultRecord.workspace_id}" not found in registry` : satellite ? "the attached vault is not registered on this machine" : "no vault registered for this repo"
|
|
@@ -27370,9 +27771,38 @@ function runConnectChecks(repoRoot, deps, opts = {}) {
|
|
|
27370
27771
|
id: "satellite_attachment",
|
|
27371
27772
|
label: "attached vault (satellite repo)",
|
|
27372
27773
|
status: satellite.conflicting ? "stale" : "present",
|
|
27373
|
-
detail: satellite.conflicting ? `manifests pin different vaults (${pinned}) -- re-run \`hs install --attach-vault <id> --force\` to make them agree` : `${satellite.vaultId ?? "(unregistered)"} at ${satellite.vaultRootAbs} (${pinned})`
|
|
27774
|
+
detail: satellite.viaMarker ? `${satellite.vaultId ?? "(no vault_id)"} via heyspex.yaml vault_path ${satellite.vaultPath} -> ${satellite.vaultRootAbs} (${satellite.cloned ? "cloned here" : "NOT cloned here: cloud-only mode"})` : satellite.conflicting ? `manifests pin different vaults (${pinned}) -- re-run \`hs install --attach-vault <id> --force\` to make them agree` : `${satellite.vaultId ?? "(unregistered)"} at ${satellite.vaultRootAbs} (${pinned})`
|
|
27374
27775
|
});
|
|
27375
27776
|
}
|
|
27777
|
+
if (satellite?.viaMarker) {
|
|
27778
|
+
const na = (id, detail) => {
|
|
27779
|
+
const r = checks.find((c) => c.id === id);
|
|
27780
|
+
if (r && (r.status === "missing" || r.status === "stale")) {
|
|
27781
|
+
r.status = "not_applicable";
|
|
27782
|
+
r.detail = detail;
|
|
27783
|
+
delete r.remedy;
|
|
27784
|
+
}
|
|
27785
|
+
};
|
|
27786
|
+
const noRow = "not required: this repo's committed heyspex.yaml binds the vault by id (a registry row appears after `hs connect` in the vault clone)";
|
|
27787
|
+
na("vault_registered", noRow);
|
|
27788
|
+
na("workspace_registered", noRow);
|
|
27789
|
+
na("id_agreement", noRow);
|
|
27790
|
+
na("account_agreement", "portable marker records no account; the signed-in account is used");
|
|
27791
|
+
if (cloudOnly) {
|
|
27792
|
+
na(
|
|
27793
|
+
"specs_dir",
|
|
27794
|
+
`satellite, vault not cloned locally -- cloud mode (clone the vault repo at ${satellite.vaultPath ?? "its vault_path"} for local reads and \`hs pull\`)`
|
|
27795
|
+
);
|
|
27796
|
+
}
|
|
27797
|
+
if (!vaultId) {
|
|
27798
|
+
const y = checks.find((c) => c.id === "heyspex_yaml");
|
|
27799
|
+
if (y) {
|
|
27800
|
+
y.status = "stale";
|
|
27801
|
+
y.detail = "heyspex.yaml has a vault_path but no vault_id; the satellite cannot name its vault";
|
|
27802
|
+
y.remedy = "add `vault_id: <the vault's uuid>` to heyspex.yaml (find it with `hs vault list`), then re-run `hs connect --check`";
|
|
27803
|
+
}
|
|
27804
|
+
}
|
|
27805
|
+
}
|
|
27376
27806
|
const connected = checks.every((c) => c.status === "present" || c.status === "not_applicable");
|
|
27377
27807
|
return {
|
|
27378
27808
|
repo_root: repoRoot,
|
|
@@ -27381,7 +27811,8 @@ function runConnectChecks(repoRoot, deps, opts = {}) {
|
|
|
27381
27811
|
...satellite ? {
|
|
27382
27812
|
satellite: {
|
|
27383
27813
|
attached_vault_root: satellite.vaultRootAbs,
|
|
27384
|
-
attached_vault_id: satellite.vaultId
|
|
27814
|
+
attached_vault_id: satellite.vaultId,
|
|
27815
|
+
...satellite.viaMarker ? { via_marker: true, cloned: satellite.cloned === true } : {}
|
|
27385
27816
|
}
|
|
27386
27817
|
} : {}
|
|
27387
27818
|
};
|
|
@@ -27507,6 +27938,22 @@ function pointerVsRepoVerdict(input) {
|
|
|
27507
27938
|
repair: `hs vault switch ${repoVault}`,
|
|
27508
27939
|
detail: `the cloud pointer names ${cloudPointer ?? "(none)"} but the registry pointer (its local cache) names ${registryPointer}: the cache is stale`
|
|
27509
27940
|
});
|
|
27941
|
+
if (markerVaultId && input.markerBoundSatellite && !registryRowId) {
|
|
27942
|
+
return {
|
|
27943
|
+
branch: "explicit_vault",
|
|
27944
|
+
agree: true,
|
|
27945
|
+
repair: null,
|
|
27946
|
+
detail: `this marker-bound satellite names ${markerVaultId} in its committed heyspex.yaml, so CLI and MCP requests route to it by id; no registry row is needed and the account pointer (${cloudPointer ?? "(none)"}) is informational`
|
|
27947
|
+
};
|
|
27948
|
+
}
|
|
27949
|
+
if (markerVaultId && registryRowId === markerVaultId) {
|
|
27950
|
+
return {
|
|
27951
|
+
branch: "explicit_vault",
|
|
27952
|
+
agree: true,
|
|
27953
|
+
repair: null,
|
|
27954
|
+
detail: cloudPointer === markerVaultId && (registryPointer === null || registryPointer === markerVaultId) ? `both pointers name ${markerVaultId}` : `the account's active pointer names ${cloudPointer ?? "(none)"} (the browser console's choice${registryPointer !== null && registryPointer !== cloudPointer ? `; the local cache names ${registryPointer}` : ""}); this repo names its own vault ${markerVaultId} explicitly in heyspex.yaml, so CLI and MCP requests route to it and the difference is informational (optional: \`hs vault switch ${markerVaultId}\`)`
|
|
27955
|
+
};
|
|
27956
|
+
}
|
|
27510
27957
|
if (markerVaultId) {
|
|
27511
27958
|
if (registryRowId !== markerVaultId) {
|
|
27512
27959
|
const rekey = ROUTING_FACTS.find((f) => f.id === "C")?.caches.find((c) => c.id === "registry-vault-id")?.repair ?? "hs vault migrate-registry";
|
|
@@ -27722,7 +28169,7 @@ async function readCloudView(http, tokens) {
|
|
|
27722
28169
|
}
|
|
27723
28170
|
async function runCloudViewChecks(repoRoot, deps, opts = {}) {
|
|
27724
28171
|
const satellite = opts.satellite !== void 0 ? opts.satellite : detectSatelliteAttachment(repoRoot, deps);
|
|
27725
|
-
const vaultSubject = satellite ? satellite.vaultRootAbs : repoRoot;
|
|
28172
|
+
const vaultSubject = satellite && !(satellite.viaMarker && satellite.cloned === false) ? satellite.vaultRootAbs : repoRoot;
|
|
27726
28173
|
const marker = readIfExists(deps, deps.joinPath(vaultSubject, "heyspex.yaml"));
|
|
27727
28174
|
const vaultId = marker ? parseVaultId(marker) : null;
|
|
27728
28175
|
const env = deps.env ?? process.env;
|
|
@@ -27733,7 +28180,11 @@ async function runCloudViewChecks(repoRoot, deps, opts = {}) {
|
|
|
27733
28180
|
homeDir,
|
|
27734
28181
|
readFile: (p) => readFile3(p) ?? ""
|
|
27735
28182
|
});
|
|
27736
|
-
const
|
|
28183
|
+
const rowForAccount = loadVaultRegistry(deps.registryOpts).vaults.find(
|
|
28184
|
+
(v) => samePath(v.repo_root_abs, vaultSubject)
|
|
28185
|
+
);
|
|
28186
|
+
const boundAccount = rowForAccount?.account_id && rowForAccount.account_id !== LEGACY_REGISTRY_ACCOUNT_ID ? rowForAccount.account_id : null;
|
|
28187
|
+
const repoAccount = boundAccount ?? signedInLocal ?? null;
|
|
27737
28188
|
const http = deps.http;
|
|
27738
28189
|
let tokens = { current: null, repo: null };
|
|
27739
28190
|
if (http && http.token !== void 0) tokens = { current: http.token, repo: http.token };
|
|
@@ -27767,7 +28218,8 @@ async function runCloudViewChecks(repoRoot, deps, opts = {}) {
|
|
|
27767
28218
|
markerVaultId: vaultId,
|
|
27768
28219
|
registryRowId: vaultRecord?.id ?? null,
|
|
27769
28220
|
cloudPointer: view.cloudPointer,
|
|
27770
|
-
registryPointer: localPointer
|
|
28221
|
+
registryPointer: localPointer,
|
|
28222
|
+
markerBoundSatellite: Boolean(satellite?.viaMarker)
|
|
27771
28223
|
});
|
|
27772
28224
|
rows.push(
|
|
27773
28225
|
compared(
|
|
@@ -27840,6 +28292,19 @@ async function runCloudViewChecks(repoRoot, deps, opts = {}) {
|
|
|
27840
28292
|
rows.push(notApplicableRow(id, "no heyspex.yaml and no signed-in account"));
|
|
27841
28293
|
} else if (view.sessionFailure) {
|
|
27842
28294
|
rows.push(unknownRow(id, repoAccount, view.sessionFailure));
|
|
28295
|
+
} else if (!repoAccount && satellite?.viaMarker) {
|
|
28296
|
+
rows.push(
|
|
28297
|
+
compared(
|
|
28298
|
+
id,
|
|
28299
|
+
{
|
|
28300
|
+
local: "(portable marker: no account recorded)",
|
|
28301
|
+
cloud: view.email ? `${view.userId} (${view.email})` : view.userId
|
|
28302
|
+
},
|
|
28303
|
+
true,
|
|
28304
|
+
"hs login",
|
|
28305
|
+
`portable marker; signed in as ${view.email ?? view.userId}`
|
|
28306
|
+
)
|
|
28307
|
+
);
|
|
27843
28308
|
} else {
|
|
27844
28309
|
const agree = repoAccount !== null && (repoAccount === view.userId || repoAccount === view.email);
|
|
27845
28310
|
rows.push(
|
|
@@ -27977,7 +28442,7 @@ function writeProjectContract(ctx, options2 = {}) {
|
|
|
27977
28442
|
}
|
|
27978
28443
|
|
|
27979
28444
|
// spex-console/src/lib/cli/init.customer-vault.ts
|
|
27980
|
-
import { basename } from "node:path";
|
|
28445
|
+
import { basename as basename2 } from "node:path";
|
|
27981
28446
|
|
|
27982
28447
|
// spex-console/src/lib/cli/template-manifest.sources.mjs
|
|
27983
28448
|
var UNIVERSAL_TEMPLATE_ROWS = [
|
|
@@ -28488,7 +28953,7 @@ function findManagedSpecs(deps, targetDir, manifestDests) {
|
|
|
28488
28953
|
return out;
|
|
28489
28954
|
}
|
|
28490
28955
|
function defaultVaultName(targetDir) {
|
|
28491
|
-
return
|
|
28956
|
+
return basename2(targetDir) || targetDir;
|
|
28492
28957
|
}
|
|
28493
28958
|
function runCustomerVaultInit(args, deps, options2 = {}) {
|
|
28494
28959
|
const { targetDir } = args;
|
|
@@ -28636,14 +29101,14 @@ function templatePinHook() {
|
|
|
28636
29101
|
}
|
|
28637
29102
|
|
|
28638
29103
|
// spex-console/src/lib/cli/principle-selection.ts
|
|
28639
|
-
import { basename as
|
|
29104
|
+
import { basename as basename3 } from "node:path";
|
|
28640
29105
|
var PRINCIPLES_INDEX_DEST = "specs/principles-index.md";
|
|
28641
29106
|
function principleKey(row2) {
|
|
28642
|
-
const base =
|
|
29107
|
+
const base = basename3(row2.dest, ".md");
|
|
28643
29108
|
return base.startsWith("principle-") ? base.slice("principle-".length) : base;
|
|
28644
29109
|
}
|
|
28645
29110
|
function principleWikilink(row2) {
|
|
28646
|
-
return
|
|
29111
|
+
return basename3(row2.dest, ".md");
|
|
28647
29112
|
}
|
|
28648
29113
|
function parsePrinciplesFlag(value, rows) {
|
|
28649
29114
|
const principleRows = rows.filter((row2) => row2.group === "principle");
|
|
@@ -28754,7 +29219,7 @@ function applyCustomerTemplate(args) {
|
|
|
28754
29219
|
}
|
|
28755
29220
|
|
|
28756
29221
|
// spex-console/src/lib/cli/install.ts
|
|
28757
|
-
import {
|
|
29222
|
+
import { isAbsolute as isAbsolute3, resolve as resolveNodePath2 } from "node:path";
|
|
28758
29223
|
|
|
28759
29224
|
// scripts/install-cursor-hooks.mjs
|
|
28760
29225
|
import fs2 from "node:fs";
|
|
@@ -28771,10 +29236,13 @@ var HOOKS = [
|
|
|
28771
29236
|
{ event: "sessionStart", hook: "wip-checkpoint", why: "Surface the previous session WIP handoff before work resumes." },
|
|
28772
29237
|
{ event: "sessionStart", hook: "inject-dev-server-awareness", why: "Once-per-session reminder that dev-server status is a tool call, not a guess." },
|
|
28773
29238
|
{ event: "sessionStart", hook: "inject-coo-seat", why: "Inject the dev-factory COO seat mandate when the COO window is open." },
|
|
29239
|
+
{ event: "sessionStart", hook: "inject-doctrine", why: "Inject the baseline SpexCode Doctrine edition at session start." },
|
|
28774
29240
|
{ event: "sessionStart", hook: "coo-guard-check", why: "Warn at session start when a breakdown has not been delegated." },
|
|
28775
29241
|
{ event: "sessionStart", hook: "check-auto-memory-budget", why: "Self-heal and budget-check the auto-memory index." },
|
|
28776
29242
|
{ event: "sessionStart", hook: "check-claude-md-budget", why: "Advisory budget check on the always-loaded agent contract." },
|
|
28777
29243
|
{ event: "sessionStart", hook: "check-mcp-freshness-hook", why: "Warn when the running MCP server is stale versus vendor." },
|
|
29244
|
+
{ event: "sessionStart", hook: "git-reflog-audit", why: "Session-start git audit (stash/reset/rebase witness)." },
|
|
29245
|
+
{ event: "sessionStart", hook: "vault-mirror-catchup", why: "Cloud write mode: detached hs pull --apply closes a stale disk mirror (HS-XJFQ)." },
|
|
28778
29246
|
// ── Compaction ──────────────────────────────────────────────────────────
|
|
28779
29247
|
// Claude re-runs SessionStart(source: compact) after compaction; Cursor fires
|
|
28780
29248
|
// preCompact BEFORE it, so the shim relays this one as SessionStart(compact)
|
|
@@ -28788,6 +29256,7 @@ var HOOKS = [
|
|
|
28788
29256
|
{ event: "beforeSubmitPrompt", hook: "preflight-reminder", why: "Name the required procedure before a filing/destructive/protocol turn." },
|
|
28789
29257
|
{ event: "beforeSubmitPrompt", hook: "intent-classifier-trigger", why: "Route capture / research / settings / defer intents to the right skill." },
|
|
28790
29258
|
{ event: "beforeSubmitPrompt", hook: "inject-communication-style", why: "Re-assert the plain-language communication rules each turn." },
|
|
29259
|
+
{ event: "beforeSubmitPrompt", hook: "inject-vault-context", timeout: 10, why: "Lexical vault retrieval for the prompt (surfaced as a user message)." },
|
|
28791
29260
|
// ── Before every tool call (any tool) ───────────────────────────────────
|
|
28792
29261
|
{ event: "preToolUse", matcher: ".*", hook: "emit-claim-status", args: ["--event=PreToolUse"], why: "Claim liveness heartbeat to the console." },
|
|
28793
29262
|
{ event: "preToolUse", matcher: ".*", hook: "coo-denylist-gate", why: "Hard deny the COO-window deny list." },
|
|
@@ -28798,21 +29267,27 @@ var HOOKS = [
|
|
|
28798
29267
|
// the vault guard because removing a profile file is the loss it exists to
|
|
28799
29268
|
// prevent, and Claude had no separate delete tool to list.
|
|
28800
29269
|
{ event: "preToolUse", matcher: "Write|Delete", hook: "env-vault-guard", why: "Block destructive writes to the encrypted env vault (ALWAYS ON)." },
|
|
29270
|
+
{ event: "preToolUse", matcher: "Write", hook: "vault-write-tier-guard", why: "Deny disk writes to vault specs in cloud write mode (HS-FCNH)." },
|
|
28801
29271
|
{ event: "preToolUse", matcher: "Write", hook: "settings-write-gate", why: "Require approval before .heyspex/config.yaml changes." },
|
|
28802
29272
|
{ event: "preToolUse", matcher: "Write", hook: "enforce-vault-naming", why: "Block vault filenames that violate the naming registry." },
|
|
28803
29273
|
{ event: "preToolUse", matcher: "Write", hook: "guard-auto-memory-writes", why: "Block new topic files in the auto-memory directory." },
|
|
28804
29274
|
{ event: "preToolUse", matcher: "Write", hook: "preflight-reminder", why: "Preflight reminder on the edit path itself." },
|
|
28805
29275
|
{ event: "preToolUse", matcher: "Write", hook: "claim-before-code-gate", why: "Deny the first code edit after plan exit with no claim." },
|
|
29276
|
+
{ event: "preToolUse", matcher: "Write", hook: "directive-obligation-gate", why: "Deny product-code writes while a capture_on_idea obligation is unmet, and (claim-before-build) with no claim/capture (HS-20SW)." },
|
|
28806
29277
|
// ── Before a shell command ──────────────────────────────────────────────
|
|
28807
29278
|
// beforeShellExecution, not preToolUse/Shell: it is the only event guaranteed
|
|
28808
29279
|
// to carry the command string, and Cursor honors allow/deny/ask on it.
|
|
28809
29280
|
{ event: "beforeShellExecution", hook: "env-vault-guard", why: "Block shell writes/exfiltration targeting the env vault (ALWAYS ON)." },
|
|
29281
|
+
{ event: "beforeShellExecution", hook: "vault-write-tier-guard", why: "Deny shell writes to vault specs in cloud write mode (HS-FCNH)." },
|
|
28810
29282
|
{ event: "beforeShellExecution", hook: "settings-write-gate", why: "Catch shell writes to .heyspex/config.yaml." },
|
|
28811
29283
|
{ event: "beforeShellExecution", hook: "enforce-vault-naming", why: "Catch vault files created via the shell." },
|
|
28812
29284
|
{ event: "beforeShellExecution", hook: "guard-auto-memory-writes", why: "Catch auto-memory topic files created via the shell." },
|
|
28813
29285
|
{ event: "beforeShellExecution", hook: "preflight-reminder", why: "Preflight reminder on rm -rf / force-push shaped commands." },
|
|
28814
29286
|
{ event: "beforeShellExecution", hook: "dev-server-task-reminder", timeout: 10, why: "Dev-server start/stop hard gate plus DB-safety reminder." },
|
|
28815
29287
|
{ event: "beforeShellExecution", hook: "git-stash-gate", timeout: 10, why: "Always ask before a mutating git stash." },
|
|
29288
|
+
{ event: "beforeShellExecution", hook: "git-identity-guard", why: "Agents never write git identity (owner ruling 2026-09-19, ALWAYS ON)." },
|
|
29289
|
+
{ event: "beforeShellExecution", hook: "capture-execution-gate", why: "Block dev-factory research/paid-model/routing shell commands past an ungrounded capture (HS-KNJ8, platform)." },
|
|
29290
|
+
{ event: "beforeShellExecution", hook: "directive-obligation-gate", why: "Deny build commands and product-code shell writes while a capture obligation is unmet (HS-20SW)." },
|
|
28816
29291
|
// ── Before specific MCP tools ───────────────────────────────────────────
|
|
28817
29292
|
// Cursor fires preToolUse for MCP tools too (matcher `MCP:<name>`), so these
|
|
28818
29293
|
// stay on preToolUse rather than beforeMCPExecution — registering both would
|
|
@@ -28822,15 +29297,20 @@ var HOOKS = [
|
|
|
28822
29297
|
{ event: "preToolUse", matcher: ".*", tool: "heyspex_file_spec$", hook: "capture-workflow-gate", timeout: 8, why: "Deny filing an implementation spec straight out of an open capture." },
|
|
28823
29298
|
{ event: "preToolUse", matcher: ".*", tool: "heyspex_settings_set$", hook: "settings-write-gate", why: "Require approval for settings changes made through MCP." },
|
|
28824
29299
|
{ event: "preToolUse", matcher: ".*", tool: "[Nn]eon__(prepare_database_migration|complete_database_migration|reset_from_parent|delete_branch)$", hook: "dev-server-task-reminder", timeout: 10, why: "DB-safety gate on Neon migration/reset/delete tools." },
|
|
29300
|
+
{ event: "preToolUse", matcher: ".*", tool: "heyspex_file_spec$", hook: "diagnose-capture-reminder", why: "Warn when a diagnose is filed with no capture holding the owner words (HS-IK2D)." },
|
|
29301
|
+
{ event: "preToolUse", matcher: ".*", tool: "^(Workflow|Skill)$", hook: "research-method-guard", why: "Research method guard on Skill/Workflow launches (HS-EN4F, ALWAYS ON)." },
|
|
28825
29302
|
{ event: "preToolUse", matcher: ".*", tool: "^(Workflow|Skill)$", hook: "deep-research-cost-gate", why: "Cost confirmation before a paid deep-research harness launch." },
|
|
28826
29303
|
{ event: "preToolUse", matcher: ".*", tool: "^(WebSearch|WebFetch|web_search|web_fetch)$|heyspex_save_research$", hook: "research-protocol-gate", why: "WebSearch/WebFetch/save_research refused until the research preflight ran (HS-K0ZP, HS-EN4F)." },
|
|
28827
29304
|
// ── Before a subagent spawn ─────────────────────────────────────────────
|
|
28828
29305
|
// subagentStart over preToolUse/Task: it carries subagent_type and the model,
|
|
28829
29306
|
// which is exactly what the routing gate decides on, and it can deny.
|
|
28830
29307
|
{ event: "subagentStart", hook: "agent-spawn-delegation-gate", why: "Block a build/breakdown/critic subagent on an off-routing model." },
|
|
29308
|
+
{ event: "subagentStart", hook: "capture-execution-gate", why: "Block a dev-factory subagent spawn past an ungrounded capture (HS-KNJ8, platform)." },
|
|
29309
|
+
{ event: "subagentStart", hook: "directive-obligation-gate", why: "Deny a build subagent while a capture obligation is unmet or the session has no claim/capture (HS-20SW)." },
|
|
28831
29310
|
// ── After every tool call ───────────────────────────────────────────────
|
|
28832
29311
|
{ event: "postToolUse", matcher: ".*", hook: "emit-claim-status", args: ["--event=PostToolUse"], why: "Claim liveness heartbeat after the call resolves." },
|
|
28833
29312
|
{ event: "postToolUse", matcher: ".*", hook: "log-session-activity", args: ["--event=PostToolUse"], why: "Append the tool result row to the session log." },
|
|
29313
|
+
{ event: "postToolUse", matcher: ".*", hook: "directive-obligation-gate", why: "Discharge the capture obligation on a capture write; track claim acquire/release (HS-20SW)." },
|
|
28834
29314
|
{ event: "postToolUse", matcher: ".*", hook: "close-terminal-on-ship", why: "Close the Launch terminal once the spec ships." },
|
|
28835
29315
|
{ event: "postToolUse", matcher: "Write", hook: "notify-status-flip", why: "Sound on an in_review status flip." },
|
|
28836
29316
|
{ event: "postToolUse", matcher: "Write", hook: "scripts/stamp-provenance-on-edit.mjs", why: "Stamp assisted_by provenance on direct spec edits." },
|
|
@@ -28843,7 +29323,11 @@ var HOOKS = [
|
|
|
28843
29323
|
// hook re-drives the agent — the same effect as Claude's exit-2 block.
|
|
28844
29324
|
{ event: "stop", hook: "emit-claim-status", args: ["--event=Stop"], why: "Mark the claim pending-on-response." },
|
|
28845
29325
|
{ event: "stop", hook: "naughty-list-gate", why: "Re-drive the turn when the final message uses banned jargon." },
|
|
29326
|
+
{ event: "stop", hook: "id-display-format-gate", why: "Re-drive the turn when a reply breaks the Naming V3 display-id format." },
|
|
29327
|
+
{ event: "stop", hook: "epic-milestone-phrasing-gate", why: "Re-drive the turn when a reply breaks the epic/milestone phrasing rules." },
|
|
29328
|
+
{ event: "stop", hook: "git-reflog-audit", why: "Session-close git audit backstop (stash/reset/rebase witness)." },
|
|
28846
29329
|
{ event: "stop", hook: "enforce-session-sequence", why: "Re-drive the turn when work shipped without record_wip." },
|
|
29330
|
+
{ event: "stop", hook: "directive-obligation-gate", why: "Re-drive the turn ONCE (followup_message) when a capture obligation is still unmet (HS-20SW)." },
|
|
28847
29331
|
// ── Session end ─────────────────────────────────────────────────────────
|
|
28848
29332
|
{ event: "sessionEnd", hook: "session-end", why: "Release the claim and notify the console." },
|
|
28849
29333
|
{ event: "sessionEnd", hook: "close-terminal-on-ship", why: "Final terminal cleanup." }
|
|
@@ -30481,6 +30965,7 @@ var INSTALL_HELP = [
|
|
|
30481
30965
|
" hs install [--target <dir>] [--platform <dir>] [--force] [--dry-run] [--json]",
|
|
30482
30966
|
" [--no-cursor] [--no-claude] [--claude-apply]",
|
|
30483
30967
|
" [--plugin | --plugin-apply] [--plugin-scope user|project|local]",
|
|
30968
|
+
" [--copilot-hooks | --copilot-plugin | --copilot-plugin-apply]",
|
|
30484
30969
|
" [--no-statusline]",
|
|
30485
30970
|
" [--cc | --command-code] [--register-vault [--workspace <id>]]",
|
|
30486
30971
|
" [--remote[=<url>]]",
|
|
@@ -30539,6 +31024,15 @@ var INSTALL_HELP = [
|
|
|
30539
31024
|
` the platform's .claude-plugin/marketplace.json (fallback: ${PLUGIN_INSTALL_SPEC}).`,
|
|
30540
31025
|
" --plugin-scope <s> Plugin install scope: user, project (default), or local.",
|
|
30541
31026
|
" Implies --plugin.",
|
|
31027
|
+
" --copilot-hooks Write .github/hooks/heyspex.json (GitHub Copilot hooks) into the",
|
|
31028
|
+
" target. Commands resolve node_modules/heyspex first, so an npm-only",
|
|
31029
|
+
" install (no heyspex repo checkout) works. Pick this OR the plugin.",
|
|
31030
|
+
" --copilot-plugin Print `copilot plugin marketplace add '<platform>'` +",
|
|
31031
|
+
" `copilot plugin install <plugin>@<marketplace>` (the platform root is",
|
|
31032
|
+
" node_modules/heyspex for a published install).",
|
|
31033
|
+
" --copilot-plugin-apply Run those two commands when `copilot` is on PATH.",
|
|
31034
|
+
" Requesting the Copilot plugin AND --copilot-hooks warns and skips the",
|
|
31035
|
+
" hooks file (both would fire every hook twice).",
|
|
30542
31036
|
" --remote[=<url>] Write .mcp.json for the REMOTE (cloud) MCP endpoint instead of",
|
|
30543
31037
|
` the local stdio server \u2014 the Streamable-HTTP form against`,
|
|
30544
31038
|
` ${DEFAULT_REMOTE_MCP_URL} (or <url>), authenticated by the`,
|
|
@@ -30587,35 +31081,15 @@ var INSTALL_HELP = [
|
|
|
30587
31081
|
|
|
30588
31082
|
// spex-console/src/lib/cli/install-identity.ts
|
|
30589
31083
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
30590
|
-
import { existsSync as
|
|
30591
|
-
import {
|
|
30592
|
-
import { join as join10 } from "node:path";
|
|
31084
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
31085
|
+
import { join as join11 } from "node:path";
|
|
30593
31086
|
import { createInterface } from "node:readline/promises";
|
|
30594
|
-
|
|
30595
|
-
// scripts/lib/shared-path-helpers.mjs
|
|
30596
|
-
import { isAbsolute, resolve as resolve4, sep } from "node:path";
|
|
30597
|
-
function resolveCodeRoot({
|
|
30598
|
-
explicit,
|
|
30599
|
-
packageRoot,
|
|
30600
|
-
vaultRoot,
|
|
30601
|
-
env = process.env
|
|
30602
|
-
} = {}) {
|
|
30603
|
-
const fromExplicit = typeof explicit === "string" ? explicit.trim() : "";
|
|
30604
|
-
if (fromExplicit) return fromExplicit;
|
|
30605
|
-
const fromEnv = typeof env?.HS_PKG_ROOT === "string" ? env.HS_PKG_ROOT.trim() : "";
|
|
30606
|
-
if (fromEnv) return fromEnv;
|
|
30607
|
-
const fromPackage = typeof packageRoot === "string" ? packageRoot.trim() : "";
|
|
30608
|
-
if (fromPackage) return fromPackage;
|
|
30609
|
-
const fromVault = typeof vaultRoot === "string" ? vaultRoot.trim() : "";
|
|
30610
|
-
return fromVault;
|
|
30611
|
-
}
|
|
30612
|
-
|
|
30613
|
-
// spex-console/src/lib/cli/install-identity.ts
|
|
30614
31087
|
init_credentials();
|
|
30615
31088
|
|
|
30616
31089
|
// vendor/heyspex-mcp/whoami.mjs
|
|
31090
|
+
init_heyspex_home();
|
|
30617
31091
|
init_credentials();
|
|
30618
|
-
import { homedir as
|
|
31092
|
+
import { homedir as homedir9 } from "node:os";
|
|
30619
31093
|
var SESSION_PATH = "/api/mcp/session";
|
|
30620
31094
|
async function probeCredentialIdentity({ consoleUrl, token, fetchImpl = fetch } = {}) {
|
|
30621
31095
|
if (!token) return { kind: "no_credential" };
|
|
@@ -30870,7 +31344,7 @@ async function runIdentityGate(io, flags, verb, wording = ATTACH_WORDING) {
|
|
|
30870
31344
|
"identity_unconfirmed"
|
|
30871
31345
|
);
|
|
30872
31346
|
}
|
|
30873
|
-
function readConfiguredAccountId(env = process.env, homeDir =
|
|
31347
|
+
function readConfiguredAccountId(env = process.env, homeDir = heyspexHome()) {
|
|
30874
31348
|
return resolveSessionOwnerUncached({ env, homeDir });
|
|
30875
31349
|
}
|
|
30876
31350
|
function accountMismatchRepair(repoAccount) {
|
|
@@ -30899,7 +31373,7 @@ function isInteractiveTerminal() {
|
|
|
30899
31373
|
}
|
|
30900
31374
|
function defaultIdentityGateIo(opts) {
|
|
30901
31375
|
const consoleUrl = opts.consoleUrl.trim().replace(/\/+$/, "");
|
|
30902
|
-
const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) =>
|
|
31376
|
+
const loginCli = [opts.platformRoot, resolveCodeRoot({})].filter((r) => Boolean(r && r.trim())).map((r) => join11(r, "vendor", "heyspex-mcp", "cli.mjs")).find((p) => existsSync5(p));
|
|
30903
31377
|
return {
|
|
30904
31378
|
consoleUrl,
|
|
30905
31379
|
interactive: opts.interactive ?? isInteractiveTerminal(),
|
|
@@ -31030,7 +31504,7 @@ function remedyFor(row2, satellite) {
|
|
|
31030
31504
|
case "heyspex_yaml":
|
|
31031
31505
|
case "vault_registered":
|
|
31032
31506
|
case "workspace_registered":
|
|
31033
|
-
return satellite ? null : "hs connect";
|
|
31507
|
+
return satellite ? row2.remedy ?? null : "hs connect";
|
|
31034
31508
|
case "mcp_entry":
|
|
31035
31509
|
return satellite ? "hs install --attach-vault <vault-id> --force" : "hs connect";
|
|
31036
31510
|
case "plugin_surface":
|
|
@@ -31038,7 +31512,7 @@ function remedyFor(row2, satellite) {
|
|
|
31038
31512
|
case "git_hooks":
|
|
31039
31513
|
return "node scripts/install-git-hooks.mjs";
|
|
31040
31514
|
case "satellite_attachment":
|
|
31041
|
-
return "hs install --attach-vault <vault-id> --force";
|
|
31515
|
+
return row2.remedy ?? "hs install --attach-vault <vault-id> --force";
|
|
31042
31516
|
case "active_vault":
|
|
31043
31517
|
case "account_agreement":
|
|
31044
31518
|
case "workspace_agreement":
|
|
@@ -31061,7 +31535,10 @@ function formatReport(report) {
|
|
|
31061
31535
|
const lines = [
|
|
31062
31536
|
`hs connect --check \u2014 ${report.repo_root}`,
|
|
31063
31537
|
...report.satellite ? [
|
|
31064
|
-
`satellite repo \u2014 attached to vault ${report.satellite.attached_vault_id ?? "(unregistered)"} at ${report.satellite.attached_vault_root}; the vault rows below describe THAT repo
|
|
31538
|
+
`satellite repo \u2014 attached to vault ${report.satellite.attached_vault_id ?? "(unregistered)"} at ${report.satellite.attached_vault_root}; the vault rows below describe THAT repo.`,
|
|
31539
|
+
...report.satellite.via_marker ? [
|
|
31540
|
+
report.satellite.cloned ? "bound by this repo's committed heyspex.yaml (vault_id + relative vault_path)." : "bound by this repo's committed heyspex.yaml; vault not cloned locally -- cloud mode (cloud reads and writes work; clone the vault at its vault_path for local reads)."
|
|
31541
|
+
] : []
|
|
31065
31542
|
] : [],
|
|
31066
31543
|
"",
|
|
31067
31544
|
...report.checks.flatMap((c) => [
|
|
@@ -31137,7 +31614,7 @@ var CANONICAL_VAULT_SUBFOLDERS = [
|
|
|
31137
31614
|
function bareSpecsDirWarning(specsDir, deps) {
|
|
31138
31615
|
let entries = [];
|
|
31139
31616
|
try {
|
|
31140
|
-
entries =
|
|
31617
|
+
entries = readdirSync2(specsDir);
|
|
31141
31618
|
} catch {
|
|
31142
31619
|
return null;
|
|
31143
31620
|
}
|
|
@@ -31259,9 +31736,10 @@ async function runConnectFullRun(args, deps) {
|
|
|
31259
31736
|
};
|
|
31260
31737
|
}
|
|
31261
31738
|
if (!satellite && strictAccount.ok) {
|
|
31262
|
-
const
|
|
31263
|
-
|
|
31264
|
-
|
|
31739
|
+
const boundRow = loadVaultRegistry(deps.registryOpts).vaults.find(
|
|
31740
|
+
(v) => samePath(v.repo_root_abs, repoRoot)
|
|
31741
|
+
);
|
|
31742
|
+
const repoAccount = boundRow?.account_id && boundRow.account_id !== "legacy" ? boundRow.account_id : null;
|
|
31265
31743
|
if (repoAccount && repoAccount !== strictAccount.accountId) {
|
|
31266
31744
|
return accountMismatchRefusal("hs connect", repoAccount, strictAccount.accountId);
|
|
31267
31745
|
}
|
|
@@ -31364,37 +31842,14 @@ async function runConnectFullRun(args, deps) {
|
|
|
31364
31842
|
if (isPresent("heyspex_yaml")) {
|
|
31365
31843
|
const content = deps.fileExists(yamlPath) ? deps.readFile(yamlPath) : null;
|
|
31366
31844
|
vaultId = content ? parseVaultId(content) ?? void 0 : void 0;
|
|
31367
|
-
const
|
|
31368
|
-
|
|
31369
|
-
|
|
31370
|
-
|
|
31371
|
-
|
|
31372
|
-
|
|
31373
|
-
|
|
31374
|
-
|
|
31375
|
-
});
|
|
31376
|
-
} else if (args.dryRun) {
|
|
31377
|
-
steps.push({
|
|
31378
|
-
id: "heyspex_yaml",
|
|
31379
|
-
label: "heyspex.yaml vault marker",
|
|
31380
|
-
status: "run",
|
|
31381
|
-
detail: `would record account: ${accountId} in ${yamlPath}`
|
|
31382
|
-
});
|
|
31383
|
-
} else {
|
|
31384
|
-
try {
|
|
31385
|
-
deps.writeFile(yamlPath, accountUpdate.content);
|
|
31386
|
-
assertMarker(deps.readFile(yamlPath), yamlPath, vaultId, accountId);
|
|
31387
|
-
steps.push({
|
|
31388
|
-
id: "heyspex_yaml",
|
|
31389
|
-
label: "heyspex.yaml vault marker",
|
|
31390
|
-
status: "run",
|
|
31391
|
-
detail: `recorded account: ${accountId} in ${yamlPath}`,
|
|
31392
|
-
confirmed_by: "read heyspex.yaml"
|
|
31393
|
-
});
|
|
31394
|
-
} catch (err) {
|
|
31395
|
-
failStep("heyspex_yaml", "heyspex.yaml vault marker", err);
|
|
31396
|
-
}
|
|
31397
|
-
}
|
|
31845
|
+
const legacyAccount = content ? parseAccountId(content) : null;
|
|
31846
|
+
steps.push({
|
|
31847
|
+
id: "heyspex_yaml",
|
|
31848
|
+
label: "heyspex.yaml vault marker",
|
|
31849
|
+
status: "skipped",
|
|
31850
|
+
detail: legacyAccount ? `already initialized (legacy account: ${legacyAccount} in the marker is ignored -- the account binding is machine-local)` : "already initialized",
|
|
31851
|
+
confirmed_by: "read heyspex.yaml"
|
|
31852
|
+
});
|
|
31398
31853
|
} else if (args.dryRun) {
|
|
31399
31854
|
steps.push({
|
|
31400
31855
|
id: "heyspex_yaml",
|
|
@@ -31432,8 +31887,7 @@ async function runConnectFullRun(args, deps) {
|
|
|
31432
31887
|
vaultRoot: "specs/",
|
|
31433
31888
|
force: false,
|
|
31434
31889
|
json: false,
|
|
31435
|
-
adoptedVaultId
|
|
31436
|
-
...accountId ? { accountId } : {}
|
|
31890
|
+
adoptedVaultId
|
|
31437
31891
|
},
|
|
31438
31892
|
initDeps
|
|
31439
31893
|
);
|
|
@@ -32452,9 +32906,6 @@ function assertMarker(content, path9, vaultId, accountId) {
|
|
|
32452
32906
|
if (vaultId && back !== vaultId) {
|
|
32453
32907
|
throw new Error(`${path9} reads back vault_id ${back ?? "(none)"}, not ${vaultId}`);
|
|
32454
32908
|
}
|
|
32455
|
-
if (accountId && parseAccountId(content) !== accountId) {
|
|
32456
|
-
throw new Error(`${path9} does not record account: ${accountId} after the write`);
|
|
32457
|
-
}
|
|
32458
32909
|
}
|
|
32459
32910
|
var MIRRORED_SKILL_DIRS = [
|
|
32460
32911
|
[".claude", "skills"],
|
|
@@ -32796,7 +33247,6 @@ var HELP3 = [
|
|
|
32796
33247
|
|
|
32797
33248
|
// spex-console/src/lib/cli/join.ts
|
|
32798
33249
|
init_credentials();
|
|
32799
|
-
import { homedir as homedir14 } from "node:os";
|
|
32800
33250
|
|
|
32801
33251
|
// spex-console/src/lib/env-vault/crypto.ts
|
|
32802
33252
|
import {
|
|
@@ -33113,17 +33563,17 @@ import {
|
|
|
33113
33563
|
import {
|
|
33114
33564
|
appendFileSync,
|
|
33115
33565
|
closeSync,
|
|
33116
|
-
existsSync as
|
|
33117
|
-
mkdirSync as
|
|
33566
|
+
existsSync as existsSync6,
|
|
33567
|
+
mkdirSync as mkdirSync6,
|
|
33118
33568
|
openSync,
|
|
33119
|
-
readFileSync as
|
|
33120
|
-
renameSync as
|
|
33569
|
+
readFileSync as readFileSync11,
|
|
33570
|
+
renameSync as renameSync4,
|
|
33121
33571
|
statSync,
|
|
33122
33572
|
unlinkSync,
|
|
33123
|
-
writeFileSync as
|
|
33573
|
+
writeFileSync as writeFileSync6,
|
|
33124
33574
|
writeSync
|
|
33125
33575
|
} from "node:fs";
|
|
33126
|
-
import { join as
|
|
33576
|
+
import { join as join12 } from "node:path";
|
|
33127
33577
|
var STORE_FILE = "env-tokens.json";
|
|
33128
33578
|
var LOCK_FILE = "env-tokens.lock";
|
|
33129
33579
|
var FILE_MODE = 384;
|
|
@@ -33159,15 +33609,15 @@ function unwrapPrivateKey(token, wrapSaltHex, wrapped) {
|
|
|
33159
33609
|
return plaintext.toString("utf8");
|
|
33160
33610
|
}
|
|
33161
33611
|
function heyspexDir(vaultRoot) {
|
|
33162
|
-
const dir =
|
|
33163
|
-
|
|
33612
|
+
const dir = join12(vaultRoot, ".heyspex");
|
|
33613
|
+
mkdirSync6(dir, { recursive: true });
|
|
33164
33614
|
return dir;
|
|
33165
33615
|
}
|
|
33166
33616
|
function memberTokenStorePath(vaultRoot) {
|
|
33167
|
-
return
|
|
33617
|
+
return join12(vaultRoot, ".heyspex", STORE_FILE);
|
|
33168
33618
|
}
|
|
33169
33619
|
function memberTokenStorePresent(vaultRoot) {
|
|
33170
|
-
return
|
|
33620
|
+
return existsSync6(memberTokenStorePath(vaultRoot));
|
|
33171
33621
|
}
|
|
33172
33622
|
function sleepSync(ms) {
|
|
33173
33623
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
@@ -33198,7 +33648,7 @@ function acquireLock(lockPath) {
|
|
|
33198
33648
|
}
|
|
33199
33649
|
}
|
|
33200
33650
|
function withTokenLock(vaultRoot, fn) {
|
|
33201
|
-
const lockPath =
|
|
33651
|
+
const lockPath = join12(heyspexDir(vaultRoot), LOCK_FILE);
|
|
33202
33652
|
const fd = acquireLock(lockPath);
|
|
33203
33653
|
try {
|
|
33204
33654
|
return fn();
|
|
@@ -33215,19 +33665,19 @@ function withTokenLock(vaultRoot, fn) {
|
|
|
33215
33665
|
}
|
|
33216
33666
|
function readStore(vaultRoot) {
|
|
33217
33667
|
const p = memberTokenStorePath(vaultRoot);
|
|
33218
|
-
if (!
|
|
33668
|
+
if (!existsSync6(p)) return [];
|
|
33219
33669
|
try {
|
|
33220
|
-
const parsed = JSON.parse(
|
|
33670
|
+
const parsed = JSON.parse(readFileSync11(p, "utf8"));
|
|
33221
33671
|
return Array.isArray(parsed) ? parsed : [];
|
|
33222
33672
|
} catch {
|
|
33223
33673
|
return [];
|
|
33224
33674
|
}
|
|
33225
33675
|
}
|
|
33226
33676
|
function writeStore(vaultRoot, records) {
|
|
33227
|
-
const p =
|
|
33677
|
+
const p = join12(heyspexDir(vaultRoot), STORE_FILE);
|
|
33228
33678
|
const tmp = `${p}.tmp`;
|
|
33229
|
-
|
|
33230
|
-
|
|
33679
|
+
writeFileSync6(tmp, JSON.stringify(records, null, 2) + "\n", { mode: FILE_MODE });
|
|
33680
|
+
renameSync4(tmp, p);
|
|
33231
33681
|
}
|
|
33232
33682
|
function isLive(r, nowMs) {
|
|
33233
33683
|
if (r.revokedAt) return false;
|
|
@@ -33282,23 +33732,23 @@ import { randomBytes as randomBytes3, scryptSync as scryptSync2, timingSafeEqual
|
|
|
33282
33732
|
import {
|
|
33283
33733
|
appendFileSync as appendFileSync2,
|
|
33284
33734
|
closeSync as closeSync2,
|
|
33285
|
-
existsSync as
|
|
33286
|
-
mkdirSync as
|
|
33735
|
+
existsSync as existsSync8,
|
|
33736
|
+
mkdirSync as mkdirSync7,
|
|
33287
33737
|
openSync as openSync2,
|
|
33288
|
-
readFileSync as
|
|
33289
|
-
renameSync as
|
|
33738
|
+
readFileSync as readFileSync12,
|
|
33739
|
+
renameSync as renameSync5,
|
|
33290
33740
|
statSync as statSync2,
|
|
33291
33741
|
unlinkSync as unlinkSync2,
|
|
33292
|
-
writeFileSync as
|
|
33742
|
+
writeFileSync as writeFileSync7,
|
|
33293
33743
|
writeSync as writeSync2
|
|
33294
33744
|
} from "node:fs";
|
|
33295
|
-
import { join as
|
|
33745
|
+
import { join as join13 } from "node:path";
|
|
33296
33746
|
|
|
33297
33747
|
// spex-console/src/lib/env-vault/sops.ts
|
|
33298
33748
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
33299
33749
|
import { createHash as createHash5 } from "node:crypto";
|
|
33300
|
-
import { existsSync as
|
|
33301
|
-
import { homedir as
|
|
33750
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
33751
|
+
import { homedir as homedir10 } from "node:os";
|
|
33302
33752
|
import path6 from "node:path";
|
|
33303
33753
|
function ageRecipientFingerprint(recipient, length = 12) {
|
|
33304
33754
|
return createHash5("sha256").update(recipient, "utf8").digest("hex").slice(0, length);
|
|
@@ -33332,14 +33782,14 @@ function candidateInvocations(tool) {
|
|
|
33332
33782
|
"mise",
|
|
33333
33783
|
"bin"
|
|
33334
33784
|
);
|
|
33335
|
-
const extra =
|
|
33785
|
+
const extra = existsSync7(miseBin) ? [miseBin] : [];
|
|
33336
33786
|
candidates.push({
|
|
33337
33787
|
command: path6.join(local, "mise", "shims", `${tool}.exe`),
|
|
33338
33788
|
env: withExtendedPath(extra)
|
|
33339
33789
|
});
|
|
33340
33790
|
}
|
|
33341
33791
|
candidates.push({
|
|
33342
|
-
command: path6.join(
|
|
33792
|
+
command: path6.join(homedir10(), ".local", "share", "mise", "shims", tool),
|
|
33343
33793
|
env: process.env
|
|
33344
33794
|
});
|
|
33345
33795
|
return candidates;
|
|
@@ -33647,9 +34097,9 @@ function formatCode(bytes) {
|
|
|
33647
34097
|
function normalizeEnvPairingCode(raw) {
|
|
33648
34098
|
const compact = raw.replace(/[\s-]+/g, "").toUpperCase();
|
|
33649
34099
|
if (LEGACY_CANONICAL_CODE.test(compact)) return group(compact);
|
|
33650
|
-
const
|
|
33651
|
-
if (!CANONICAL_CODE.test(
|
|
33652
|
-
return group(
|
|
34100
|
+
const mapped2 = compact.replace(/O/g, "0").replace(/[IL]/g, "1");
|
|
34101
|
+
if (!CANONICAL_CODE.test(mapped2)) return null;
|
|
34102
|
+
return group(mapped2);
|
|
33653
34103
|
}
|
|
33654
34104
|
var SCRYPT2 = { N: 1 << 14, r: 8, p: 1, maxmem: 64 * 1024 * 1024 };
|
|
33655
34105
|
var SCRYPT_KEYLEN2 = 32;
|
|
@@ -33663,18 +34113,18 @@ function hashEquals2(a, b) {
|
|
|
33663
34113
|
return timingSafeEqual2(bufA, bufB);
|
|
33664
34114
|
}
|
|
33665
34115
|
function heyspexDir2(vaultRoot) {
|
|
33666
|
-
const dir =
|
|
33667
|
-
|
|
34116
|
+
const dir = join13(vaultRoot, ".heyspex");
|
|
34117
|
+
mkdirSync7(dir, { recursive: true });
|
|
33668
34118
|
return dir;
|
|
33669
34119
|
}
|
|
33670
34120
|
function envPairingStorePath(vaultRoot) {
|
|
33671
|
-
return
|
|
34121
|
+
return join13(vaultRoot, ".heyspex", STORE_FILE2);
|
|
33672
34122
|
}
|
|
33673
34123
|
function envPairingStorePresent(vaultRoot) {
|
|
33674
|
-
return
|
|
34124
|
+
return existsSync8(envPairingStorePath(vaultRoot));
|
|
33675
34125
|
}
|
|
33676
34126
|
function auditPath(vaultRoot) {
|
|
33677
|
-
return
|
|
34127
|
+
return join13(heyspexDir2(vaultRoot), AUDIT_FILE);
|
|
33678
34128
|
}
|
|
33679
34129
|
function sleepSync2(ms) {
|
|
33680
34130
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
@@ -33705,7 +34155,7 @@ function acquireLock2(lockPath) {
|
|
|
33705
34155
|
}
|
|
33706
34156
|
}
|
|
33707
34157
|
function withPairingLock(vaultRoot, fn) {
|
|
33708
|
-
const lockPath =
|
|
34158
|
+
const lockPath = join13(heyspexDir2(vaultRoot), LOCK_FILE2);
|
|
33709
34159
|
const fd = acquireLock2(lockPath);
|
|
33710
34160
|
try {
|
|
33711
34161
|
return fn();
|
|
@@ -33722,9 +34172,9 @@ function withPairingLock(vaultRoot, fn) {
|
|
|
33722
34172
|
}
|
|
33723
34173
|
function readStore2(vaultRoot) {
|
|
33724
34174
|
const p = envPairingStorePath(vaultRoot);
|
|
33725
|
-
if (!
|
|
34175
|
+
if (!existsSync8(p)) return [];
|
|
33726
34176
|
try {
|
|
33727
|
-
const parsed = JSON.parse(
|
|
34177
|
+
const parsed = JSON.parse(readFileSync12(p, "utf8"));
|
|
33728
34178
|
return Array.isArray(parsed) ? parsed : [];
|
|
33729
34179
|
} catch {
|
|
33730
34180
|
return [];
|
|
@@ -33738,10 +34188,10 @@ function prune(records) {
|
|
|
33738
34188
|
});
|
|
33739
34189
|
}
|
|
33740
34190
|
function writeStore2(vaultRoot, records) {
|
|
33741
|
-
const p =
|
|
34191
|
+
const p = join13(heyspexDir2(vaultRoot), STORE_FILE2);
|
|
33742
34192
|
const tmp = `${p}.tmp`;
|
|
33743
|
-
|
|
33744
|
-
|
|
34193
|
+
writeFileSync7(tmp, JSON.stringify(prune(records), null, 2) + "\n", { mode: FILE_MODE2 });
|
|
34194
|
+
renameSync5(tmp, p);
|
|
33745
34195
|
}
|
|
33746
34196
|
function appendEnvPairingAudit(vaultRoot, event) {
|
|
33747
34197
|
const line = {
|
|
@@ -33755,18 +34205,18 @@ function appendEnvPairingAudit(vaultRoot, event) {
|
|
|
33755
34205
|
appendFileSync2(auditPath(vaultRoot), JSON.stringify(line) + "\n", { mode: FILE_MODE2 });
|
|
33756
34206
|
}
|
|
33757
34207
|
function strikesPath(vaultRoot) {
|
|
33758
|
-
return
|
|
34208
|
+
return join13(heyspexDir2(vaultRoot), STRIKES_FILE);
|
|
33759
34209
|
}
|
|
33760
34210
|
function readStrikes(vaultRoot) {
|
|
33761
34211
|
try {
|
|
33762
|
-
const parsed = JSON.parse(
|
|
34212
|
+
const parsed = JSON.parse(readFileSync12(strikesPath(vaultRoot), "utf8"));
|
|
33763
34213
|
return typeof parsed.count === "number" && Number.isFinite(parsed.count) ? Math.max(0, Math.floor(parsed.count)) : 0;
|
|
33764
34214
|
} catch {
|
|
33765
34215
|
return 0;
|
|
33766
34216
|
}
|
|
33767
34217
|
}
|
|
33768
34218
|
function writeStrikes(vaultRoot, count) {
|
|
33769
|
-
|
|
34219
|
+
writeFileSync7(
|
|
33770
34220
|
strikesPath(vaultRoot),
|
|
33771
34221
|
JSON.stringify({ count, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }) + "\n",
|
|
33772
34222
|
{ mode: FILE_MODE2 }
|
|
@@ -33975,7 +34425,7 @@ function markEnvInviteApplied(vaultRoot, id) {
|
|
|
33975
34425
|
}
|
|
33976
34426
|
|
|
33977
34427
|
// spex-console/src/lib/env-vault/policy.ts
|
|
33978
|
-
import { existsSync as
|
|
34428
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync8 } from "node:fs";
|
|
33979
34429
|
import path7 from "node:path";
|
|
33980
34430
|
var POLICY_REL_PATH = ".heyspex/env/policy.json";
|
|
33981
34431
|
var POLICY_VERSION = 1;
|
|
@@ -34008,7 +34458,7 @@ function readPolicy(vaultRoot, profile) {
|
|
|
34008
34458
|
const file = policyPath(vaultRoot);
|
|
34009
34459
|
let text2;
|
|
34010
34460
|
try {
|
|
34011
|
-
text2 =
|
|
34461
|
+
text2 = readFileSync13(file, "utf-8");
|
|
34012
34462
|
} catch {
|
|
34013
34463
|
return { mode: "allow", source: "default" };
|
|
34014
34464
|
}
|
|
@@ -34028,8 +34478,8 @@ function serializePolicyFile(file) {
|
|
|
34028
34478
|
}
|
|
34029
34479
|
function loadForWrite(vaultRoot) {
|
|
34030
34480
|
const file = policyPath(vaultRoot);
|
|
34031
|
-
if (!
|
|
34032
|
-
const parsed = parsePolicyFile(
|
|
34481
|
+
if (!existsSync9(file)) return { version: POLICY_VERSION, profiles: {} };
|
|
34482
|
+
const parsed = parsePolicyFile(readFileSync13(file, "utf-8"));
|
|
34033
34483
|
if (parsed === null) {
|
|
34034
34484
|
throw new Error(
|
|
34035
34485
|
`${POLICY_REL_PATH} exists but is not a valid v${POLICY_VERSION} policy file \u2014 fix or remove it before changing policy (refusing to overwrite what it meant to say).`
|
|
@@ -34039,10 +34489,10 @@ function loadForWrite(vaultRoot) {
|
|
|
34039
34489
|
}
|
|
34040
34490
|
function writePolicyFile(vaultRoot, file) {
|
|
34041
34491
|
const target = policyPath(vaultRoot);
|
|
34042
|
-
|
|
34492
|
+
mkdirSync8(path7.dirname(target), { recursive: true });
|
|
34043
34493
|
const tmp = `${target}.tmp`;
|
|
34044
|
-
|
|
34045
|
-
|
|
34494
|
+
writeFileSync8(tmp, serializePolicyFile(file), "utf-8");
|
|
34495
|
+
renameSync6(tmp, target);
|
|
34046
34496
|
}
|
|
34047
34497
|
function writePolicy(vaultRoot, profile, mode) {
|
|
34048
34498
|
if (!isPolicyMode(mode)) {
|
|
@@ -34054,7 +34504,7 @@ function writePolicy(vaultRoot, profile, mode) {
|
|
|
34054
34504
|
}
|
|
34055
34505
|
function clearPolicy(vaultRoot, profile) {
|
|
34056
34506
|
const file = policyPath(vaultRoot);
|
|
34057
|
-
if (!
|
|
34507
|
+
if (!existsSync9(file)) return;
|
|
34058
34508
|
const parsed = loadForWrite(vaultRoot);
|
|
34059
34509
|
if (!(profile in parsed.profiles)) return;
|
|
34060
34510
|
delete parsed.profiles[profile];
|
|
@@ -34645,7 +35095,7 @@ function readJoinManifest(deps, repoRoot, profile) {
|
|
|
34645
35095
|
}
|
|
34646
35096
|
}
|
|
34647
35097
|
function hasLoginCredentials(deps) {
|
|
34648
|
-
const raw = deps.readFile(deps.joinPath(
|
|
35098
|
+
const raw = deps.readFile(deps.joinPath(heyspexHome(), ".heyspex", "credentials.yaml"));
|
|
34649
35099
|
return parseMcpToken(raw) !== null;
|
|
34650
35100
|
}
|
|
34651
35101
|
function repoTargetName(repo) {
|
|
@@ -35661,16 +36111,16 @@ import { spawnSync as spawnSync4 } from "node:child_process";
|
|
|
35661
36111
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
35662
36112
|
import {
|
|
35663
36113
|
chmodSync,
|
|
35664
|
-
existsSync as
|
|
35665
|
-
mkdirSync as
|
|
35666
|
-
readdirSync as
|
|
35667
|
-
readFileSync as
|
|
35668
|
-
renameSync as
|
|
36114
|
+
existsSync as existsSync10,
|
|
36115
|
+
mkdirSync as mkdirSync9,
|
|
36116
|
+
readdirSync as readdirSync3,
|
|
36117
|
+
readFileSync as readFileSync14,
|
|
36118
|
+
renameSync as renameSync7,
|
|
35669
36119
|
statSync as statSync3,
|
|
35670
36120
|
unlinkSync as unlinkSync3,
|
|
35671
|
-
writeFileSync as
|
|
36121
|
+
writeFileSync as writeFileSync9
|
|
35672
36122
|
} from "node:fs";
|
|
35673
|
-
import { homedir as
|
|
36123
|
+
import { homedir as homedir12 } from "node:os";
|
|
35674
36124
|
import path8 from "node:path";
|
|
35675
36125
|
|
|
35676
36126
|
// node_modules/sops-age/dist/index.js
|
|
@@ -40592,7 +41042,7 @@ function alphabet(letters) {
|
|
|
40592
41042
|
};
|
|
40593
41043
|
}
|
|
40594
41044
|
// @__NO_SIDE_EFFECTS__
|
|
40595
|
-
function
|
|
41045
|
+
function join14(separator = "") {
|
|
40596
41046
|
astr("join", separator);
|
|
40597
41047
|
return {
|
|
40598
41048
|
encode: (from) => {
|
|
@@ -40722,9 +41172,9 @@ var base64 = hasBase64Builtin ? {
|
|
|
40722
41172
|
decode(s) {
|
|
40723
41173
|
return decodeBase64Builtin(s, false);
|
|
40724
41174
|
}
|
|
40725
|
-
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */
|
|
40726
|
-
var base64nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */
|
|
40727
|
-
var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */
|
|
41175
|
+
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join14(""));
|
|
41176
|
+
var base64nopad = /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ join14(""));
|
|
41177
|
+
var BECH_ALPHABET = /* @__PURE__ */ chain(/* @__PURE__ */ alphabet("qpzry9x8gf2tvdw0s3jn54khce6mua7l"), /* @__PURE__ */ join14(""));
|
|
40728
41178
|
var POLYMOD_GENERATORS = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
40729
41179
|
function bech32Polymod(pre) {
|
|
40730
41180
|
const b = pre >> 25;
|
|
@@ -44340,8 +44790,8 @@ var gcm = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 12, tagLength
|
|
|
44340
44790
|
|
|
44341
44791
|
// node_modules/sops-age/dist/index.js
|
|
44342
44792
|
import { readFile } from "node:fs/promises";
|
|
44343
|
-
import { join as
|
|
44344
|
-
import { homedir as
|
|
44793
|
+
import { join as join15 } from "node:path";
|
|
44794
|
+
import { homedir as homedir11, platform } from "node:os";
|
|
44345
44795
|
import { exec } from "node:child_process";
|
|
44346
44796
|
import { promisify } from "node:util";
|
|
44347
44797
|
var import_sshpk = __toESM(require_lib2(), 1);
|
|
@@ -44682,11 +45132,11 @@ async function getUserConfigDir() {
|
|
|
44682
45132
|
if (!appData) throw new Error("APPDATA env var not set");
|
|
44683
45133
|
return appData;
|
|
44684
45134
|
case "darwin":
|
|
44685
|
-
return
|
|
45135
|
+
return join15(homedir11(), "Library", "Application Support");
|
|
44686
45136
|
default:
|
|
44687
45137
|
const xdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
44688
45138
|
if (xdgConfigHome && xdgConfigHome.trim() !== "") return xdgConfigHome;
|
|
44689
|
-
return
|
|
45139
|
+
return join15(homedir11(), ".config");
|
|
44690
45140
|
}
|
|
44691
45141
|
}
|
|
44692
45142
|
async function findAllAgeKeys() {
|
|
@@ -44707,11 +45157,11 @@ async function findAllAgeKeys() {
|
|
|
44707
45157
|
);
|
|
44708
45158
|
}
|
|
44709
45159
|
} else {
|
|
44710
|
-
const userHomeDir =
|
|
45160
|
+
const userHomeDir = homedir11();
|
|
44711
45161
|
if (userHomeDir) {
|
|
44712
45162
|
const defaultSshPaths = [
|
|
44713
|
-
|
|
44714
|
-
|
|
45163
|
+
join15(userHomeDir, ".ssh", "id_ed25519"),
|
|
45164
|
+
join15(userHomeDir, ".ssh", "id_rsa")
|
|
44715
45165
|
];
|
|
44716
45166
|
for (const sshPath of defaultSshPaths) {
|
|
44717
45167
|
if (foundKeySource && convertedSshKeys.length > 0) {
|
|
@@ -44788,7 +45238,7 @@ async function findAllAgeKeys() {
|
|
|
44788
45238
|
}
|
|
44789
45239
|
}
|
|
44790
45240
|
if (userConfigDirPath) {
|
|
44791
|
-
const sopsKeysFilePath =
|
|
45241
|
+
const sopsKeysFilePath = join15(
|
|
44792
45242
|
userConfigDirPath,
|
|
44793
45243
|
SOPS_AGE_KEY_USER_CONFIG_PATH
|
|
44794
45244
|
);
|
|
@@ -45281,14 +45731,14 @@ function keysPath(vaultRoot) {
|
|
|
45281
45731
|
return path8.join(vaultRoot, ".heyspex", "env.keys");
|
|
45282
45732
|
}
|
|
45283
45733
|
function homeKeysPath() {
|
|
45284
|
-
return path8.join(process.env.HEYSPEX_ENV_KEYS_HOME ||
|
|
45734
|
+
return path8.join(process.env.HEYSPEX_ENV_KEYS_HOME || homedir12(), ".heyspex", "env.keys");
|
|
45285
45735
|
}
|
|
45286
45736
|
function dotenvPath(vaultRoot) {
|
|
45287
45737
|
return path8.join(vaultRoot, ".env");
|
|
45288
45738
|
}
|
|
45289
45739
|
function readOrNull(file) {
|
|
45290
45740
|
try {
|
|
45291
|
-
return
|
|
45741
|
+
return readFileSync14(file, "utf-8");
|
|
45292
45742
|
} catch {
|
|
45293
45743
|
return null;
|
|
45294
45744
|
}
|
|
@@ -45302,7 +45752,7 @@ function renameWithRetry(tmp, file) {
|
|
|
45302
45752
|
const retryable = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
45303
45753
|
for (let attempt = 0; ; attempt++) {
|
|
45304
45754
|
try {
|
|
45305
|
-
|
|
45755
|
+
renameSync7(tmp, file);
|
|
45306
45756
|
return;
|
|
45307
45757
|
} catch (err) {
|
|
45308
45758
|
const code = err.code ?? "";
|
|
@@ -45320,14 +45770,14 @@ function unlinkQuiet(file) {
|
|
|
45320
45770
|
function atomicWrite(file, content, mode = 420) {
|
|
45321
45771
|
const secret = mode === SECRET_MODE;
|
|
45322
45772
|
const dir = path8.dirname(file);
|
|
45323
|
-
|
|
45773
|
+
mkdirSync9(dir, { recursive: true, ...secret ? { mode: SECRET_DIR_MODE } : {} });
|
|
45324
45774
|
const tmp = path8.join(
|
|
45325
45775
|
dir,
|
|
45326
45776
|
`.${path8.basename(file)}.${process.pid}.${randomBytes5(4).toString("hex")}.tmp`
|
|
45327
45777
|
);
|
|
45328
45778
|
let renamed = false;
|
|
45329
45779
|
try {
|
|
45330
|
-
|
|
45780
|
+
writeFileSync9(tmp, content, { encoding: "utf-8", mode });
|
|
45331
45781
|
if (secret) chmodOwnerOnly(tmp);
|
|
45332
45782
|
renameWithRetry(tmp, file);
|
|
45333
45783
|
renamed = true;
|
|
@@ -45341,11 +45791,11 @@ var LOCK_RETRY_MS3 = 50;
|
|
|
45341
45791
|
var LOCK_STALE_MS3 = 5e3;
|
|
45342
45792
|
function withFileLock(file, fn) {
|
|
45343
45793
|
const lock = `${file}.lock`;
|
|
45344
|
-
|
|
45794
|
+
mkdirSync9(path8.dirname(file), { recursive: true, mode: SECRET_DIR_MODE });
|
|
45345
45795
|
let held = false;
|
|
45346
45796
|
for (let attempt = 0; attempt < LOCK_RETRIES && !held; attempt++) {
|
|
45347
45797
|
try {
|
|
45348
|
-
|
|
45798
|
+
writeFileSync9(lock, `${process.pid}
|
|
45349
45799
|
`, { flag: "wx" });
|
|
45350
45800
|
held = true;
|
|
45351
45801
|
} catch (err) {
|
|
@@ -45537,7 +45987,7 @@ function legacyDecryptAllSync(vaultRoot, name) {
|
|
|
45537
45987
|
}
|
|
45538
45988
|
function initProfile(vaultRoot, name = DEFAULT_PROFILE2, opts = {}) {
|
|
45539
45989
|
const pFile = profilePath(vaultRoot, name);
|
|
45540
|
-
if (
|
|
45990
|
+
if (existsSync10(pFile) && !opts.force) {
|
|
45541
45991
|
throw new EnvVaultConfigError(
|
|
45542
45992
|
`Env profile "${name}" already exists at ${pFile}. Pass --force to re-key it (this discards existing values and mints a new keypair).`
|
|
45543
45993
|
);
|
|
@@ -45583,7 +46033,7 @@ function listProfiles(vaultRoot) {
|
|
|
45583
46033
|
} catch {
|
|
45584
46034
|
return [];
|
|
45585
46035
|
}
|
|
45586
|
-
return
|
|
46036
|
+
return readdirSync3(dir).filter((f) => f.endsWith(".env")).map((f) => f.slice(0, -".env".length)).sort();
|
|
45587
46037
|
}
|
|
45588
46038
|
function listProfilesStatus(vaultRoot) {
|
|
45589
46039
|
return listProfiles(vaultRoot).map((name) => {
|
|
@@ -45923,7 +46373,7 @@ function removeGrant(vaultRoot, profileName, name, opts = {}) {
|
|
|
45923
46373
|
}
|
|
45924
46374
|
async function exportProfile(vaultRoot, name = DEFAULT_PROFILE2, opts = {}) {
|
|
45925
46375
|
const target = dotenvPath(vaultRoot);
|
|
45926
|
-
if (
|
|
46376
|
+
if (existsSync10(target) && !opts.force) {
|
|
45927
46377
|
throw new EnvVaultConfigError(
|
|
45928
46378
|
`${target} already exists. Pass --force to overwrite it (it is disposable \u2014 the encrypted profile is the system of record).`
|
|
45929
46379
|
);
|
|
@@ -45952,7 +46402,7 @@ function writeTextFile(vaultRoot, filePath, content) {
|
|
|
45952
46402
|
atomicWrite(resolveVaultPath(vaultRoot, filePath), content, SECRET_MODE);
|
|
45953
46403
|
}
|
|
45954
46404
|
function pathExists(vaultRoot, filePath) {
|
|
45955
|
-
return
|
|
46405
|
+
return existsSync10(resolveVaultPath(vaultRoot, filePath));
|
|
45956
46406
|
}
|
|
45957
46407
|
function fileMtime(vaultRoot, filePath) {
|
|
45958
46408
|
const resolved = resolveVaultPath(vaultRoot, filePath);
|
|
@@ -45979,7 +46429,7 @@ function listRepoSourceFiles(vaultRoot) {
|
|
|
45979
46429
|
const walk = (dir, rel) => {
|
|
45980
46430
|
let entries;
|
|
45981
46431
|
try {
|
|
45982
|
-
entries =
|
|
46432
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
45983
46433
|
} catch {
|
|
45984
46434
|
return;
|
|
45985
46435
|
}
|
|
@@ -46134,7 +46584,7 @@ async function confirm(question) {
|
|
|
46134
46584
|
function findRepoRoot(start) {
|
|
46135
46585
|
let dir = resolve5(start);
|
|
46136
46586
|
for (; ; ) {
|
|
46137
|
-
if (
|
|
46587
|
+
if (existsSync11(join16(dir, ".git"))) return dir;
|
|
46138
46588
|
const parent = dirname6(dir);
|
|
46139
46589
|
if (parent === dir) return null;
|
|
46140
46590
|
dir = parent;
|
|
@@ -46142,7 +46592,7 @@ function findRepoRoot(start) {
|
|
|
46142
46592
|
}
|
|
46143
46593
|
function readFile2(p) {
|
|
46144
46594
|
try {
|
|
46145
|
-
return
|
|
46595
|
+
return readFileSync15(p, "utf-8");
|
|
46146
46596
|
} catch {
|
|
46147
46597
|
return null;
|
|
46148
46598
|
}
|
|
@@ -46150,12 +46600,12 @@ function readFile2(p) {
|
|
|
46150
46600
|
function connectCliDeps(repoRoot) {
|
|
46151
46601
|
return {
|
|
46152
46602
|
repoRoot,
|
|
46153
|
-
fileExists: (p) =>
|
|
46603
|
+
fileExists: (p) => existsSync11(p),
|
|
46154
46604
|
readFile: readFile2,
|
|
46155
|
-
writeFile: (p, c) =>
|
|
46156
|
-
mkdir: (p) =>
|
|
46157
|
-
joinPath: (...s) =>
|
|
46158
|
-
basename: (p) =>
|
|
46605
|
+
writeFile: (p, c) => writeFileSync10(p, c, "utf-8"),
|
|
46606
|
+
mkdir: (p) => mkdirSync10(p, { recursive: true }),
|
|
46607
|
+
joinPath: (...s) => join16(...s),
|
|
46608
|
+
basename: (p) => basename4(p),
|
|
46159
46609
|
now: () => (/* @__PURE__ */ new Date()).toISOString(),
|
|
46160
46610
|
spawn: (command, spawnArgs, opts) => {
|
|
46161
46611
|
const res = spawnSync5(command, spawnArgs, {
|
|
@@ -46204,7 +46654,7 @@ async function main() {
|
|
|
46204
46654
|
const launchCwd = process4.env.HS_JOIN_CWD ? resolve5(process4.env.HS_JOIN_CWD) : process4.cwd();
|
|
46205
46655
|
const walked = findRepoRoot(launchCwd);
|
|
46206
46656
|
const rawHint = walked ?? (process4.env.HS_VAULT_ROOT ? resolve5(process4.env.HS_VAULT_ROOT) : null);
|
|
46207
|
-
const repoRootHint = rawHint &&
|
|
46657
|
+
const repoRootHint = rawHint && existsSync11(join16(rawHint, "package.json")) ? rawHint : null;
|
|
46208
46658
|
let userName = null;
|
|
46209
46659
|
try {
|
|
46210
46660
|
userName = userInfo().username || null;
|
|
@@ -46217,9 +46667,9 @@ async function main() {
|
|
|
46217
46667
|
platform: process4.platform,
|
|
46218
46668
|
interactive: INTERACTIVE,
|
|
46219
46669
|
userName,
|
|
46220
|
-
pathExists: (p) =>
|
|
46670
|
+
pathExists: (p) => existsSync11(p),
|
|
46221
46671
|
readFile: readFile2,
|
|
46222
|
-
joinPath: (...s) =>
|
|
46672
|
+
joinPath: (...s) => join16(...s),
|
|
46223
46673
|
resolvePath: (...s) => resolve5(...s),
|
|
46224
46674
|
commandExists,
|
|
46225
46675
|
// npm/mise/git resolve through the shell on Windows (.cmd shims).
|
|
@@ -46266,9 +46716,9 @@ async function main() {
|
|
|
46266
46716
|
connect: {
|
|
46267
46717
|
isConnected: (repoRoot) => Promise.resolve(
|
|
46268
46718
|
runConnectChecks(repoRoot, {
|
|
46269
|
-
fileExists: (p) =>
|
|
46719
|
+
fileExists: (p) => existsSync11(p),
|
|
46270
46720
|
readFile: readFile2,
|
|
46271
|
-
joinPath: (...s) =>
|
|
46721
|
+
joinPath: (...s) => join16(...s)
|
|
46272
46722
|
}).connected
|
|
46273
46723
|
),
|
|
46274
46724
|
findUnconnectedVaultForRemote: async (githubRemote) => {
|