heyspex 0.2.2 → 0.2.4
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 +2 -1
- package/README.md +11 -0
- package/bin/hs-vault-binding.mjs +157 -0
- package/bin/hs.mjs +83 -79
- package/bin/spexcode.mjs +91 -0
- package/dev-factory/engine/worktree.mjs +36 -7
- package/dev-factory/lib/epic-detect.mjs +2 -2
- package/dev-factory/lib/fleet-readiness.mjs +3 -5
- package/dev-factory/lib/fleet-state.mjs +2 -2
- package/dev-factory/lib/sufficiency-score.mjs +3 -5
- package/dist/cli/hs-check.mjs +76 -46
- package/dist/cli/hs-claim.mjs +63 -33
- package/dist/cli/hs-connect.mjs +520 -214
- package/dist/cli/hs-defer.mjs +161 -19
- package/dist/cli/hs-dev.mjs +11 -5
- package/dist/cli/hs-env.mjs +187 -161
- package/dist/cli/hs-index-rebuild.mjs +123 -78
- package/dist/cli/hs-index-reconcile.mjs +203 -107
- package/dist/cli/hs-init.mjs +426 -179
- package/dist/cli/hs-install.mjs +759 -437
- package/dist/cli/hs-join.mjs +590 -383
- package/dist/cli/hs-vault-pull.mjs +359 -254
- package/dist/cli/hs-vault-push.mjs +151 -113
- package/dist/cli/hs-vault.mjs +271 -104
- package/dist/cli/hs-workspace.mjs +90 -57
- package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +178 -91
- package/docs/copilot-plugin-install.md +200 -177
- package/docs/driving-heyspex-from-copilot.md +174 -165
- package/docs/mcp-client-config.md +93 -25
- package/package.json +257 -250
- package/plugins/heyspex/.claude-plugin/plugin.json +13 -12
- package/plugins/heyspex/.cursor-plugin/plugin.json +16 -15
- package/plugins/heyspex/.mcp.json +3 -4
- package/plugins/heyspex/README.md +323 -312
- package/plugins/heyspex/assets/heyspex-glyph-128.png +0 -0
- package/plugins/heyspex/assets/heyspex-glyph-256.png +0 -0
- package/plugins/heyspex/assets/heyspex-glyph.svg +12 -0
- package/plugins/heyspex/bin/launch-mcp.mjs +21 -7
- package/plugins/heyspex/mcp.json +12 -13
- package/scripts/check-mcp-freshness.mjs +6 -4
- package/scripts/claude-hooks/_intent-classifier-router.mjs +518 -456
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +305 -279
- package/scripts/claude-hooks/research-method-guard.mjs +2 -2
- package/scripts/claude-hooks/wip-checkpoint.mjs +33 -9
- package/scripts/claude-statusline/fonts/HeySpexIcons.ttf +0 -0
- package/scripts/claude-statusline/statusline-command.mjs +1073 -0
- package/scripts/copilot-statusline.mjs +130 -0
- package/scripts/dev-server-status.mjs +3 -2
- package/scripts/dev-server-stop.mjs +3 -2
- package/scripts/hs-capture-cli.mjs +2 -2
- package/scripts/hs-settings.mjs +2 -2
- package/scripts/hs-view.mjs +3 -2
- package/scripts/install-copilot-hooks.mjs +152 -36
- package/scripts/lib/copilot-cli.mjs +108 -0
- package/scripts/lib/customer-kit.mjs +223 -30
- package/scripts/lib/dev-mode.mjs +28 -2
- package/scripts/lib/shared-path-helpers.mjs +32 -0
- package/scripts/lib/standalone-doctor.mjs +131 -32
- package/scripts/wip-ledger-health.mjs +3 -2
- package/spexharness/src/gates/vault-mirror-catchup.mjs +113 -24
- package/spexharness/src/gates/vault-mirror-catchup.runner.mjs +52 -0
- package/spexharness/src/intent-directive.mjs +839 -626
- package/spexharness/src/rule-table.mjs +1 -1
- package/vendor/heyspex-mcp/cli-verbs.mjs +16 -2
- package/vendor/heyspex-mcp/cli.mjs +20 -4
- package/vendor/heyspex-mcp/console-fetch.mjs +15 -3
- package/vendor/heyspex-mcp/dev-tools.mjs +11 -4
- package/vendor/heyspex-mcp/events-tools.mjs +1 -1
- package/vendor/heyspex-mcp/factory-run-status.mjs +42 -0
- package/vendor/heyspex-mcp/http-transport.mjs +6 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +4 -2
- package/vendor/heyspex-mcp/server-identity.mjs +13 -0
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +26 -1
- package/vendor/heyspex-mcp/test-helpers.mjs +5 -2
- package/vendor/heyspex-mcp/tool-catalog.mjs +12 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +63 -23
- package/vendor/heyspex-mcp/usable-dir-env.mjs +82 -0
- package/vendor/heyspex-mcp/vault-binding.mjs +839 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +353 -245
|
@@ -161,9 +161,9 @@ function parseCredentialsStore(raw) {
|
|
|
161
161
|
accounts
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
-
function readTextOrNull(read,
|
|
164
|
+
function readTextOrNull(read, path16) {
|
|
165
165
|
try {
|
|
166
|
-
const raw = read(
|
|
166
|
+
const raw = read(path16);
|
|
167
167
|
return typeof raw === "string" ? raw : null;
|
|
168
168
|
} catch {
|
|
169
169
|
return null;
|
|
@@ -215,14 +215,14 @@ var init_session_owner = __esm({
|
|
|
215
215
|
});
|
|
216
216
|
|
|
217
217
|
// vendor/heyspex-mcp/vault-less-endpoints.mjs
|
|
218
|
-
function pathnameOf(
|
|
219
|
-
const raw = String(
|
|
218
|
+
function pathnameOf(path16) {
|
|
219
|
+
const raw = String(path16 ?? "").split("?")[0].split("#")[0];
|
|
220
220
|
const noTrailing = raw.length > 1 ? raw.replace(/\/+$/, "") : raw;
|
|
221
221
|
return noTrailing || "/";
|
|
222
222
|
}
|
|
223
|
-
function isVaultLessEndpoint(method,
|
|
223
|
+
function isVaultLessEndpoint(method, path16) {
|
|
224
224
|
const m = String(method || "GET").toUpperCase();
|
|
225
|
-
const pathname = pathnameOf(
|
|
225
|
+
const pathname = pathnameOf(path16);
|
|
226
226
|
return VAULT_LESS_ENDPOINTS.some((row) => {
|
|
227
227
|
const [rowMethod, rowPath] = row.split(" ");
|
|
228
228
|
return rowPath === pathname && (rowMethod === "*" || rowMethod === m);
|
|
@@ -245,7 +245,9 @@ var init_vault_less_endpoints = __esm({
|
|
|
245
245
|
});
|
|
246
246
|
|
|
247
247
|
// scripts/lib/shared-path-helpers.mjs
|
|
248
|
+
import { realpathSync } from "node:fs";
|
|
248
249
|
import { isAbsolute, resolve as resolve2, sep } from "node:path";
|
|
250
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
249
251
|
var init_shared_path_helpers = __esm({
|
|
250
252
|
"scripts/lib/shared-path-helpers.mjs"() {
|
|
251
253
|
"use strict";
|
|
@@ -270,20 +272,52 @@ var init_write_outbox = __esm({
|
|
|
270
272
|
}
|
|
271
273
|
});
|
|
272
274
|
|
|
275
|
+
// vendor/heyspex-mcp/usable-dir-env.mjs
|
|
276
|
+
import { statSync } from "node:fs";
|
|
277
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
278
|
+
var init_usable_dir_env = __esm({
|
|
279
|
+
"vendor/heyspex-mcp/usable-dir-env.mjs"() {
|
|
280
|
+
"use strict";
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// vendor/heyspex-mcp/vault-binding.mjs
|
|
285
|
+
import {
|
|
286
|
+
copyFileSync,
|
|
287
|
+
existsSync as existsSync3,
|
|
288
|
+
mkdirSync as mkdirSync3,
|
|
289
|
+
readFileSync as readFileSync4,
|
|
290
|
+
realpathSync as realpathSync2,
|
|
291
|
+
renameSync as renameSync3,
|
|
292
|
+
statSync as statSync2,
|
|
293
|
+
unlinkSync,
|
|
294
|
+
writeFileSync as writeFileSync3
|
|
295
|
+
} from "node:fs";
|
|
296
|
+
import { homedir as homedir4 } from "node:os";
|
|
297
|
+
import path from "node:path";
|
|
298
|
+
var init_vault_binding = __esm({
|
|
299
|
+
"vendor/heyspex-mcp/vault-binding.mjs"() {
|
|
300
|
+
"use strict";
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
273
304
|
// vendor/heyspex-mcp/vault-resolve.mjs
|
|
274
|
-
import { existsSync as
|
|
275
|
-
import { homedir as
|
|
276
|
-
import { dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
|
|
305
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
306
|
+
import { homedir as homedir5, tmpdir } from "node:os";
|
|
307
|
+
import { basename as basename2, dirname as dirname3, join as join5, resolve as resolve3 } from "node:path";
|
|
277
308
|
var init_vault_resolve = __esm({
|
|
278
309
|
"vendor/heyspex-mcp/vault-resolve.mjs"() {
|
|
279
310
|
"use strict";
|
|
280
311
|
init_heyspex_home();
|
|
312
|
+
init_usable_dir_env();
|
|
313
|
+
init_vault_binding();
|
|
314
|
+
init_vault_binding();
|
|
281
315
|
}
|
|
282
316
|
});
|
|
283
317
|
|
|
284
318
|
// vendor/heyspex-mcp/console-fetch.mjs
|
|
285
|
-
import { readFileSync as
|
|
286
|
-
import { homedir as
|
|
319
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
320
|
+
import { homedir as homedir6 } from "node:os";
|
|
287
321
|
import { join as join6 } from "node:path";
|
|
288
322
|
function isCloudVaultId(value) {
|
|
289
323
|
return typeof value === "string" && CLOUD_VAULT_ID_RE.test(value.trim());
|
|
@@ -305,13 +339,13 @@ var init_console_fetch = __esm({
|
|
|
305
339
|
});
|
|
306
340
|
|
|
307
341
|
// spex-console/src/lib/heyspex-home.ts
|
|
308
|
-
import { homedir as
|
|
342
|
+
import { homedir as homedir7 } from "node:os";
|
|
309
343
|
function heyspexHome2(env = process.env) {
|
|
310
344
|
const override = env?.HEYSPEX_HOME;
|
|
311
345
|
if (typeof override === "string" && override.trim().length > 0) {
|
|
312
346
|
return override.trim();
|
|
313
347
|
}
|
|
314
|
-
return
|
|
348
|
+
return homedir7();
|
|
315
349
|
}
|
|
316
350
|
var init_heyspex_home2 = __esm({
|
|
317
351
|
"spex-console/src/lib/heyspex-home.ts"() {
|
|
@@ -339,7 +373,7 @@ var init_types = __esm({
|
|
|
339
373
|
});
|
|
340
374
|
|
|
341
375
|
// spex-console/src/lib/vault-registry/registry-file.ts
|
|
342
|
-
import { existsSync as
|
|
376
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
343
377
|
import { dirname as dirname4, join as join7, resolve as resolve4 } from "node:path";
|
|
344
378
|
function vaultRegistryDir(opts = {}) {
|
|
345
379
|
return opts.registryDir ?? join7(heyspexHome2(), ".heyspex");
|
|
@@ -446,9 +480,9 @@ function parseVaultRegistryFile(json) {
|
|
|
446
480
|
}
|
|
447
481
|
function readRegistryJson(opts) {
|
|
448
482
|
const filePath = vaultRegistryFilePath(opts);
|
|
449
|
-
if (!
|
|
483
|
+
if (!existsSync5(filePath)) return { kind: "missing" };
|
|
450
484
|
try {
|
|
451
|
-
const raw =
|
|
485
|
+
const raw = readFileSync7(filePath, "utf8");
|
|
452
486
|
const json = JSON.parse(raw);
|
|
453
487
|
if (registryFileSchemaOf(json) === null) return { kind: "unreadable" };
|
|
454
488
|
return { kind: "ok", raw, json };
|
|
@@ -484,8 +518,8 @@ var init_registry_file = __esm({
|
|
|
484
518
|
});
|
|
485
519
|
|
|
486
520
|
// scripts/lib/dev-mode.mjs
|
|
487
|
-
import { mkdirSync as
|
|
488
|
-
import { homedir as
|
|
521
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
522
|
+
import { homedir as homedir8 } from "node:os";
|
|
489
523
|
import { join as join8 } from "node:path";
|
|
490
524
|
function devModeFilePath(homeDir = heyspexHome()) {
|
|
491
525
|
return join8(homeDir, ".heyspex", DEV_MODE_FILENAME);
|
|
@@ -540,6 +574,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultRea
|
|
|
540
574
|
return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
|
|
541
575
|
}
|
|
542
576
|
}
|
|
577
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
578
|
+
if (source !== "local-override") return "not-applicable";
|
|
579
|
+
if (!cloudKnown) return "unverified";
|
|
580
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
581
|
+
}
|
|
543
582
|
function resolveDevMode({
|
|
544
583
|
env = process.env,
|
|
545
584
|
homeDir = heyspexHome(),
|
|
@@ -579,6 +618,7 @@ function resolveDevMode({
|
|
|
579
618
|
const semantics = deriveSemantics(mode, reliance);
|
|
580
619
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
581
620
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
621
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
582
622
|
return {
|
|
583
623
|
mode,
|
|
584
624
|
reliance: semantics.reliance,
|
|
@@ -596,7 +636,8 @@ function resolveDevMode({
|
|
|
596
636
|
present: local.record !== null,
|
|
597
637
|
record: local.record,
|
|
598
638
|
parseError: local.parseError,
|
|
599
|
-
staleAgainstCloud
|
|
639
|
+
staleAgainstCloud,
|
|
640
|
+
cloudCheck
|
|
600
641
|
},
|
|
601
642
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
602
643
|
};
|
|
@@ -610,13 +651,13 @@ var init_dev_mode = __esm({
|
|
|
610
651
|
CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
611
652
|
LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
612
653
|
DEV_MODE_FILENAME = "dev-mode.json";
|
|
613
|
-
defaultReadFile = (p) =>
|
|
654
|
+
defaultReadFile = (p) => readFileSync8(p, "utf8");
|
|
614
655
|
}
|
|
615
656
|
});
|
|
616
657
|
|
|
617
658
|
// scripts/lib/console-url-resolver.mjs
|
|
618
|
-
import { readFileSync as
|
|
619
|
-
import { homedir as
|
|
659
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
660
|
+
import { homedir as homedir9 } from "node:os";
|
|
620
661
|
import { join as join9 } from "node:path";
|
|
621
662
|
function trimTrailingSlash(url) {
|
|
622
663
|
return url.replace(/\/+$/, "");
|
|
@@ -651,7 +692,7 @@ function readCredentialsConsoleUrl({ homeDir = heyspexHome(), readFile = default
|
|
|
651
692
|
}
|
|
652
693
|
}
|
|
653
694
|
function defaultReadFile2(p) {
|
|
654
|
-
return
|
|
695
|
+
return readFileSync9(p, "utf8");
|
|
655
696
|
}
|
|
656
697
|
function readDiscoveryFileConsoleUrl(repoRoot, { readDiscovery: readDiscovery2 = defaultReadFile2 } = {}) {
|
|
657
698
|
if (!repoRoot) return null;
|
|
@@ -726,9 +767,9 @@ var init_console_url_resolver = __esm({
|
|
|
726
767
|
});
|
|
727
768
|
|
|
728
769
|
// spex-console/src/lib/vault-registry/repo-identity.ts
|
|
729
|
-
import
|
|
770
|
+
import path3 from "node:path";
|
|
730
771
|
function normalizeRepoRoot(p) {
|
|
731
|
-
let resolved =
|
|
772
|
+
let resolved = path3.resolve(p);
|
|
732
773
|
while (resolved.length > 1 && (resolved.endsWith("/") || resolved.endsWith("\\"))) {
|
|
733
774
|
resolved = resolved.slice(0, -1);
|
|
734
775
|
}
|
|
@@ -737,8 +778,8 @@ function normalizeRepoRoot(p) {
|
|
|
737
778
|
function repoRootCandidates(p) {
|
|
738
779
|
const norm = normalizeRepoRoot(p);
|
|
739
780
|
const candidates = [norm];
|
|
740
|
-
if (
|
|
741
|
-
const parent =
|
|
781
|
+
if (path3.basename(norm) === "specs") {
|
|
782
|
+
const parent = path3.dirname(norm);
|
|
742
783
|
if (parent !== norm) candidates.push(parent);
|
|
743
784
|
}
|
|
744
785
|
return candidates;
|
|
@@ -763,22 +804,22 @@ var init_default_workspace = __esm({
|
|
|
763
804
|
});
|
|
764
805
|
|
|
765
806
|
// spex-console/src/lib/cli/vault-workspace-shared.ts
|
|
766
|
-
import { readFileSync as
|
|
807
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
767
808
|
function resolveConfiguredAccountId(opts = {}) {
|
|
768
809
|
return resolveSessionOwnerUncached({
|
|
769
810
|
env: opts.env ?? process.env,
|
|
770
811
|
homeDir: opts.homeDir ?? heyspexHome2(),
|
|
771
|
-
readFile: opts.readFile ?? ((p) =>
|
|
812
|
+
readFile: opts.readFile ?? ((p) => readFileSync10(p, "utf-8"))
|
|
772
813
|
});
|
|
773
814
|
}
|
|
774
815
|
function resolveCloudAccountIdOrRefuse(opts = {}) {
|
|
775
816
|
const accountId = resolveConfiguredAccountId(opts);
|
|
776
817
|
return accountId ? { ok: true, accountId } : { ok: false, message: CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE };
|
|
777
818
|
}
|
|
778
|
-
async function cloudHttpRequest(http, method,
|
|
779
|
-
const scopeHeaders = http.scope ? http.scope.headersFor(method,
|
|
819
|
+
async function cloudHttpRequest(http, method, path16, body) {
|
|
820
|
+
const scopeHeaders = http.scope ? http.scope.headersFor(method, path16) : {};
|
|
780
821
|
try {
|
|
781
|
-
const res = await http.fetchImpl(`${http.consoleUrl}${
|
|
822
|
+
const res = await http.fetchImpl(`${http.consoleUrl}${path16}`, {
|
|
782
823
|
method,
|
|
783
824
|
headers: {
|
|
784
825
|
"content-type": "application/json",
|
|
@@ -1639,7 +1680,7 @@ var init_vault_shared = __esm({
|
|
|
1639
1680
|
// spex-console/src/lib/indexer/parser.ts
|
|
1640
1681
|
import { createHash } from "node:crypto";
|
|
1641
1682
|
import { promises as fs2 } from "node:fs";
|
|
1642
|
-
import
|
|
1683
|
+
import path5 from "node:path";
|
|
1643
1684
|
function normalizeWikilink2(raw) {
|
|
1644
1685
|
let t = raw.trim();
|
|
1645
1686
|
if (t.startsWith("[[") && t.endsWith("]]")) {
|
|
@@ -3812,9 +3853,9 @@ var init_satellite = __esm({
|
|
|
3812
3853
|
});
|
|
3813
3854
|
|
|
3814
3855
|
// spex-console/src/lib/vault-resolve/index.ts
|
|
3815
|
-
import { existsSync as
|
|
3856
|
+
import { existsSync as existsSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
3816
3857
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
3817
|
-
import
|
|
3858
|
+
import path6 from "node:path";
|
|
3818
3859
|
var init_vault_resolve2 = __esm({
|
|
3819
3860
|
"spex-console/src/lib/vault-resolve/index.ts"() {
|
|
3820
3861
|
"use strict";
|
|
@@ -3851,12 +3892,12 @@ var init_resolve_status = __esm({
|
|
|
3851
3892
|
});
|
|
3852
3893
|
|
|
3853
3894
|
// spex-console/src/lib/vault-resolve/active.ts
|
|
3854
|
-
import { existsSync as
|
|
3855
|
-
import
|
|
3895
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
3896
|
+
import path7 from "node:path";
|
|
3856
3897
|
function fallbackSpecsDir() {
|
|
3857
3898
|
const override = process.env.HEYSPEX_VAULT_DIR;
|
|
3858
|
-
if (override) return
|
|
3859
|
-
return
|
|
3899
|
+
if (override) return path7.resolve(override);
|
|
3900
|
+
return path7.resolve(process.cwd(), "..", "specs");
|
|
3860
3901
|
}
|
|
3861
3902
|
function getActiveSpecsDir() {
|
|
3862
3903
|
return cacheStore.value ? cacheStore.value.specsDirAbs : fallbackSpecsDir();
|
|
@@ -3885,7 +3926,7 @@ var init_active = __esm({
|
|
|
3885
3926
|
|
|
3886
3927
|
// spex-console/src/lib/control-plane/statusline-focus-cache.ts
|
|
3887
3928
|
import fs3 from "node:fs";
|
|
3888
|
-
import
|
|
3929
|
+
import path8 from "node:path";
|
|
3889
3930
|
var init_statusline_focus_cache = __esm({
|
|
3890
3931
|
"spex-console/src/lib/control-plane/statusline-focus-cache.ts"() {
|
|
3891
3932
|
"use strict";
|
|
@@ -4502,10 +4543,10 @@ function trashPathFor(relPath, now) {
|
|
|
4502
4543
|
const normalized = relPath.split("\\").join("/");
|
|
4503
4544
|
const slashIdx = normalized.lastIndexOf("/");
|
|
4504
4545
|
const folder = slashIdx === -1 ? "" : normalized.slice(0, slashIdx);
|
|
4505
|
-
const
|
|
4546
|
+
const basename3 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
|
|
4506
4547
|
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
4507
4548
|
const trashDir = folder ? `${folder}/.trash` : ".trash";
|
|
4508
|
-
return `${trashDir}/${stamp}-${
|
|
4549
|
+
return `${trashDir}/${stamp}-${basename3}`;
|
|
4509
4550
|
}
|
|
4510
4551
|
function materializeSpecMarkdown(bundle) {
|
|
4511
4552
|
return stringifySpecMarkdown(bundle.bodyText, bundle.frontmatter);
|
|
@@ -4627,10 +4668,10 @@ var init_export_status = __esm({
|
|
|
4627
4668
|
function isGitHubFailure(value) {
|
|
4628
4669
|
return typeof value === "object" && value !== null && "status" in value;
|
|
4629
4670
|
}
|
|
4630
|
-
async function githubJson(
|
|
4671
|
+
async function githubJson(path16, target, fetchImpl, init) {
|
|
4631
4672
|
let response;
|
|
4632
4673
|
try {
|
|
4633
|
-
response = await fetchImpl(`${GITHUB_API_BASE2}${
|
|
4674
|
+
response = await fetchImpl(`${GITHUB_API_BASE2}${path16}`, {
|
|
4634
4675
|
...init,
|
|
4635
4676
|
headers: {
|
|
4636
4677
|
...githubHeaders(target.token),
|
|
@@ -5473,7 +5514,7 @@ var init_diagnose_spawned = __esm({
|
|
|
5473
5514
|
|
|
5474
5515
|
// spex-console/src/lib/guardrail-naming.ts
|
|
5475
5516
|
import fs4 from "node:fs";
|
|
5476
|
-
import
|
|
5517
|
+
import path9 from "node:path";
|
|
5477
5518
|
var init_guardrail_naming = __esm({
|
|
5478
5519
|
"spex-console/src/lib/guardrail-naming.ts"() {
|
|
5479
5520
|
"use strict";
|
|
@@ -5576,7 +5617,7 @@ var init_scoped_context = __esm({
|
|
|
5576
5617
|
// spex-console/src/lib/indexer/indexer-service.ts
|
|
5577
5618
|
import { spawnSync } from "node:child_process";
|
|
5578
5619
|
import { promises as fs5 } from "node:fs";
|
|
5579
|
-
import
|
|
5620
|
+
import path10 from "node:path";
|
|
5580
5621
|
var init_indexer_service = __esm({
|
|
5581
5622
|
"spex-console/src/lib/indexer/indexer-service.ts"() {
|
|
5582
5623
|
"use strict";
|
|
@@ -5595,7 +5636,7 @@ var init_indexer_service = __esm({
|
|
|
5595
5636
|
|
|
5596
5637
|
// spex-console/src/lib/indexer/outbox-index-feeder.ts
|
|
5597
5638
|
import { promises as fs6 } from "node:fs";
|
|
5598
|
-
import
|
|
5639
|
+
import path11 from "node:path";
|
|
5599
5640
|
var init_outbox_index_feeder = __esm({
|
|
5600
5641
|
"spex-console/src/lib/indexer/outbox-index-feeder.ts"() {
|
|
5601
5642
|
"use strict";
|
|
@@ -5874,7 +5915,7 @@ var init_list_filter = __esm({
|
|
|
5874
5915
|
});
|
|
5875
5916
|
|
|
5876
5917
|
// spex-console/src/lib/specs/list-query.ts
|
|
5877
|
-
import
|
|
5918
|
+
import path12 from "node:path";
|
|
5878
5919
|
var init_list_query = __esm({
|
|
5879
5920
|
"spex-console/src/lib/specs/list-query.ts"() {
|
|
5880
5921
|
"use strict";
|
|
@@ -5917,7 +5958,7 @@ var init_pipeline_neighborhood = __esm({
|
|
|
5917
5958
|
});
|
|
5918
5959
|
|
|
5919
5960
|
// spex-console/src/lib/specs/spec-by-id.ts
|
|
5920
|
-
import
|
|
5961
|
+
import path13 from "node:path";
|
|
5921
5962
|
var init_spec_by_id = __esm({
|
|
5922
5963
|
"spex-console/src/lib/specs/spec-by-id.ts"() {
|
|
5923
5964
|
"use strict";
|
|
@@ -5974,9 +6015,9 @@ var init_search_body_hit = __esm({
|
|
|
5974
6015
|
|
|
5975
6016
|
// spex-console/src/lib/vault.ts
|
|
5976
6017
|
import { spawn } from "node:child_process";
|
|
5977
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
6018
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
|
|
5978
6019
|
import { promises as fs7 } from "node:fs";
|
|
5979
|
-
import
|
|
6020
|
+
import path14 from "node:path";
|
|
5980
6021
|
import yaml3 from "js-yaml";
|
|
5981
6022
|
function normalizeDateField(value) {
|
|
5982
6023
|
let d = null;
|
|
@@ -6017,18 +6058,18 @@ function extractBodyLinks(body) {
|
|
|
6017
6058
|
return links;
|
|
6018
6059
|
}
|
|
6019
6060
|
function toSummary(absPath, root, fm, body) {
|
|
6020
|
-
const
|
|
6021
|
-
const relPath =
|
|
6061
|
+
const basename3 = path14.basename(absPath).replace(/\.md$/i, "");
|
|
6062
|
+
const relPath = path14.relative(root, absPath).split(path14.sep).join("/");
|
|
6022
6063
|
return {
|
|
6023
|
-
id: typeof fm.id === "string" && fm.id ? fm.id :
|
|
6024
|
-
name: typeof fm.name === "string" && fm.name ? fm.name :
|
|
6064
|
+
id: typeof fm.id === "string" && fm.id ? fm.id : basename3,
|
|
6065
|
+
name: typeof fm.name === "string" && fm.name ? fm.name : basename3,
|
|
6025
6066
|
type: typeof fm.type === "string" ? fm.type : "unknown",
|
|
6026
6067
|
status: typeof fm.status === "string" ? fm.status : "unknown",
|
|
6027
6068
|
effort: typeof fm.effort === "number" ? fm.effort : null,
|
|
6028
6069
|
owner: typeof fm.owner === "string" ? fm.owner : null,
|
|
6029
6070
|
tags: Array.isArray(fm.tags) ? fm.tags.map(String) : [],
|
|
6030
6071
|
archived: fm.archived === true,
|
|
6031
|
-
basename:
|
|
6072
|
+
basename: basename3,
|
|
6032
6073
|
relPath,
|
|
6033
6074
|
links: extractBodyLinks(body),
|
|
6034
6075
|
created: normalizeDateField(fm.created),
|
|
@@ -6114,9 +6155,9 @@ function invalidateVaultCacheForFile(absPath) {
|
|
|
6114
6155
|
else evictProcessVaultHotRows();
|
|
6115
6156
|
}
|
|
6116
6157
|
function specIdFromAbsPath(absPath) {
|
|
6117
|
-
const
|
|
6118
|
-
if (!
|
|
6119
|
-
const parsed = parseSpecId(
|
|
6158
|
+
const basename3 = path14.basename(absPath).replace(/\.md$/i, "");
|
|
6159
|
+
if (!basename3) return null;
|
|
6160
|
+
const parsed = parseSpecId(basename3);
|
|
6120
6161
|
if (parsed?.era === 3 && parsed.fileKey) return `HS-${parsed.fileKey.toUpperCase()}`;
|
|
6121
6162
|
return null;
|
|
6122
6163
|
}
|
|
@@ -6205,8 +6246,8 @@ function isLocalVaultMode() {
|
|
|
6205
6246
|
}
|
|
6206
6247
|
async function writeSpecBytesToVault(relPath, content) {
|
|
6207
6248
|
try {
|
|
6208
|
-
const absPath =
|
|
6209
|
-
const dir =
|
|
6249
|
+
const absPath = path14.join(vaultRoot(), relPath);
|
|
6250
|
+
const dir = path14.dirname(absPath);
|
|
6210
6251
|
await fs7.mkdir(dir, { recursive: true });
|
|
6211
6252
|
await fs7.writeFile(absPath, content, "utf8");
|
|
6212
6253
|
} catch (err) {
|
|
@@ -6372,9 +6413,9 @@ var init_vault = __esm({
|
|
|
6372
6413
|
|
|
6373
6414
|
// spex-console/bin/hs-index-reconcile.ts
|
|
6374
6415
|
import { execFileSync } from "node:child_process";
|
|
6375
|
-
import { appendFileSync, mkdirSync as
|
|
6416
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "node:fs";
|
|
6376
6417
|
import { promises as fs10 } from "node:fs";
|
|
6377
|
-
import
|
|
6418
|
+
import path15 from "node:path";
|
|
6378
6419
|
import process4 from "node:process";
|
|
6379
6420
|
|
|
6380
6421
|
// spex-console/src/lib/cli/control-shared.ts
|
|
@@ -6384,14 +6425,14 @@ init_credentials();
|
|
|
6384
6425
|
init_session_owner();
|
|
6385
6426
|
init_vault_less_endpoints();
|
|
6386
6427
|
init_registry_file();
|
|
6387
|
-
import
|
|
6428
|
+
import path2 from "node:path";
|
|
6388
6429
|
var VAULT_ID_HEADER = "x-heyspex-vault-id";
|
|
6389
6430
|
var ACCOUNT_ID_HEADER = "x-heyspex-account-id";
|
|
6390
|
-
function scopeHeadersFor(scope, method,
|
|
6431
|
+
function scopeHeadersFor(scope, method, path16) {
|
|
6391
6432
|
if (scope === "none") {
|
|
6392
|
-
if (!isVaultLessEndpoint(method,
|
|
6433
|
+
if (!isVaultLessEndpoint(method, path16)) {
|
|
6393
6434
|
throw new Error(
|
|
6394
|
-
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${
|
|
6435
|
+
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${path16} is vault-scoped and must name { accountId, vaultId }.`
|
|
6395
6436
|
);
|
|
6396
6437
|
}
|
|
6397
6438
|
return {};
|
|
@@ -6402,7 +6443,7 @@ function cloudRequestScope(scope) {
|
|
|
6402
6443
|
const named = { accountId: scope.accountId, vaultId: scope.vaultId };
|
|
6403
6444
|
return {
|
|
6404
6445
|
...named,
|
|
6405
|
-
headersFor: (method,
|
|
6446
|
+
headersFor: (method, path16) => isVaultLessEndpoint(method, path16) ? {} : scopeHeadersFor(named, method, path16)
|
|
6406
6447
|
};
|
|
6407
6448
|
}
|
|
6408
6449
|
|
|
@@ -6475,12 +6516,15 @@ var HELP2 = [
|
|
|
6475
6516
|
"(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
|
|
6476
6517
|
"copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
|
|
6477
6518
|
"plus a generated CLAUDE.md, the kept-principles index, and the template_version",
|
|
6478
|
-
"pin; with --git, one initial `git init` + commit.
|
|
6479
|
-
"holds heyspex.yaml
|
|
6480
|
-
"
|
|
6481
|
-
"
|
|
6482
|
-
"
|
|
6483
|
-
"
|
|
6519
|
+
"pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
|
|
6520
|
+
"holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
|
|
6521
|
+
"vault-template row names: only missing shipped files are added, template_version",
|
|
6522
|
+
"is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
|
|
6523
|
+
"nothing existing is ever overwritten (an existing CLAUDE.md gets a",
|
|
6524
|
+
"CLAUDE.md.template beside it), the result lists each manifest row as created or",
|
|
6525
|
+
"kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
|
|
6526
|
+
"names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
|
|
6527
|
+
"non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
|
|
6484
6528
|
"",
|
|
6485
6529
|
"Flags:",
|
|
6486
6530
|
" --console-url <url> console_url value (default: the mode-aware console --",
|
|
@@ -6507,7 +6551,7 @@ init_console_fetch();
|
|
|
6507
6551
|
init_registry_file();
|
|
6508
6552
|
init_repo_identity();
|
|
6509
6553
|
import { promises as fs } from "node:fs";
|
|
6510
|
-
import
|
|
6554
|
+
import path4 from "node:path";
|
|
6511
6555
|
init_vault_workspace_shared();
|
|
6512
6556
|
var SYNC_VAULT_EXIT_UNRESOLVED = 11;
|
|
6513
6557
|
function refuse(message) {
|
|
@@ -6558,7 +6602,7 @@ async function resolveSyncVaultTargetFromEnv(deps) {
|
|
|
6558
6602
|
let markerVaultId = null;
|
|
6559
6603
|
try {
|
|
6560
6604
|
markerVaultId = parseVaultId(
|
|
6561
|
-
await fs.readFile(
|
|
6605
|
+
await fs.readFile(path4.join(deps.vaultRoot, "heyspex.yaml"), "utf8")
|
|
6562
6606
|
);
|
|
6563
6607
|
} catch {
|
|
6564
6608
|
markerVaultId = null;
|
|
@@ -6608,6 +6652,25 @@ init_vault_workspace_shared();
|
|
|
6608
6652
|
init_parser();
|
|
6609
6653
|
|
|
6610
6654
|
// spex-console/src/lib/vault-sync/pull.ts
|
|
6655
|
+
function detectEol(text2) {
|
|
6656
|
+
const crlf = (text2.match(/\r\n/g) ?? []).length;
|
|
6657
|
+
const all = (text2.match(/\n/g) ?? []).length;
|
|
6658
|
+
if (all === 0) return null;
|
|
6659
|
+
return crlf * 2 >= all ? "\r\n" : "\n";
|
|
6660
|
+
}
|
|
6661
|
+
function classifyHeyspexYaml(text2) {
|
|
6662
|
+
if (text2 === null) return "none";
|
|
6663
|
+
if (/^vault_path:[ \t]*[^\s#]/m.test(text2)) return "satellite";
|
|
6664
|
+
if (/^kind:[ \t]*["']?repo-binding["']?[ \t]*$/m.test(text2)) return "repo-binding";
|
|
6665
|
+
const markerKey = /^(vault_id|vault_root):/m.test(text2);
|
|
6666
|
+
if (!markerKey && /^vault:/m.test(text2)) return "repo-binding";
|
|
6667
|
+
return "vault";
|
|
6668
|
+
}
|
|
6669
|
+
function pullTargetRefusal(repoRoot, markerText) {
|
|
6670
|
+
const kind = classifyHeyspexYaml(markerText);
|
|
6671
|
+
if (kind !== "satellite" && kind !== "repo-binding") return null;
|
|
6672
|
+
return `refusing to write specs/ into ${repoRoot}: its heyspex.yaml is a ${kind} marker (a code repo that names its vault elsewhere), not a vault checkout. Nothing was written. Run \`hs pull\` inside the vault's own checkout (\`hs vault resolve\` shows which one this repo is bound to).`;
|
|
6673
|
+
}
|
|
6611
6674
|
function normalizeSpecRelPath(raw) {
|
|
6612
6675
|
if (typeof raw !== "string") return null;
|
|
6613
6676
|
let p = raw.trim();
|
|
@@ -6702,9 +6765,9 @@ function isRoutableStatus(status, type) {
|
|
|
6702
6765
|
if (ROUTE_SETTABLE_STATUSES.has(status)) return true;
|
|
6703
6766
|
return (type ?? "").toLowerCase() === "adr" && ADR_SETTABLE_STATUSES.has(status);
|
|
6704
6767
|
}
|
|
6705
|
-
function matchesSelector(id,
|
|
6768
|
+
function matchesSelector(id, basename3, selectors) {
|
|
6706
6769
|
const i = id.toLowerCase();
|
|
6707
|
-
const b =
|
|
6770
|
+
const b = basename3.toLowerCase();
|
|
6708
6771
|
return selectors.some((raw) => {
|
|
6709
6772
|
const s = raw.trim().toLowerCase();
|
|
6710
6773
|
return s.length > 0 && (i === s || b === s || i.startsWith(`${s}-`) || b.startsWith(`${s}-`));
|
|
@@ -6744,20 +6807,20 @@ function decideReconcilePlan({
|
|
|
6744
6807
|
const c = cloudById.get(id) ?? null;
|
|
6745
6808
|
const primary = d ?? c;
|
|
6746
6809
|
const relPath = reconcileRelPath(primary.relPath);
|
|
6747
|
-
const
|
|
6810
|
+
const basename3 = primary.basename || basenameOfRelPath(relPath);
|
|
6748
6811
|
const type = d?.type ?? c?.type ?? null;
|
|
6749
6812
|
const base = {
|
|
6750
6813
|
id,
|
|
6751
6814
|
type,
|
|
6752
|
-
basename:
|
|
6815
|
+
basename: basename3,
|
|
6753
6816
|
relPath,
|
|
6754
6817
|
disk: view(d),
|
|
6755
6818
|
cloud: view(c),
|
|
6756
6819
|
...d?.closeReason ? { closeReason: d.closeReason } : {}
|
|
6757
6820
|
};
|
|
6758
6821
|
if (d && isCapabilityDoc(d.relPath)) capDocsOnDisk += 1;
|
|
6759
|
-
if (only.length > 0 && !matchesSelector(id,
|
|
6760
|
-
if (skip.length > 0 && matchesSelector(id,
|
|
6822
|
+
if (only.length > 0 && !matchesSelector(id, basename3, only)) continue;
|
|
6823
|
+
if (skip.length > 0 && matchesSelector(id, basename3, skip)) {
|
|
6761
6824
|
rows.push(hold({ ...base, winner: "none" }, "skipped", "held by --skip"));
|
|
6762
6825
|
continue;
|
|
6763
6826
|
}
|
|
@@ -7025,10 +7088,10 @@ async function applyReconcilePlan(deps, plan) {
|
|
|
7025
7088
|
}
|
|
7026
7089
|
return summarizeResult(rows, plan, sideEffects);
|
|
7027
7090
|
}
|
|
7028
|
-
function interpretPatch(requested, res,
|
|
7091
|
+
function interpretPatch(requested, res, basename3, sideEffects) {
|
|
7029
7092
|
const json = rec(res.json);
|
|
7030
7093
|
if (res.status >= 200 && res.status < 300) {
|
|
7031
|
-
pushSideEffects(sideEffects,
|
|
7094
|
+
pushSideEffects(sideEffects, basename3, json);
|
|
7032
7095
|
const coercedTo = str2(json.coerced_to) ?? str2(json.coercedTo);
|
|
7033
7096
|
if (coercedTo && coercedTo !== requested) {
|
|
7034
7097
|
return {
|
|
@@ -7307,16 +7370,17 @@ async function postEdgesChunk(input, specs2) {
|
|
|
7307
7370
|
}
|
|
7308
7371
|
|
|
7309
7372
|
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
7310
|
-
import { promises as fs9 } from "node:fs";
|
|
7373
|
+
import { promises as fs9, readFileSync as readFileSync14 } from "node:fs";
|
|
7374
|
+
import { EOL } from "node:os";
|
|
7311
7375
|
import nodePath2 from "node:path";
|
|
7312
7376
|
|
|
7313
7377
|
// spex-console/src/lib/dev-mode.ts
|
|
7314
7378
|
init_heyspex_home2();
|
|
7315
7379
|
init_credentials();
|
|
7316
|
-
import { mkdirSync as
|
|
7380
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
7317
7381
|
import { join as join10 } from "node:path";
|
|
7318
7382
|
var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
|
|
7319
|
-
var defaultReadFile3 = (p) =>
|
|
7383
|
+
var defaultReadFile3 = (p) => readFileSync11(p, "utf8");
|
|
7320
7384
|
function readCredentialsMcpToken({
|
|
7321
7385
|
homeDir = heyspexHome2(),
|
|
7322
7386
|
readFile = defaultReadFile3
|
|
@@ -7344,6 +7408,30 @@ function hashContent(content) {
|
|
|
7344
7408
|
}
|
|
7345
7409
|
|
|
7346
7410
|
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
7411
|
+
var DEFAULT_PULL_EOL = EOL === "\r\n" ? "\r\n" : "\n";
|
|
7412
|
+
async function eolForPath(abs, fallback = DEFAULT_PULL_EOL) {
|
|
7413
|
+
try {
|
|
7414
|
+
return detectEol(await fs9.readFile(abs, "utf8")) ?? fallback;
|
|
7415
|
+
} catch {
|
|
7416
|
+
return fallback;
|
|
7417
|
+
}
|
|
7418
|
+
}
|
|
7419
|
+
var PullTargetNotVaultError = class extends Error {
|
|
7420
|
+
constructor(message) {
|
|
7421
|
+
super(message);
|
|
7422
|
+
this.name = "PullTargetNotVaultError";
|
|
7423
|
+
}
|
|
7424
|
+
};
|
|
7425
|
+
function assertPullTargetIsVault(repoRoot) {
|
|
7426
|
+
let text2;
|
|
7427
|
+
try {
|
|
7428
|
+
text2 = readFileSync14(nodePath2.join(repoRoot, "heyspex.yaml"), "utf8");
|
|
7429
|
+
} catch {
|
|
7430
|
+
text2 = null;
|
|
7431
|
+
}
|
|
7432
|
+
const refusal = pullTargetRefusal(repoRoot, text2);
|
|
7433
|
+
if (refusal) throw new PullTargetNotVaultError(refusal);
|
|
7434
|
+
}
|
|
7347
7435
|
var LIST_FIELDS = "id,basename,relPath,type,status,updated,updatedAt";
|
|
7348
7436
|
var PULL_LIST_PAGE_LIMIT = 500;
|
|
7349
7437
|
var PULL_LIST_MAX_PAGES = 2e3;
|
|
@@ -7367,10 +7455,10 @@ function resolvePullSourceUrl(explicit) {
|
|
|
7367
7455
|
const raw = explicit?.trim() || process.env.HEYSPEX_PULL_SOURCE_URL?.trim() || CLOUD_CONSOLE_URL2;
|
|
7368
7456
|
return raw.replace(/\/+$/, "");
|
|
7369
7457
|
}
|
|
7370
|
-
async function getJson({ baseUrl, token, fetchImpl = fetch, scopeHeaders = {} },
|
|
7458
|
+
async function getJson({ baseUrl, token, fetchImpl = fetch, scopeHeaders = {} }, path16) {
|
|
7371
7459
|
const headers = { Accept: "application/json", ...scopeHeaders };
|
|
7372
7460
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
7373
|
-
const res = await fetchImpl(`${baseUrl}/${
|
|
7461
|
+
const res = await fetchImpl(`${baseUrl}/${path16.replace(/^\/+/, "")}`, { headers });
|
|
7374
7462
|
const text2 = await res.text();
|
|
7375
7463
|
let json = null;
|
|
7376
7464
|
try {
|
|
@@ -7535,6 +7623,12 @@ function createVaultPullDeps({
|
|
|
7535
7623
|
}) {
|
|
7536
7624
|
const http = { baseUrl, token, fetchImpl, scopeHeaders };
|
|
7537
7625
|
const prefetched = /* @__PURE__ */ new Map();
|
|
7626
|
+
let targetChecked = false;
|
|
7627
|
+
const guardTarget = () => {
|
|
7628
|
+
if (!localRoot || targetChecked) return;
|
|
7629
|
+
assertPullTargetIsVault(nodePath2.dirname(localRoot));
|
|
7630
|
+
targetChecked = true;
|
|
7631
|
+
};
|
|
7538
7632
|
return {
|
|
7539
7633
|
listRemote: () => listRemoteSpecs(http),
|
|
7540
7634
|
getRemoteRef: async (selector) => {
|
|
@@ -7556,7 +7650,9 @@ function createVaultPullDeps({
|
|
|
7556
7650
|
finish: ledger ? () => ledger.flush() : void 0,
|
|
7557
7651
|
listLocal: () => listLocalSpecRelPaths(localRoot ?? vaultRoot()),
|
|
7558
7652
|
serialize: stringifySpecMarkdown,
|
|
7653
|
+
eolFor: (relPath) => eolForPath(nodePath2.join(localRoot ?? vaultRoot(), relPath)),
|
|
7559
7654
|
writeSpec: localRoot ? async (relPath, content, opts) => {
|
|
7655
|
+
guardTarget();
|
|
7560
7656
|
const abs = nodePath2.join(localRoot, relPath);
|
|
7561
7657
|
await fs9.mkdir(nodePath2.dirname(abs), { recursive: true });
|
|
7562
7658
|
onLocalWrite?.(relPath);
|
|
@@ -7733,7 +7829,7 @@ function say(args, text2) {
|
|
|
7733
7829
|
}
|
|
7734
7830
|
function readDiscovery(p) {
|
|
7735
7831
|
try {
|
|
7736
|
-
return
|
|
7832
|
+
return readFileSync15(p, "utf-8");
|
|
7737
7833
|
} catch {
|
|
7738
7834
|
return null;
|
|
7739
7835
|
}
|
|
@@ -7745,7 +7841,7 @@ function resolveConsole(args) {
|
|
|
7745
7841
|
}
|
|
7746
7842
|
function readMarkerVaultId(vaultRoot2) {
|
|
7747
7843
|
try {
|
|
7748
|
-
return parseVaultId(
|
|
7844
|
+
return parseVaultId(readFileSync15(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
|
|
7749
7845
|
} catch {
|
|
7750
7846
|
return null;
|
|
7751
7847
|
}
|
|
@@ -7760,7 +7856,7 @@ function rosterSizeOf(fm, type) {
|
|
|
7760
7856
|
return Array.isArray(list) ? list.filter((x) => x !== null && x !== void 0 && x !== "").length : 0;
|
|
7761
7857
|
}
|
|
7762
7858
|
async function walkLocal(args) {
|
|
7763
|
-
const specsDir =
|
|
7859
|
+
const specsDir = path15.join(args.vaultRoot, "specs");
|
|
7764
7860
|
const files = [];
|
|
7765
7861
|
async function walk(dir) {
|
|
7766
7862
|
let entries;
|
|
@@ -7770,7 +7866,7 @@ async function walkLocal(args) {
|
|
|
7770
7866
|
return;
|
|
7771
7867
|
}
|
|
7772
7868
|
for (const entry of entries) {
|
|
7773
|
-
const full =
|
|
7869
|
+
const full = path15.join(dir, entry.name);
|
|
7774
7870
|
if (entry.isDirectory()) {
|
|
7775
7871
|
if (entry.name === "node_modules") continue;
|
|
7776
7872
|
await walk(full);
|
|
@@ -7785,12 +7881,12 @@ async function walkLocal(args) {
|
|
|
7785
7881
|
const contents = /* @__PURE__ */ new Map();
|
|
7786
7882
|
let failed = 0;
|
|
7787
7883
|
for (const abs of files) {
|
|
7788
|
-
const relPath =
|
|
7884
|
+
const relPath = path15.relative(specsDir, abs).split(path15.sep).join("/");
|
|
7789
7885
|
try {
|
|
7790
7886
|
const content = await fs10.readFile(abs, "utf8");
|
|
7791
7887
|
const parsed = parseSpecFromContent(content, {
|
|
7792
7888
|
relPath: `specs/${relPath}`,
|
|
7793
|
-
basenameFallback:
|
|
7889
|
+
basenameFallback: path15.basename(abs).replace(/\.md$/i, "")
|
|
7794
7890
|
});
|
|
7795
7891
|
const fm = parsed.rawFrontmatter ?? {};
|
|
7796
7892
|
records.push({
|
|
@@ -7920,18 +8016,18 @@ function reportStamp() {
|
|
|
7920
8016
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7921
8017
|
}
|
|
7922
8018
|
function writeReport(vaultRoot2, payload) {
|
|
7923
|
-
const dir =
|
|
7924
|
-
|
|
7925
|
-
const file =
|
|
7926
|
-
|
|
8019
|
+
const dir = path15.join(vaultRoot2, ".heyspex", "index-reconcile");
|
|
8020
|
+
mkdirSync7(dir, { recursive: true });
|
|
8021
|
+
const file = path15.join(dir, `${reportStamp()}.json`);
|
|
8022
|
+
writeFileSync7(file, `${JSON.stringify(payload, null, 2)}
|
|
7927
8023
|
`, "utf-8");
|
|
7928
8024
|
return file;
|
|
7929
8025
|
}
|
|
7930
8026
|
function appendJournal(vaultRoot2, message) {
|
|
7931
|
-
const heyspexDir =
|
|
7932
|
-
|
|
8027
|
+
const heyspexDir = path15.join(vaultRoot2, ".heyspex");
|
|
8028
|
+
mkdirSync7(heyspexDir, { recursive: true });
|
|
7933
8029
|
appendFileSync(
|
|
7934
|
-
|
|
8030
|
+
path15.join(heyspexDir, "index-reconcile.log"),
|
|
7935
8031
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
7936
8032
|
`,
|
|
7937
8033
|
"utf-8"
|
|
@@ -7961,7 +8057,7 @@ async function main() {
|
|
|
7961
8057
|
if (args.apply && markerVaultId && markerVaultId !== vaultId) {
|
|
7962
8058
|
fail(
|
|
7963
8059
|
RECONCILE_EXIT.VAULT_UNRESOLVED,
|
|
7964
|
-
`refusing --apply: it would reconcile vault ${vaultId} against ${
|
|
8060
|
+
`refusing --apply: it would reconcile vault ${vaultId} against ${path15.join(args.vaultRoot, "specs")}, but that repo's heyspex.yaml names vault ${markerVaultId}. Nothing was written.`
|
|
7965
8061
|
);
|
|
7966
8062
|
}
|
|
7967
8063
|
const account = resolveCloudAccountIdOrRefuse();
|
|
@@ -8023,11 +8119,11 @@ async function main() {
|
|
|
8023
8119
|
baseUrl: consoleUrl,
|
|
8024
8120
|
token,
|
|
8025
8121
|
scopeHeaders: scope.headersFor("GET", "/api/specs"),
|
|
8026
|
-
localRoot:
|
|
8122
|
+
localRoot: path15.join(args.vaultRoot, "specs")
|
|
8027
8123
|
});
|
|
8028
8124
|
const deps = {
|
|
8029
|
-
patchStatus: async (
|
|
8030
|
-
const route = `/api/specs/${encodeURIComponent(
|
|
8125
|
+
patchStatus: async (basename3, body) => {
|
|
8126
|
+
const route = `/api/specs/${encodeURIComponent(basename3)}/status`;
|
|
8031
8127
|
try {
|
|
8032
8128
|
const res = await fetch(`${consoleUrl}${route}`, {
|
|
8033
8129
|
method: "PATCH",
|
|
@@ -8116,7 +8212,7 @@ async function main() {
|
|
|
8116
8212
|
rows: final.rows
|
|
8117
8213
|
};
|
|
8118
8214
|
const reportPath = writeReport(args.vaultRoot, payload);
|
|
8119
|
-
const reportRel =
|
|
8215
|
+
const reportRel = path15.relative(args.vaultRoot, reportPath).split(path15.sep).join("/");
|
|
8120
8216
|
if (args.json) {
|
|
8121
8217
|
process4.stdout.write(`${JSON.stringify({ ...payload, report: reportRel })}
|
|
8122
8218
|
`);
|