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
package/dist/cli/hs-defer.mjs
CHANGED
|
@@ -234,6 +234,11 @@ function readLocalDevModeRecord({ homeDir = heyspexHome(), readFile = defaultRea
|
|
|
234
234
|
return { record: null, parseError: `unparseable dev-mode.json: ${String(err?.message ?? err)}` };
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
238
|
+
if (source !== "local-override") return "not-applicable";
|
|
239
|
+
if (!cloudKnown) return "unverified";
|
|
240
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
241
|
+
}
|
|
237
242
|
function resolveDevMode({
|
|
238
243
|
env = process.env,
|
|
239
244
|
homeDir = heyspexHome(),
|
|
@@ -273,6 +278,7 @@ function resolveDevMode({
|
|
|
273
278
|
const semantics = deriveSemantics(mode, reliance);
|
|
274
279
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
275
280
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
281
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
276
282
|
return {
|
|
277
283
|
mode,
|
|
278
284
|
reliance: semantics.reliance,
|
|
@@ -290,7 +296,8 @@ function resolveDevMode({
|
|
|
290
296
|
present: local.record !== null,
|
|
291
297
|
record: local.record,
|
|
292
298
|
parseError: local.parseError,
|
|
293
|
-
staleAgainstCloud
|
|
299
|
+
staleAgainstCloud,
|
|
300
|
+
cloudCheck
|
|
294
301
|
},
|
|
295
302
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
296
303
|
};
|
|
@@ -3615,7 +3622,7 @@ var init_satellite = __esm({
|
|
|
3615
3622
|
});
|
|
3616
3623
|
|
|
3617
3624
|
// spex-console/src/lib/vault-resolve/index.ts
|
|
3618
|
-
import { existsSync as existsSync2, readFileSync as readFileSync7 } from "node:fs";
|
|
3625
|
+
import { existsSync as existsSync2, readFileSync as readFileSync7, realpathSync, statSync } from "node:fs";
|
|
3619
3626
|
import { tmpdir } from "node:os";
|
|
3620
3627
|
import path2 from "node:path";
|
|
3621
3628
|
async function defaultRegistryLookup() {
|
|
@@ -3669,8 +3676,44 @@ function readMarkerSatellite(dir) {
|
|
|
3669
3676
|
function isVaultRootDir(dir) {
|
|
3670
3677
|
const norm = (p) => path2.resolve(p).replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
3671
3678
|
if (norm(dir) === norm(tmpdir())) return false;
|
|
3679
|
+
if (!existsSync2(path2.join(dir, "specs")) && readRepoBinding(dir)) return false;
|
|
3672
3680
|
return existsSync2(path2.join(dir, "heyspex.yaml")) || existsSync2(path2.join(dir, "specs"));
|
|
3673
3681
|
}
|
|
3682
|
+
function parseRepoBinding(content) {
|
|
3683
|
+
const src = String(content ?? "");
|
|
3684
|
+
if (/^(vault_id|vault_root):/m.test(src)) return null;
|
|
3685
|
+
const kind = src.match(/^kind:\s*(.+?)\s*$/m)?.[1]?.replace(/^["']|["']$/g, "").trim();
|
|
3686
|
+
if (kind && kind !== "repo-binding") return null;
|
|
3687
|
+
const lines = src.split(/\r?\n/);
|
|
3688
|
+
const start = lines.findIndex((l) => /^vault:(\s|$)/.test(l));
|
|
3689
|
+
if (start === -1) return null;
|
|
3690
|
+
const unq = (v) => v.trim().replace(/\s+#.*$/, "").replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1").trim();
|
|
3691
|
+
const scalar = lines[start].slice("vault:".length).trim();
|
|
3692
|
+
if (scalar && !scalar.startsWith("#")) {
|
|
3693
|
+
const id = unq(scalar);
|
|
3694
|
+
return id ? { id, name: null, remote: null } : null;
|
|
3695
|
+
}
|
|
3696
|
+
const out = {
|
|
3697
|
+
id: null,
|
|
3698
|
+
name: null,
|
|
3699
|
+
remote: null
|
|
3700
|
+
};
|
|
3701
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
3702
|
+
const l = lines[i];
|
|
3703
|
+
if (!l.trim() || /^\s*#/.test(l)) continue;
|
|
3704
|
+
if (!/^\s+/.test(l)) break;
|
|
3705
|
+
const m = l.match(/^\s+([a-z_]+):\s*(.*)$/);
|
|
3706
|
+
if (m && (m[1] === "id" || m[1] === "name" || m[1] === "remote")) out[m[1]] = unq(m[2]) || null;
|
|
3707
|
+
}
|
|
3708
|
+
return out.id ? { id: out.id, name: out.name, remote: out.remote } : null;
|
|
3709
|
+
}
|
|
3710
|
+
function readRepoBinding(dir) {
|
|
3711
|
+
try {
|
|
3712
|
+
return parseRepoBinding(readFileSync7(path2.join(dir, "heyspex.yaml"), "utf8"));
|
|
3713
|
+
} catch {
|
|
3714
|
+
return null;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3674
3717
|
function findCwdVaultOrAttachment(startDir, stopAt) {
|
|
3675
3718
|
const deps = {
|
|
3676
3719
|
fileExists: (p) => existsSync2(p),
|
|
@@ -3697,6 +3740,8 @@ function findCwdVaultOrAttachment(startDir, stopAt) {
|
|
|
3697
3740
|
const pinned = readPinnedVaultRoot(dir, deps).find((pin) => !isAtOrUnder(pin.value, dir));
|
|
3698
3741
|
if (pinned) return { repoRootAbs: path2.resolve(pinned.value), satellite: true };
|
|
3699
3742
|
if (isVaultRootDir(dir)) return { repoRootAbs: dir, satellite: false };
|
|
3743
|
+
const binding = readRepoBinding(dir);
|
|
3744
|
+
if (binding) return { repoRootAbs: dir, satellite: false, binding };
|
|
3700
3745
|
if (boundary && dir === boundary) return null;
|
|
3701
3746
|
const parent = path2.dirname(dir);
|
|
3702
3747
|
if (parent === dir) return null;
|
|
@@ -3704,6 +3749,22 @@ function findCwdVaultOrAttachment(startDir, stopAt) {
|
|
|
3704
3749
|
}
|
|
3705
3750
|
return null;
|
|
3706
3751
|
}
|
|
3752
|
+
function isExistingDir(p) {
|
|
3753
|
+
try {
|
|
3754
|
+
return statSync(p).isDirectory();
|
|
3755
|
+
} catch {
|
|
3756
|
+
return false;
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
function envNotVaultError(name, raw, abs, from) {
|
|
3760
|
+
const shown = abs.replace(/\\/g, "/");
|
|
3761
|
+
return new VaultResolutionFailure(
|
|
3762
|
+
"VAULT_NOT_FOUND",
|
|
3763
|
+
`${name}=${raw} does not name a vault checkout (${shown}). Unset ${name} so the vault bound to this repo is used (\`hs vault resolve\` shows it), or point it at a vault checkout.`,
|
|
3764
|
+
`unset ${name}`,
|
|
3765
|
+
from
|
|
3766
|
+
);
|
|
3767
|
+
}
|
|
3707
3768
|
async function resolveActiveVaultRoot(options2 = {}) {
|
|
3708
3769
|
const registryLookup = options2.registryLookup ?? defaultRegistryLookup;
|
|
3709
3770
|
const accountPointerLookup = options2.accountPointerLookup ?? defaultAccountPointerLookup;
|
|
@@ -3735,8 +3796,70 @@ async function resolveActiveVaultRoot(options2 = {}) {
|
|
|
3735
3796
|
detail: `explicit vaultId "${options2.vaultId}" has no registry.json row and no Neon row`
|
|
3736
3797
|
});
|
|
3737
3798
|
}
|
|
3738
|
-
const
|
|
3799
|
+
const fromDir = path2.resolve(options2.cwd ?? process.cwd());
|
|
3800
|
+
const envRoot = process.env.HEYSPEX_VAULT_ROOT;
|
|
3801
|
+
if (envRoot) {
|
|
3802
|
+
const root = path2.resolve(envRoot.trim());
|
|
3803
|
+
const envIsVault = isExistingDir(root) && isVaultRootDir(root);
|
|
3804
|
+
if (!envIsVault && !await registryLookupByRoot(root)) {
|
|
3805
|
+
onTier({
|
|
3806
|
+
tier: "env-vault-root",
|
|
3807
|
+
outcome: "empty",
|
|
3808
|
+
detail: `${envRoot} is not a vault checkout`
|
|
3809
|
+
});
|
|
3810
|
+
throw envNotVaultError("HEYSPEX_VAULT_ROOT", envRoot, root, fromDir);
|
|
3811
|
+
}
|
|
3812
|
+
onTier({ tier: "env-vault-root", outcome: "resolved", detail: envRoot });
|
|
3813
|
+
return finalizePathTier(root, "env-vault-root");
|
|
3814
|
+
}
|
|
3815
|
+
onTier({ tier: "env-vault-root", outcome: "skipped", detail: "HEYSPEX_VAULT_ROOT unset" });
|
|
3816
|
+
const envDir = process.env.HEYSPEX_VAULT_DIR;
|
|
3817
|
+
if (envDir) {
|
|
3818
|
+
const specsDirFromEnv = path2.resolve(envDir.trim());
|
|
3819
|
+
const envDirIsSpecs = isExistingDir(specsDirFromEnv) && path2.basename(specsDirFromEnv).toLowerCase() === "specs";
|
|
3820
|
+
const envDirRegistered = Boolean(await registryLookupByRoot(path2.dirname(specsDirFromEnv)));
|
|
3821
|
+
if (!envDirIsSpecs && !envDirRegistered) {
|
|
3822
|
+
onTier({
|
|
3823
|
+
tier: "env-vault-dir",
|
|
3824
|
+
outcome: "empty",
|
|
3825
|
+
detail: `${envDir} is not a specs/ folder`
|
|
3826
|
+
});
|
|
3827
|
+
throw envNotVaultError("HEYSPEX_VAULT_DIR", envDir, specsDirFromEnv, fromDir);
|
|
3828
|
+
}
|
|
3829
|
+
onTier({ tier: "env-vault-dir", outcome: "resolved", detail: envDir });
|
|
3830
|
+
return finalizePathTier(path2.dirname(specsDirFromEnv), "env-vault-dir");
|
|
3831
|
+
}
|
|
3832
|
+
onTier({ tier: "env-vault-dir", outcome: "skipped", detail: "HEYSPEX_VAULT_DIR unset" });
|
|
3833
|
+
let cwdBase = path2.resolve(options2.cwd ?? process.cwd());
|
|
3834
|
+
try {
|
|
3835
|
+
cwdBase = realpathSync(cwdBase);
|
|
3836
|
+
} catch {
|
|
3837
|
+
}
|
|
3739
3838
|
const cwdMatch = findCwdVaultOrAttachment(cwdBase, options2.cwdWalkRoot);
|
|
3839
|
+
if (cwdMatch?.binding) {
|
|
3840
|
+
const record = await registryLookup(cwdMatch.binding.id);
|
|
3841
|
+
const root = record ? record.repoRootAbs : null;
|
|
3842
|
+
if (root) {
|
|
3843
|
+
onTier({
|
|
3844
|
+
tier: "repo-binding",
|
|
3845
|
+
outcome: "resolved",
|
|
3846
|
+
detail: `${cwdMatch.repoRootAbs} -> ${cwdMatch.binding.id}`
|
|
3847
|
+
});
|
|
3848
|
+
return finalize(cwdMatch.binding.id, root, "repo-binding");
|
|
3849
|
+
}
|
|
3850
|
+
onTier({
|
|
3851
|
+
tier: "repo-binding",
|
|
3852
|
+
outcome: "no-registry-row",
|
|
3853
|
+
detail: `${cwdMatch.repoRootAbs}/heyspex.yaml binds ${cwdMatch.binding.id}, which has no registry row (or folder) on this machine`
|
|
3854
|
+
});
|
|
3855
|
+
const bindingDir = cwdMatch.repoRootAbs.replace(/\\/g, "/");
|
|
3856
|
+
throw new VaultResolutionFailure(
|
|
3857
|
+
"VAULT_BINDING_BROKEN",
|
|
3858
|
+
`${bindingDir}/heyspex.yaml binds this repo to vault ${cwdMatch.binding.name ?? cwdMatch.binding.id} (${cwdMatch.binding.id}), but this machine has no registered checkout of it. Clone it (or \`hs pull\` in an existing checkout), \`hs vault register <path>\`, then \`hs vault bind --vault ${cwdMatch.binding.id}\` in ${bindingDir}.`,
|
|
3859
|
+
`hs vault bind --vault ${cwdMatch.binding.id}`,
|
|
3860
|
+
cwdBase
|
|
3861
|
+
);
|
|
3862
|
+
}
|
|
3740
3863
|
if (cwdMatch?.satellite) {
|
|
3741
3864
|
onTier({ tier: "satellite-pin", outcome: "resolved", detail: cwdMatch.repoRootAbs });
|
|
3742
3865
|
if (cwdMatch.markerVaultId) {
|
|
@@ -3744,7 +3867,7 @@ async function resolveActiveVaultRoot(options2 = {}) {
|
|
|
3744
3867
|
}
|
|
3745
3868
|
return finalizePathTier(cwdMatch.repoRootAbs, "satellite-pin");
|
|
3746
3869
|
}
|
|
3747
|
-
if (cwdMatch) {
|
|
3870
|
+
if (cwdMatch && !cwdMatch.binding) {
|
|
3748
3871
|
const root = path2.resolve(cwdMatch.repoRootAbs);
|
|
3749
3872
|
const record = await registryLookupByRoot(root);
|
|
3750
3873
|
const markerVaultId = readMarkerVaultId(root);
|
|
@@ -3768,19 +3891,18 @@ async function resolveActiveVaultRoot(options2 = {}) {
|
|
|
3768
3891
|
outcome: "empty",
|
|
3769
3892
|
detail: `no satellite pin, heyspex.yaml or specs/ at or above ${cwdBase}`
|
|
3770
3893
|
});
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3894
|
+
if (!options2.allowAccountPointer) {
|
|
3895
|
+
onTier({
|
|
3896
|
+
tier: "account-pointer",
|
|
3897
|
+
outcome: "skipped",
|
|
3898
|
+
detail: "account pointer retired for local resolution (HS-TYJA)"
|
|
3899
|
+
});
|
|
3900
|
+
const unbound = new NoActiveVaultError(
|
|
3901
|
+
`No HeySpex vault is bound to ${cwdBase.replace(/\\/g, "/")}, and it is not inside a vault checkout. There is no default "active" vault. Bind this repo: \`hs vault bind --vault <name|id>\` (register a vault checkout first with \`hs vault register <path>\` if \`hs vault list\` shows none).`
|
|
3902
|
+
);
|
|
3903
|
+
unbound.from = cwdBase.replace(/\\/g, "/");
|
|
3904
|
+
throw unbound;
|
|
3782
3905
|
}
|
|
3783
|
-
onTier({ tier: "env-vault-dir", outcome: "skipped", detail: "HEYSPEX_VAULT_DIR unset" });
|
|
3784
3906
|
const pointerVaultId = await accountPointerLookup(options2.accountId);
|
|
3785
3907
|
if (pointerVaultId) {
|
|
3786
3908
|
const record = await registryLookup(pointerVaultId);
|
|
@@ -3802,7 +3924,7 @@ async function resolveActiveVaultRoot(options2 = {}) {
|
|
|
3802
3924
|
}
|
|
3803
3925
|
throw new NoActiveVaultError();
|
|
3804
3926
|
}
|
|
3805
|
-
var UNREGISTERED_VAULT_ID, NoActiveVaultError;
|
|
3927
|
+
var UNREGISTERED_VAULT_ID, NoActiveVaultError, VaultResolutionFailure;
|
|
3806
3928
|
var init_vault_resolve = __esm({
|
|
3807
3929
|
"spex-console/src/lib/vault-resolve/index.ts"() {
|
|
3808
3930
|
"use strict";
|
|
@@ -3814,6 +3936,22 @@ var init_vault_resolve = __esm({
|
|
|
3814
3936
|
this.name = "NoActiveVaultError";
|
|
3815
3937
|
Object.setPrototypeOf(this, _NoActiveVaultError.prototype);
|
|
3816
3938
|
}
|
|
3939
|
+
/** Stable machine code ([[HS-TYJA]]), shared with `vault-binding.mjs`. */
|
|
3940
|
+
code = "VAULT_UNBOUND";
|
|
3941
|
+
/** The exact repair command. */
|
|
3942
|
+
fix = "hs vault bind --vault <name|id>";
|
|
3943
|
+
/** The folder resolution walked from (forward slashes), when known. */
|
|
3944
|
+
from = null;
|
|
3945
|
+
};
|
|
3946
|
+
VaultResolutionFailure = class _VaultResolutionFailure extends NoActiveVaultError {
|
|
3947
|
+
constructor(code, message, fix, from) {
|
|
3948
|
+
super(message);
|
|
3949
|
+
this.name = "VaultResolutionError";
|
|
3950
|
+
this.code = code;
|
|
3951
|
+
this.fix = fix;
|
|
3952
|
+
this.from = from ? from.replace(/\\/g, "/") : null;
|
|
3953
|
+
Object.setPrototypeOf(this, _VaultResolutionFailure.prototype);
|
|
3954
|
+
}
|
|
3817
3955
|
};
|
|
3818
3956
|
}
|
|
3819
3957
|
});
|
|
@@ -4484,7 +4622,11 @@ async function ensureActiveVaultResolved(options2 = {}) {
|
|
|
4484
4622
|
registryLookup: buildRegistryLookup(options2),
|
|
4485
4623
|
accountPointerLookup: buildAccountPointerLookup(accountId, options2),
|
|
4486
4624
|
registryLookupByRoot: buildRegistryLookupByRoot(options2.registryOpts),
|
|
4487
|
-
onTier: recordVaultResolveTier
|
|
4625
|
+
onTier: recordVaultResolveTier,
|
|
4626
|
+
// Cloud console server: no repo on disk to walk, so the account pointer
|
|
4627
|
+
// is still its last resort until every request names its vault
|
|
4628
|
+
// (follow-up to [[HS-TYJA]]; local resolvers never read it).
|
|
4629
|
+
allowAccountPointer: true
|
|
4488
4630
|
});
|
|
4489
4631
|
} catch (err) {
|
|
4490
4632
|
markVaultResolveFailed(err);
|
|
@@ -9523,7 +9665,7 @@ var init_search_body_hit = __esm({
|
|
|
9523
9665
|
|
|
9524
9666
|
// spex-console/src/lib/vault.ts
|
|
9525
9667
|
import { spawn } from "node:child_process";
|
|
9526
|
-
import { readdirSync, readFileSync as readFileSync9, statSync } from "node:fs";
|
|
9668
|
+
import { readdirSync, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
|
|
9527
9669
|
import { promises as fs6 } from "node:fs";
|
|
9528
9670
|
import path11 from "node:path";
|
|
9529
9671
|
import yaml3 from "js-yaml";
|
package/dist/cli/hs-dev.mjs
CHANGED
|
@@ -56,6 +56,11 @@ function assertNotRealHomeUnderTest(filePath) {
|
|
|
56
56
|
// spex-console/src/lib/dev-mode.ts
|
|
57
57
|
var CLOUD_CONSOLE_URL = "https://app.heyspex.com";
|
|
58
58
|
var LOCAL_CONSOLE_URL = "http://localhost:3000";
|
|
59
|
+
function overrideCloudCheck(source, cloudKnown, staleAgainstCloud) {
|
|
60
|
+
if (source !== "local-override") return "not-applicable";
|
|
61
|
+
if (!cloudKnown) return "unverified";
|
|
62
|
+
return staleAgainstCloud ? "stale" : "in-sync";
|
|
63
|
+
}
|
|
59
64
|
function normalizeMode(raw) {
|
|
60
65
|
const v = String(raw ?? "").trim().toLowerCase();
|
|
61
66
|
if (v === "prod" || v === "production") return { mode: "prod", reliance: "cloud" };
|
|
@@ -163,6 +168,7 @@ function resolveDevMode({
|
|
|
163
168
|
const semantics = deriveSemantics(mode, reliance);
|
|
164
169
|
const consoleUrl = source === "local-override" && local.record?.console_url ? local.record.console_url.replace(/\/+$/, "") : semantics.consoleUrl;
|
|
165
170
|
const staleAgainstCloud = source === "local-override" && cloudValue !== null && (cloudValue.mode !== mode || mode === "dev" && cloudValue.reliance !== semantics.reliance);
|
|
171
|
+
const cloudCheck = overrideCloudCheck(source, cloudKnown, staleAgainstCloud);
|
|
166
172
|
return {
|
|
167
173
|
mode,
|
|
168
174
|
reliance: semantics.reliance,
|
|
@@ -180,7 +186,8 @@ function resolveDevMode({
|
|
|
180
186
|
present: local.record !== null,
|
|
181
187
|
record: local.record,
|
|
182
188
|
parseError: local.parseError,
|
|
183
|
-
staleAgainstCloud
|
|
189
|
+
staleAgainstCloud,
|
|
190
|
+
cloudCheck
|
|
184
191
|
},
|
|
185
192
|
cloudSetting: { known: cloudKnown, value: cloudValue }
|
|
186
193
|
};
|
|
@@ -275,7 +282,7 @@ async function discardResponseBody(res) {
|
|
|
275
282
|
}
|
|
276
283
|
}
|
|
277
284
|
async function fetchCloudDevMode({
|
|
278
|
-
consoleUrl,
|
|
285
|
+
consoleUrl = CLOUD_CONSOLE_URL,
|
|
279
286
|
fetchImpl = fetch,
|
|
280
287
|
homeDir = heyspexHome(),
|
|
281
288
|
readFile = defaultReadFile,
|
|
@@ -493,7 +500,7 @@ function printStatus(resolved, cloudError) {
|
|
|
493
500
|
if (resolved.localOverride.present) {
|
|
494
501
|
const rec = resolved.localOverride.record;
|
|
495
502
|
out.push(
|
|
496
|
-
` local override: active (written ${rec?.updated_at ?? "?"} by ${rec?.source ?? "?"})` + (resolved.localOverride.
|
|
503
|
+
` local override: active (written ${rec?.updated_at ?? "?"} by ${rec?.source ?? "?"})` + (resolved.localOverride.cloudCheck === "stale" ? " \u2014 DISAGREES with the cloud setting (stale override?)" : resolved.localOverride.cloudCheck === "unverified" ? " \u2014 UNVERIFIED: the cloud setting could not be read, so staleness is unknown" : "")
|
|
497
504
|
);
|
|
498
505
|
} else if (resolved.localOverride.parseError) {
|
|
499
506
|
out.push(` local override: UNREADABLE, ignored \u2014 ${resolved.localOverride.parseError}`);
|
|
@@ -523,8 +530,7 @@ async function main() {
|
|
|
523
530
|
const positional = argv.filter((a) => !a.startsWith("--"));
|
|
524
531
|
const token = positional[0]?.toLowerCase();
|
|
525
532
|
if (token === void 0 || token === "status") {
|
|
526
|
-
const
|
|
527
|
-
const cloud = await fetchCloudDevMode({ consoleUrl: pre.consoleUrl });
|
|
533
|
+
const cloud = await fetchCloudDevMode({ consoleUrl: CLOUD_CONSOLE_URL });
|
|
528
534
|
const resolved = resolveDevMode({ cloudSetting: cloud.known ? cloud.value : void 0 });
|
|
529
535
|
if (json) {
|
|
530
536
|
process3.stdout.write(
|