heyspex 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/bin/hs-vault-binding.mjs +157 -0
- package/bin/hs.mjs +99 -81
- 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 +189 -162
- package/docs/driving-heyspex-from-copilot.md +174 -165
- package/docs/mcp-client-config.md +93 -25
- package/package.json +255 -249
- package/plugins/heyspex/.mcp.json +3 -4
- package/plugins/heyspex/README.md +323 -309
- 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 +1058 -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 +318 -0
- 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/mcp-config.mjs +4 -2
- 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 +59 -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
|
var init_dev_mode = __esm({
|
|
491
525
|
"scripts/lib/dev-mode.mjs"() {
|
|
@@ -496,8 +530,8 @@ var init_dev_mode = __esm({
|
|
|
496
530
|
});
|
|
497
531
|
|
|
498
532
|
// scripts/lib/console-url-resolver.mjs
|
|
499
|
-
import { readFileSync as
|
|
500
|
-
import { homedir as
|
|
533
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
534
|
+
import { homedir as homedir9 } from "node:os";
|
|
501
535
|
import { join as join9 } from "node:path";
|
|
502
536
|
var init_console_url_resolver = __esm({
|
|
503
537
|
"scripts/lib/console-url-resolver.mjs"() {
|
|
@@ -508,9 +542,9 @@ var init_console_url_resolver = __esm({
|
|
|
508
542
|
});
|
|
509
543
|
|
|
510
544
|
// spex-console/src/lib/vault-registry/repo-identity.ts
|
|
511
|
-
import
|
|
545
|
+
import path3 from "node:path";
|
|
512
546
|
function normalizeRepoRoot(p) {
|
|
513
|
-
let resolved =
|
|
547
|
+
let resolved = path3.resolve(p);
|
|
514
548
|
while (resolved.length > 1 && (resolved.endsWith("/") || resolved.endsWith("\\"))) {
|
|
515
549
|
resolved = resolved.slice(0, -1);
|
|
516
550
|
}
|
|
@@ -519,8 +553,8 @@ function normalizeRepoRoot(p) {
|
|
|
519
553
|
function repoRootCandidates(p) {
|
|
520
554
|
const norm = normalizeRepoRoot(p);
|
|
521
555
|
const candidates = [norm];
|
|
522
|
-
if (
|
|
523
|
-
const parent =
|
|
556
|
+
if (path3.basename(norm) === "specs") {
|
|
557
|
+
const parent = path3.dirname(norm);
|
|
524
558
|
if (parent !== norm) candidates.push(parent);
|
|
525
559
|
}
|
|
526
560
|
return candidates;
|
|
@@ -545,22 +579,22 @@ var init_default_workspace = __esm({
|
|
|
545
579
|
});
|
|
546
580
|
|
|
547
581
|
// spex-console/src/lib/cli/vault-workspace-shared.ts
|
|
548
|
-
import { readFileSync as
|
|
582
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
549
583
|
function resolveConfiguredAccountId(opts = {}) {
|
|
550
584
|
return resolveSessionOwnerUncached({
|
|
551
585
|
env: opts.env ?? process.env,
|
|
552
586
|
homeDir: opts.homeDir ?? heyspexHome2(),
|
|
553
|
-
readFile: opts.readFile ?? ((p) =>
|
|
587
|
+
readFile: opts.readFile ?? ((p) => readFileSync10(p, "utf-8"))
|
|
554
588
|
});
|
|
555
589
|
}
|
|
556
590
|
function resolveCloudAccountIdOrRefuse(opts = {}) {
|
|
557
591
|
const accountId = resolveConfiguredAccountId(opts);
|
|
558
592
|
return accountId ? { ok: true, accountId } : { ok: false, message: CLOUD_ACCOUNT_ID_REQUIRED_MESSAGE };
|
|
559
593
|
}
|
|
560
|
-
async function cloudHttpRequest(http, method,
|
|
561
|
-
const scopeHeaders = http.scope ? http.scope.headersFor(method,
|
|
594
|
+
async function cloudHttpRequest(http, method, path16, body) {
|
|
595
|
+
const scopeHeaders = http.scope ? http.scope.headersFor(method, path16) : {};
|
|
562
596
|
try {
|
|
563
|
-
const res = await http.fetchImpl(`${http.consoleUrl}${
|
|
597
|
+
const res = await http.fetchImpl(`${http.consoleUrl}${path16}`, {
|
|
564
598
|
method,
|
|
565
599
|
headers: {
|
|
566
600
|
"content-type": "application/json",
|
|
@@ -3170,9 +3204,9 @@ var init_satellite = __esm({
|
|
|
3170
3204
|
});
|
|
3171
3205
|
|
|
3172
3206
|
// spex-console/src/lib/vault-resolve/index.ts
|
|
3173
|
-
import { existsSync as
|
|
3207
|
+
import { existsSync as existsSync6, readFileSync as readFileSync12, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
3174
3208
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
3175
|
-
import
|
|
3209
|
+
import path5 from "node:path";
|
|
3176
3210
|
var init_vault_resolve2 = __esm({
|
|
3177
3211
|
"spex-console/src/lib/vault-resolve/index.ts"() {
|
|
3178
3212
|
"use strict";
|
|
@@ -3209,12 +3243,12 @@ var init_resolve_status = __esm({
|
|
|
3209
3243
|
});
|
|
3210
3244
|
|
|
3211
3245
|
// spex-console/src/lib/vault-resolve/active.ts
|
|
3212
|
-
import { existsSync as
|
|
3213
|
-
import
|
|
3246
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
3247
|
+
import path6 from "node:path";
|
|
3214
3248
|
function fallbackSpecsDir() {
|
|
3215
3249
|
const override = process.env.HEYSPEX_VAULT_DIR;
|
|
3216
|
-
if (override) return
|
|
3217
|
-
return
|
|
3250
|
+
if (override) return path6.resolve(override);
|
|
3251
|
+
return path6.resolve(process.cwd(), "..", "specs");
|
|
3218
3252
|
}
|
|
3219
3253
|
function getActiveSpecsDir() {
|
|
3220
3254
|
return cacheStore.value ? cacheStore.value.specsDirAbs : fallbackSpecsDir();
|
|
@@ -3240,7 +3274,7 @@ var init_active = __esm({
|
|
|
3240
3274
|
|
|
3241
3275
|
// spex-console/src/lib/control-plane/statusline-focus-cache.ts
|
|
3242
3276
|
import fs4 from "node:fs";
|
|
3243
|
-
import
|
|
3277
|
+
import path7 from "node:path";
|
|
3244
3278
|
var init_statusline_focus_cache = __esm({
|
|
3245
3279
|
"spex-console/src/lib/control-plane/statusline-focus-cache.ts"() {
|
|
3246
3280
|
"use strict";
|
|
@@ -3867,10 +3901,10 @@ function trashPathFor(relPath, now) {
|
|
|
3867
3901
|
const normalized = relPath.split("\\").join("/");
|
|
3868
3902
|
const slashIdx = normalized.lastIndexOf("/");
|
|
3869
3903
|
const folder = slashIdx === -1 ? "" : normalized.slice(0, slashIdx);
|
|
3870
|
-
const
|
|
3904
|
+
const basename3 = slashIdx === -1 ? normalized : normalized.slice(slashIdx + 1);
|
|
3871
3905
|
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
3872
3906
|
const trashDir = folder ? `${folder}/.trash` : ".trash";
|
|
3873
|
-
return `${trashDir}/${stamp}-${
|
|
3907
|
+
return `${trashDir}/${stamp}-${basename3}`;
|
|
3874
3908
|
}
|
|
3875
3909
|
function materializeSpecMarkdown(bundle) {
|
|
3876
3910
|
return stringifySpecMarkdown(bundle.bodyText, bundle.frontmatter);
|
|
@@ -3992,10 +4026,10 @@ var init_export_status = __esm({
|
|
|
3992
4026
|
function isGitHubFailure(value) {
|
|
3993
4027
|
return typeof value === "object" && value !== null && "status" in value;
|
|
3994
4028
|
}
|
|
3995
|
-
async function githubJson(
|
|
4029
|
+
async function githubJson(path16, target, fetchImpl, init) {
|
|
3996
4030
|
let response;
|
|
3997
4031
|
try {
|
|
3998
|
-
response = await fetchImpl(`${GITHUB_API_BASE2}${
|
|
4032
|
+
response = await fetchImpl(`${GITHUB_API_BASE2}${path16}`, {
|
|
3999
4033
|
...init,
|
|
4000
4034
|
headers: {
|
|
4001
4035
|
...githubHeaders(target.token),
|
|
@@ -4732,7 +4766,7 @@ var init_diagnose_spawned = __esm({
|
|
|
4732
4766
|
|
|
4733
4767
|
// spex-console/src/lib/guardrail-naming.ts
|
|
4734
4768
|
import fs5 from "node:fs";
|
|
4735
|
-
import
|
|
4769
|
+
import path8 from "node:path";
|
|
4736
4770
|
var init_guardrail_naming = __esm({
|
|
4737
4771
|
"spex-console/src/lib/guardrail-naming.ts"() {
|
|
4738
4772
|
"use strict";
|
|
@@ -4742,7 +4776,7 @@ var init_guardrail_naming = __esm({
|
|
|
4742
4776
|
// spex-console/src/lib/indexer/parser.ts
|
|
4743
4777
|
import { createHash as createHash3 } from "node:crypto";
|
|
4744
4778
|
import { promises as fs6 } from "node:fs";
|
|
4745
|
-
import
|
|
4779
|
+
import path9 from "node:path";
|
|
4746
4780
|
var import_gray_matter2;
|
|
4747
4781
|
var init_parser = __esm({
|
|
4748
4782
|
"spex-console/src/lib/indexer/parser.ts"() {
|
|
@@ -4849,7 +4883,7 @@ var init_scoped_context = __esm({
|
|
|
4849
4883
|
// spex-console/src/lib/indexer/indexer-service.ts
|
|
4850
4884
|
import { spawnSync } from "node:child_process";
|
|
4851
4885
|
import { promises as fs7 } from "node:fs";
|
|
4852
|
-
import
|
|
4886
|
+
import path10 from "node:path";
|
|
4853
4887
|
var init_indexer_service = __esm({
|
|
4854
4888
|
"spex-console/src/lib/indexer/indexer-service.ts"() {
|
|
4855
4889
|
"use strict";
|
|
@@ -4868,7 +4902,7 @@ var init_indexer_service = __esm({
|
|
|
4868
4902
|
|
|
4869
4903
|
// spex-console/src/lib/indexer/outbox-index-feeder.ts
|
|
4870
4904
|
import { promises as fs8 } from "node:fs";
|
|
4871
|
-
import
|
|
4905
|
+
import path11 from "node:path";
|
|
4872
4906
|
var init_outbox_index_feeder = __esm({
|
|
4873
4907
|
"spex-console/src/lib/indexer/outbox-index-feeder.ts"() {
|
|
4874
4908
|
"use strict";
|
|
@@ -5066,7 +5100,7 @@ var init_list_filter = __esm({
|
|
|
5066
5100
|
});
|
|
5067
5101
|
|
|
5068
5102
|
// spex-console/src/lib/specs/list-query.ts
|
|
5069
|
-
import
|
|
5103
|
+
import path12 from "node:path";
|
|
5070
5104
|
var init_list_query = __esm({
|
|
5071
5105
|
"spex-console/src/lib/specs/list-query.ts"() {
|
|
5072
5106
|
"use strict";
|
|
@@ -5109,7 +5143,7 @@ var init_pipeline_neighborhood = __esm({
|
|
|
5109
5143
|
});
|
|
5110
5144
|
|
|
5111
5145
|
// spex-console/src/lib/specs/spec-by-id.ts
|
|
5112
|
-
import
|
|
5146
|
+
import path13 from "node:path";
|
|
5113
5147
|
var init_spec_by_id = __esm({
|
|
5114
5148
|
"spex-console/src/lib/specs/spec-by-id.ts"() {
|
|
5115
5149
|
"use strict";
|
|
@@ -5166,9 +5200,9 @@ var init_search_body_hit = __esm({
|
|
|
5166
5200
|
|
|
5167
5201
|
// spex-console/src/lib/vault.ts
|
|
5168
5202
|
import { spawn } from "node:child_process";
|
|
5169
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
5203
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
|
|
5170
5204
|
import { promises as fs9 } from "node:fs";
|
|
5171
|
-
import
|
|
5205
|
+
import path14 from "node:path";
|
|
5172
5206
|
import yaml3 from "js-yaml";
|
|
5173
5207
|
function vaultRoot() {
|
|
5174
5208
|
return getActiveSpecsDir();
|
|
@@ -5288,8 +5322,8 @@ var init_vault = __esm({
|
|
|
5288
5322
|
});
|
|
5289
5323
|
|
|
5290
5324
|
// spex-console/bin/hs-vault-push.ts
|
|
5291
|
-
import { readFileSync as
|
|
5292
|
-
import
|
|
5325
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
5326
|
+
import path15 from "node:path";
|
|
5293
5327
|
import process4 from "node:process";
|
|
5294
5328
|
|
|
5295
5329
|
// spex-console/src/lib/cli/control-shared.ts
|
|
@@ -5299,14 +5333,14 @@ init_credentials();
|
|
|
5299
5333
|
init_session_owner();
|
|
5300
5334
|
init_vault_less_endpoints();
|
|
5301
5335
|
init_registry_file();
|
|
5302
|
-
import
|
|
5336
|
+
import path2 from "node:path";
|
|
5303
5337
|
var VAULT_ID_HEADER = "x-heyspex-vault-id";
|
|
5304
5338
|
var ACCOUNT_ID_HEADER = "x-heyspex-account-id";
|
|
5305
|
-
function scopeHeadersFor(scope, method,
|
|
5339
|
+
function scopeHeadersFor(scope, method, path16) {
|
|
5306
5340
|
if (scope === "none") {
|
|
5307
|
-
if (!isVaultLessEndpoint(method,
|
|
5341
|
+
if (!isVaultLessEndpoint(method, path16)) {
|
|
5308
5342
|
throw new Error(
|
|
5309
|
-
`controlRequest: scope "none" is only allowed for the vault-less endpoints (${VAULT_LESS_ENDPOINTS.join(", ")}); ${method.toUpperCase()} ${
|
|
5343
|
+
`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 }.`
|
|
5310
5344
|
);
|
|
5311
5345
|
}
|
|
5312
5346
|
return {};
|
|
@@ -5317,7 +5351,7 @@ function cloudRequestScope(scope) {
|
|
|
5317
5351
|
const named = { accountId: scope.accountId, vaultId: scope.vaultId };
|
|
5318
5352
|
return {
|
|
5319
5353
|
...named,
|
|
5320
|
-
headersFor: (method,
|
|
5354
|
+
headersFor: (method, path16) => isVaultLessEndpoint(method, path16) ? {} : scopeHeadersFor(named, method, path16)
|
|
5321
5355
|
};
|
|
5322
5356
|
}
|
|
5323
5357
|
|
|
@@ -5356,12 +5390,15 @@ var HELP = [
|
|
|
5356
5390
|
"(spex-console/src/lib/cli/template-manifest.ts) \u2014 universal docs",
|
|
5357
5391
|
"copied verbatim, template-shape docs written as author-prompt skeletons \u2014",
|
|
5358
5392
|
"plus a generated CLAUDE.md, the kept-principles index, and the template_version",
|
|
5359
|
-
"pin; with --git, one initial `git init` + commit.
|
|
5360
|
-
"holds heyspex.yaml
|
|
5361
|
-
"
|
|
5362
|
-
"
|
|
5363
|
-
"
|
|
5364
|
-
"
|
|
5393
|
+
"pin; with --git, one initial `git init` + commit. On an EXISTING vault (<dir>",
|
|
5394
|
+
"holds heyspex.yaml with a vault_id) it is a REFRESH \u2014 the repair `hs doctor`'s",
|
|
5395
|
+
"vault-template row names: only missing shipped files are added, template_version",
|
|
5396
|
+
"is written/updated in heyspex.yaml (every other byte and the vault_id kept),",
|
|
5397
|
+
"nothing existing is ever overwritten (an existing CLAUDE.md gets a",
|
|
5398
|
+
"CLAUDE.md.template beside it), the result lists each manifest row as created or",
|
|
5399
|
+
"kept, and a second run writes nothing. <dir> may be any path (a satellite repo",
|
|
5400
|
+
"names its vault, e.g. `hs init --customer-vault ../my-specs`). Refuses only a",
|
|
5401
|
+
"non-empty <dir> that has no heyspex.yaml. See init.customer-vault.ts.",
|
|
5365
5402
|
"",
|
|
5366
5403
|
"Flags:",
|
|
5367
5404
|
" --console-url <url> console_url value (default: the mode-aware console --",
|
|
@@ -5467,7 +5504,7 @@ init_console_fetch();
|
|
|
5467
5504
|
init_registry_file();
|
|
5468
5505
|
init_repo_identity();
|
|
5469
5506
|
import { promises as fs } from "node:fs";
|
|
5470
|
-
import
|
|
5507
|
+
import path4 from "node:path";
|
|
5471
5508
|
init_vault_workspace_shared();
|
|
5472
5509
|
var SYNC_VAULT_EXIT_UNRESOLVED = 11;
|
|
5473
5510
|
function refuse(message) {
|
|
@@ -5518,7 +5555,7 @@ async function resolveSyncVaultTargetFromEnv(deps) {
|
|
|
5518
5555
|
let markerVaultId = null;
|
|
5519
5556
|
try {
|
|
5520
5557
|
markerVaultId = parseVaultId(
|
|
5521
|
-
await fs.readFile(
|
|
5558
|
+
await fs.readFile(path4.join(deps.vaultRoot, "heyspex.yaml"), "utf8")
|
|
5522
5559
|
);
|
|
5523
5560
|
} catch {
|
|
5524
5561
|
markerVaultId = null;
|
|
@@ -5669,16 +5706,17 @@ async function writeDirtyMarker(vaultRoot2, id, now = () => /* @__PURE__ */ new
|
|
|
5669
5706
|
}
|
|
5670
5707
|
|
|
5671
5708
|
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
5672
|
-
import { promises as fs10 } from "node:fs";
|
|
5709
|
+
import { promises as fs10, readFileSync as readFileSync14 } from "node:fs";
|
|
5710
|
+
import { EOL } from "node:os";
|
|
5673
5711
|
import nodePath3 from "node:path";
|
|
5674
5712
|
|
|
5675
5713
|
// spex-console/src/lib/dev-mode.ts
|
|
5676
5714
|
init_heyspex_home2();
|
|
5677
5715
|
init_credentials();
|
|
5678
|
-
import { mkdirSync as
|
|
5716
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
5679
5717
|
import { join as join10 } from "node:path";
|
|
5680
5718
|
var CLOUD_CONSOLE_URL2 = "https://app.heyspex.com";
|
|
5681
|
-
var defaultReadFile = (p) =>
|
|
5719
|
+
var defaultReadFile = (p) => readFileSync11(p, "utf8");
|
|
5682
5720
|
function readCredentialsMcpToken({
|
|
5683
5721
|
homeDir = heyspexHome2(),
|
|
5684
5722
|
readFile = defaultReadFile
|
|
@@ -5696,6 +5734,37 @@ function readCredentialsMcpToken({
|
|
|
5696
5734
|
init_heyspex_home2();
|
|
5697
5735
|
init_vault();
|
|
5698
5736
|
init_vault_frontmatter();
|
|
5737
|
+
|
|
5738
|
+
// spex-console/src/lib/vault-sync/pull.ts
|
|
5739
|
+
function normalizeSpecRelPath(raw) {
|
|
5740
|
+
if (typeof raw !== "string") return null;
|
|
5741
|
+
let p = raw.trim();
|
|
5742
|
+
if (p.length === 0) return null;
|
|
5743
|
+
if (/^[a-zA-Z]:[\\/]/.test(p)) return null;
|
|
5744
|
+
p = p.replace(/\\/g, "/");
|
|
5745
|
+
if (p.startsWith("/")) return null;
|
|
5746
|
+
p = p.replace(/\/{2,}/g, "/");
|
|
5747
|
+
while (p.startsWith("./")) p = p.slice(2);
|
|
5748
|
+
if (p.startsWith("specs/")) p = p.slice("specs/".length);
|
|
5749
|
+
if (p.length === 0) return null;
|
|
5750
|
+
const segments = p.split("/");
|
|
5751
|
+
if (segments.some((s) => s === ".." || s === "." || s.length === 0)) return null;
|
|
5752
|
+
if (!/\.md$/i.test(p)) return null;
|
|
5753
|
+
return p;
|
|
5754
|
+
}
|
|
5755
|
+
function hasDotSegment(relPath) {
|
|
5756
|
+
const segments = relPath.split("/");
|
|
5757
|
+
return segments.slice(0, -1).some((s) => s.startsWith("."));
|
|
5758
|
+
}
|
|
5759
|
+
function basenameOfRelPath(relPath) {
|
|
5760
|
+
const last = relPath.split("/").pop() ?? relPath;
|
|
5761
|
+
return last.replace(/\.md$/i, "");
|
|
5762
|
+
}
|
|
5763
|
+
function instantMs(value) {
|
|
5764
|
+
return typeof value === "string" && value ? Date.parse(value) : Number.NaN;
|
|
5765
|
+
}
|
|
5766
|
+
|
|
5767
|
+
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
5699
5768
|
var LIST_FIELDS = "id,basename,relPath,type,status,updated,updatedAt";
|
|
5700
5769
|
var PULL_LIST_PAGE_LIMIT = 500;
|
|
5701
5770
|
var PULL_LIST_MAX_PAGES = 2e3;
|
|
@@ -5719,10 +5788,10 @@ function resolvePullSourceUrl(explicit) {
|
|
|
5719
5788
|
const raw = explicit?.trim() || process.env.HEYSPEX_PULL_SOURCE_URL?.trim() || CLOUD_CONSOLE_URL2;
|
|
5720
5789
|
return raw.replace(/\/+$/, "");
|
|
5721
5790
|
}
|
|
5722
|
-
async function getJson({ baseUrl, token, fetchImpl = fetch, scopeHeaders = {} },
|
|
5791
|
+
async function getJson({ baseUrl, token, fetchImpl = fetch, scopeHeaders = {} }, path16) {
|
|
5723
5792
|
const headers = { Accept: "application/json", ...scopeHeaders };
|
|
5724
5793
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
5725
|
-
const res = await fetchImpl(`${baseUrl}/${
|
|
5794
|
+
const res = await fetchImpl(`${baseUrl}/${path16.replace(/^\/+/, "")}`, { headers });
|
|
5726
5795
|
const text2 = await res.text();
|
|
5727
5796
|
let json = null;
|
|
5728
5797
|
try {
|
|
@@ -5820,37 +5889,6 @@ async function listLocalSpecRelPaths(root = vaultRoot()) {
|
|
|
5820
5889
|
|
|
5821
5890
|
// spex-console/src/lib/vault-sync/push.ts
|
|
5822
5891
|
init_vault_frontmatter();
|
|
5823
|
-
|
|
5824
|
-
// spex-console/src/lib/vault-sync/pull.ts
|
|
5825
|
-
function normalizeSpecRelPath(raw) {
|
|
5826
|
-
if (typeof raw !== "string") return null;
|
|
5827
|
-
let p = raw.trim();
|
|
5828
|
-
if (p.length === 0) return null;
|
|
5829
|
-
if (/^[a-zA-Z]:[\\/]/.test(p)) return null;
|
|
5830
|
-
p = p.replace(/\\/g, "/");
|
|
5831
|
-
if (p.startsWith("/")) return null;
|
|
5832
|
-
p = p.replace(/\/{2,}/g, "/");
|
|
5833
|
-
while (p.startsWith("./")) p = p.slice(2);
|
|
5834
|
-
if (p.startsWith("specs/")) p = p.slice("specs/".length);
|
|
5835
|
-
if (p.length === 0) return null;
|
|
5836
|
-
const segments = p.split("/");
|
|
5837
|
-
if (segments.some((s) => s === ".." || s === "." || s.length === 0)) return null;
|
|
5838
|
-
if (!/\.md$/i.test(p)) return null;
|
|
5839
|
-
return p;
|
|
5840
|
-
}
|
|
5841
|
-
function hasDotSegment(relPath) {
|
|
5842
|
-
const segments = relPath.split("/");
|
|
5843
|
-
return segments.slice(0, -1).some((s) => s.startsWith("."));
|
|
5844
|
-
}
|
|
5845
|
-
function basenameOfRelPath(relPath) {
|
|
5846
|
-
const last = relPath.split("/").pop() ?? relPath;
|
|
5847
|
-
return last.replace(/\.md$/i, "");
|
|
5848
|
-
}
|
|
5849
|
-
function instantMs(value) {
|
|
5850
|
-
return typeof value === "string" && value ? Date.parse(value) : Number.NaN;
|
|
5851
|
-
}
|
|
5852
|
-
|
|
5853
|
-
// spex-console/src/lib/vault-sync/push.ts
|
|
5854
5892
|
var skip = (reason, detail) => ({
|
|
5855
5893
|
reason,
|
|
5856
5894
|
push: false,
|
|
@@ -5994,9 +6032,9 @@ async function runVaultPush(deps, options2 = {}) {
|
|
|
5994
6032
|
result.skipped.push(item(pre.reason, pre.detail));
|
|
5995
6033
|
continue;
|
|
5996
6034
|
}
|
|
5997
|
-
const
|
|
6035
|
+
const basename3 = basenameOfRelPath(relPath);
|
|
5998
6036
|
if (remoteIndex) {
|
|
5999
|
-
const stamp = remoteIndex.get(
|
|
6037
|
+
const stamp = remoteIndex.get(basename3.toLowerCase());
|
|
6000
6038
|
if (stamp === void 0) {
|
|
6001
6039
|
result.skipped.push(
|
|
6002
6040
|
item("new-on-disk", "no remote row - hs never creates cloud specs from a disk file")
|
|
@@ -6015,7 +6053,7 @@ async function runVaultPush(deps, options2 = {}) {
|
|
|
6015
6053
|
}
|
|
6016
6054
|
let remote;
|
|
6017
6055
|
try {
|
|
6018
|
-
remote = await deps.fetchRemote(
|
|
6056
|
+
remote = await deps.fetchRemote(basename3);
|
|
6019
6057
|
} catch (err) {
|
|
6020
6058
|
result.failed.push({
|
|
6021
6059
|
relPath,
|
|
@@ -6063,7 +6101,7 @@ async function runVaultPush(deps, options2 = {}) {
|
|
|
6063
6101
|
puts += 1;
|
|
6064
6102
|
let put;
|
|
6065
6103
|
try {
|
|
6066
|
-
put = await deps.putRemote(
|
|
6104
|
+
put = await deps.putRemote(basename3, {
|
|
6067
6105
|
frontmatter: payload.frontmatter,
|
|
6068
6106
|
body: payload.body
|
|
6069
6107
|
});
|
|
@@ -6172,7 +6210,7 @@ import nodePath4 from "node:path";
|
|
|
6172
6210
|
init_vault_frontmatter();
|
|
6173
6211
|
async function putRemoteSpec(http, id, payload) {
|
|
6174
6212
|
const { baseUrl, token, fetchImpl = fetch } = http;
|
|
6175
|
-
const
|
|
6213
|
+
const path16 = `/api/specs/${encodeURIComponent(id)}`;
|
|
6176
6214
|
const scope = http.accountId && http.vaultId ? cloudRequestScope({ accountId: http.accountId, vaultId: http.vaultId }).headersFor(
|
|
6177
6215
|
"PUT",
|
|
6178
6216
|
"/api/specs/:id"
|
|
@@ -6183,7 +6221,7 @@ async function putRemoteSpec(http, id, payload) {
|
|
|
6183
6221
|
...scope
|
|
6184
6222
|
};
|
|
6185
6223
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
6186
|
-
const res = await fetchImpl(`${baseUrl}${
|
|
6224
|
+
const res = await fetchImpl(`${baseUrl}${path16}`, {
|
|
6187
6225
|
method: "PUT",
|
|
6188
6226
|
headers,
|
|
6189
6227
|
body: JSON.stringify({ frontmatter: payload.frontmatter, body: payload.body })
|
|
@@ -6199,7 +6237,7 @@ async function putRemoteSpec(http, id, payload) {
|
|
|
6199
6237
|
return {
|
|
6200
6238
|
ok: false,
|
|
6201
6239
|
status: res.status,
|
|
6202
|
-
error: `PUT ${
|
|
6240
|
+
error: `PUT ${path16} returned ${res.status}: ${text2.slice(0, 200)}`,
|
|
6203
6241
|
...res.status === 429 && Number.isFinite(retry) && retry > 0 ? { retryAfterSec: retry } : {}
|
|
6204
6242
|
};
|
|
6205
6243
|
}
|
|
@@ -6211,19 +6249,19 @@ async function putRemoteSpec(http, id, payload) {
|
|
|
6211
6249
|
statusIgnored: json?.status_ignored === true
|
|
6212
6250
|
};
|
|
6213
6251
|
}
|
|
6214
|
-
async function fetchPushRemote(http,
|
|
6215
|
-
const { status, json, text: text2 } = await getJson(http, `api/specs/${encodeURIComponent(
|
|
6252
|
+
async function fetchPushRemote(http, basename3) {
|
|
6253
|
+
const { status, json, text: text2 } = await getJson(http, `api/specs/${encodeURIComponent(basename3)}`);
|
|
6216
6254
|
if (status === 404) return null;
|
|
6217
6255
|
if (status !== 200) {
|
|
6218
6256
|
throw new VaultPullSourceError(
|
|
6219
|
-
`GET /api/specs/${
|
|
6257
|
+
`GET /api/specs/${basename3} returned ${status}: ${text2.slice(0, 200)}`,
|
|
6220
6258
|
status
|
|
6221
6259
|
);
|
|
6222
6260
|
}
|
|
6223
6261
|
const spec = json?.spec;
|
|
6224
6262
|
const fm = spec?.frontmatter && typeof spec.frontmatter === "object" ? spec.frontmatter : null;
|
|
6225
6263
|
if (!spec || typeof spec.body !== "string" || !fm) {
|
|
6226
|
-
throw new VaultPullSourceError(`GET /api/specs/${
|
|
6264
|
+
throw new VaultPullSourceError(`GET /api/specs/${basename3} returned no usable spec`, status);
|
|
6227
6265
|
}
|
|
6228
6266
|
return {
|
|
6229
6267
|
id: String(spec.id ?? fm.id ?? ""),
|
|
@@ -6265,10 +6303,10 @@ function createVaultPushDeps({
|
|
|
6265
6303
|
}
|
|
6266
6304
|
},
|
|
6267
6305
|
readLedger: () => ledger.snapshot(),
|
|
6268
|
-
fetchRemote: (
|
|
6306
|
+
fetchRemote: (basename3) => fetchPushRemote(http, basename3),
|
|
6269
6307
|
listRemote: withListIndex ? () => listRemoteSpecs(http) : void 0,
|
|
6270
6308
|
serialize: stringifySpecMarkdown,
|
|
6271
|
-
putRemote: (
|
|
6309
|
+
putRemote: (basename3, payload) => putRemoteSpec(http, basename3, payload),
|
|
6272
6310
|
recordLedger: (relPath, entry) => ledger.record(relPath, entry),
|
|
6273
6311
|
flushLedger: () => ledger.flush(),
|
|
6274
6312
|
markDirty
|
|
@@ -6314,7 +6352,7 @@ function runEntry(name, main2, options2 = {}) {
|
|
|
6314
6352
|
// spex-console/bin/hs-vault-push.ts
|
|
6315
6353
|
function readMarkerVaultId(vaultRoot2) {
|
|
6316
6354
|
try {
|
|
6317
|
-
return parseVaultId(
|
|
6355
|
+
return parseVaultId(readFileSync15(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
|
|
6318
6356
|
} catch {
|
|
6319
6357
|
return null;
|
|
6320
6358
|
}
|