heyspex 0.2.5 → 0.2.6
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 +1 -1
- package/bin/hs.mjs +281 -30
- package/dev-factory/lib/dispatch-guard.mjs +75 -0
- package/dev-factory/lib/runner-path.mjs +51 -0
- package/dev-factory/spexcrawl/spexcrawl-cli.mjs +734 -0
- package/dist/cli/hs-connect.mjs +20 -1
- package/dist/cli/hs-defer.mjs +3802 -3744
- package/dist/cli/hs-events.mjs +1296 -0
- package/dist/cli/hs-export-events.mjs +1252 -0
- package/dist/cli/hs-handoff.mjs +2576 -0
- package/dist/cli/hs-index-rebuild.mjs +209 -36
- package/dist/cli/hs-index-reconcile.mjs +212 -39
- package/dist/cli/hs-init.mjs +20 -1
- package/dist/cli/hs-install.mjs +20 -1
- package/dist/cli/hs-join.mjs +20 -1
- package/dist/cli/hs-next-launch.mjs +2698 -0
- package/dist/cli/hs-pin.mjs +1336 -0
- package/dist/cli/hs-prereq.mjs +1380 -0
- package/dist/cli/hs-revalidate.mjs +3257 -0
- package/dist/cli/hs-spawn.mjs +12377 -0
- package/dist/cli/hs-test.mjs +1046 -0
- package/dist/cli/hs-trash.mjs +362 -0
- package/dist/cli/hs-trust.mjs +197 -0
- package/dist/cli/hs-vault-pull.mjs +214 -41
- package/dist/cli/hs-vault-push.mjs +212 -39
- package/dist/cli-templates/harness-commands/claude/hs-res.md +71 -63
- package/dist/cli-templates/harness-commands/cursor/hs-res.md +66 -58
- package/docs/copilot-plugin-install.md +2 -2
- package/package.json +261 -257
- package/plugins/heyspex/.claude-plugin/plugin.json +1 -1
- package/plugins/heyspex/.cursor-plugin/plugin.json +1 -1
- package/scripts/claude-hooks/wip-checkpoint.mjs +17 -2
- package/scripts/claude-statusline/statusline-command.mjs +287 -27
- package/scripts/hs-companion.mjs +8 -0
- package/scripts/install-claude-code-hooks.mjs +104 -3
- package/scripts/install-copilot-hooks.mjs +21 -1
- package/scripts/install-cursor-hooks.mjs +4 -3
- package/scripts/lib/capture-execution-guard.mjs +221 -0
- package/scripts/lib/customer-kit.mjs +48 -0
- package/scripts/lib/hs-companion-settings.mjs +25 -0
- package/scripts/record-grounding.mjs +38 -0
- package/vendor/heyspex-mcp/http-transport.mjs +1 -1
- package/vendor/heyspex-mcp/test-helpers.mjs +37 -23
- package/bin/postinstall.mjs +0 -88
|
@@ -1073,10 +1073,10 @@ var require_stringify = __commonJS({
|
|
|
1073
1073
|
data = Object.assign({}, file.data, data);
|
|
1074
1074
|
const open = opts.delimiters[0];
|
|
1075
1075
|
const close = opts.delimiters[1];
|
|
1076
|
-
const
|
|
1076
|
+
const matter5 = engine.stringify(data, options2).trim();
|
|
1077
1077
|
let buf = "";
|
|
1078
|
-
if (
|
|
1079
|
-
buf = newline(open) + newline(
|
|
1078
|
+
if (matter5 !== "{}") {
|
|
1079
|
+
buf = newline(open) + newline(matter5) + newline(close);
|
|
1080
1080
|
}
|
|
1081
1081
|
if (typeof file.excerpt === "string" && file.excerpt !== "") {
|
|
1082
1082
|
if (str2.indexOf(file.excerpt.trim()) === -1) {
|
|
@@ -1182,19 +1182,19 @@ var require_gray_matter = __commonJS({
|
|
|
1182
1182
|
var toFile = require_to_file();
|
|
1183
1183
|
var parse2 = require_parse();
|
|
1184
1184
|
var utils = require_utils();
|
|
1185
|
-
function
|
|
1185
|
+
function matter5(input, options2) {
|
|
1186
1186
|
if (input === "") {
|
|
1187
1187
|
return { data: {}, content: input, excerpt: "", orig: input };
|
|
1188
1188
|
}
|
|
1189
1189
|
let file = toFile(input);
|
|
1190
|
-
const cached =
|
|
1190
|
+
const cached = matter5.cache[file.content];
|
|
1191
1191
|
if (!options2) {
|
|
1192
1192
|
if (cached) {
|
|
1193
1193
|
file = Object.assign({}, cached);
|
|
1194
1194
|
file.orig = cached.orig;
|
|
1195
1195
|
return file;
|
|
1196
1196
|
}
|
|
1197
|
-
|
|
1197
|
+
matter5.cache[file.content] = file;
|
|
1198
1198
|
}
|
|
1199
1199
|
return parseMatter(file, options2);
|
|
1200
1200
|
}
|
|
@@ -1216,7 +1216,7 @@ var require_gray_matter = __commonJS({
|
|
|
1216
1216
|
}
|
|
1217
1217
|
str2 = str2.slice(openLen);
|
|
1218
1218
|
const len = str2.length;
|
|
1219
|
-
const language =
|
|
1219
|
+
const language = matter5.language(str2, opts);
|
|
1220
1220
|
if (language.name) {
|
|
1221
1221
|
file.language = language.name;
|
|
1222
1222
|
str2 = str2.slice(language.raw.length);
|
|
@@ -1251,24 +1251,24 @@ var require_gray_matter = __commonJS({
|
|
|
1251
1251
|
}
|
|
1252
1252
|
return file;
|
|
1253
1253
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
if (typeof file === "string") file =
|
|
1254
|
+
matter5.engines = engines2;
|
|
1255
|
+
matter5.stringify = function(file, data, options2) {
|
|
1256
|
+
if (typeof file === "string") file = matter5(file, options2);
|
|
1257
1257
|
return stringify(file, data, options2);
|
|
1258
1258
|
};
|
|
1259
|
-
|
|
1259
|
+
matter5.read = function(filepath, options2) {
|
|
1260
1260
|
const str2 = fs12.readFileSync(filepath, "utf8");
|
|
1261
|
-
const file =
|
|
1261
|
+
const file = matter5(str2, options2);
|
|
1262
1262
|
file.path = filepath;
|
|
1263
1263
|
return file;
|
|
1264
1264
|
};
|
|
1265
|
-
|
|
1265
|
+
matter5.test = function(str2, options2) {
|
|
1266
1266
|
return utils.startsWith(str2, defaults(options2).delimiters[0]);
|
|
1267
1267
|
};
|
|
1268
|
-
|
|
1268
|
+
matter5.language = function(str2, options2) {
|
|
1269
1269
|
const opts = defaults(options2);
|
|
1270
1270
|
const open = opts.delimiters[0];
|
|
1271
|
-
if (
|
|
1271
|
+
if (matter5.test(str2)) {
|
|
1272
1272
|
str2 = str2.slice(open.length);
|
|
1273
1273
|
}
|
|
1274
1274
|
const language = str2.slice(0, str2.search(/\r?\n/));
|
|
@@ -1277,11 +1277,11 @@ var require_gray_matter = __commonJS({
|
|
|
1277
1277
|
name: language ? language.trim() : ""
|
|
1278
1278
|
};
|
|
1279
1279
|
};
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1280
|
+
matter5.cache = {};
|
|
1281
|
+
matter5.clearCache = function() {
|
|
1282
|
+
matter5.cache = {};
|
|
1283
1283
|
};
|
|
1284
|
-
module2.exports =
|
|
1284
|
+
module2.exports = matter5;
|
|
1285
1285
|
}
|
|
1286
1286
|
});
|
|
1287
1287
|
|
|
@@ -3068,11 +3068,12 @@ function getPostgresAdminDb() {
|
|
|
3068
3068
|
function inertClient() {
|
|
3069
3069
|
return false;
|
|
3070
3070
|
}
|
|
3071
|
-
var isNeonPool;
|
|
3071
|
+
var isNeonPool, registerVaultScopedDbAccessor;
|
|
3072
3072
|
var init_postgres_client_cli_shim = __esm({
|
|
3073
3073
|
"dist/cli/._postgres-client.cli-shim.mjs"() {
|
|
3074
3074
|
"use strict";
|
|
3075
3075
|
isNeonPool = inertClient;
|
|
3076
|
+
registerVaultScopedDbAccessor = inertClient;
|
|
3076
3077
|
}
|
|
3077
3078
|
});
|
|
3078
3079
|
|
|
@@ -3323,6 +3324,141 @@ var init_clients = __esm({
|
|
|
3323
3324
|
}
|
|
3324
3325
|
});
|
|
3325
3326
|
|
|
3327
|
+
// spex-console/src/lib/rate-limits.ts
|
|
3328
|
+
var RATE_LIMIT_BUDGETS;
|
|
3329
|
+
var init_rate_limits = __esm({
|
|
3330
|
+
"spex-console/src/lib/rate-limits.ts"() {
|
|
3331
|
+
"use strict";
|
|
3332
|
+
RATE_LIMIT_BUDGETS = {
|
|
3333
|
+
mcp_tool_calls: { daily: 500, hourly: 125 },
|
|
3334
|
+
spec_writes: { daily: 100, hourly: 25 },
|
|
3335
|
+
capture_sessions: { daily: 20, hourly: 5 },
|
|
3336
|
+
// Paul's Advocate free-text chat ([[HS-4YYB-XEBI-TASK-paul-s-advocate-answers-real-questions]]):
|
|
3337
|
+
// a public YC reviewer can otherwise run up LLM spend on an unauthenticated-feeling
|
|
3338
|
+
// widget. 30/hour matches the builder brief's "e.g. 30 messages/hour" starting point;
|
|
3339
|
+
// daily is 4x hourly (looser than the 25%-of-daily pattern above — a reviewer chatting
|
|
3340
|
+
// steadily through a demo session should not get cut off mid-conversation).
|
|
3341
|
+
advocate_chat: { daily: 120, hourly: 30 }
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
});
|
|
3345
|
+
|
|
3346
|
+
// spex-console/src/lib/plan-tier.ts
|
|
3347
|
+
function scaledBudgets(multiplier) {
|
|
3348
|
+
const out = {};
|
|
3349
|
+
for (const [bucket, budget] of Object.entries(RATE_LIMIT_BUDGETS)) {
|
|
3350
|
+
out[bucket] = {
|
|
3351
|
+
daily: Math.round(budget.daily * multiplier),
|
|
3352
|
+
hourly: Math.round(budget.hourly * multiplier)
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3355
|
+
return Object.freeze(out);
|
|
3356
|
+
}
|
|
3357
|
+
var PLAN_TIER_RATE_LIMIT_MULTIPLIER, PLAN_TIER_LIMITS;
|
|
3358
|
+
var init_plan_tier = __esm({
|
|
3359
|
+
"spex-console/src/lib/plan-tier.ts"() {
|
|
3360
|
+
"use strict";
|
|
3361
|
+
init_sql_orm_inert_cli_shim();
|
|
3362
|
+
init_postgres_client_cli_shim();
|
|
3363
|
+
init_postgres();
|
|
3364
|
+
init_rate_limits();
|
|
3365
|
+
PLAN_TIER_RATE_LIMIT_MULTIPLIER = {
|
|
3366
|
+
free: 1,
|
|
3367
|
+
pro: 4,
|
|
3368
|
+
enterprise: 20
|
|
3369
|
+
};
|
|
3370
|
+
PLAN_TIER_LIMITS = {
|
|
3371
|
+
free: {
|
|
3372
|
+
statement_timeout_ms: 5e3,
|
|
3373
|
+
read_work_mem: "4MB",
|
|
3374
|
+
rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.free)
|
|
3375
|
+
},
|
|
3376
|
+
pro: {
|
|
3377
|
+
statement_timeout_ms: 15e3,
|
|
3378
|
+
read_work_mem: "16MB",
|
|
3379
|
+
rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.pro)
|
|
3380
|
+
},
|
|
3381
|
+
enterprise: {
|
|
3382
|
+
statement_timeout_ms: 3e4,
|
|
3383
|
+
read_work_mem: "64MB",
|
|
3384
|
+
rate_limit_budgets: scaledBudgets(PLAN_TIER_RATE_LIMIT_MULTIPLIER.enterprise)
|
|
3385
|
+
}
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
});
|
|
3389
|
+
|
|
3390
|
+
// spex-console/src/lib/db/clients/vault-router.ts
|
|
3391
|
+
import { existsSync as existsSync8, readFileSync as readFileSync13 } from "node:fs";
|
|
3392
|
+
import { homedir as homedir10 } from "node:os";
|
|
3393
|
+
import { join as join11 } from "node:path";
|
|
3394
|
+
var CREDENTIALS_PATH, ROUTER_KEY, globalRouter, router;
|
|
3395
|
+
var init_vault_router = __esm({
|
|
3396
|
+
"spex-console/src/lib/db/clients/vault-router.ts"() {
|
|
3397
|
+
"use strict";
|
|
3398
|
+
init_sql_orm_inert_cli_shim();
|
|
3399
|
+
init_sql_orm_inert_cli_shim();
|
|
3400
|
+
init_postgres();
|
|
3401
|
+
init_postgres_client_cli_shim();
|
|
3402
|
+
CREDENTIALS_PATH = join11(homedir10(), ".heyspex", "credentials.yaml");
|
|
3403
|
+
ROUTER_KEY = Symbol.for("heyspex.vaultRouter");
|
|
3404
|
+
globalRouter = globalThis;
|
|
3405
|
+
if (!globalRouter[ROUTER_KEY]) {
|
|
3406
|
+
globalRouter[ROUTER_KEY] = {
|
|
3407
|
+
dedicated: /* @__PURE__ */ new Map(),
|
|
3408
|
+
refCache: /* @__PURE__ */ new Map(),
|
|
3409
|
+
warnedSchemaMissing: false
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
router = globalRouter[ROUTER_KEY];
|
|
3413
|
+
}
|
|
3414
|
+
});
|
|
3415
|
+
|
|
3416
|
+
// spex-console/src/lib/db/clients/vault-scope.ts
|
|
3417
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3418
|
+
function warnScopeClosedOnce(ctx) {
|
|
3419
|
+
if (ctx.warnedClosed) return;
|
|
3420
|
+
ctx.warnedClosed = true;
|
|
3421
|
+
console.warn(
|
|
3422
|
+
`[vault-scope] db use after vault scope closed; falling back to the pool connection (unscoped) (vaultId: ${ctx.vaultId})`
|
|
3423
|
+
);
|
|
3424
|
+
}
|
|
3425
|
+
function handleScopeClosedAccess(ctx) {
|
|
3426
|
+
if (ctx.onStaleUse === "throw") {
|
|
3427
|
+
throw new VaultScopeClosedError(ctx.vaultId);
|
|
3428
|
+
}
|
|
3429
|
+
warnScopeClosedOnce(ctx);
|
|
3430
|
+
}
|
|
3431
|
+
var VaultScopeClosedError, vaultScopeStorage;
|
|
3432
|
+
var init_vault_scope = __esm({
|
|
3433
|
+
"spex-console/src/lib/db/clients/vault-scope.ts"() {
|
|
3434
|
+
"use strict";
|
|
3435
|
+
init_sql_orm_inert_cli_shim();
|
|
3436
|
+
init_plan_tier();
|
|
3437
|
+
init_postgres_client_cli_shim();
|
|
3438
|
+
init_vault_router();
|
|
3439
|
+
VaultScopeClosedError = class extends Error {
|
|
3440
|
+
vaultId;
|
|
3441
|
+
constructor(vaultId) {
|
|
3442
|
+
super(
|
|
3443
|
+
`Vault scope for "${vaultId}" is closed; this path opted into onStaleUseAfterClose: "throw", so a db handle used after the scope's transaction settled fails loudly instead of silently falling back to the unscoped pool connection.`
|
|
3444
|
+
);
|
|
3445
|
+
this.name = "VaultScopeClosedError";
|
|
3446
|
+
this.vaultId = vaultId;
|
|
3447
|
+
}
|
|
3448
|
+
};
|
|
3449
|
+
vaultScopeStorage = new AsyncLocalStorage();
|
|
3450
|
+
registerVaultScopedDbAccessor(() => {
|
|
3451
|
+
const ctx = vaultScopeStorage.getStore();
|
|
3452
|
+
if (!ctx) return null;
|
|
3453
|
+
if (ctx.closed) {
|
|
3454
|
+
handleScopeClosedAccess(ctx);
|
|
3455
|
+
return null;
|
|
3456
|
+
}
|
|
3457
|
+
return ctx.proxy;
|
|
3458
|
+
});
|
|
3459
|
+
}
|
|
3460
|
+
});
|
|
3461
|
+
|
|
3326
3462
|
// spex-console/src/lib/scheduler/types.ts
|
|
3327
3463
|
var init_types2 = __esm({
|
|
3328
3464
|
"spex-console/src/lib/scheduler/types.ts"() {
|
|
@@ -3857,10 +3993,45 @@ var init_config = __esm({
|
|
|
3857
3993
|
}
|
|
3858
3994
|
});
|
|
3859
3995
|
|
|
3860
|
-
// spex-console/src/lib/
|
|
3996
|
+
// spex-console/src/lib/safe-yaml.ts
|
|
3861
3997
|
import yaml2 from "js-yaml";
|
|
3998
|
+
function dropUndefinedDeep(value) {
|
|
3999
|
+
if (Array.isArray(value)) {
|
|
4000
|
+
return value.filter((item) => item !== void 0).map((item) => dropUndefinedDeep(item));
|
|
4001
|
+
}
|
|
4002
|
+
if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
4003
|
+
const out = {};
|
|
4004
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4005
|
+
if (v === void 0) continue;
|
|
4006
|
+
out[k] = dropUndefinedDeep(v);
|
|
4007
|
+
}
|
|
4008
|
+
return out;
|
|
4009
|
+
}
|
|
4010
|
+
return value;
|
|
4011
|
+
}
|
|
4012
|
+
function safeYamlDump(value, options2 = {}) {
|
|
4013
|
+
const { schema: _ignoredSchema, ...rest } = options2;
|
|
4014
|
+
const data = dropUndefinedDeep(value);
|
|
4015
|
+
if (grayMatterYamlStringify) return grayMatterYamlStringify(data, rest);
|
|
4016
|
+
return yaml2.dump(data, { ...rest, schema: SAFE_YAML_SCHEMA });
|
|
4017
|
+
}
|
|
4018
|
+
var import_gray_matter, SAFE_YAML_SCHEMA, grayMatterYamlStringify, YAML_FAILSAFE_SCHEMA;
|
|
4019
|
+
var init_safe_yaml = __esm({
|
|
4020
|
+
"spex-console/src/lib/safe-yaml.ts"() {
|
|
4021
|
+
"use strict";
|
|
4022
|
+
import_gray_matter = __toESM(require_gray_matter(), 1);
|
|
4023
|
+
SAFE_YAML_SCHEMA = yaml2.DEFAULT_SAFE_SCHEMA ?? yaml2.DEFAULT_SCHEMA;
|
|
4024
|
+
grayMatterYamlStringify = (() => {
|
|
4025
|
+
const engine = import_gray_matter.default.engines?.yaml;
|
|
4026
|
+
return typeof engine?.stringify === "function" ? engine.stringify : void 0;
|
|
4027
|
+
})();
|
|
4028
|
+
YAML_FAILSAFE_SCHEMA = yaml2.FAILSAFE_SCHEMA;
|
|
4029
|
+
}
|
|
4030
|
+
});
|
|
4031
|
+
|
|
4032
|
+
// spex-console/src/lib/vault-frontmatter.ts
|
|
3862
4033
|
function normalizeFrontmatterForYaml(frontmatter) {
|
|
3863
|
-
const processed =
|
|
4034
|
+
const processed = dropUndefinedDeep(frontmatter);
|
|
3864
4035
|
for (const [key, value] of Object.entries(processed)) {
|
|
3865
4036
|
if (value instanceof Date) {
|
|
3866
4037
|
processed[key] = value.toISOString();
|
|
@@ -3869,20 +4040,21 @@ function normalizeFrontmatterForYaml(frontmatter) {
|
|
|
3869
4040
|
return processed;
|
|
3870
4041
|
}
|
|
3871
4042
|
function stringifySpecMarkdown(body, frontmatter) {
|
|
3872
|
-
const fm =
|
|
4043
|
+
const fm = safeYamlDump(normalizeFrontmatterForYaml(frontmatter), YAML_STRINGIFY_OPTIONS).trim();
|
|
3873
4044
|
return `---
|
|
3874
4045
|
${fm}
|
|
3875
4046
|
---
|
|
3876
4047
|
${body}`;
|
|
3877
4048
|
}
|
|
3878
4049
|
function parseSpecMarkdown(raw) {
|
|
3879
|
-
return (0,
|
|
4050
|
+
return (0, import_gray_matter2.default)(raw);
|
|
3880
4051
|
}
|
|
3881
|
-
var
|
|
4052
|
+
var import_gray_matter2, YAML_STRINGIFY_OPTIONS;
|
|
3882
4053
|
var init_vault_frontmatter = __esm({
|
|
3883
4054
|
"spex-console/src/lib/vault-frontmatter.ts"() {
|
|
3884
4055
|
"use strict";
|
|
3885
|
-
|
|
4056
|
+
import_gray_matter2 = __toESM(require_gray_matter(), 1);
|
|
4057
|
+
init_safe_yaml();
|
|
3886
4058
|
YAML_STRINGIFY_OPTIONS = {
|
|
3887
4059
|
quotingType: '"',
|
|
3888
4060
|
skipInvalid: true,
|
|
@@ -4777,11 +4949,11 @@ var init_guardrail_naming = __esm({
|
|
|
4777
4949
|
import { createHash as createHash3 } from "node:crypto";
|
|
4778
4950
|
import { promises as fs6 } from "node:fs";
|
|
4779
4951
|
import path9 from "node:path";
|
|
4780
|
-
var
|
|
4952
|
+
var import_gray_matter3;
|
|
4781
4953
|
var init_parser = __esm({
|
|
4782
4954
|
"spex-console/src/lib/indexer/parser.ts"() {
|
|
4783
4955
|
"use strict";
|
|
4784
|
-
|
|
4956
|
+
import_gray_matter3 = __toESM(require_gray_matter(), 1);
|
|
4785
4957
|
init_vault_id();
|
|
4786
4958
|
init_vault_shared();
|
|
4787
4959
|
}
|
|
@@ -4920,12 +5092,12 @@ var init_linkage_validation = __esm({
|
|
|
4920
5092
|
});
|
|
4921
5093
|
|
|
4922
5094
|
// spex-console/src/lib/perf-timing.ts
|
|
4923
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5095
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
4924
5096
|
var storage;
|
|
4925
5097
|
var init_perf_timing = __esm({
|
|
4926
5098
|
"spex-console/src/lib/perf-timing.ts"() {
|
|
4927
5099
|
"use strict";
|
|
4928
|
-
storage = new
|
|
5100
|
+
storage = new AsyncLocalStorage2();
|
|
4929
5101
|
}
|
|
4930
5102
|
});
|
|
4931
5103
|
|
|
@@ -4947,12 +5119,12 @@ var init_planning_flow_model = __esm({
|
|
|
4947
5119
|
});
|
|
4948
5120
|
|
|
4949
5121
|
// spex-console/src/lib/request-memo.ts
|
|
4950
|
-
import { AsyncLocalStorage as
|
|
5122
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
4951
5123
|
var storage2;
|
|
4952
5124
|
var init_request_memo = __esm({
|
|
4953
5125
|
"spex-console/src/lib/request-memo.ts"() {
|
|
4954
5126
|
"use strict";
|
|
4955
|
-
storage2 = new
|
|
5127
|
+
storage2 = new AsyncLocalStorage3();
|
|
4956
5128
|
}
|
|
4957
5129
|
});
|
|
4958
5130
|
|
|
@@ -5200,18 +5372,17 @@ var init_search_body_hit = __esm({
|
|
|
5200
5372
|
|
|
5201
5373
|
// spex-console/src/lib/vault.ts
|
|
5202
5374
|
import { spawn } from "node:child_process";
|
|
5203
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
5375
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
|
|
5204
5376
|
import { promises as fs9 } from "node:fs";
|
|
5205
5377
|
import path14 from "node:path";
|
|
5206
|
-
import yaml3 from "js-yaml";
|
|
5207
5378
|
function vaultRoot() {
|
|
5208
5379
|
return getActiveSpecsDir();
|
|
5209
5380
|
}
|
|
5210
|
-
var
|
|
5381
|
+
var import_gray_matter4, VAULT_CACHE_TTL_WATCHED_MS, VAULT_CACHE_KEY, vaultCacheStore, VAULT_CACHE_DELTA_LISTENERS_KEY, vaultCacheDeltaListeners, SEARCH_TEXT_KEY, searchTextStore, VAULT_SNAPSHOT_KEY, vaultSnapshotStore;
|
|
5211
5382
|
var init_vault = __esm({
|
|
5212
5383
|
"spex-console/src/lib/vault.ts"() {
|
|
5213
5384
|
"use strict";
|
|
5214
|
-
|
|
5385
|
+
import_gray_matter4 = __toESM(require_gray_matter(), 1);
|
|
5215
5386
|
init_diagnose_skeleton();
|
|
5216
5387
|
init_auth();
|
|
5217
5388
|
init_capture_vault();
|
|
@@ -5222,6 +5393,7 @@ var init_vault = __esm({
|
|
|
5222
5393
|
init_skips();
|
|
5223
5394
|
init_clients();
|
|
5224
5395
|
init_postgres_client_cli_shim();
|
|
5396
|
+
init_vault_scope();
|
|
5225
5397
|
init_helpers();
|
|
5226
5398
|
init_index_write_service();
|
|
5227
5399
|
init_spec_read_service();
|
|
@@ -5238,6 +5410,7 @@ var init_vault = __esm({
|
|
|
5238
5410
|
init_planning_flow_model();
|
|
5239
5411
|
init_request_memo();
|
|
5240
5412
|
init_review_mode();
|
|
5413
|
+
init_safe_yaml();
|
|
5241
5414
|
init_active_milestone();
|
|
5242
5415
|
init_area();
|
|
5243
5416
|
init_claim_eligibility();
|
|
@@ -5322,7 +5495,7 @@ var init_vault = __esm({
|
|
|
5322
5495
|
});
|
|
5323
5496
|
|
|
5324
5497
|
// spex-console/bin/hs-vault-push.ts
|
|
5325
|
-
import { readFileSync as
|
|
5498
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
5326
5499
|
import path15 from "node:path";
|
|
5327
5500
|
import process4 from "node:process";
|
|
5328
5501
|
|
|
@@ -5706,7 +5879,7 @@ async function writeDirtyMarker(vaultRoot2, id, now = () => /* @__PURE__ */ new
|
|
|
5706
5879
|
}
|
|
5707
5880
|
|
|
5708
5881
|
// spex-console/src/lib/vault-sync/pull-io.ts
|
|
5709
|
-
import { promises as fs10, readFileSync as
|
|
5882
|
+
import { promises as fs10, readFileSync as readFileSync15 } from "node:fs";
|
|
5710
5883
|
import { EOL } from "node:os";
|
|
5711
5884
|
import nodePath3 from "node:path";
|
|
5712
5885
|
|
|
@@ -6352,7 +6525,7 @@ function runEntry(name, main2, options2 = {}) {
|
|
|
6352
6525
|
// spex-console/bin/hs-vault-push.ts
|
|
6353
6526
|
function readMarkerVaultId(vaultRoot2) {
|
|
6354
6527
|
try {
|
|
6355
|
-
return parseVaultId(
|
|
6528
|
+
return parseVaultId(readFileSync16(path15.join(vaultRoot2, "heyspex.yaml"), "utf8"));
|
|
6356
6529
|
} catch {
|
|
6357
6530
|
return null;
|
|
6358
6531
|
}
|
|
@@ -1,63 +1,71 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Confidence-ladder research method's entrypoint — SpexCrawl via the Companion (search/gather) is PRIMARY + DeepSeek (synthesis); self-hosted Firecrawl is an AUTOMATIC fallback (no permission needed, owner ruling 2026-08-22), zero Claude/Anthropic involvement.
|
|
3
|
-
argument-hint: "[research question/topic]"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Research (`/hs-res`)
|
|
7
|
-
|
|
8
|
-
> Parity: `.commandcode/skills/hs-res/SKILL.md` · `.cursor/skills/hs-res/SKILL.md` · `.agents/skills/hs-res/SKILL.md`
|
|
9
|
-
|
|
10
|
-
## What to do
|
|
11
|
-
|
|
12
|
-
1. **Take the question.** `$ARGUMENTS` is the research question/topic. If empty, load the
|
|
13
|
-
console doc-toolbar Promote pin via `heyspex_active_research`
|
|
14
|
-
(`.heyspex/research-focus.yaml` → `source_idea_id`, `scope`, `spec_path`) and read the source
|
|
15
|
-
capture body as the question. Either way, restate the scope and confirm with the user.
|
|
16
|
-
|
|
17
|
-
2. **Gather via SpexCrawl (the Companion) — the PRIMARY method** (owner ruling 2026-08-14,
|
|
18
|
-
[[spexcrawl-through-the-companion-is-the]]: "Companion > SpexCrawl is now default method
|
|
19
|
-
for research"). Decompose into a few independent search angles, then per angle:
|
|
20
|
-
`node dev-factory/spexcrawl/spexcrawl-cli.mjs search "<query>" --json` — one call
|
|
21
|
-
searches AND returns the cleaned pages of the top results. Specific extra URLs:
|
|
22
|
-
`node dev-factory/spexcrawl/spexcrawl-cli.mjs scrape "<url>" --json`.
|
|
23
|
-
Prerequisites: the Companion running and paired to the console. LAN mode
|
|
24
|
-
(`npm run console:log:lan`) is needed ONLY when the Companion runs on a DIFFERENT
|
|
25
|
-
machine than the console — since HS-V9WH the Companion polls loopback consoles too
|
|
26
|
-
([[a-queue-is-dead-if-its-only-consumer]]: the LAN line was a bug workaround
|
|
27
|
-
misdocumented as a requirement). When missing, the CLI exits 3 with
|
|
28
|
-
`SPEXCRAWL_UNAVAILABLE` naming the fix; a silent hang means the Companion is not
|
|
29
|
-
running or not polling this console.
|
|
30
|
-
|
|
31
|
-
**Fallback — self-hosted Firecrawl, AUTOMATIC, no permission needed (owner ruling
|
|
32
|
-
2026-08-22).** When the SpexCrawl path malfunctions, times out, hits a gate, or is otherwise
|
|
33
|
-
unavailable, fall back immediately: `node dev-factory/spexcrawl/firecrawl-cli.mjs
|
|
34
|
-
search "<query>" --json` and `node dev-factory/spexcrawl/firecrawl-cli.mjs scrape
|
|
35
|
-
"<url>" --json`. May run on any fleet machine (coffee-geek, basement-geek, spec-ops) —
|
|
36
|
-
prefer other reachable machines over the one the orchestrating session runs on; check
|
|
37
|
-
`docker info`, start Docker/the stack if needed, poll the real endpoint for readiness before
|
|
38
|
-
use. If this task's changed/claimed files are SpexCrawl-related, skip SpexCrawl entirely and
|
|
39
|
-
go straight to Firecrawl. Hosted `api.firecrawl.dev` credits are unchanged — still
|
|
40
|
-
ask-before-spend every time. Full contract: `.commandcode/skills/hs-res/SKILL.md`.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1
|
+
---
|
|
2
|
+
description: Confidence-ladder research method's entrypoint — SpexCrawl via the Companion (search/gather) is PRIMARY + DeepSeek (synthesis); self-hosted Firecrawl is an AUTOMATIC fallback (no permission needed, owner ruling 2026-08-22), zero Claude/Anthropic involvement.
|
|
3
|
+
argument-hint: "[research question/topic]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Research (`/hs-res`)
|
|
7
|
+
|
|
8
|
+
> Parity: `.commandcode/skills/hs-res/SKILL.md` · `.cursor/skills/hs-res/SKILL.md` · `.agents/skills/hs-res/SKILL.md`
|
|
9
|
+
|
|
10
|
+
## What to do
|
|
11
|
+
|
|
12
|
+
1. **Take the question.** `$ARGUMENTS` is the research question/topic. If empty, load the
|
|
13
|
+
console doc-toolbar Promote pin via `heyspex_active_research`
|
|
14
|
+
(`.heyspex/research-focus.yaml` → `source_idea_id`, `scope`, `spec_path`) and read the source
|
|
15
|
+
capture body as the question. Either way, restate the scope and confirm with the user.
|
|
16
|
+
|
|
17
|
+
2. **Gather via SpexCrawl (the Companion) — the PRIMARY method** (owner ruling 2026-08-14,
|
|
18
|
+
[[spexcrawl-through-the-companion-is-the]]: "Companion > SpexCrawl is now default method
|
|
19
|
+
for research"). Decompose into a few independent search angles, then per angle:
|
|
20
|
+
`node dev-factory/spexcrawl/spexcrawl-cli.mjs search "<query>" --json` — one call
|
|
21
|
+
searches AND returns the cleaned pages of the top results. Specific extra URLs:
|
|
22
|
+
`node dev-factory/spexcrawl/spexcrawl-cli.mjs scrape "<url>" --json`.
|
|
23
|
+
Prerequisites: the Companion running and paired to the console. LAN mode
|
|
24
|
+
(`npm run console:log:lan`) is needed ONLY when the Companion runs on a DIFFERENT
|
|
25
|
+
machine than the console — since HS-V9WH the Companion polls loopback consoles too
|
|
26
|
+
([[a-queue-is-dead-if-its-only-consumer]]: the LAN line was a bug workaround
|
|
27
|
+
misdocumented as a requirement). When missing, the CLI exits 3 with
|
|
28
|
+
`SPEXCRAWL_UNAVAILABLE` naming the fix; a silent hang means the Companion is not
|
|
29
|
+
running or not polling this console.
|
|
30
|
+
|
|
31
|
+
**Fallback — self-hosted Firecrawl, AUTOMATIC, no permission needed (owner ruling
|
|
32
|
+
2026-08-22).** When the SpexCrawl path malfunctions, times out, hits a gate, or is otherwise
|
|
33
|
+
unavailable, fall back immediately: `node dev-factory/spexcrawl/firecrawl-cli.mjs
|
|
34
|
+
search "<query>" --json` and `node dev-factory/spexcrawl/firecrawl-cli.mjs scrape
|
|
35
|
+
"<url>" --json`. May run on any fleet machine (coffee-geek, basement-geek, spec-ops) —
|
|
36
|
+
prefer other reachable machines over the one the orchestrating session runs on; check
|
|
37
|
+
`docker info`, start Docker/the stack if needed, poll the real endpoint for readiness before
|
|
38
|
+
use. If this task's changed/claimed files are SpexCrawl-related, skip SpexCrawl entirely and
|
|
39
|
+
go straight to Firecrawl. Hosted `api.firecrawl.dev` credits are unchanged — still
|
|
40
|
+
ask-before-spend every time. Full contract: `.commandcode/skills/hs-res/SKILL.md`.
|
|
41
|
+
|
|
42
|
+
**In a repo that uses the `heyspex` npm package** (not a HeySpex platform checkout): the
|
|
43
|
+
package ships only the SpexCrawl client (`dev-factory/spexcrawl/spexcrawl-cli.mjs` under
|
|
44
|
+
`node_modules/heyspex/`). It needs the HeySpex Companion, which the npm package does not
|
|
45
|
+
distribute yet, so it usually exits 3 `SPEXCRAWL_UNAVAILABLE`. The Firecrawl fallback and
|
|
46
|
+
the DeepSeek synthesis CLI (`firecrawl-cli.mjs`, `synthesize-cli.mjs`) are HeySpex-platform
|
|
47
|
+
only and are not in the package. When a CLI named in steps 2 or 4 is missing or reports
|
|
48
|
+
unavailable, stop and tell the user this research method is not available in their install.
|
|
49
|
+
|
|
50
|
+
3. **Assemble a numbered sources block** (`[S1]`, `[S2]`, …) from the gathered
|
|
51
|
+
`{title, url, snippet, markdown}` results, write it plus the question and cite-only
|
|
52
|
+
instructions to a temp prompt file.
|
|
53
|
+
|
|
54
|
+
4. **Synthesize via DeepSeek** —
|
|
55
|
+
`node dev-factory/spexcrawl/synthesize-cli.mjs --prompt-file <path> --json`
|
|
56
|
+
(defaults `--max-tokens` to 32000 and auto-escalates ×3 up to a 96000 ceiling if DeepSeek's
|
|
57
|
+
default-on "thinking" mode burns the whole budget on reasoning — never pass a lower
|
|
58
|
+
`--max-tokens` yourself; the CLI self-heals this failure mode. Exhausted escalation exits 3
|
|
59
|
+
with `SYNTHESIZE_UNAVAILABLE`).
|
|
60
|
+
|
|
61
|
+
5. **Save** via `heyspex_save_research` (or `heyspex_complete_research` under the console pin
|
|
62
|
+
pipeline), with `sources` populated from the gathered URLs.
|
|
63
|
+
|
|
64
|
+
**Never fall back** to `deep-research`/`deep-research-quick|medium|thorough`, the
|
|
65
|
+
`Workflow({ scriptPath: '.claude/workflows/deep-research-*.js' })` launch, or native
|
|
66
|
+
`WebSearch`/`WebFetch` for this method. Self-hosted Firecrawl fallback is automatic (no
|
|
67
|
+
permission needed, 2026-08-22 ruling); hosted `api.firecrawl.dev` credits still need the
|
|
68
|
+
user's explicit per-run permission, every time. If a CLI reports unavailable
|
|
69
|
+
(`SPEXCRAWL_UNAVAILABLE` / `FIRECRAWL_UNAVAILABLE` / `SYNTHESIZE_UNAVAILABLE`, exit 3),
|
|
70
|
+
**stop** and tell the user — never silently substitute another backend. Full method,
|
|
71
|
+
escalation rule, and hard prohibitions: `.commandcode/skills/hs-res/SKILL.md`.
|