@mrciphersmith/keryx 0.3.16 → 0.3.17
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/dist/cli.js +1314 -673
- package/dist/core.js +50 -8
- package/package.json +1 -1
- package/src/gdgraph/service.ts +7 -0
package/dist/cli.js
CHANGED
|
@@ -5275,10 +5275,14 @@ function configuredProviders(env = process.env, dir) {
|
|
|
5275
5275
|
}
|
|
5276
5276
|
async function providersCommand(args, deps = {}) {
|
|
5277
5277
|
const command = args[0];
|
|
5278
|
-
if (
|
|
5278
|
+
if (command === "--help" || command === "-h") {
|
|
5279
5279
|
printProvidersHelp();
|
|
5280
5280
|
return;
|
|
5281
5281
|
}
|
|
5282
|
+
if (!command) {
|
|
5283
|
+
await runProvidersStatus([], deps);
|
|
5284
|
+
return;
|
|
5285
|
+
}
|
|
5282
5286
|
if (command === "list") {
|
|
5283
5287
|
runProvidersList(args.slice(1));
|
|
5284
5288
|
return;
|
|
@@ -17637,7 +17641,7 @@ var package_default;
|
|
|
17637
17641
|
var init_package = __esm(() => {
|
|
17638
17642
|
package_default = {
|
|
17639
17643
|
name: "@mrciphersmith/keryx",
|
|
17640
|
-
version: "0.3.
|
|
17644
|
+
version: "0.3.17",
|
|
17641
17645
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
17642
17646
|
private: false,
|
|
17643
17647
|
publishConfig: {
|
|
@@ -34739,6 +34743,23 @@ function tokenize(text) {
|
|
|
34739
34743
|
function tokenSet(text) {
|
|
34740
34744
|
return new Set(tokenize(text));
|
|
34741
34745
|
}
|
|
34746
|
+
function stem(token) {
|
|
34747
|
+
let base = token;
|
|
34748
|
+
if (base.length >= 6 && base.endsWith("ies")) {
|
|
34749
|
+
base = `${base.slice(0, -3)}y`;
|
|
34750
|
+
} else {
|
|
34751
|
+
for (const suffix of ["ing", "edly", "ed", "es", "s"]) {
|
|
34752
|
+
if (base.length - suffix.length >= 3 && base.endsWith(suffix)) {
|
|
34753
|
+
base = base.slice(0, base.length - suffix.length);
|
|
34754
|
+
break;
|
|
34755
|
+
}
|
|
34756
|
+
}
|
|
34757
|
+
}
|
|
34758
|
+
if (base.length >= 4 && base.endsWith("e")) {
|
|
34759
|
+
base = base.slice(0, -1);
|
|
34760
|
+
}
|
|
34761
|
+
return base;
|
|
34762
|
+
}
|
|
34742
34763
|
function jaccard(a, b) {
|
|
34743
34764
|
if (a.size === 0 || b.size === 0) {
|
|
34744
34765
|
return 0;
|
|
@@ -35296,7 +35317,7 @@ var init_ingest = __esm(() => {
|
|
|
35296
35317
|
function searchEntries(entries, query, filters, config, now) {
|
|
35297
35318
|
validateQuery(query);
|
|
35298
35319
|
validateSearchFilters(filters, now);
|
|
35299
|
-
const queryTokens = [...new Set(tokenize(query))];
|
|
35320
|
+
const queryTokens = [...new Set(tokenize(query).map(stem))];
|
|
35300
35321
|
const today = now.toISOString().slice(0, 10);
|
|
35301
35322
|
const filtered = entries.filter((entry) => matchesFilters(entry, filters) && classMatch(entry, filters.class) && temporalMatch(entry, filters.asOf ?? null, config, today, Boolean(filters.status)));
|
|
35302
35323
|
const scored = filtered.map((entry) => scoreEntry(entry, queryTokens, filters, config, now));
|
|
@@ -35305,7 +35326,7 @@ function searchEntries(entries, query, filters, config, now) {
|
|
|
35305
35326
|
function candidatePool(entries, query, filters, config, now, k) {
|
|
35306
35327
|
validateQuery(query);
|
|
35307
35328
|
validateSearchFilters(filters, now);
|
|
35308
|
-
const queryTokens = [...new Set(tokenize(query))];
|
|
35329
|
+
const queryTokens = [...new Set(tokenize(query).map(stem))];
|
|
35309
35330
|
const today = now.toISOString().slice(0, 10);
|
|
35310
35331
|
return entries.filter((entry) => matchesFilters(entry, filters) && classMatch(entry, filters.class) && temporalMatch(entry, filters.asOf ?? null, config, today, Boolean(filters.status))).map((entry) => scoreEntry(entry, queryTokens, filters, config, now)).sort((a, b) => b.score - a.score).slice(0, Math.min(MAX_GENERAL_RESULTS, Math.max(0, k)));
|
|
35311
35332
|
}
|
|
@@ -35356,8 +35377,8 @@ function temporalMatch(entry, asOf, config, today, hasStatusFilter) {
|
|
|
35356
35377
|
}, new Date(`${today}T00:00:00.000Z`)).current;
|
|
35357
35378
|
}
|
|
35358
35379
|
function scoreEntry(entry, queryTokens, filters, config, now) {
|
|
35359
|
-
const bodyTokens = tokenSet(`${entry.title} ${entry.summary} ${entry.details} ${entry.tags.join(" ")}`);
|
|
35360
|
-
const titleTokens = tokenSet(entry.title);
|
|
35380
|
+
const bodyTokens = new Set([...tokenSet(`${entry.title} ${entry.summary} ${entry.details} ${entry.tags.join(" ")}`)].map(stem));
|
|
35381
|
+
const titleTokens = new Set([...tokenSet(entry.title)].map(stem));
|
|
35361
35382
|
let hits = 0;
|
|
35362
35383
|
let titleHits = 0;
|
|
35363
35384
|
for (const token of queryTokens) {
|
|
@@ -36214,6 +36235,7 @@ var init_service5 = __esm(() => {
|
|
|
36214
36235
|
init_harness_identity();
|
|
36215
36236
|
init_embedding();
|
|
36216
36237
|
init_types3();
|
|
36238
|
+
init_embedding();
|
|
36217
36239
|
});
|
|
36218
36240
|
|
|
36219
36241
|
// src/wiki/ask.ts
|
|
@@ -39595,6 +39617,7 @@ var init_service7 = __esm(() => {
|
|
|
39595
39617
|
init_query();
|
|
39596
39618
|
init_repomap();
|
|
39597
39619
|
init_affected_report();
|
|
39620
|
+
init_staleness();
|
|
39598
39621
|
UnknownGraphTargetError = class UnknownGraphTargetError extends Error {
|
|
39599
39622
|
target;
|
|
39600
39623
|
constructor(target) {
|
|
@@ -60502,7 +60525,8 @@ function computeGate2(input) {
|
|
|
60502
60525
|
}
|
|
60503
60526
|
const missingOptional = sources.filter((s) => !s.required && s.status === "missing");
|
|
60504
60527
|
for (const source of missingOptional) {
|
|
60505
|
-
|
|
60528
|
+
const detail = source.error ? `: ${source.error}` : "";
|
|
60529
|
+
reasons.push(`OPTIONAL: ${source.source} source missing${detail}`);
|
|
60506
60530
|
}
|
|
60507
60531
|
const brokenOptional = sources.filter((s) => !s.required && s.status === "configured-but-failed");
|
|
60508
60532
|
if (brokenOptional.length > 0) {
|
|
@@ -62307,6 +62331,12 @@ function errorCodeSuffix(error) {
|
|
|
62307
62331
|
const code = safeErrorCode(error);
|
|
62308
62332
|
return code ? ` (${code})` : "";
|
|
62309
62333
|
}
|
|
62334
|
+
function missingSourceReason(sourceId) {
|
|
62335
|
+
if (sourceId === "tests") {
|
|
62336
|
+
return "no `bun` binary found (checked node_modules/.bin and PATH)";
|
|
62337
|
+
}
|
|
62338
|
+
return;
|
|
62339
|
+
}
|
|
62310
62340
|
function safeValidationError(error) {
|
|
62311
62341
|
return error !== undefined && KNOWN_VALIDATION_ERRORS.has(error) ? error : "source output format was not recognized";
|
|
62312
62342
|
}
|
|
@@ -62341,7 +62371,8 @@ async function runAdapter(adapter, ctx, cfg, stamp) {
|
|
|
62341
62371
|
};
|
|
62342
62372
|
}
|
|
62343
62373
|
if (status === "skipped" || status === "missing") {
|
|
62344
|
-
|
|
62374
|
+
const reason = missingSourceReason(adapter.id);
|
|
62375
|
+
return { info: { ...base, status, ...reason !== undefined ? { error: reason } : {} }, findings: [] };
|
|
62345
62376
|
}
|
|
62346
62377
|
let raw;
|
|
62347
62378
|
try {
|
|
@@ -62355,7 +62386,7 @@ async function runAdapter(adapter, ctx, cfg, stamp) {
|
|
|
62355
62386
|
} catch (error) {
|
|
62356
62387
|
if (error instanceof NoImportError) {
|
|
62357
62388
|
if (adapter.id === "tests") {
|
|
62358
|
-
return { info: { ...base, status
|
|
62389
|
+
return { info: { ...base, status }, findings: [] };
|
|
62359
62390
|
}
|
|
62360
62391
|
raw = await adapter.run(ctx);
|
|
62361
62392
|
} else {
|
|
@@ -62720,6 +62751,7 @@ var init_service10 = __esm(() => {
|
|
|
62720
62751
|
init_sources();
|
|
62721
62752
|
init_util();
|
|
62722
62753
|
init_lifecycle2();
|
|
62754
|
+
init_wiki_freshness();
|
|
62723
62755
|
});
|
|
62724
62756
|
|
|
62725
62757
|
// src/flow/machine.ts
|
|
@@ -70731,6 +70763,7 @@ async function runSearch(args) {
|
|
|
70731
70763
|
}
|
|
70732
70764
|
const report = args.includes("--save-report") ? await getService().writeReport({ cwd: process.cwd(), search: result, filters }) : undefined;
|
|
70733
70765
|
const removalTrail = result.results.length === 0 ? searchRemovals(await loadDeletionTrail(process.cwd()), query) : null;
|
|
70766
|
+
const semanticHint = result.results.length === 0 && !filters.semantic ? await loadEmbeddingIndex(process.cwd()) !== null ? "Zero hits in lexical mode. An embeddings index exists for this project \u2014 try `--semantic`." : "Zero hits in lexical mode. No embeddings index exists yet \u2014 build one with `keryx memory index --embeddings`, then retry with `--semantic`." : undefined;
|
|
70734
70767
|
const asOfMode = Boolean(filters.asOf);
|
|
70735
70768
|
const now = new Date;
|
|
70736
70769
|
const historicalByPath = new Map;
|
|
@@ -70759,11 +70792,16 @@ async function runSearch(args) {
|
|
|
70759
70792
|
};
|
|
70760
70793
|
}),
|
|
70761
70794
|
...removalTrail ? { removalTrail } : {},
|
|
70795
|
+
...semanticHint ? { semanticHint } : {},
|
|
70762
70796
|
...report ? { report } : {}
|
|
70763
70797
|
}, null, 2));
|
|
70764
70798
|
return;
|
|
70765
70799
|
}
|
|
70766
70800
|
console.log(renderSearchMarkdown(query, result.results).trimEnd());
|
|
70801
|
+
if (semanticHint) {
|
|
70802
|
+
console.log("");
|
|
70803
|
+
console.log(semanticHint);
|
|
70804
|
+
}
|
|
70767
70805
|
if (removalTrail) {
|
|
70768
70806
|
console.log("");
|
|
70769
70807
|
console.log("## Removal trail");
|
|
@@ -71060,6 +71098,7 @@ var init_memory = __esm(() => {
|
|
|
71060
71098
|
init_config4();
|
|
71061
71099
|
init_reflect();
|
|
71062
71100
|
init_search();
|
|
71101
|
+
init_service5();
|
|
71063
71102
|
init_report2();
|
|
71064
71103
|
init_lifecycle();
|
|
71065
71104
|
init_args();
|
|
@@ -71636,7 +71675,7 @@ var init_debug_log = __esm(() => {
|
|
|
71636
71675
|
// src/tui/debug-watcher.ts
|
|
71637
71676
|
import { spawn as spawn8, spawnSync as spawnSync8 } from "child_process";
|
|
71638
71677
|
import { closeSync as closeSync6, constants as constants4, openSync as openSync7, readdirSync as readdirSync11, readFileSync as readFileSync24, readlinkSync as readlinkSync3, statSync as statSync16 } from "fs";
|
|
71639
|
-
import
|
|
71678
|
+
import path317 from "path";
|
|
71640
71679
|
function parseEpollFdinfo(text) {
|
|
71641
71680
|
const out = [];
|
|
71642
71681
|
for (const line of text.split(`
|
|
@@ -71825,8 +71864,8 @@ async function runDebugWatcher(argv) {
|
|
|
71825
71864
|
process.exitCode = 2;
|
|
71826
71865
|
return;
|
|
71827
71866
|
}
|
|
71828
|
-
const logFile =
|
|
71829
|
-
const shellLog =
|
|
71867
|
+
const logFile = path317.join(dirArg, "watcher.ndjson");
|
|
71868
|
+
const shellLog = path317.join(dirArg, "shell.ndjson");
|
|
71830
71869
|
const log = (kind, data = {}) => {
|
|
71831
71870
|
try {
|
|
71832
71871
|
appendOwnerOnlyLine(logFile, JSON.stringify({ ts: new Date().toISOString(), watcher: process.pid, target: pid, kind, ...data }));
|
|
@@ -107084,6 +107123,18 @@ var HELP_GROUPS = [
|
|
|
107084
107123
|
summary: "Grouped command help by task \u2014 keryx help [group|command]."
|
|
107085
107124
|
},
|
|
107086
107125
|
{ kind: "slash", name: "/help", group: "Start here", summary: "Show available commands, grouped by task." },
|
|
107126
|
+
{
|
|
107127
|
+
kind: "cli",
|
|
107128
|
+
name: "doctor",
|
|
107129
|
+
group: "Start here",
|
|
107130
|
+
summary: "One-page health check: version, Bun floor, ripgrep, sandbox, providers, MCP, integrations, standard, worktrees, graph/wiki freshness."
|
|
107131
|
+
},
|
|
107132
|
+
{
|
|
107133
|
+
kind: "slash",
|
|
107134
|
+
name: "/doctor",
|
|
107135
|
+
group: "Start here",
|
|
107136
|
+
summary: "Same one-page health check, inside the session."
|
|
107137
|
+
},
|
|
107087
107138
|
{
|
|
107088
107139
|
kind: "cli",
|
|
107089
107140
|
name: "auth",
|
|
@@ -109216,6 +109267,19 @@ var COMMAND_DESCRIPTORS = [
|
|
|
109216
109267
|
json: false,
|
|
109217
109268
|
read: true
|
|
109218
109269
|
},
|
|
109270
|
+
{
|
|
109271
|
+
module: "core",
|
|
109272
|
+
command: "doctor",
|
|
109273
|
+
summary: "One-page health check: version, Bun floor, ripgrep, sandbox, providers, MCP, integrations, standard, worktrees, graph/wiki freshness.",
|
|
109274
|
+
intent: ["keryx doctor", "health check", "\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435", "is keryx set up correctly", "\u0434\u0438\u0430\u0433\u043D\u043E\u0441\u0442\u0438\u043A\u0430"],
|
|
109275
|
+
args: [{ name: "json", type: "bool", required: false, desc: "structured {checks:[{id,status,detail,fix?}]}" }],
|
|
109276
|
+
json: true,
|
|
109277
|
+
read: false,
|
|
109278
|
+
sideEffects: [
|
|
109279
|
+
"makes an outbound HTTPS request to the npm registry as part of its version check, unless served from the local cache or failure-backoff window",
|
|
109280
|
+
"writes <per-user keryx config dir>/version-check.json (outside this project) as part of that same check"
|
|
109281
|
+
]
|
|
109282
|
+
},
|
|
109219
109283
|
{
|
|
109220
109284
|
module: "core",
|
|
109221
109285
|
command: "status",
|
|
@@ -111307,6 +111371,49 @@ async function sandboxCommand(args = [], deps = {}) {
|
|
|
111307
111371
|
init_args();
|
|
111308
111372
|
init_ui();
|
|
111309
111373
|
|
|
111374
|
+
// src/lib/suggest.ts
|
|
111375
|
+
var MAX_SUGGESTIONS = 3;
|
|
111376
|
+
var MAX_EDIT_DISTANCE = 2;
|
|
111377
|
+
function levenshtein2(a, b) {
|
|
111378
|
+
const rows = a.length + 1;
|
|
111379
|
+
const cols = b.length + 1;
|
|
111380
|
+
const dp = Array.from({ length: rows }, () => new Array(cols).fill(0));
|
|
111381
|
+
for (let i = 0;i < rows; i += 1) {
|
|
111382
|
+
const row = dp[i];
|
|
111383
|
+
if (row !== undefined)
|
|
111384
|
+
row[0] = i;
|
|
111385
|
+
}
|
|
111386
|
+
const first = dp[0];
|
|
111387
|
+
if (first !== undefined) {
|
|
111388
|
+
for (let j = 0;j < cols; j += 1)
|
|
111389
|
+
first[j] = j;
|
|
111390
|
+
}
|
|
111391
|
+
for (let i = 1;i < rows; i += 1) {
|
|
111392
|
+
for (let j = 1;j < cols; j += 1) {
|
|
111393
|
+
const same = a[i - 1] === b[j - 1];
|
|
111394
|
+
const up = dp[i - 1]?.[j] ?? 0;
|
|
111395
|
+
const left = dp[i]?.[j - 1] ?? 0;
|
|
111396
|
+
const diag = dp[i - 1]?.[j - 1] ?? 0;
|
|
111397
|
+
const row = dp[i];
|
|
111398
|
+
if (row !== undefined)
|
|
111399
|
+
row[j] = same ? diag : 1 + Math.min(up, left, diag);
|
|
111400
|
+
}
|
|
111401
|
+
}
|
|
111402
|
+
return dp[rows - 1]?.[cols - 1] ?? Math.max(a.length, b.length);
|
|
111403
|
+
}
|
|
111404
|
+
function suggestClosest(input, known) {
|
|
111405
|
+
const needle = input.trim().toLowerCase();
|
|
111406
|
+
if (needle.length === 0) {
|
|
111407
|
+
return [];
|
|
111408
|
+
}
|
|
111409
|
+
return [...new Set(known)].map((candidate) => ({ candidate, distance: levenshtein2(needle, candidate.toLowerCase()) })).filter((entry) => entry.distance <= MAX_EDIT_DISTANCE).sort((a, b) => a.distance - b.distance || a.candidate.localeCompare(b.candidate)).slice(0, MAX_SUGGESTIONS).map((entry) => entry.candidate);
|
|
111410
|
+
}
|
|
111411
|
+
function formatUnknownCommandMessage(input, known, helpCommand = "keryx --help") {
|
|
111412
|
+
const suggestions = suggestClosest(input, known);
|
|
111413
|
+
const didYouMean = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : "";
|
|
111414
|
+
return `Unknown command: ${input}.${didYouMean} Run \`${helpCommand}\` for the list.`;
|
|
111415
|
+
}
|
|
111416
|
+
|
|
111310
111417
|
// src/commands/integrate.ts
|
|
111311
111418
|
init_ui();
|
|
111312
111419
|
import path257 from "path";
|
|
@@ -111853,12 +111960,12 @@ function loadMcpServers(options) {
|
|
|
111853
111960
|
const home = options.home ?? (options.configDir === undefined ? undefined : NO_HOME);
|
|
111854
111961
|
for (const entry of compatFiles(options.cwd, home)) {
|
|
111855
111962
|
const read = readCompatFile(entry, options.cwd);
|
|
111856
|
-
problems.push(...read.problems);
|
|
111963
|
+
problems.push(...read.problems.map((p) => ({ ...p, foreign: true })));
|
|
111857
111964
|
for (const [name, value] of Object.entries(read.servers)) {
|
|
111858
111965
|
const found = entryProblems(name, value);
|
|
111859
111966
|
if (found.length > 0) {
|
|
111860
111967
|
for (const message of found)
|
|
111861
|
-
problems.push({ file: entry.file, message });
|
|
111968
|
+
problems.push({ file: entry.file, message, foreign: true });
|
|
111862
111969
|
continue;
|
|
111863
111970
|
}
|
|
111864
111971
|
winner.set(name, {
|
|
@@ -114032,19 +114139,31 @@ function publicView(server, env) {
|
|
|
114032
114139
|
function listCommand(args, deps) {
|
|
114033
114140
|
const config = load(deps);
|
|
114034
114141
|
const wantJson = splitAtSeparator(args).own.includes("--json");
|
|
114142
|
+
const ownProblems = config.problems.filter((p) => !p.foreign);
|
|
114143
|
+
const foreignProblems = config.problems.filter((p) => p.foreign);
|
|
114035
114144
|
if (wantJson) {
|
|
114036
114145
|
const env = deps.env ?? process.env;
|
|
114037
|
-
deps.log(JSON.stringify({
|
|
114038
|
-
|
|
114146
|
+
deps.log(JSON.stringify({
|
|
114147
|
+
servers: config.servers.map((s) => publicView(s, env)),
|
|
114148
|
+
problems: ownProblems,
|
|
114149
|
+
warnings: foreignProblems
|
|
114150
|
+
}, null, 2));
|
|
114151
|
+
return ownProblems.length > 0 ? 1 : 0;
|
|
114039
114152
|
}
|
|
114040
|
-
for (const problem of
|
|
114153
|
+
for (const problem of ownProblems) {
|
|
114041
114154
|
deps.err(`config problem \u2014 ${problem.file}: ${problem.message}`);
|
|
114042
114155
|
}
|
|
114156
|
+
if (foreignProblems.length > 0) {
|
|
114157
|
+
deps.err("warnings:");
|
|
114158
|
+
for (const problem of foreignProblems) {
|
|
114159
|
+
deps.err(` ${problem.file}: ${problem.message}`);
|
|
114160
|
+
}
|
|
114161
|
+
}
|
|
114043
114162
|
if (config.servers.length === 0) {
|
|
114044
114163
|
deps.log("No MCP servers configured.");
|
|
114045
114164
|
deps.log(` user: ${userConfigFile(deps.configDir)}`);
|
|
114046
114165
|
deps.log(` project: ${projectConfigFile(projectRootOf2(deps))}`);
|
|
114047
|
-
return
|
|
114166
|
+
return ownProblems.length > 0 ? 1 : 0;
|
|
114048
114167
|
}
|
|
114049
114168
|
const approvals = loadTrustStore(deps.configDir);
|
|
114050
114169
|
let held = 0;
|
|
@@ -114063,7 +114182,7 @@ function listCommand(args, deps) {
|
|
|
114063
114182
|
deps.log(`${held} server(s) from committed config are not started until approved \u2014 a committed config is code someone else wrote.`);
|
|
114064
114183
|
deps.log("Read what it launches above, then: keryx mcp trust <name>");
|
|
114065
114184
|
}
|
|
114066
|
-
return
|
|
114185
|
+
return ownProblems.length > 0 ? 1 : 0;
|
|
114067
114186
|
}
|
|
114068
114187
|
function describeTarget(server) {
|
|
114069
114188
|
const raw = server.raw;
|
|
@@ -116960,8 +117079,12 @@ async function mcpCommand(args = [], cwd = process.cwd()) {
|
|
|
116960
117079
|
await serveMcpCommand(args.slice(1), cwd);
|
|
116961
117080
|
return;
|
|
116962
117081
|
}
|
|
116963
|
-
console.error(
|
|
116964
|
-
|
|
117082
|
+
console.error(formatUnknownCommandMessage(subcommand ?? "", [
|
|
117083
|
+
...MCP_CONSUMER_SUBCOMMANDS,
|
|
117084
|
+
"install",
|
|
117085
|
+
"uninstall",
|
|
117086
|
+
"serve"
|
|
117087
|
+
], "keryx mcp --help"));
|
|
116965
117088
|
process.exitCode = 1;
|
|
116966
117089
|
}
|
|
116967
117090
|
function announceRename(retired, replacement) {
|
|
@@ -135479,6 +135602,569 @@ class NextStepSuggestionGate {
|
|
|
135479
135602
|
}
|
|
135480
135603
|
}
|
|
135481
135604
|
|
|
135605
|
+
// src/commands/doctor.ts
|
|
135606
|
+
init_package();
|
|
135607
|
+
import { readdir as readdir53 } from "fs/promises";
|
|
135608
|
+
import { existsSync as existsSync48 } from "fs";
|
|
135609
|
+
import path302 from "path";
|
|
135610
|
+
|
|
135611
|
+
// src/lib/version-check.ts
|
|
135612
|
+
init_config_dir();
|
|
135613
|
+
init_fs();
|
|
135614
|
+
import path301 from "path";
|
|
135615
|
+
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
135616
|
+
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
135617
|
+
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
135618
|
+
var VERSION_STRING_LIMIT_CHARS = 64;
|
|
135619
|
+
var REQUEST_TIMEOUT_MS = 2000;
|
|
135620
|
+
var SUCCESS_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
135621
|
+
var FAILURE_BACKOFF_MS = 15 * 60 * 1000;
|
|
135622
|
+
var CACHE_LOCK_TIMEOUT_MS2 = 250;
|
|
135623
|
+
var CACHE_LOCK_RETRY_MS2 = 10;
|
|
135624
|
+
var CACHE_LOCK_STALE_MS2 = 5000;
|
|
135625
|
+
var SYSTEM_TIMER = {
|
|
135626
|
+
schedule: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
135627
|
+
cancel: (handle) => clearTimeout(handle)
|
|
135628
|
+
};
|
|
135629
|
+
function formatVersionUpdateAdvisory(result) {
|
|
135630
|
+
if (result.status !== "update-available")
|
|
135631
|
+
return;
|
|
135632
|
+
return `Keryx update ${result.currentVersion} \u2192 ${result.latestVersion}
|
|
135633
|
+
${result.installCommand}`;
|
|
135634
|
+
}
|
|
135635
|
+
var STRICT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
|
135636
|
+
function parseSemVer(value) {
|
|
135637
|
+
if (value.length > VERSION_STRING_LIMIT_CHARS)
|
|
135638
|
+
return;
|
|
135639
|
+
const match = STRICT_SEMVER.exec(value);
|
|
135640
|
+
if (match === null)
|
|
135641
|
+
return;
|
|
135642
|
+
const prereleaseText = match[4];
|
|
135643
|
+
const prerelease = [];
|
|
135644
|
+
if (prereleaseText !== undefined) {
|
|
135645
|
+
for (const identifier of prereleaseText.split(".")) {
|
|
135646
|
+
if (/^\d+$/.test(identifier)) {
|
|
135647
|
+
if (identifier.length > 1 && identifier.startsWith("0"))
|
|
135648
|
+
return;
|
|
135649
|
+
prerelease.push(BigInt(identifier));
|
|
135650
|
+
} else {
|
|
135651
|
+
prerelease.push(identifier);
|
|
135652
|
+
}
|
|
135653
|
+
}
|
|
135654
|
+
}
|
|
135655
|
+
return {
|
|
135656
|
+
major: BigInt(match[1]),
|
|
135657
|
+
minor: BigInt(match[2]),
|
|
135658
|
+
patch: BigInt(match[3]),
|
|
135659
|
+
prerelease
|
|
135660
|
+
};
|
|
135661
|
+
}
|
|
135662
|
+
function compareSemVer(left, right) {
|
|
135663
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
135664
|
+
if (left[key] < right[key])
|
|
135665
|
+
return -1;
|
|
135666
|
+
if (left[key] > right[key])
|
|
135667
|
+
return 1;
|
|
135668
|
+
}
|
|
135669
|
+
if (left.prerelease.length === 0 || right.prerelease.length === 0) {
|
|
135670
|
+
if (left.prerelease.length === right.prerelease.length)
|
|
135671
|
+
return 0;
|
|
135672
|
+
return left.prerelease.length === 0 ? 1 : -1;
|
|
135673
|
+
}
|
|
135674
|
+
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
135675
|
+
for (let index = 0;index < length; index += 1) {
|
|
135676
|
+
const a = left.prerelease[index];
|
|
135677
|
+
const b = right.prerelease[index];
|
|
135678
|
+
if (a === undefined || b === undefined) {
|
|
135679
|
+
if (a === b)
|
|
135680
|
+
return 0;
|
|
135681
|
+
return a === undefined ? -1 : 1;
|
|
135682
|
+
}
|
|
135683
|
+
if (typeof a === "bigint" && typeof b === "bigint") {
|
|
135684
|
+
if (a < b)
|
|
135685
|
+
return -1;
|
|
135686
|
+
if (a > b)
|
|
135687
|
+
return 1;
|
|
135688
|
+
} else if (typeof a === "bigint") {
|
|
135689
|
+
return -1;
|
|
135690
|
+
} else if (typeof b === "bigint") {
|
|
135691
|
+
return 1;
|
|
135692
|
+
} else if (a !== b) {
|
|
135693
|
+
return a < b ? -1 : 1;
|
|
135694
|
+
}
|
|
135695
|
+
}
|
|
135696
|
+
return 0;
|
|
135697
|
+
}
|
|
135698
|
+
function parseCache(file) {
|
|
135699
|
+
const read = readConfigFile(file);
|
|
135700
|
+
if (!read.ok)
|
|
135701
|
+
return;
|
|
135702
|
+
try {
|
|
135703
|
+
const value = JSON.parse(read.text);
|
|
135704
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
135705
|
+
return;
|
|
135706
|
+
const record = value;
|
|
135707
|
+
const cache = {};
|
|
135708
|
+
if (typeof record.latestVersion === "string" && parseSemVer(record.latestVersion) !== undefined) {
|
|
135709
|
+
cache.latestVersion = record.latestVersion;
|
|
135710
|
+
}
|
|
135711
|
+
if (typeof record.successAt === "number" && Number.isFinite(record.successAt))
|
|
135712
|
+
cache.successAt = record.successAt;
|
|
135713
|
+
if (typeof record.failureAt === "number" && Number.isFinite(record.failureAt))
|
|
135714
|
+
cache.failureAt = record.failureAt;
|
|
135715
|
+
return cache;
|
|
135716
|
+
} catch {
|
|
135717
|
+
return;
|
|
135718
|
+
}
|
|
135719
|
+
}
|
|
135720
|
+
function resultFor(currentVersion, current, latestVersion, source) {
|
|
135721
|
+
const latest = parseSemVer(latestVersion);
|
|
135722
|
+
return compareSemVer(current, latest) < 0 ? { status: "update-available", currentVersion, latestVersion, installCommand: FIXED_INSTALL_COMMAND, source } : { status: "up-to-date", currentVersion, latestVersion, source };
|
|
135723
|
+
}
|
|
135724
|
+
function cancelWithoutWaiting(cancel) {
|
|
135725
|
+
try {
|
|
135726
|
+
cancel().catch(() => {});
|
|
135727
|
+
} catch {}
|
|
135728
|
+
}
|
|
135729
|
+
async function boundedResponseText(response) {
|
|
135730
|
+
const declared = response.headers.get("content-length");
|
|
135731
|
+
if (declared !== null && /^\d+$/.test(declared) && BigInt(declared) > BigInt(RESPONSE_BODY_LIMIT_BYTES)) {
|
|
135732
|
+
if (response.body !== null) {
|
|
135733
|
+
cancelWithoutWaiting(() => response.body.cancel());
|
|
135734
|
+
}
|
|
135735
|
+
return;
|
|
135736
|
+
}
|
|
135737
|
+
if (response.body === null)
|
|
135738
|
+
return "";
|
|
135739
|
+
const reader = response.body.getReader();
|
|
135740
|
+
const decoder = new TextDecoder;
|
|
135741
|
+
let size = 0;
|
|
135742
|
+
let text = "";
|
|
135743
|
+
try {
|
|
135744
|
+
for (;; ) {
|
|
135745
|
+
const chunk = await reader.read();
|
|
135746
|
+
if (chunk.done)
|
|
135747
|
+
break;
|
|
135748
|
+
size += chunk.value.byteLength;
|
|
135749
|
+
if (size > RESPONSE_BODY_LIMIT_BYTES) {
|
|
135750
|
+
cancelWithoutWaiting(() => reader.cancel());
|
|
135751
|
+
return;
|
|
135752
|
+
}
|
|
135753
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
135754
|
+
}
|
|
135755
|
+
return text + decoder.decode();
|
|
135756
|
+
} finally {
|
|
135757
|
+
reader.releaseLock();
|
|
135758
|
+
}
|
|
135759
|
+
}
|
|
135760
|
+
function unavailable(currentVersion, reason, cache) {
|
|
135761
|
+
return {
|
|
135762
|
+
status: "unavailable",
|
|
135763
|
+
currentVersion,
|
|
135764
|
+
reason,
|
|
135765
|
+
...cache?.latestVersion !== undefined ? { cachedLatestVersion: cache.latestVersion } : {}
|
|
135766
|
+
};
|
|
135767
|
+
}
|
|
135768
|
+
function saveCache2(file, cache) {
|
|
135769
|
+
try {
|
|
135770
|
+
writeOwnerOnlyFileAtomic(file, `${JSON.stringify(cache, null, 2)}
|
|
135771
|
+
`);
|
|
135772
|
+
return true;
|
|
135773
|
+
} catch {
|
|
135774
|
+
return false;
|
|
135775
|
+
}
|
|
135776
|
+
}
|
|
135777
|
+
function mergeSuccessfulCache(committed, latestVersion, successAt) {
|
|
135778
|
+
if (committed.latestVersion === undefined || committed.successAt === undefined) {
|
|
135779
|
+
return { latestVersion, successAt };
|
|
135780
|
+
}
|
|
135781
|
+
if (committed.successAt > successAt)
|
|
135782
|
+
return committed;
|
|
135783
|
+
if (committed.successAt < successAt)
|
|
135784
|
+
return { latestVersion, successAt };
|
|
135785
|
+
const committedVersion = parseSemVer(committed.latestVersion);
|
|
135786
|
+
const candidateVersion = parseSemVer(latestVersion);
|
|
135787
|
+
if (committedVersion !== undefined && candidateVersion !== undefined) {
|
|
135788
|
+
const precedence = compareSemVer(committedVersion, candidateVersion);
|
|
135789
|
+
if (precedence > 0)
|
|
135790
|
+
return committed;
|
|
135791
|
+
if (precedence < 0)
|
|
135792
|
+
return { latestVersion, successAt };
|
|
135793
|
+
if (committed.latestVersion >= latestVersion)
|
|
135794
|
+
return committed;
|
|
135795
|
+
}
|
|
135796
|
+
return { latestVersion, successAt };
|
|
135797
|
+
}
|
|
135798
|
+
async function updateCache(file, update) {
|
|
135799
|
+
try {
|
|
135800
|
+
return await withFileLock(`${file}.lock`, async () => saveCache2(file, update(parseCache(file) ?? {})), {
|
|
135801
|
+
timeoutMs: CACHE_LOCK_TIMEOUT_MS2,
|
|
135802
|
+
retryMs: CACHE_LOCK_RETRY_MS2,
|
|
135803
|
+
staleMs: CACHE_LOCK_STALE_MS2
|
|
135804
|
+
});
|
|
135805
|
+
} catch {
|
|
135806
|
+
return false;
|
|
135807
|
+
}
|
|
135808
|
+
}
|
|
135809
|
+
async function checkVersion(options) {
|
|
135810
|
+
const current = parseSemVer(options.currentVersion);
|
|
135811
|
+
if (current === undefined)
|
|
135812
|
+
return unavailable(options.currentVersion, "invalid-current-version");
|
|
135813
|
+
const now = options.now ?? Date.now;
|
|
135814
|
+
const timestamp = now();
|
|
135815
|
+
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
135816
|
+
const cacheFile = path301.join(configDir, "version-check.json");
|
|
135817
|
+
const cache = parseCache(cacheFile);
|
|
135818
|
+
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
135819
|
+
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
135820
|
+
}
|
|
135821
|
+
if (cache?.failureAt !== undefined && timestamp - cache.failureAt >= 0 && timestamp - cache.failureAt < FAILURE_BACKOFF_MS) {
|
|
135822
|
+
return unavailable(options.currentVersion, "suppressed", cache);
|
|
135823
|
+
}
|
|
135824
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
135825
|
+
const controller = new AbortController;
|
|
135826
|
+
const timer = options.timer ?? SYSTEM_TIMER;
|
|
135827
|
+
let timeout;
|
|
135828
|
+
let timeoutScheduled = false;
|
|
135829
|
+
let failure;
|
|
135830
|
+
let latestVersion;
|
|
135831
|
+
try {
|
|
135832
|
+
timeout = timer.schedule(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
135833
|
+
timeoutScheduled = true;
|
|
135834
|
+
const response = await fetchImpl(REGISTRY_URL, {
|
|
135835
|
+
method: "GET",
|
|
135836
|
+
headers: { accept: "application/json" },
|
|
135837
|
+
signal: controller.signal,
|
|
135838
|
+
credentials: "omit"
|
|
135839
|
+
});
|
|
135840
|
+
if (!response.ok) {
|
|
135841
|
+
failure = "http";
|
|
135842
|
+
} else {
|
|
135843
|
+
const body = await boundedResponseText(response);
|
|
135844
|
+
if (body === undefined) {
|
|
135845
|
+
failure = "response-too-large";
|
|
135846
|
+
} else {
|
|
135847
|
+
let parsed;
|
|
135848
|
+
try {
|
|
135849
|
+
parsed = JSON.parse(body);
|
|
135850
|
+
} catch {
|
|
135851
|
+
parsed = undefined;
|
|
135852
|
+
}
|
|
135853
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
135854
|
+
failure = "malformed-response";
|
|
135855
|
+
} else {
|
|
135856
|
+
const record = parsed;
|
|
135857
|
+
if (record.name !== "@mrciphersmith/keryx" || typeof record.version !== "string") {
|
|
135858
|
+
failure = "malformed-response";
|
|
135859
|
+
} else if (parseSemVer(record.version) === undefined) {
|
|
135860
|
+
failure = "invalid-latest-version";
|
|
135861
|
+
} else {
|
|
135862
|
+
latestVersion = record.version;
|
|
135863
|
+
}
|
|
135864
|
+
}
|
|
135865
|
+
}
|
|
135866
|
+
}
|
|
135867
|
+
} catch {
|
|
135868
|
+
failure = controller.signal.aborted ? "timeout" : "network";
|
|
135869
|
+
} finally {
|
|
135870
|
+
if (timeoutScheduled) {
|
|
135871
|
+
try {
|
|
135872
|
+
timer.cancel(timeout);
|
|
135873
|
+
} catch {}
|
|
135874
|
+
}
|
|
135875
|
+
}
|
|
135876
|
+
if (latestVersion === undefined) {
|
|
135877
|
+
await updateCache(cacheFile, (committed) => ({ ...committed, failureAt: timestamp }));
|
|
135878
|
+
return unavailable(options.currentVersion, failure ?? "network", cache);
|
|
135879
|
+
}
|
|
135880
|
+
await updateCache(cacheFile, (committed) => mergeSuccessfulCache(committed, latestVersion, timestamp));
|
|
135881
|
+
return resultFor(options.currentVersion, current, latestVersion, "registry");
|
|
135882
|
+
}
|
|
135883
|
+
|
|
135884
|
+
// src/commands/doctor.ts
|
|
135885
|
+
init_contained_path();
|
|
135886
|
+
init_providers();
|
|
135887
|
+
init_service();
|
|
135888
|
+
init_service7();
|
|
135889
|
+
init_service10();
|
|
135890
|
+
async function registeredWorktrees(cwd) {
|
|
135891
|
+
if (!Bun.which("git"))
|
|
135892
|
+
return new Set;
|
|
135893
|
+
try {
|
|
135894
|
+
const proc = Bun.spawn(["git", "worktree", "list", "--porcelain"], { cwd, stdout: "pipe", stderr: "ignore" });
|
|
135895
|
+
const [stdout, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
135896
|
+
if (code !== 0)
|
|
135897
|
+
return new Set;
|
|
135898
|
+
const paths = new Set;
|
|
135899
|
+
for (const line of stdout.split(`
|
|
135900
|
+
`)) {
|
|
135901
|
+
if (line.startsWith("worktree ")) {
|
|
135902
|
+
paths.add(path302.resolve(line.slice("worktree ".length).trim()));
|
|
135903
|
+
}
|
|
135904
|
+
}
|
|
135905
|
+
return paths;
|
|
135906
|
+
} catch {
|
|
135907
|
+
return new Set;
|
|
135908
|
+
}
|
|
135909
|
+
}
|
|
135910
|
+
async function checkWorktrees(cwd) {
|
|
135911
|
+
const worktreesDir = path302.join(cwd, ".claude", "worktrees");
|
|
135912
|
+
let entries;
|
|
135913
|
+
try {
|
|
135914
|
+
entries = (await readdir53(worktreesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
135915
|
+
} catch {
|
|
135916
|
+
return { id: "worktrees", status: "ok", detail: "no .claude/worktrees directory" };
|
|
135917
|
+
}
|
|
135918
|
+
if (entries.length === 0) {
|
|
135919
|
+
return { id: "worktrees", status: "ok", detail: ".claude/worktrees is empty" };
|
|
135920
|
+
}
|
|
135921
|
+
const registered = await registeredWorktrees(cwd);
|
|
135922
|
+
const stale = entries.filter((name) => {
|
|
135923
|
+
const full = path302.resolve(worktreesDir, name);
|
|
135924
|
+
return !existsSync48(path302.join(full, ".git")) || !registered.has(full);
|
|
135925
|
+
});
|
|
135926
|
+
if (stale.length === 0) {
|
|
135927
|
+
return {
|
|
135928
|
+
id: "worktrees",
|
|
135929
|
+
status: "ok",
|
|
135930
|
+
detail: `${entries.length} worktree(s) under .claude/worktrees, all registered`
|
|
135931
|
+
};
|
|
135932
|
+
}
|
|
135933
|
+
return {
|
|
135934
|
+
id: "worktrees",
|
|
135935
|
+
status: "warn",
|
|
135936
|
+
detail: `${stale.length} stale entr${stale.length === 1 ? "y" : "ies"} under .claude/worktrees: ${stale.join(", ")}`,
|
|
135937
|
+
fix: "git worktree prune, then remove the listed directories under .claude/worktrees by hand"
|
|
135938
|
+
};
|
|
135939
|
+
}
|
|
135940
|
+
function meetsBunFloor(current, floor) {
|
|
135941
|
+
const floorMatch = /^>=\s*(\d+)\.(\d+)\.(\d+)/.exec(floor);
|
|
135942
|
+
const currentMatch = /^(\d+)\.(\d+)\.(\d+)/.exec(current);
|
|
135943
|
+
if (!floorMatch || !currentMatch)
|
|
135944
|
+
return true;
|
|
135945
|
+
for (let i = 1;i <= 3; i += 1) {
|
|
135946
|
+
const f = Number(floorMatch[i]);
|
|
135947
|
+
const c = Number(currentMatch[i]);
|
|
135948
|
+
if (c !== f)
|
|
135949
|
+
return c > f;
|
|
135950
|
+
}
|
|
135951
|
+
return true;
|
|
135952
|
+
}
|
|
135953
|
+
function checkBun(bunFloorOverride) {
|
|
135954
|
+
const floor = bunFloorOverride ?? package_default.engines?.bun;
|
|
135955
|
+
const current = Bun.version;
|
|
135956
|
+
if (floor === undefined || meetsBunFloor(current, floor)) {
|
|
135957
|
+
return { id: "bun", status: "ok", detail: `Bun ${current}${floor ? ` (floor ${floor})` : ""}` };
|
|
135958
|
+
}
|
|
135959
|
+
return {
|
|
135960
|
+
id: "bun",
|
|
135961
|
+
status: "fail",
|
|
135962
|
+
detail: `Bun ${current} is below the documented floor ${floor}`,
|
|
135963
|
+
fix: "upgrade Bun: https://bun.sh/docs/installation"
|
|
135964
|
+
};
|
|
135965
|
+
}
|
|
135966
|
+
function checkRipgrep() {
|
|
135967
|
+
const rg = Bun.which("rg");
|
|
135968
|
+
return rg ? { id: "ripgrep", status: "ok", detail: `rg at ${rg}` } : {
|
|
135969
|
+
id: "ripgrep",
|
|
135970
|
+
status: "warn",
|
|
135971
|
+
detail: "ripgrep (rg) not found on PATH \u2014 keryx ctx rg and search_code are unavailable",
|
|
135972
|
+
fix: "brew install ripgrep (macOS) or apt install ripgrep (Debian/Ubuntu)"
|
|
135973
|
+
};
|
|
135974
|
+
}
|
|
135975
|
+
async function checkVersionAvailability() {
|
|
135976
|
+
const result = await checkVersion({ currentVersion: package_default.version });
|
|
135977
|
+
if (result.status === "update-available") {
|
|
135978
|
+
return {
|
|
135979
|
+
id: "version",
|
|
135980
|
+
status: "warn",
|
|
135981
|
+
detail: `keryx ${result.currentVersion} \u2192 ${result.latestVersion} is available`,
|
|
135982
|
+
fix: result.installCommand
|
|
135983
|
+
};
|
|
135984
|
+
}
|
|
135985
|
+
if (result.status === "up-to-date") {
|
|
135986
|
+
return { id: "version", status: "ok", detail: `keryx ${result.currentVersion} is up to date` };
|
|
135987
|
+
}
|
|
135988
|
+
return { id: "version", status: "ok", detail: `keryx ${result.currentVersion}; update check unavailable (${result.reason})` };
|
|
135989
|
+
}
|
|
135990
|
+
function checkSandbox() {
|
|
135991
|
+
const report = buildSandboxReport();
|
|
135992
|
+
if (report.launcher.available) {
|
|
135993
|
+
return { id: "sandbox", status: "ok", detail: `${report.launcherName ?? "sandbox launcher"} available` };
|
|
135994
|
+
}
|
|
135995
|
+
const known = report.launcherName !== undefined;
|
|
135996
|
+
return {
|
|
135997
|
+
id: "sandbox",
|
|
135998
|
+
status: "warn",
|
|
135999
|
+
detail: known ? `${report.launcherName} not installed on ${report.platform} \u2014 OS-sandboxed shell exec is unavailable` : `no OS sandbox launcher support on ${report.platform}`,
|
|
136000
|
+
fix: known ? "keryx sandbox status for what installing it would unlock" : "keryx sandbox status for details"
|
|
136001
|
+
};
|
|
136002
|
+
}
|
|
136003
|
+
function checkProviders(env) {
|
|
136004
|
+
const providers = configuredProviders(env);
|
|
136005
|
+
if (providers.length === 0) {
|
|
136006
|
+
return {
|
|
136007
|
+
id: "providers",
|
|
136008
|
+
status: "warn",
|
|
136009
|
+
detail: "no provider credentials configured",
|
|
136010
|
+
fix: "keryx auth login <provider>, or set a provider API key"
|
|
136011
|
+
};
|
|
136012
|
+
}
|
|
136013
|
+
return {
|
|
136014
|
+
id: "providers",
|
|
136015
|
+
status: "ok",
|
|
136016
|
+
detail: `${providers.length} provider(s) configured: ${providers.map((p) => p.name).join(", ")}`
|
|
136017
|
+
};
|
|
136018
|
+
}
|
|
136019
|
+
function checkMcp(cwd) {
|
|
136020
|
+
const projectRoot = resolveProjectRoot(cwd);
|
|
136021
|
+
const config = loadMcpServers({ cwd, gitRoot: projectRoot });
|
|
136022
|
+
const ownProblems = config.problems.filter((p) => !p.foreign);
|
|
136023
|
+
if (ownProblems.length > 0) {
|
|
136024
|
+
return {
|
|
136025
|
+
id: "mcp",
|
|
136026
|
+
status: "fail",
|
|
136027
|
+
detail: `${ownProblems.length} config problem(s) in keryx's own MCP config: ${ownProblems.map((p) => p.message).join("; ")}`,
|
|
136028
|
+
fix: "keryx mcp list for details"
|
|
136029
|
+
};
|
|
136030
|
+
}
|
|
136031
|
+
const approvals = loadTrustStore();
|
|
136032
|
+
const held = config.servers.filter((server) => server.enabled && requiresApproval(server, approvals));
|
|
136033
|
+
if (held.length > 0) {
|
|
136034
|
+
return {
|
|
136035
|
+
id: "mcp",
|
|
136036
|
+
status: "warn",
|
|
136037
|
+
detail: `${held.length} server(s) from committed config awaiting approval: ${held.map((s) => s.name).join(", ")}`,
|
|
136038
|
+
fix: "keryx mcp trust <name>, after reading what it launches"
|
|
136039
|
+
};
|
|
136040
|
+
}
|
|
136041
|
+
return {
|
|
136042
|
+
id: "mcp",
|
|
136043
|
+
status: "ok",
|
|
136044
|
+
detail: `${config.servers.length} server(s) configured; run \`keryx mcp doctor\` to also check connectivity`
|
|
136045
|
+
};
|
|
136046
|
+
}
|
|
136047
|
+
async function checkIntegrations(cwd) {
|
|
136048
|
+
const results = await Promise.all(harnessAdapterIds().map(async (id) => {
|
|
136049
|
+
try {
|
|
136050
|
+
return await doctorIntegration(cwd, id);
|
|
136051
|
+
} catch {
|
|
136052
|
+
return { runtimeId: id, surfaces: [], problems: [], ok: true };
|
|
136053
|
+
}
|
|
136054
|
+
}));
|
|
136055
|
+
const drifted = results.filter((r) => !r.ok);
|
|
136056
|
+
if (drifted.length === 0) {
|
|
136057
|
+
return { id: "integrations", status: "ok", detail: "no drift in any installed integration" };
|
|
136058
|
+
}
|
|
136059
|
+
return {
|
|
136060
|
+
id: "integrations",
|
|
136061
|
+
status: "warn",
|
|
136062
|
+
detail: `drift in: ${drifted.map((r) => r.runtimeId).join(", ")}`,
|
|
136063
|
+
fix: "keryx integrations doctor --runtime all"
|
|
136064
|
+
};
|
|
136065
|
+
}
|
|
136066
|
+
async function checkStandard(cwd) {
|
|
136067
|
+
const result = await runDoctor(cwd);
|
|
136068
|
+
if (result.errors.length > 0) {
|
|
136069
|
+
return {
|
|
136070
|
+
id: "standard",
|
|
136071
|
+
status: "fail",
|
|
136072
|
+
detail: `${result.errors.length} error(s): ${result.errors.map((e) => e.message).join("; ")}`,
|
|
136073
|
+
fix: "keryx standard doctor"
|
|
136074
|
+
};
|
|
136075
|
+
}
|
|
136076
|
+
if (result.warnings.length > 0) {
|
|
136077
|
+
return {
|
|
136078
|
+
id: "standard",
|
|
136079
|
+
status: "warn",
|
|
136080
|
+
detail: `${result.warnings.length} warning(s): ${result.warnings.map((w) => w.message).join("; ")}`,
|
|
136081
|
+
fix: "keryx standard doctor"
|
|
136082
|
+
};
|
|
136083
|
+
}
|
|
136084
|
+
return { id: "standard", status: "ok", detail: "workspace is Metaproject Standard compliant" };
|
|
136085
|
+
}
|
|
136086
|
+
async function checkGraphFreshness(cwd) {
|
|
136087
|
+
const result = await checkGraphStaleness(cwd);
|
|
136088
|
+
if (result.status === "fresh") {
|
|
136089
|
+
return { id: "graph-freshness", status: "ok", detail: "code graph is fresh" };
|
|
136090
|
+
}
|
|
136091
|
+
return {
|
|
136092
|
+
id: "graph-freshness",
|
|
136093
|
+
status: "warn",
|
|
136094
|
+
detail: result.reasons.join("; ") || `code graph is ${result.status}`,
|
|
136095
|
+
fix: "keryx gdgraph build"
|
|
136096
|
+
};
|
|
136097
|
+
}
|
|
136098
|
+
async function checkWikiFreshness(cwd) {
|
|
136099
|
+
const metric = await readWikiFreshnessMetric(cwd);
|
|
136100
|
+
if (metric.status === "measured") {
|
|
136101
|
+
const pct = metric.ratio !== undefined ? `${Math.round(metric.ratio * 100)}%` : "an unmeasured share";
|
|
136102
|
+
return {
|
|
136103
|
+
id: "wiki-freshness",
|
|
136104
|
+
status: "ok",
|
|
136105
|
+
detail: `${pct} of ${metric.pagesTotal ?? "?"} wiki page(s) fresh`
|
|
136106
|
+
};
|
|
136107
|
+
}
|
|
136108
|
+
return {
|
|
136109
|
+
id: "wiki-freshness",
|
|
136110
|
+
status: "warn",
|
|
136111
|
+
detail: metric.reason ?? `wiki freshness: ${metric.status}`,
|
|
136112
|
+
fix: "keryx wiki freshness"
|
|
136113
|
+
};
|
|
136114
|
+
}
|
|
136115
|
+
async function buildDoctorReport(cwd, env = process.env, overrides = {}) {
|
|
136116
|
+
const checks = await Promise.all([
|
|
136117
|
+
checkVersionAvailability(),
|
|
136118
|
+
Promise.resolve(checkBun(overrides.bunFloor)),
|
|
136119
|
+
Promise.resolve(checkRipgrep()),
|
|
136120
|
+
Promise.resolve(checkSandbox()),
|
|
136121
|
+
Promise.resolve(checkProviders(env)),
|
|
136122
|
+
Promise.resolve(checkMcp(cwd)),
|
|
136123
|
+
checkIntegrations(cwd),
|
|
136124
|
+
checkStandard(cwd),
|
|
136125
|
+
checkWorktrees(cwd),
|
|
136126
|
+
checkGraphFreshness(cwd),
|
|
136127
|
+
checkWikiFreshness(cwd)
|
|
136128
|
+
]);
|
|
136129
|
+
return { checks };
|
|
136130
|
+
}
|
|
136131
|
+
function doctorFailed(report) {
|
|
136132
|
+
return report.checks.some((check) => check.status === "fail");
|
|
136133
|
+
}
|
|
136134
|
+
function formatDoctorReport2(report) {
|
|
136135
|
+
const symbolFor = { ok: "\u2713", warn: "!", fail: "\u2717" };
|
|
136136
|
+
const lines = ["keryx doctor", ""];
|
|
136137
|
+
for (const check of report.checks) {
|
|
136138
|
+
lines.push(` ${symbolFor[check.status]} ${check.id}: ${check.detail}`);
|
|
136139
|
+
if (check.fix) {
|
|
136140
|
+
lines.push(` fix: ${check.fix}`);
|
|
136141
|
+
}
|
|
136142
|
+
}
|
|
136143
|
+
return lines.join(`
|
|
136144
|
+
`);
|
|
136145
|
+
}
|
|
136146
|
+
async function doctorCommand2(args = [], cwd = process.cwd(), overrides = {}) {
|
|
136147
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
136148
|
+
console.log(`keryx doctor [--json]
|
|
136149
|
+
|
|
136150
|
+
One page: keryx's own version and update availability, the Bun floor,
|
|
136151
|
+
ripgrep on PATH, the OS sandbox launcher, providers with a credential
|
|
136152
|
+
present (names only), MCP servers and their trust state, integrations
|
|
136153
|
+
drift, Metaproject Standard warnings, stale .claude/worktrees entries, and
|
|
136154
|
+
graph/wiki freshness. Exits 0 unless something is a "fail".`);
|
|
136155
|
+
return;
|
|
136156
|
+
}
|
|
136157
|
+
const report = await buildDoctorReport(cwd, process.env, overrides);
|
|
136158
|
+
if (args.includes("--json")) {
|
|
136159
|
+
console.log(JSON.stringify(report, null, 2));
|
|
136160
|
+
} else {
|
|
136161
|
+
console.log(formatDoctorReport2(report));
|
|
136162
|
+
}
|
|
136163
|
+
if (doctorFailed(report)) {
|
|
136164
|
+
process.exitCode = 1;
|
|
136165
|
+
}
|
|
136166
|
+
}
|
|
136167
|
+
|
|
135482
136168
|
// src/tui/tui-shell.ts
|
|
135483
136169
|
init_slate_lifecycle();
|
|
135484
136170
|
init_slate();
|
|
@@ -135491,7 +136177,7 @@ init_workspace_service();
|
|
|
135491
136177
|
init_service11();
|
|
135492
136178
|
init_store3();
|
|
135493
136179
|
init_fs();
|
|
135494
|
-
import
|
|
136180
|
+
import path303 from "path";
|
|
135495
136181
|
import { readFile as readFile157 } from "fs/promises";
|
|
135496
136182
|
var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
|
|
135497
136183
|
var DEFAULT_AUTO_GOAL_ROUNDS = 8;
|
|
@@ -135567,7 +136253,7 @@ function getFlowService() {
|
|
|
135567
136253
|
async function autoProvisionFlow(cwd, goalText) {
|
|
135568
136254
|
const service = getFlowService();
|
|
135569
136255
|
const result = await service.init({ cwd, title: goalText });
|
|
135570
|
-
const acFile =
|
|
136256
|
+
const acFile = path303.join(cwd, result.dir, "acceptance-criteria.md");
|
|
135571
136257
|
await writeFileAtomic(acFile, [
|
|
135572
136258
|
"# Acceptance Criteria",
|
|
135573
136259
|
"",
|
|
@@ -137255,8 +137941,8 @@ init_store4();
|
|
|
137255
137941
|
init_proposal_lifecycle();
|
|
137256
137942
|
init_workspace_service();
|
|
137257
137943
|
import { randomUUID as randomUUID34 } from "crypto";
|
|
137258
|
-
import { readdir as
|
|
137259
|
-
import
|
|
137944
|
+
import { readdir as readdir54, stat as stat30 } from "fs/promises";
|
|
137945
|
+
import path304 from "path";
|
|
137260
137946
|
|
|
137261
137947
|
// src/sac/lifecycle-flag.ts
|
|
137262
137948
|
init_store();
|
|
@@ -137395,7 +138081,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
137395
138081
|
const extDir = externalSlatesDir(cwd);
|
|
137396
138082
|
let externalIds;
|
|
137397
138083
|
try {
|
|
137398
|
-
const entries = await
|
|
138084
|
+
const entries = await readdir54(extDir);
|
|
137399
138085
|
externalIds = entries.filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -".json".length));
|
|
137400
138086
|
} catch {
|
|
137401
138087
|
return [];
|
|
@@ -137426,7 +138112,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
137426
138112
|
}
|
|
137427
138113
|
return groups;
|
|
137428
138114
|
}, []));
|
|
137429
|
-
const metaPath =
|
|
138115
|
+
const metaPath = path304.join(extDir, `${id}.json`);
|
|
137430
138116
|
if (await pathExists(unboundDismissedReceiptPath(metaPath)))
|
|
137431
138117
|
continue;
|
|
137432
138118
|
candidates.push({
|
|
@@ -137440,16 +138126,16 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
137440
138126
|
return candidates;
|
|
137441
138127
|
}
|
|
137442
138128
|
async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
137443
|
-
const evidenceDir =
|
|
138129
|
+
const evidenceDir = path304.join(externalSlatesDir(cwd), externalSessionId);
|
|
137444
138130
|
let entries;
|
|
137445
138131
|
try {
|
|
137446
|
-
entries = (await
|
|
138132
|
+
entries = (await readdir54(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
137447
138133
|
} catch {
|
|
137448
138134
|
return;
|
|
137449
138135
|
}
|
|
137450
138136
|
entries.sort();
|
|
137451
138137
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
137452
|
-
const evidencePath =
|
|
138138
|
+
const evidencePath = path304.join(evidenceDir, entries[i]);
|
|
137453
138139
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
137454
138140
|
continue;
|
|
137455
138141
|
const result = readConfigFile(evidencePath);
|
|
@@ -137492,23 +138178,23 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
|
|
|
137492
138178
|
var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
|
|
137493
138179
|
async function collectReceiptTargets(cwd, owner) {
|
|
137494
138180
|
const targets = new Set;
|
|
137495
|
-
const workspacesDir =
|
|
138181
|
+
const workspacesDir = path304.join(cwd, ".metaproject", "workspaces");
|
|
137496
138182
|
let workspaceIds;
|
|
137497
138183
|
try {
|
|
137498
|
-
workspaceIds = (await
|
|
138184
|
+
workspaceIds = (await readdir54(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
137499
138185
|
} catch {
|
|
137500
138186
|
return targets;
|
|
137501
138187
|
}
|
|
137502
138188
|
for (const workspaceId of workspaceIds) {
|
|
137503
|
-
const receiptsDir =
|
|
138189
|
+
const receiptsDir = path304.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
|
|
137504
138190
|
let files;
|
|
137505
138191
|
try {
|
|
137506
|
-
files = (await
|
|
138192
|
+
files = (await readdir54(receiptsDir)).filter((name) => name.endsWith(".json"));
|
|
137507
138193
|
} catch {
|
|
137508
138194
|
continue;
|
|
137509
138195
|
}
|
|
137510
138196
|
for (const file of files) {
|
|
137511
|
-
const result = readConfigFile(
|
|
138197
|
+
const result = readConfigFile(path304.join(receiptsDir, file));
|
|
137512
138198
|
if (!result.ok)
|
|
137513
138199
|
continue;
|
|
137514
138200
|
try {
|
|
@@ -137545,13 +138231,13 @@ async function attributeToSession(absolutePath, sessionsNewestFirst) {
|
|
|
137545
138231
|
async function findSkillFiles(dir) {
|
|
137546
138232
|
let entries;
|
|
137547
138233
|
try {
|
|
137548
|
-
entries = await
|
|
138234
|
+
entries = await readdir54(dir, { withFileTypes: true });
|
|
137549
138235
|
} catch {
|
|
137550
138236
|
return [];
|
|
137551
138237
|
}
|
|
137552
138238
|
const found = [];
|
|
137553
138239
|
for (const entry of entries) {
|
|
137554
|
-
const full =
|
|
138240
|
+
const full = path304.join(dir, entry.name);
|
|
137555
138241
|
if (entry.isDirectory()) {
|
|
137556
138242
|
found.push(...await findSkillFiles(full));
|
|
137557
138243
|
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
@@ -137610,10 +138296,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
137610
138296
|
changedAt: attribution.changedAt
|
|
137611
138297
|
});
|
|
137612
138298
|
}
|
|
137613
|
-
const sacSkillsDir =
|
|
138299
|
+
const sacSkillsDir = path304.join(cwd, ".metaproject", "project-skills", "sac");
|
|
137614
138300
|
const skillFiles = await findSkillFiles(sacSkillsDir);
|
|
137615
138301
|
for (const absolutePath of skillFiles) {
|
|
137616
|
-
const targetRef =
|
|
138302
|
+
const targetRef = path304.relative(path304.join(cwd, ".metaproject"), absolutePath).split(path304.sep).join("/");
|
|
137617
138303
|
if (skillReceipts.has(targetRef))
|
|
137618
138304
|
continue;
|
|
137619
138305
|
const attribution = await attributeToSession(absolutePath, scoped);
|
|
@@ -137631,12 +138317,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
137631
138317
|
return items;
|
|
137632
138318
|
}
|
|
137633
138319
|
async function isSlateEngaged(dir) {
|
|
137634
|
-
if (await pathExists(
|
|
138320
|
+
if (await pathExists(path304.join(dir, "slate.json")))
|
|
137635
138321
|
return true;
|
|
137636
|
-
if (await pathExists(
|
|
138322
|
+
if (await pathExists(path304.join(dir, "terminal-state.json")))
|
|
137637
138323
|
return true;
|
|
137638
138324
|
try {
|
|
137639
|
-
const entries = await
|
|
138325
|
+
const entries = await readdir54(path304.join(dir, "slate-archive"));
|
|
137640
138326
|
return entries.length > 0;
|
|
137641
138327
|
} catch {
|
|
137642
138328
|
return false;
|
|
@@ -137661,7 +138347,7 @@ async function safeReadSlate(dir) {
|
|
|
137661
138347
|
}
|
|
137662
138348
|
}
|
|
137663
138349
|
async function readTerminalState(dir) {
|
|
137664
|
-
const result = readConfigFile(
|
|
138350
|
+
const result = readConfigFile(path304.join(dir, "terminal-state.json"));
|
|
137665
138351
|
if (!result.ok) {
|
|
137666
138352
|
return;
|
|
137667
138353
|
}
|
|
@@ -137685,17 +138371,17 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
137685
138371
|
if (session === undefined) {
|
|
137686
138372
|
throw new Error(`No session or evidence path matches "${target}"`);
|
|
137687
138373
|
}
|
|
137688
|
-
const archiveDir =
|
|
138374
|
+
const archiveDir = path304.join(sessionDir(session.projectPath, session.id), "slate-archive");
|
|
137689
138375
|
let entries;
|
|
137690
138376
|
try {
|
|
137691
|
-
entries = (await
|
|
138377
|
+
entries = (await readdir54(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
137692
138378
|
} catch {
|
|
137693
138379
|
throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
|
|
137694
138380
|
}
|
|
137695
138381
|
if (entries.length === 0)
|
|
137696
138382
|
throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
|
|
137697
138383
|
entries.sort();
|
|
137698
|
-
return
|
|
138384
|
+
return path304.join(archiveDir, entries[entries.length - 1]);
|
|
137699
138385
|
}
|
|
137700
138386
|
async function dismissUnboundByTarget(cwd, target, reason) {
|
|
137701
138387
|
const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
|
|
@@ -137721,16 +138407,16 @@ async function dismissUnboundCandidate(candidatePath, reason) {
|
|
|
137721
138407
|
return { removed: candidatePath, receipt: receiptPath };
|
|
137722
138408
|
}
|
|
137723
138409
|
async function readNewestUnboundCandidate(dir) {
|
|
137724
|
-
const archiveDir =
|
|
138410
|
+
const archiveDir = path304.join(dir, "slate-archive");
|
|
137725
138411
|
let entries;
|
|
137726
138412
|
try {
|
|
137727
|
-
entries = (await
|
|
138413
|
+
entries = (await readdir54(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
137728
138414
|
} catch {
|
|
137729
138415
|
return;
|
|
137730
138416
|
}
|
|
137731
138417
|
entries.sort();
|
|
137732
138418
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
137733
|
-
const evidencePath =
|
|
138419
|
+
const evidencePath = path304.join(archiveDir, entries[i]);
|
|
137734
138420
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
137735
138421
|
continue;
|
|
137736
138422
|
const result = readConfigFile(evidencePath);
|
|
@@ -137760,16 +138446,16 @@ function isFailureOutcome2(g) {
|
|
|
137760
138446
|
return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
|
|
137761
138447
|
}
|
|
137762
138448
|
async function readNewestWrapUpOutcome(dir) {
|
|
137763
|
-
const archiveDir =
|
|
138449
|
+
const archiveDir = path304.join(dir, "slate-archive");
|
|
137764
138450
|
let entries;
|
|
137765
138451
|
try {
|
|
137766
|
-
entries = (await
|
|
138452
|
+
entries = (await readdir54(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
|
|
137767
138453
|
} catch {
|
|
137768
138454
|
return;
|
|
137769
138455
|
}
|
|
137770
138456
|
entries.sort();
|
|
137771
138457
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
137772
|
-
const evidencePath =
|
|
138458
|
+
const evidencePath = path304.join(archiveDir, entries[i]);
|
|
137773
138459
|
const result = readConfigFile(evidencePath);
|
|
137774
138460
|
if (!result.ok) {
|
|
137775
138461
|
continue;
|
|
@@ -138376,6 +139062,11 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
138376
139062
|
modes: BOTH
|
|
138377
139063
|
},
|
|
138378
139064
|
{ name: "/flows", description: "Browse project flows and inspect one", modes: BOTH },
|
|
139065
|
+
{
|
|
139066
|
+
name: "/doctor",
|
|
139067
|
+
description: "One-page health check: version, sandbox, providers, MCP, integrations, freshness",
|
|
139068
|
+
modes: BOTH
|
|
139069
|
+
},
|
|
138379
139070
|
{
|
|
138380
139071
|
name: "/ac",
|
|
138381
139072
|
description: "Check the active flow's acceptance criteria (advisory, cached) \u2014 press `c` in /flows to re-check",
|
|
@@ -138718,7 +139409,7 @@ function classifyBusyDispatch(params) {
|
|
|
138718
139409
|
init_fs();
|
|
138719
139410
|
init_json();
|
|
138720
139411
|
import { mkdir as mkdir67 } from "fs/promises";
|
|
138721
|
-
import
|
|
139412
|
+
import path306 from "path";
|
|
138722
139413
|
|
|
138723
139414
|
// src/governance/aggregate.ts
|
|
138724
139415
|
init_store3();
|
|
@@ -138726,17 +139417,17 @@ init_store3();
|
|
|
138726
139417
|
// src/governance/spend.ts
|
|
138727
139418
|
init_fs();
|
|
138728
139419
|
init_store3();
|
|
138729
|
-
import { readdir as
|
|
138730
|
-
import
|
|
139420
|
+
import { readdir as readdir55, readFile as readFile158 } from "fs/promises";
|
|
139421
|
+
import path305 from "path";
|
|
138731
139422
|
async function readFlowReviewManifests(cwd, flowDir) {
|
|
138732
|
-
const reviewsDir =
|
|
139423
|
+
const reviewsDir = path305.join(flowsRoot(cwd), flowDir, "reviews");
|
|
138733
139424
|
if (!await pathExists(reviewsDir)) {
|
|
138734
139425
|
return [];
|
|
138735
139426
|
}
|
|
138736
|
-
const entries = (await
|
|
139427
|
+
const entries = (await readdir55(reviewsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory());
|
|
138737
139428
|
const manifests = [];
|
|
138738
139429
|
for (const entry of entries) {
|
|
138739
|
-
const manifestPath =
|
|
139430
|
+
const manifestPath = path305.join(reviewsDir, entry.name, "manifest.json");
|
|
138740
139431
|
if (!await pathExists(manifestPath)) {
|
|
138741
139432
|
continue;
|
|
138742
139433
|
}
|
|
@@ -138983,11 +139674,11 @@ async function collectProjectGovernance(root, filters, displayName) {
|
|
|
138983
139674
|
// src/governance/report.ts
|
|
138984
139675
|
var GOVERNANCE_SCHEMA_VERSION = 1;
|
|
138985
139676
|
function governanceDataRoot(cwd) {
|
|
138986
|
-
return
|
|
139677
|
+
return path306.join(cwd, ".metaproject", "data", "governance");
|
|
138987
139678
|
}
|
|
138988
139679
|
function artifactPaths(cwd) {
|
|
138989
|
-
const artifacts =
|
|
138990
|
-
return { markdown:
|
|
139680
|
+
const artifacts = path306.join(governanceDataRoot(cwd), "artifacts");
|
|
139681
|
+
return { markdown: path306.join(artifacts, "latest.md"), json: path306.join(artifacts, "latest.json") };
|
|
138991
139682
|
}
|
|
138992
139683
|
async function buildGovernanceReport(options) {
|
|
138993
139684
|
const { cwd, filters, allProjects, now } = options;
|
|
@@ -138995,9 +139686,9 @@ async function buildGovernanceReport(options) {
|
|
|
138995
139686
|
if (allProjects) {
|
|
138996
139687
|
const warnings = [];
|
|
138997
139688
|
const registered = listProjects(options.registryDir, (message) => warnings.push(message));
|
|
138998
|
-
const seen = new Set([
|
|
139689
|
+
const seen = new Set([path306.resolve(cwd)]);
|
|
138999
139690
|
for (const entry of registered) {
|
|
139000
|
-
const resolved =
|
|
139691
|
+
const resolved = path306.resolve(entry.path);
|
|
139001
139692
|
if (seen.has(resolved)) {
|
|
139002
139693
|
continue;
|
|
139003
139694
|
}
|
|
@@ -139150,7 +139841,7 @@ function renderTriggerSpendLine(spend) {
|
|
|
139150
139841
|
}
|
|
139151
139842
|
async function writeGovernanceArtifacts(cwd, report) {
|
|
139152
139843
|
const { markdown, json } = artifactPaths(cwd);
|
|
139153
|
-
await mkdir67(
|
|
139844
|
+
await mkdir67(path306.dirname(markdown), { recursive: true });
|
|
139154
139845
|
await writeFileAtomic(markdown, renderGovernanceMarkdown(report));
|
|
139155
139846
|
await writeFileAtomic(json, `${JSON.stringify(report, null, 2)}
|
|
139156
139847
|
`);
|
|
@@ -139355,7 +140046,7 @@ function mountGovernancePanel(otui, renderer, parent, options) {
|
|
|
139355
140046
|
|
|
139356
140047
|
// src/tui/governance-inspector.ts
|
|
139357
140048
|
import { readFile as readFile159 } from "fs/promises";
|
|
139358
|
-
import
|
|
140049
|
+
import path307 from "path";
|
|
139359
140050
|
var GOVERNANCE_COMMAND = "/governance";
|
|
139360
140051
|
var GOVERNANCE_FOOTER = [
|
|
139361
140052
|
{ key: "\u2191/\u2193 j/k", label: "scroll" },
|
|
@@ -139366,7 +140057,7 @@ var GOVERNANCE_FOOTER = [
|
|
|
139366
140057
|
var HEADER_TEXT_ROWS = 2;
|
|
139367
140058
|
var GOVERNANCE_HEADER_ROWS = HEADER_TEXT_ROWS + 1;
|
|
139368
140059
|
function governanceMarkdownPath(cwd) {
|
|
139369
|
-
return
|
|
140060
|
+
return path307.join(governanceDataRoot(cwd), "artifacts", "latest.md");
|
|
139370
140061
|
}
|
|
139371
140062
|
async function defaultReadMarkdown(cwd) {
|
|
139372
140063
|
try {
|
|
@@ -139511,7 +140202,7 @@ function openGovernanceReport(otui, chrome, options) {
|
|
|
139511
140202
|
|
|
139512
140203
|
// src/tui/trigger-ledger.ts
|
|
139513
140204
|
import { stat as fsStat2 } from "fs/promises";
|
|
139514
|
-
import
|
|
140205
|
+
import path308 from "path";
|
|
139515
140206
|
var DEFAULT_RECORDS_PER_ENTRY = 20;
|
|
139516
140207
|
function isScheduledEntry(entry) {
|
|
139517
140208
|
return entry.fire.kind === "schedule";
|
|
@@ -139570,7 +140261,7 @@ function triggerLedgerPaths(root) {
|
|
|
139570
140261
|
return {
|
|
139571
140262
|
runs: triggerRunsPath(root),
|
|
139572
140263
|
triggers: triggersConfigPath(root),
|
|
139573
|
-
governance:
|
|
140264
|
+
governance: path308.join(governanceDataRoot(root), "artifacts", "latest.json"),
|
|
139574
140265
|
schedules: scheduleStorePath(root)
|
|
139575
140266
|
};
|
|
139576
140267
|
}
|
|
@@ -140143,7 +140834,7 @@ function openTriggers(otui, chrome, options) {
|
|
|
140143
140834
|
// src/tui/trigger-run-now.ts
|
|
140144
140835
|
import { spawn as nodeSpawn2 } from "child_process";
|
|
140145
140836
|
import { closeSync as closeSync5, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync7, openSync as openSync5, readdirSync as readdirSync10, readSync as readSync3, unlinkSync as unlinkSync4, writeSync } from "fs";
|
|
140146
|
-
import
|
|
140837
|
+
import path309 from "path";
|
|
140147
140838
|
var OUTPUT_TAIL_CHARS = 4000;
|
|
140148
140839
|
var defaultSpawn = (command, args, options) => nodeSpawn2(command, [...args], { cwd: options.cwd, env: options.env, detached: true, stdio: [...options.stdio] });
|
|
140149
140840
|
function triggerRunArgv(name, invocation = resolveKeryxInvocation(), options = {}) {
|
|
@@ -140158,7 +140849,7 @@ function runNowChildEnv(env) {
|
|
|
140158
140849
|
return out;
|
|
140159
140850
|
}
|
|
140160
140851
|
function runNowLogDir(root) {
|
|
140161
|
-
return
|
|
140852
|
+
return path309.join(triggerDataDir(root), "run-now");
|
|
140162
140853
|
}
|
|
140163
140854
|
var RUN_NOW_LOGS_KEPT = 5;
|
|
140164
140855
|
var OUTPUT_TAIL_BYTES = 16 * 1024;
|
|
@@ -140166,13 +140857,13 @@ function stampOf(iso) {
|
|
|
140166
140857
|
return iso.replace(/[:.]/g, "-");
|
|
140167
140858
|
}
|
|
140168
140859
|
function runNowLogPath(root, name, startedAt) {
|
|
140169
|
-
return
|
|
140860
|
+
return path309.join(runNowLogDir(root), `${name}-${stampOf(startedAt)}.log`);
|
|
140170
140861
|
}
|
|
140171
140862
|
function isCode(error, code) {
|
|
140172
140863
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
140173
140864
|
}
|
|
140174
140865
|
function prepareRunNowLogDir(root) {
|
|
140175
|
-
mkdirSync7(
|
|
140866
|
+
mkdirSync7(path309.join(root, ".metaproject", "data"), { recursive: true });
|
|
140176
140867
|
for (const segment of [triggerDataDir(root), runNowLogDir(root)]) {
|
|
140177
140868
|
let info;
|
|
140178
140869
|
try {
|
|
@@ -140191,7 +140882,7 @@ function prepareRunNowLogDir(root) {
|
|
|
140191
140882
|
}
|
|
140192
140883
|
const dir = runNowLogDir(root);
|
|
140193
140884
|
try {
|
|
140194
|
-
const fd = openSync5(
|
|
140885
|
+
const fd = openSync5(path309.join(dir, ".gitignore"), constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 420);
|
|
140195
140886
|
try {
|
|
140196
140887
|
writeSync(fd, `# keryx run-now logs \u2014 never commit
|
|
140197
140888
|
*
|
|
@@ -140232,7 +140923,7 @@ function pruneRunLogs(dir, name, keep) {
|
|
|
140232
140923
|
}
|
|
140233
140924
|
for (const old of names.slice(0, Math.max(0, names.length - keep))) {
|
|
140234
140925
|
try {
|
|
140235
|
-
unlinkSync4(
|
|
140926
|
+
unlinkSync4(path309.join(dir, old));
|
|
140236
140927
|
} catch {}
|
|
140237
140928
|
}
|
|
140238
140929
|
}
|
|
@@ -141250,279 +141941,6 @@ function mountSchedulesSidebar(options) {
|
|
|
141250
141941
|
// src/tui/tui-shell.ts
|
|
141251
141942
|
init_debug_log();
|
|
141252
141943
|
|
|
141253
|
-
// src/lib/version-check.ts
|
|
141254
|
-
init_config_dir();
|
|
141255
|
-
init_fs();
|
|
141256
|
-
import path308 from "path";
|
|
141257
|
-
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
141258
|
-
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
141259
|
-
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
141260
|
-
var VERSION_STRING_LIMIT_CHARS = 64;
|
|
141261
|
-
var REQUEST_TIMEOUT_MS = 2000;
|
|
141262
|
-
var SUCCESS_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
141263
|
-
var FAILURE_BACKOFF_MS = 15 * 60 * 1000;
|
|
141264
|
-
var CACHE_LOCK_TIMEOUT_MS2 = 250;
|
|
141265
|
-
var CACHE_LOCK_RETRY_MS2 = 10;
|
|
141266
|
-
var CACHE_LOCK_STALE_MS2 = 5000;
|
|
141267
|
-
var SYSTEM_TIMER = {
|
|
141268
|
-
schedule: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
141269
|
-
cancel: (handle) => clearTimeout(handle)
|
|
141270
|
-
};
|
|
141271
|
-
function formatVersionUpdateAdvisory(result) {
|
|
141272
|
-
if (result.status !== "update-available")
|
|
141273
|
-
return;
|
|
141274
|
-
return `Keryx update ${result.currentVersion} \u2192 ${result.latestVersion}
|
|
141275
|
-
${result.installCommand}`;
|
|
141276
|
-
}
|
|
141277
|
-
var STRICT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
|
141278
|
-
function parseSemVer(value) {
|
|
141279
|
-
if (value.length > VERSION_STRING_LIMIT_CHARS)
|
|
141280
|
-
return;
|
|
141281
|
-
const match = STRICT_SEMVER.exec(value);
|
|
141282
|
-
if (match === null)
|
|
141283
|
-
return;
|
|
141284
|
-
const prereleaseText = match[4];
|
|
141285
|
-
const prerelease = [];
|
|
141286
|
-
if (prereleaseText !== undefined) {
|
|
141287
|
-
for (const identifier of prereleaseText.split(".")) {
|
|
141288
|
-
if (/^\d+$/.test(identifier)) {
|
|
141289
|
-
if (identifier.length > 1 && identifier.startsWith("0"))
|
|
141290
|
-
return;
|
|
141291
|
-
prerelease.push(BigInt(identifier));
|
|
141292
|
-
} else {
|
|
141293
|
-
prerelease.push(identifier);
|
|
141294
|
-
}
|
|
141295
|
-
}
|
|
141296
|
-
}
|
|
141297
|
-
return {
|
|
141298
|
-
major: BigInt(match[1]),
|
|
141299
|
-
minor: BigInt(match[2]),
|
|
141300
|
-
patch: BigInt(match[3]),
|
|
141301
|
-
prerelease
|
|
141302
|
-
};
|
|
141303
|
-
}
|
|
141304
|
-
function compareSemVer(left, right) {
|
|
141305
|
-
for (const key of ["major", "minor", "patch"]) {
|
|
141306
|
-
if (left[key] < right[key])
|
|
141307
|
-
return -1;
|
|
141308
|
-
if (left[key] > right[key])
|
|
141309
|
-
return 1;
|
|
141310
|
-
}
|
|
141311
|
-
if (left.prerelease.length === 0 || right.prerelease.length === 0) {
|
|
141312
|
-
if (left.prerelease.length === right.prerelease.length)
|
|
141313
|
-
return 0;
|
|
141314
|
-
return left.prerelease.length === 0 ? 1 : -1;
|
|
141315
|
-
}
|
|
141316
|
-
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
141317
|
-
for (let index = 0;index < length; index += 1) {
|
|
141318
|
-
const a = left.prerelease[index];
|
|
141319
|
-
const b = right.prerelease[index];
|
|
141320
|
-
if (a === undefined || b === undefined) {
|
|
141321
|
-
if (a === b)
|
|
141322
|
-
return 0;
|
|
141323
|
-
return a === undefined ? -1 : 1;
|
|
141324
|
-
}
|
|
141325
|
-
if (typeof a === "bigint" && typeof b === "bigint") {
|
|
141326
|
-
if (a < b)
|
|
141327
|
-
return -1;
|
|
141328
|
-
if (a > b)
|
|
141329
|
-
return 1;
|
|
141330
|
-
} else if (typeof a === "bigint") {
|
|
141331
|
-
return -1;
|
|
141332
|
-
} else if (typeof b === "bigint") {
|
|
141333
|
-
return 1;
|
|
141334
|
-
} else if (a !== b) {
|
|
141335
|
-
return a < b ? -1 : 1;
|
|
141336
|
-
}
|
|
141337
|
-
}
|
|
141338
|
-
return 0;
|
|
141339
|
-
}
|
|
141340
|
-
function parseCache(file) {
|
|
141341
|
-
const read = readConfigFile(file);
|
|
141342
|
-
if (!read.ok)
|
|
141343
|
-
return;
|
|
141344
|
-
try {
|
|
141345
|
-
const value = JSON.parse(read.text);
|
|
141346
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
141347
|
-
return;
|
|
141348
|
-
const record = value;
|
|
141349
|
-
const cache = {};
|
|
141350
|
-
if (typeof record.latestVersion === "string" && parseSemVer(record.latestVersion) !== undefined) {
|
|
141351
|
-
cache.latestVersion = record.latestVersion;
|
|
141352
|
-
}
|
|
141353
|
-
if (typeof record.successAt === "number" && Number.isFinite(record.successAt))
|
|
141354
|
-
cache.successAt = record.successAt;
|
|
141355
|
-
if (typeof record.failureAt === "number" && Number.isFinite(record.failureAt))
|
|
141356
|
-
cache.failureAt = record.failureAt;
|
|
141357
|
-
return cache;
|
|
141358
|
-
} catch {
|
|
141359
|
-
return;
|
|
141360
|
-
}
|
|
141361
|
-
}
|
|
141362
|
-
function resultFor(currentVersion, current, latestVersion, source) {
|
|
141363
|
-
const latest = parseSemVer(latestVersion);
|
|
141364
|
-
return compareSemVer(current, latest) < 0 ? { status: "update-available", currentVersion, latestVersion, installCommand: FIXED_INSTALL_COMMAND, source } : { status: "up-to-date", currentVersion, latestVersion, source };
|
|
141365
|
-
}
|
|
141366
|
-
function cancelWithoutWaiting(cancel) {
|
|
141367
|
-
try {
|
|
141368
|
-
cancel().catch(() => {});
|
|
141369
|
-
} catch {}
|
|
141370
|
-
}
|
|
141371
|
-
async function boundedResponseText(response) {
|
|
141372
|
-
const declared = response.headers.get("content-length");
|
|
141373
|
-
if (declared !== null && /^\d+$/.test(declared) && BigInt(declared) > BigInt(RESPONSE_BODY_LIMIT_BYTES)) {
|
|
141374
|
-
if (response.body !== null) {
|
|
141375
|
-
cancelWithoutWaiting(() => response.body.cancel());
|
|
141376
|
-
}
|
|
141377
|
-
return;
|
|
141378
|
-
}
|
|
141379
|
-
if (response.body === null)
|
|
141380
|
-
return "";
|
|
141381
|
-
const reader = response.body.getReader();
|
|
141382
|
-
const decoder = new TextDecoder;
|
|
141383
|
-
let size = 0;
|
|
141384
|
-
let text = "";
|
|
141385
|
-
try {
|
|
141386
|
-
for (;; ) {
|
|
141387
|
-
const chunk = await reader.read();
|
|
141388
|
-
if (chunk.done)
|
|
141389
|
-
break;
|
|
141390
|
-
size += chunk.value.byteLength;
|
|
141391
|
-
if (size > RESPONSE_BODY_LIMIT_BYTES) {
|
|
141392
|
-
cancelWithoutWaiting(() => reader.cancel());
|
|
141393
|
-
return;
|
|
141394
|
-
}
|
|
141395
|
-
text += decoder.decode(chunk.value, { stream: true });
|
|
141396
|
-
}
|
|
141397
|
-
return text + decoder.decode();
|
|
141398
|
-
} finally {
|
|
141399
|
-
reader.releaseLock();
|
|
141400
|
-
}
|
|
141401
|
-
}
|
|
141402
|
-
function unavailable(currentVersion, reason, cache) {
|
|
141403
|
-
return {
|
|
141404
|
-
status: "unavailable",
|
|
141405
|
-
currentVersion,
|
|
141406
|
-
reason,
|
|
141407
|
-
...cache?.latestVersion !== undefined ? { cachedLatestVersion: cache.latestVersion } : {}
|
|
141408
|
-
};
|
|
141409
|
-
}
|
|
141410
|
-
function saveCache2(file, cache) {
|
|
141411
|
-
try {
|
|
141412
|
-
writeOwnerOnlyFileAtomic(file, `${JSON.stringify(cache, null, 2)}
|
|
141413
|
-
`);
|
|
141414
|
-
return true;
|
|
141415
|
-
} catch {
|
|
141416
|
-
return false;
|
|
141417
|
-
}
|
|
141418
|
-
}
|
|
141419
|
-
function mergeSuccessfulCache(committed, latestVersion, successAt) {
|
|
141420
|
-
if (committed.latestVersion === undefined || committed.successAt === undefined) {
|
|
141421
|
-
return { latestVersion, successAt };
|
|
141422
|
-
}
|
|
141423
|
-
if (committed.successAt > successAt)
|
|
141424
|
-
return committed;
|
|
141425
|
-
if (committed.successAt < successAt)
|
|
141426
|
-
return { latestVersion, successAt };
|
|
141427
|
-
const committedVersion = parseSemVer(committed.latestVersion);
|
|
141428
|
-
const candidateVersion = parseSemVer(latestVersion);
|
|
141429
|
-
if (committedVersion !== undefined && candidateVersion !== undefined) {
|
|
141430
|
-
const precedence = compareSemVer(committedVersion, candidateVersion);
|
|
141431
|
-
if (precedence > 0)
|
|
141432
|
-
return committed;
|
|
141433
|
-
if (precedence < 0)
|
|
141434
|
-
return { latestVersion, successAt };
|
|
141435
|
-
if (committed.latestVersion >= latestVersion)
|
|
141436
|
-
return committed;
|
|
141437
|
-
}
|
|
141438
|
-
return { latestVersion, successAt };
|
|
141439
|
-
}
|
|
141440
|
-
async function updateCache(file, update) {
|
|
141441
|
-
try {
|
|
141442
|
-
return await withFileLock(`${file}.lock`, async () => saveCache2(file, update(parseCache(file) ?? {})), {
|
|
141443
|
-
timeoutMs: CACHE_LOCK_TIMEOUT_MS2,
|
|
141444
|
-
retryMs: CACHE_LOCK_RETRY_MS2,
|
|
141445
|
-
staleMs: CACHE_LOCK_STALE_MS2
|
|
141446
|
-
});
|
|
141447
|
-
} catch {
|
|
141448
|
-
return false;
|
|
141449
|
-
}
|
|
141450
|
-
}
|
|
141451
|
-
async function checkVersion(options) {
|
|
141452
|
-
const current = parseSemVer(options.currentVersion);
|
|
141453
|
-
if (current === undefined)
|
|
141454
|
-
return unavailable(options.currentVersion, "invalid-current-version");
|
|
141455
|
-
const now = options.now ?? Date.now;
|
|
141456
|
-
const timestamp = now();
|
|
141457
|
-
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
141458
|
-
const cacheFile = path308.join(configDir, "version-check.json");
|
|
141459
|
-
const cache = parseCache(cacheFile);
|
|
141460
|
-
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
141461
|
-
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
141462
|
-
}
|
|
141463
|
-
if (cache?.failureAt !== undefined && timestamp - cache.failureAt >= 0 && timestamp - cache.failureAt < FAILURE_BACKOFF_MS) {
|
|
141464
|
-
return unavailable(options.currentVersion, "suppressed", cache);
|
|
141465
|
-
}
|
|
141466
|
-
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
141467
|
-
const controller = new AbortController;
|
|
141468
|
-
const timer = options.timer ?? SYSTEM_TIMER;
|
|
141469
|
-
let timeout;
|
|
141470
|
-
let timeoutScheduled = false;
|
|
141471
|
-
let failure;
|
|
141472
|
-
let latestVersion;
|
|
141473
|
-
try {
|
|
141474
|
-
timeout = timer.schedule(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
141475
|
-
timeoutScheduled = true;
|
|
141476
|
-
const response = await fetchImpl(REGISTRY_URL, {
|
|
141477
|
-
method: "GET",
|
|
141478
|
-
headers: { accept: "application/json" },
|
|
141479
|
-
signal: controller.signal,
|
|
141480
|
-
credentials: "omit"
|
|
141481
|
-
});
|
|
141482
|
-
if (!response.ok) {
|
|
141483
|
-
failure = "http";
|
|
141484
|
-
} else {
|
|
141485
|
-
const body = await boundedResponseText(response);
|
|
141486
|
-
if (body === undefined) {
|
|
141487
|
-
failure = "response-too-large";
|
|
141488
|
-
} else {
|
|
141489
|
-
let parsed;
|
|
141490
|
-
try {
|
|
141491
|
-
parsed = JSON.parse(body);
|
|
141492
|
-
} catch {
|
|
141493
|
-
parsed = undefined;
|
|
141494
|
-
}
|
|
141495
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
141496
|
-
failure = "malformed-response";
|
|
141497
|
-
} else {
|
|
141498
|
-
const record = parsed;
|
|
141499
|
-
if (record.name !== "@mrciphersmith/keryx" || typeof record.version !== "string") {
|
|
141500
|
-
failure = "malformed-response";
|
|
141501
|
-
} else if (parseSemVer(record.version) === undefined) {
|
|
141502
|
-
failure = "invalid-latest-version";
|
|
141503
|
-
} else {
|
|
141504
|
-
latestVersion = record.version;
|
|
141505
|
-
}
|
|
141506
|
-
}
|
|
141507
|
-
}
|
|
141508
|
-
}
|
|
141509
|
-
} catch {
|
|
141510
|
-
failure = controller.signal.aborted ? "timeout" : "network";
|
|
141511
|
-
} finally {
|
|
141512
|
-
if (timeoutScheduled) {
|
|
141513
|
-
try {
|
|
141514
|
-
timer.cancel(timeout);
|
|
141515
|
-
} catch {}
|
|
141516
|
-
}
|
|
141517
|
-
}
|
|
141518
|
-
if (latestVersion === undefined) {
|
|
141519
|
-
await updateCache(cacheFile, (committed) => ({ ...committed, failureAt: timestamp }));
|
|
141520
|
-
return unavailable(options.currentVersion, failure ?? "network", cache);
|
|
141521
|
-
}
|
|
141522
|
-
await updateCache(cacheFile, (committed) => mergeSuccessfulCache(committed, latestVersion, timestamp));
|
|
141523
|
-
return resultFor(options.currentVersion, current, latestVersion, "registry");
|
|
141524
|
-
}
|
|
141525
|
-
|
|
141526
141944
|
// src/tui/shell-chrome.ts
|
|
141527
141945
|
init_debug_log();
|
|
141528
141946
|
|
|
@@ -143826,7 +144244,7 @@ init_model_tier();
|
|
|
143826
144244
|
init_config_dir();
|
|
143827
144245
|
init_fs();
|
|
143828
144246
|
init_table();
|
|
143829
|
-
import
|
|
144247
|
+
import path310 from "path";
|
|
143830
144248
|
var UNCATEGORIZED = "uncategorized";
|
|
143831
144249
|
var EMPTY_TASK_COST_STORE = {};
|
|
143832
144250
|
var MIN_MEASURED_TASKS = 20;
|
|
@@ -143892,7 +144310,7 @@ var TASK_COST_LOCK_TIMEOUT_MS = 3000;
|
|
|
143892
144310
|
var TASK_COST_LOCK_RETRY_MS = 15;
|
|
143893
144311
|
var TASK_COST_LOCK_STALE_MS = 1e4;
|
|
143894
144312
|
function taskCostFilePath(dir) {
|
|
143895
|
-
return
|
|
144313
|
+
return path310.join(ensureKeryxSubdir(["routing"], dir), TASK_COST_FILE);
|
|
143896
144314
|
}
|
|
143897
144315
|
function isTaskCostRecord(value) {
|
|
143898
144316
|
if (typeof value !== "object" || value === null)
|
|
@@ -144956,11 +145374,11 @@ function openConform(otui, chrome, options) {
|
|
|
144956
145374
|
}
|
|
144957
145375
|
|
|
144958
145376
|
// src/tui/conform-source.ts
|
|
144959
|
-
import { readFile as readFile160, readdir as
|
|
144960
|
-
import
|
|
145377
|
+
import { readFile as readFile160, readdir as readdir56, stat as stat31 } from "fs/promises";
|
|
145378
|
+
import path311 from "path";
|
|
144961
145379
|
async function readRecentConformDocs2(cwd) {
|
|
144962
145380
|
try {
|
|
144963
|
-
const raw = JSON.parse(await readFile160(
|
|
145381
|
+
const raw = JSON.parse(await readFile160(path311.join(cwd, CONFORM_RECENTS_PATH), "utf8"));
|
|
144964
145382
|
return Array.isArray(raw) ? raw : [];
|
|
144965
145383
|
} catch {
|
|
144966
145384
|
return [];
|
|
@@ -144981,25 +145399,25 @@ async function currentBranchPrTarget(cwd) {
|
|
|
144981
145399
|
}
|
|
144982
145400
|
}
|
|
144983
145401
|
async function mostRecentReviewPackage(cwd) {
|
|
144984
|
-
const flowsDir =
|
|
145402
|
+
const flowsDir = path311.join(cwd, ".metaproject", "flows");
|
|
144985
145403
|
let best;
|
|
144986
145404
|
try {
|
|
144987
|
-
for (const flowEntry of await
|
|
145405
|
+
for (const flowEntry of await readdir56(flowsDir, { withFileTypes: true })) {
|
|
144988
145406
|
if (!flowEntry.isDirectory())
|
|
144989
145407
|
continue;
|
|
144990
|
-
const reviewsDir =
|
|
145408
|
+
const reviewsDir = path311.join(flowsDir, flowEntry.name, "reviews");
|
|
144991
145409
|
let reviewEntries;
|
|
144992
145410
|
try {
|
|
144993
|
-
reviewEntries = await
|
|
145411
|
+
reviewEntries = await readdir56(reviewsDir, { withFileTypes: true });
|
|
144994
145412
|
} catch {
|
|
144995
145413
|
continue;
|
|
144996
145414
|
}
|
|
144997
145415
|
for (const reviewEntry of reviewEntries) {
|
|
144998
145416
|
if (!reviewEntry.isDirectory())
|
|
144999
145417
|
continue;
|
|
145000
|
-
const dir =
|
|
145418
|
+
const dir = path311.join(reviewsDir, reviewEntry.name);
|
|
145001
145419
|
try {
|
|
145002
|
-
const info = await stat31(
|
|
145420
|
+
const info = await stat31(path311.join(dir, "report.md"));
|
|
145003
145421
|
if (best === undefined || info.mtimeMs > best.mtimeMs)
|
|
145004
145422
|
best = { dir, mtimeMs: info.mtimeMs };
|
|
145005
145423
|
} catch {}
|
|
@@ -145025,14 +145443,14 @@ async function loadConformSetup(cwd) {
|
|
|
145025
145443
|
targets.push(pr);
|
|
145026
145444
|
const reviewDir = await mostRecentReviewPackage(cwd);
|
|
145027
145445
|
if (reviewDir !== undefined) {
|
|
145028
|
-
targets.push({ id: `report:${reviewDir}`, kind: "report", label: `most recent review package \u2014 ${
|
|
145446
|
+
targets.push({ id: `report:${reviewDir}`, kind: "report", label: `most recent review package \u2014 ${path311.relative(cwd, reviewDir)}` });
|
|
145029
145447
|
}
|
|
145030
145448
|
targets.push({ id: "diff:working", kind: "diff", label: "the working diff" });
|
|
145031
145449
|
return { recents, targets };
|
|
145032
145450
|
}
|
|
145033
145451
|
async function readReportFindings2(reportDir) {
|
|
145034
145452
|
try {
|
|
145035
|
-
const raw = JSON.parse(await readFile160(
|
|
145453
|
+
const raw = JSON.parse(await readFile160(path311.join(reportDir, "findings.json"), "utf8"));
|
|
145036
145454
|
return Array.isArray(raw) ? raw : [];
|
|
145037
145455
|
} catch {
|
|
145038
145456
|
return [];
|
|
@@ -145148,7 +145566,7 @@ async function runConformForTarget(cwd, refPath, target, signal) {
|
|
|
145148
145566
|
await scoreHunkRegions(hunkClauses, hunkRegionsFromDiff(info.diff));
|
|
145149
145567
|
} else if (target.kind === "report") {
|
|
145150
145568
|
const reportDir = target.id.slice("report:".length);
|
|
145151
|
-
const reportMarkdown = await readFile160(
|
|
145569
|
+
const reportMarkdown = await readFile160(path311.join(reportDir, "report.md"), "utf8").catch(() => "");
|
|
145152
145570
|
const findings = await readReportFindings2(reportDir);
|
|
145153
145571
|
const facts = computeReportConformFacts(reportMarkdown, findings);
|
|
145154
145572
|
const redacted = reportRedactedStateText(reportMarkdown);
|
|
@@ -145174,7 +145592,7 @@ async function runConformForTarget(cwd, refPath, target, signal) {
|
|
|
145174
145592
|
async function writeRecentConformDoc(cwd, refPath) {
|
|
145175
145593
|
await Promise.resolve().then(() => init_fs());
|
|
145176
145594
|
const current = await readRecentConformDocs2(cwd);
|
|
145177
|
-
await writeFileAtomic(
|
|
145595
|
+
await writeFileAtomic(path311.join(cwd, CONFORM_RECENTS_PATH), `${JSON.stringify(withRecentDoc(current, refPath), null, 2)}
|
|
145178
145596
|
`, {
|
|
145179
145597
|
mode: 384
|
|
145180
145598
|
}).catch(() => {});
|
|
@@ -145310,8 +145728,8 @@ async function runJevContractForShell(cwd, gitDiff = defaultGitDiff3) {
|
|
|
145310
145728
|
}
|
|
145311
145729
|
|
|
145312
145730
|
// src/tui/jev-triage-command.ts
|
|
145313
|
-
import { readdir as
|
|
145314
|
-
import
|
|
145731
|
+
import { readdir as readdir57, readFile as readFile161, stat as stat32 } from "fs/promises";
|
|
145732
|
+
import path312 from "path";
|
|
145315
145733
|
var JEV_TRIAGE_COMMAND = "/triage";
|
|
145316
145734
|
function isJevTriageCommand(name) {
|
|
145317
145735
|
return name === JEV_TRIAGE_COMMAND;
|
|
@@ -145320,25 +145738,25 @@ function renderJevTriageForShell(result) {
|
|
|
145320
145738
|
return renderTriageMarkdown(result).trimEnd();
|
|
145321
145739
|
}
|
|
145322
145740
|
async function resolveLatestReviewPackage(cwd) {
|
|
145323
|
-
const flowsDir =
|
|
145741
|
+
const flowsDir = path312.join(cwd, ".metaproject", "flows");
|
|
145324
145742
|
let flowEntries;
|
|
145325
145743
|
try {
|
|
145326
|
-
flowEntries = (await
|
|
145744
|
+
flowEntries = (await readdir57(flowsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
145327
145745
|
} catch {
|
|
145328
145746
|
return;
|
|
145329
145747
|
}
|
|
145330
145748
|
const candidates = [];
|
|
145331
145749
|
for (const flowName of flowEntries) {
|
|
145332
|
-
const reviewsDir =
|
|
145750
|
+
const reviewsDir = path312.join(flowsDir, flowName, "reviews");
|
|
145333
145751
|
let reviewEntries;
|
|
145334
145752
|
try {
|
|
145335
|
-
reviewEntries = (await
|
|
145753
|
+
reviewEntries = (await readdir57(reviewsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
145336
145754
|
} catch {
|
|
145337
145755
|
continue;
|
|
145338
145756
|
}
|
|
145339
145757
|
for (const reviewName of reviewEntries) {
|
|
145340
|
-
const packageDir =
|
|
145341
|
-
const findingsPath =
|
|
145758
|
+
const packageDir = path312.join(reviewsDir, reviewName);
|
|
145759
|
+
const findingsPath = path312.join(packageDir, "findings.json");
|
|
145342
145760
|
try {
|
|
145343
145761
|
const stats = await stat32(findingsPath);
|
|
145344
145762
|
candidates.push({ dir: packageDir, mtimeMs: stats.mtimeMs });
|
|
@@ -145361,9 +145779,9 @@ async function runJevTriageForShell(cwd) {
|
|
|
145361
145779
|
if (packageDir === undefined) {
|
|
145362
145780
|
return "review-jev-triage: no review package found under .metaproject/flows/*/reviews/ \u2014 run a review round first.";
|
|
145363
145781
|
}
|
|
145364
|
-
const findingsRaw = JSON.parse(await readFile161(
|
|
145782
|
+
const findingsRaw = JSON.parse(await readFile161(path312.join(packageDir, "findings.json"), "utf8"));
|
|
145365
145783
|
const result = await computeJevTriageResult({ findingsRaw });
|
|
145366
|
-
return `review-jev-triage: ${
|
|
145784
|
+
return `review-jev-triage: ${path312.relative(cwd, packageDir)}
|
|
145367
145785
|
${renderJevTriageForShell(result)}`;
|
|
145368
145786
|
}
|
|
145369
145787
|
|
|
@@ -148277,10 +148695,10 @@ function openHelpModal(otui, chrome, options) {
|
|
|
148277
148695
|
|
|
148278
148696
|
// src/tui/help-first-run.ts
|
|
148279
148697
|
init_config_dir();
|
|
148280
|
-
import
|
|
148698
|
+
import path313 from "path";
|
|
148281
148699
|
var FIRST_RUN_HELP_FILE = "help-first-run.json";
|
|
148282
148700
|
function firstRunHelpPath(dir) {
|
|
148283
|
-
return
|
|
148701
|
+
return path313.join(keryxConfigDir(dir), FIRST_RUN_HELP_FILE);
|
|
148284
148702
|
}
|
|
148285
148703
|
function helpFirstRunShown(dir) {
|
|
148286
148704
|
const result = readConfigFile(firstRunHelpPath(dir));
|
|
@@ -149364,10 +149782,10 @@ function describeElicitationPrompt(tool, inputJson) {
|
|
|
149364
149782
|
// src/lib/permission-mode-config.ts
|
|
149365
149783
|
init_permission_mode();
|
|
149366
149784
|
init_config_dir();
|
|
149367
|
-
import
|
|
149785
|
+
import path314 from "path";
|
|
149368
149786
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
149369
149787
|
function permissionModeConfigPath(dir) {
|
|
149370
|
-
return
|
|
149788
|
+
return path314.join(keryxConfigDir(dir), "permission-mode.json");
|
|
149371
149789
|
}
|
|
149372
149790
|
function withRegistryLock2(dir, fn) {
|
|
149373
149791
|
return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -152316,7 +152734,7 @@ function mountExecutionPlanPanel(otui, renderer, parent, options) {
|
|
|
152316
152734
|
// src/commands/schedule.ts
|
|
152317
152735
|
import { createInterface } from "readline/promises";
|
|
152318
152736
|
import { readFile as readFile162 } from "fs/promises";
|
|
152319
|
-
import
|
|
152737
|
+
import path316 from "path";
|
|
152320
152738
|
|
|
152321
152739
|
// src/commands/trigger.ts
|
|
152322
152740
|
init_gdgraph();
|
|
@@ -152327,9 +152745,9 @@ init_agent();
|
|
|
152327
152745
|
init_agent_hooks();
|
|
152328
152746
|
import { execFile as execFile11 } from "child_process";
|
|
152329
152747
|
import { randomUUID as randomUUID36 } from "crypto";
|
|
152330
|
-
import { mkdtemp as mkdtemp5, mkdir as mkdir68, readdir as
|
|
152748
|
+
import { mkdtemp as mkdtemp5, mkdir as mkdir68, readdir as readdir58, rm as rm19, writeFile as writeFile47 } from "fs/promises";
|
|
152331
152749
|
import { homedir as homedir13, tmpdir as tmpdir11 } from "os";
|
|
152332
|
-
import
|
|
152750
|
+
import path315 from "path";
|
|
152333
152751
|
init_interactive_tools();
|
|
152334
152752
|
|
|
152335
152753
|
// src/harness/process/sandbox/proxy.ts
|
|
@@ -152797,9 +153215,9 @@ function refusal3(runId, code, detail, action) {
|
|
|
152797
153215
|
function agentTaskScratchParent(env = process.env) {
|
|
152798
153216
|
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
152799
153217
|
const runtime = env["XDG_RUNTIME_DIR"];
|
|
152800
|
-
if (runtime !== undefined && runtime.length > 0 &&
|
|
152801
|
-
return
|
|
152802
|
-
return
|
|
153218
|
+
if (runtime !== undefined && runtime.length > 0 && path315.isAbsolute(runtime))
|
|
153219
|
+
return path315.join(runtime, "keryx-agent-tasks");
|
|
153220
|
+
return path315.join(tmpdir11(), `keryx-agent-tasks-${uid}`);
|
|
152803
153221
|
}
|
|
152804
153222
|
function lastAssistantText(history) {
|
|
152805
153223
|
for (let i = history.length - 1;i >= 0; i -= 1) {
|
|
@@ -152810,9 +153228,9 @@ function lastAssistantText(history) {
|
|
|
152810
153228
|
return "";
|
|
152811
153229
|
}
|
|
152812
153230
|
async function pruneReports(dir, keep) {
|
|
152813
|
-
const names = (await
|
|
153231
|
+
const names = (await readdir58(dir).catch(() => [])).filter((n) => n.endsWith(".md")).sort();
|
|
152814
153232
|
for (const old of names.slice(0, Math.max(0, names.length - keep))) {
|
|
152815
|
-
await rm19(
|
|
153233
|
+
await rm19(path315.join(dir, old), { force: true }).catch(() => {});
|
|
152816
153234
|
}
|
|
152817
153235
|
}
|
|
152818
153236
|
async function runAgentTaskDispatch(projectRoot, entry, action, deps = {}) {
|
|
@@ -152849,10 +153267,10 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
152849
153267
|
};
|
|
152850
153268
|
}
|
|
152851
153269
|
const scratchParent = parent.dir;
|
|
152852
|
-
const scratch = await mkdtemp5(
|
|
152853
|
-
const workdir =
|
|
152854
|
-
const scratchHome =
|
|
152855
|
-
const grantedCwd =
|
|
153270
|
+
const scratch = await mkdtemp5(path315.join(scratchParent, `${entry.name.replace(/[^A-Za-z0-9._-]/g, "-")}-`));
|
|
153271
|
+
const workdir = path315.join(scratch, "work");
|
|
153272
|
+
const scratchHome = path315.join(scratch, "home");
|
|
153273
|
+
const grantedCwd = path315.join(scratch, "granted-cwd");
|
|
152856
153274
|
await mkdir68(workdir, { recursive: true });
|
|
152857
153275
|
await mkdir68(scratchHome, { recursive: true });
|
|
152858
153276
|
await mkdir68(grantedCwd, { recursive: true, mode: 448 });
|
|
@@ -152865,7 +153283,7 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
152865
153283
|
const invocation = keryxInvocation();
|
|
152866
153284
|
let networkInput = action.grants.network === "full";
|
|
152867
153285
|
if (action.grants.network === "allowlist") {
|
|
152868
|
-
const proxySocketPath =
|
|
153286
|
+
const proxySocketPath = path315.join(scratch, "net-proxy.sock");
|
|
152869
153287
|
try {
|
|
152870
153288
|
const proxy = await createAllowlistProxy({
|
|
152871
153289
|
allowedDomains: [...action.grants.domains],
|
|
@@ -153014,12 +153432,12 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
153014
153432
|
let reportNote = "";
|
|
153015
153433
|
try {
|
|
153016
153434
|
await ensureTriggerDataIgnored(projectRoot);
|
|
153017
|
-
const dir =
|
|
153435
|
+
const dir = path315.join(triggerReportsDir(projectRoot), entry.name);
|
|
153018
153436
|
await mkdir68(dir, { recursive: true });
|
|
153019
|
-
const file =
|
|
153437
|
+
const file = path315.join(dir, `${runId}.md`);
|
|
153020
153438
|
await writeFile47(file, renderReport2({ entry, action, runId, at: now().toISOString(), outcome, why, cost, grantedCalls, denials, sandboxNote, summary, networkDecisions }), "utf8");
|
|
153021
153439
|
await pruneReports(dir, action.report.keep);
|
|
153022
|
-
reportPath =
|
|
153440
|
+
reportPath = path315.relative(projectRoot, file);
|
|
153023
153441
|
} catch (error) {
|
|
153024
153442
|
reportNote = ` (report NOT written: ${error instanceof Error ? error.message : String(error)})`;
|
|
153025
153443
|
}
|
|
@@ -153927,7 +154345,7 @@ async function showSubcommand(cwd, name, host, deps) {
|
|
|
153927
154345
|
if (row.reportPath !== undefined) {
|
|
153928
154346
|
console.log(`
|
|
153929
154347
|
--- ${row.reportPath} ---`);
|
|
153930
|
-
console.log(await readFile162(
|
|
154348
|
+
console.log(await readFile162(path316.join(cwd, row.reportPath), "utf8").catch(() => "(the report could not be read)"));
|
|
153931
154349
|
}
|
|
153932
154350
|
}
|
|
153933
154351
|
async function simpleAction(cwd, name, verb, action) {
|
|
@@ -159560,6 +159978,14 @@ ${formatThemeList(getThemeId())}`);
|
|
|
159560
159978
|
});
|
|
159561
159979
|
return;
|
|
159562
159980
|
}
|
|
159981
|
+
if (command.name === "/doctor") {
|
|
159982
|
+
(async () => {
|
|
159983
|
+
const report = await buildDoctorReport(sessionCwd);
|
|
159984
|
+
io.onSystem?.(`${formatDoctorReport2(report)}
|
|
159985
|
+
`);
|
|
159986
|
+
})();
|
|
159987
|
+
return;
|
|
159988
|
+
}
|
|
159563
159989
|
if (command.name === "/game") {
|
|
159564
159990
|
showGame(line);
|
|
159565
159991
|
return;
|
|
@@ -160515,6 +160941,7 @@ var READLINE_AGENT_COMMANDS = [
|
|
|
160515
160941
|
"/compact",
|
|
160516
160942
|
"/status",
|
|
160517
160943
|
"/flows",
|
|
160944
|
+
"/doctor",
|
|
160518
160945
|
"/theme",
|
|
160519
160946
|
"/mode",
|
|
160520
160947
|
"/reasoning",
|
|
@@ -161025,6 +161452,12 @@ Kept the current session.
|
|
|
161025
161452
|
system(HELP_TEXT);
|
|
161026
161453
|
continue;
|
|
161027
161454
|
}
|
|
161455
|
+
if (command === "/doctor") {
|
|
161456
|
+
const report = await buildDoctorReport(deps.session?.cwd ?? process.cwd());
|
|
161457
|
+
system(`${formatDoctorReport2(report)}
|
|
161458
|
+
`);
|
|
161459
|
+
continue;
|
|
161460
|
+
}
|
|
161028
161461
|
if (isSessionInfoCommand(command)) {
|
|
161029
161462
|
const cwd = deps.session?.cwd;
|
|
161030
161463
|
const [workspaces, flows] = cwd === undefined ? [[], []] : await Promise.all([loadInspectorWorkspaces(cwd), loadInspectorFlows(cwd)]);
|
|
@@ -162194,6 +162627,10 @@ ${GUTTER}${turnSeparator()}
|
|
|
162194
162627
|
await runBusSlashCommand(bus, rest, (text) => agentIo.onSystem?.(text));
|
|
162195
162628
|
} else if (command === "/help") {
|
|
162196
162629
|
agentIo.onSystem?.(readlineAgentHelpText());
|
|
162630
|
+
} else if (command === "/doctor") {
|
|
162631
|
+
const report = await buildDoctorReport(sessionCwd);
|
|
162632
|
+
agentIo.onSystem?.(`${formatDoctorReport2(report)}
|
|
162633
|
+
`);
|
|
162197
162634
|
} else if (isSessionInfoCommand(command)) {
|
|
162198
162635
|
const cwd = sessionCwd;
|
|
162199
162636
|
const [workspaces, flows] = await Promise.all([
|
|
@@ -165738,7 +166175,7 @@ init_fs();
|
|
|
165738
166175
|
init_prompt();
|
|
165739
166176
|
import { readFile as readFile163 } from "fs/promises";
|
|
165740
166177
|
import { stdin } from "process";
|
|
165741
|
-
import
|
|
166178
|
+
import path318 from "path";
|
|
165742
166179
|
init_ui();
|
|
165743
166180
|
var MODULES = [
|
|
165744
166181
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
@@ -165785,8 +166222,8 @@ async function modulesCommand(args = []) {
|
|
|
165785
166222
|
return;
|
|
165786
166223
|
}
|
|
165787
166224
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
165788
|
-
const metaprojectRoot =
|
|
165789
|
-
const manifestPath =
|
|
166225
|
+
const metaprojectRoot = path318.join(process.cwd(), ".metaproject");
|
|
166226
|
+
const manifestPath = path318.join(metaprojectRoot, "metaproject.json");
|
|
165790
166227
|
if (!await pathExists(manifestPath)) {
|
|
165791
166228
|
if (wantsJson) {
|
|
165792
166229
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -165909,7 +166346,7 @@ import { createHash as createHash68, randomBytes as randomBytes11, randomUUID as
|
|
|
165909
166346
|
import {
|
|
165910
166347
|
chmodSync as chmodSync5,
|
|
165911
166348
|
closeSync as closeSync7,
|
|
165912
|
-
existsSync as
|
|
166349
|
+
existsSync as existsSync49,
|
|
165913
166350
|
fsyncSync as fsyncSync2,
|
|
165914
166351
|
openSync as openSync8,
|
|
165915
166352
|
renameSync as renameSync5,
|
|
@@ -165917,9 +166354,9 @@ import {
|
|
|
165917
166354
|
unlinkSync as unlinkSync6,
|
|
165918
166355
|
writeFileSync as writeFileSync10
|
|
165919
166356
|
} from "fs";
|
|
165920
|
-
import
|
|
166357
|
+
import path319 from "path";
|
|
165921
166358
|
function serveCredentialPath(dir) {
|
|
165922
|
-
return
|
|
166359
|
+
return path319.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
165923
166360
|
}
|
|
165924
166361
|
function constantTimeEqual(a, b) {
|
|
165925
166362
|
const width = Math.max(a.length, b.length);
|
|
@@ -165953,7 +166390,7 @@ function isGroupOrOtherAccessible(file) {
|
|
|
165953
166390
|
}
|
|
165954
166391
|
function readServeCredential(dir) {
|
|
165955
166392
|
const file = serveCredentialPath(dir);
|
|
165956
|
-
if (!
|
|
166393
|
+
if (!existsSync49(file)) {
|
|
165957
166394
|
return { status: "absent" };
|
|
165958
166395
|
}
|
|
165959
166396
|
if (isGroupOrOtherAccessible(file)) {
|
|
@@ -166152,23 +166589,23 @@ class AuthFailureThrottle {
|
|
|
166152
166589
|
// src/lib/serve-turn-store.ts
|
|
166153
166590
|
init_config_dir();
|
|
166154
166591
|
import { createHash as createHash69 } from "crypto";
|
|
166155
|
-
import { existsSync as
|
|
166156
|
-
import
|
|
166592
|
+
import { existsSync as existsSync50, readdirSync as readdirSync12, rmSync as rmSync4 } from "fs";
|
|
166593
|
+
import path320 from "path";
|
|
166157
166594
|
var MAX_TURN_EVENTS = 1e4;
|
|
166158
166595
|
function turnsRoot(dir) {
|
|
166159
|
-
return
|
|
166596
|
+
return path320.join(keryxConfigDir(dir), "turns");
|
|
166160
166597
|
}
|
|
166161
166598
|
function turnDir(turnId, dir) {
|
|
166162
|
-
return
|
|
166599
|
+
return path320.join(turnsRoot(dir), turnId);
|
|
166163
166600
|
}
|
|
166164
166601
|
function keyPath(project, idempotencyKey, dir) {
|
|
166165
166602
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
166166
166603
|
const digest = createHash69("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
166167
|
-
return
|
|
166604
|
+
return path320.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
166168
166605
|
}
|
|
166169
166606
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
166170
166607
|
const digest = createHash69("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
166171
|
-
return
|
|
166608
|
+
return path320.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
166172
166609
|
}
|
|
166173
166610
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
166174
166611
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -166232,7 +166669,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
166232
166669
|
}
|
|
166233
166670
|
function createTurnRecord(record, dir) {
|
|
166234
166671
|
ensureTurnDir(record.turnId, dir);
|
|
166235
|
-
writeOwnerOnlyFile(
|
|
166672
|
+
writeOwnerOnlyFile(path320.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
166236
166673
|
`);
|
|
166237
166674
|
}
|
|
166238
166675
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -166241,12 +166678,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
166241
166678
|
}
|
|
166242
166679
|
const line = JSON.stringify(event);
|
|
166243
166680
|
try {
|
|
166244
|
-
appendOwnerOnlyLine(
|
|
166681
|
+
appendOwnerOnlyLine(path320.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
166245
166682
|
} catch (error) {
|
|
166246
166683
|
if (error?.code !== "ENOENT") {
|
|
166247
166684
|
throw error;
|
|
166248
166685
|
}
|
|
166249
|
-
appendOwnerOnlyLine(
|
|
166686
|
+
appendOwnerOnlyLine(path320.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
166250
166687
|
}
|
|
166251
166688
|
return true;
|
|
166252
166689
|
}
|
|
@@ -166254,7 +166691,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
166254
166691
|
if (!isTurnId(turnId)) {
|
|
166255
166692
|
return { ok: false, reason: "not-a-turn-id" };
|
|
166256
166693
|
}
|
|
166257
|
-
const read = readTurnFile(
|
|
166694
|
+
const read = readTurnFile(path320.join(turnDir(turnId, dir), "events.jsonl"));
|
|
166258
166695
|
if (!read.ok) {
|
|
166259
166696
|
if (isDefiniteAbsence2(read.reason)) {
|
|
166260
166697
|
return { ok: true, value: [] };
|
|
@@ -166282,7 +166719,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
166282
166719
|
if (!isTurnId(turnId)) {
|
|
166283
166720
|
return { ok: false, reason: "not-a-turn-id" };
|
|
166284
166721
|
}
|
|
166285
|
-
const read = readTurnFile(
|
|
166722
|
+
const read = readTurnFile(path320.join(turnDir(turnId, dir), "turn.json"));
|
|
166286
166723
|
if (!read.ok) {
|
|
166287
166724
|
return { ok: false, reason: read.reason };
|
|
166288
166725
|
}
|
|
@@ -166301,7 +166738,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
166301
166738
|
if (!record.ok) {
|
|
166302
166739
|
return false;
|
|
166303
166740
|
}
|
|
166304
|
-
writeOwnerOnlyFile(
|
|
166741
|
+
writeOwnerOnlyFile(path320.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
166305
166742
|
`);
|
|
166306
166743
|
return true;
|
|
166307
166744
|
}
|
|
@@ -166348,7 +166785,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
166348
166785
|
// src/lib/serve-turn.ts
|
|
166349
166786
|
import { randomUUID as randomUUID44 } from "crypto";
|
|
166350
166787
|
init_hooks();
|
|
166351
|
-
import
|
|
166788
|
+
import path321 from "path";
|
|
166352
166789
|
init_service4();
|
|
166353
166790
|
init_service9();
|
|
166354
166791
|
init_impact_evidence_hook_adapter();
|
|
@@ -166419,9 +166856,9 @@ function isUuid(value) {
|
|
|
166419
166856
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value);
|
|
166420
166857
|
}
|
|
166421
166858
|
function resolveProject(declared, dir) {
|
|
166422
|
-
const wanted =
|
|
166859
|
+
const wanted = path321.resolve(declared);
|
|
166423
166860
|
for (const entry of listProjects(dir, () => {})) {
|
|
166424
|
-
if (
|
|
166861
|
+
if (path321.resolve(entry.path) === wanted) {
|
|
166425
166862
|
return { ok: true, project: entry.path };
|
|
166426
166863
|
}
|
|
166427
166864
|
}
|
|
@@ -166494,7 +166931,7 @@ function buildRemoteHookRuntime(opts) {
|
|
|
166494
166931
|
});
|
|
166495
166932
|
emitRemoteHookNotices(opts.projectRoot, formatHookLoadNotices(loaded, {
|
|
166496
166933
|
projectRoot: opts.projectRoot,
|
|
166497
|
-
userFile:
|
|
166934
|
+
userFile: path321.join(homeDir, ".keryx", "hooks.json"),
|
|
166498
166935
|
surface: "headless"
|
|
166499
166936
|
}));
|
|
166500
166937
|
if (!loaded.ok) {
|
|
@@ -167543,9 +167980,9 @@ function printHelp23() {
|
|
|
167543
167980
|
// src/commands/update.ts
|
|
167544
167981
|
init_contained_write();
|
|
167545
167982
|
import { spawn as spawn9 } from "child_process";
|
|
167546
|
-
import { readFile as readFile164, readdir as
|
|
167547
|
-
import { access as access4, constants as constants5, existsSync as
|
|
167548
|
-
import
|
|
167983
|
+
import { readFile as readFile164, readdir as readdir59 } from "fs/promises";
|
|
167984
|
+
import { access as access4, constants as constants5, existsSync as existsSync51 } from "fs";
|
|
167985
|
+
import path322 from "path";
|
|
167549
167986
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
167550
167987
|
init_catalog2();
|
|
167551
167988
|
init_model_choice();
|
|
@@ -167560,17 +167997,17 @@ init_ui();
|
|
|
167560
167997
|
init_install_plan();
|
|
167561
167998
|
init_templates3();
|
|
167562
167999
|
function containFromMetaprojectPath(filePath) {
|
|
167563
|
-
const marker = `${
|
|
168000
|
+
const marker = `${path322.sep}.metaproject${path322.sep}`;
|
|
167564
168001
|
const idx = filePath.lastIndexOf(marker);
|
|
167565
168002
|
if (idx < 0) {
|
|
167566
|
-
if (filePath.endsWith(`${
|
|
167567
|
-
const root = filePath.slice(0, filePath.length - ".metaproject".length -
|
|
168003
|
+
if (filePath.endsWith(`${path322.sep}.metaproject`)) {
|
|
168004
|
+
const root = filePath.slice(0, filePath.length - ".metaproject".length - path322.sep.length);
|
|
167568
168005
|
return { root, rel: ".metaproject" };
|
|
167569
168006
|
}
|
|
167570
168007
|
throw new Error(`${filePath}: expected a path under a .metaproject directory`);
|
|
167571
168008
|
}
|
|
167572
168009
|
const root = filePath.slice(0, idx);
|
|
167573
|
-
const rel = filePath.slice(idx +
|
|
168010
|
+
const rel = filePath.slice(idx + path322.sep.length).split(path322.sep).join("/");
|
|
167574
168011
|
return { root, rel };
|
|
167575
168012
|
}
|
|
167576
168013
|
async function updateCommand(args = []) {
|
|
@@ -167580,8 +168017,8 @@ async function updateCommand(args = []) {
|
|
|
167580
168017
|
return;
|
|
167581
168018
|
}
|
|
167582
168019
|
const projectRoot = process.cwd();
|
|
167583
|
-
const metaprojectRoot =
|
|
167584
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
168020
|
+
const metaprojectRoot = path322.join(projectRoot, ".metaproject");
|
|
168021
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path322.basename(projectRoot)}/`);
|
|
167585
168022
|
if (!await pathExists(metaprojectRoot)) {
|
|
167586
168023
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
167587
168024
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -167657,7 +168094,7 @@ async function updateCommand(args = []) {
|
|
|
167657
168094
|
nextSteps(steps);
|
|
167658
168095
|
}
|
|
167659
168096
|
async function previewServiceFiles(projectRoot, options) {
|
|
167660
|
-
const metaprojectRoot =
|
|
168097
|
+
const metaprojectRoot = path322.join(projectRoot, ".metaproject");
|
|
167661
168098
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
167662
168099
|
const manifest = manifestState.manifest;
|
|
167663
168100
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -167686,13 +168123,13 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
167686
168123
|
});
|
|
167687
168124
|
}
|
|
167688
168125
|
async function refreshServiceFiles(projectRoot, options) {
|
|
167689
|
-
const metaprojectRoot =
|
|
168126
|
+
const metaprojectRoot = path322.join(projectRoot, ".metaproject");
|
|
167690
168127
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
167691
168128
|
const manifest = manifestState.manifest;
|
|
167692
168129
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
167693
168130
|
if (manifestState.migrated) {
|
|
167694
168131
|
{
|
|
167695
|
-
const { root, rel } = containFromMetaprojectPath(
|
|
168132
|
+
const { root, rel } = containFromMetaprojectPath(path322.join(metaprojectRoot, "metaproject.json"));
|
|
167696
168133
|
await writeContained(root, rel, `${JSON.stringify(manifest, null, 2)}
|
|
167697
168134
|
`);
|
|
167698
168135
|
}
|
|
@@ -167732,9 +168169,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167732
168169
|
enableSecurity,
|
|
167733
168170
|
enableSac
|
|
167734
168171
|
});
|
|
167735
|
-
await writeTextIfChanged4(
|
|
167736
|
-
await writeTextIfChanged4(
|
|
167737
|
-
await writeTextIfChanged4(
|
|
168172
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
168173
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
168174
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
167738
168175
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
167739
168176
|
enableGdgraph,
|
|
167740
168177
|
enableGdctx,
|
|
@@ -167758,7 +168195,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167758
168195
|
}
|
|
167759
168196
|
});
|
|
167760
168197
|
console.log(` ${await describeModelChoiceStatus(projectRoot)}`);
|
|
167761
|
-
await writeTextIfChanged4(
|
|
168198
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
167762
168199
|
enableGdgraph,
|
|
167763
168200
|
enableGdctx,
|
|
167764
168201
|
enableGdwiki,
|
|
@@ -167770,7 +168207,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167770
168207
|
enableSecurity,
|
|
167771
168208
|
data: dashboardData
|
|
167772
168209
|
}));
|
|
167773
|
-
await writeTextIfMissing4(
|
|
168210
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
167774
168211
|
enableGdgraph,
|
|
167775
168212
|
enableGdctx,
|
|
167776
168213
|
enableGdwiki,
|
|
@@ -167783,9 +168220,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167783
168220
|
}));
|
|
167784
168221
|
if (enableGdgraph) {
|
|
167785
168222
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
167786
|
-
await writeTextIfChanged4(
|
|
167787
|
-
await writeTextIfChanged4(
|
|
167788
|
-
await writeTextIfChanged4(
|
|
168223
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
168224
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
168225
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
167789
168226
|
await seedAssetsLock(metaprojectRoot);
|
|
167790
168227
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
167791
168228
|
const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
@@ -167794,15 +168231,15 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167794
168231
|
}
|
|
167795
168232
|
}
|
|
167796
168233
|
if (enableGdctx) {
|
|
167797
|
-
await writeTextIfMissing4(
|
|
167798
|
-
await writeTextIfChanged4(
|
|
167799
|
-
await writeTextIfChanged4(
|
|
167800
|
-
await writeTextIfChanged4(
|
|
168234
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
168235
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
168236
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
168237
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
167801
168238
|
}
|
|
167802
168239
|
if (enableGdwiki) {
|
|
167803
|
-
await writeTextIfMissing4(
|
|
167804
|
-
await writeTextIfChanged4(
|
|
167805
|
-
await writeTextIfChanged4(
|
|
168240
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
168241
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
168242
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
167806
168243
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
167807
168244
|
const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
167808
168245
|
if (warning)
|
|
@@ -167810,8 +168247,8 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167810
168247
|
}
|
|
167811
168248
|
}
|
|
167812
168249
|
if (enableSac) {
|
|
167813
|
-
await writeTextIfMissing4(
|
|
167814
|
-
await writeTextIfChanged4(
|
|
168250
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
168251
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
167815
168252
|
}
|
|
167816
168253
|
let gdskillsWarnings = [];
|
|
167817
168254
|
let gdskillsNotices = [];
|
|
@@ -167826,10 +168263,10 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167826
168263
|
}
|
|
167827
168264
|
}
|
|
167828
168265
|
if (enableHealth) {
|
|
167829
|
-
await writeTextIfMissing4(
|
|
167830
|
-
await writeTextIfChanged4(
|
|
167831
|
-
await writeTextIfChanged4(
|
|
167832
|
-
await writeTextIfChanged4(
|
|
168266
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
168267
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
168268
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
168269
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
167833
168270
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
167834
168271
|
const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
167835
168272
|
if (warning)
|
|
@@ -167837,16 +168274,16 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167837
168274
|
}
|
|
167838
168275
|
}
|
|
167839
168276
|
if (enableTesting) {
|
|
167840
|
-
await writeTextIfMissing4(
|
|
168277
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
167841
168278
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
167842
168279
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
167843
168280
|
}));
|
|
167844
|
-
await writeTextIfChanged4(
|
|
167845
|
-
await writeTextIfChanged4(
|
|
167846
|
-
await writeTextIfChanged4(
|
|
168281
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
168282
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
168283
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
167847
168284
|
if (enableGdwiki) {
|
|
167848
|
-
await writeTextIfMissing4(
|
|
167849
|
-
await writeTextIfMissing4(
|
|
168285
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
168286
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
167850
168287
|
}
|
|
167851
168288
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
167852
168289
|
const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -167865,24 +168302,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167865
168302
|
hookWarnings.push(warning);
|
|
167866
168303
|
}
|
|
167867
168304
|
if (enableMemory) {
|
|
167868
|
-
await writeTextIfMissing4(
|
|
167869
|
-
await writeTextIfMissing4(
|
|
167870
|
-
await writeTextIfChanged4(
|
|
167871
|
-
await writeTextIfChanged4(
|
|
167872
|
-
await writeTextIfChanged4(
|
|
168305
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
168306
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
168307
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
168308
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
168309
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
167873
168310
|
}
|
|
167874
168311
|
if (enableTasks) {
|
|
167875
|
-
await writeTextIfChanged4(
|
|
167876
|
-
await writeTextIfChanged4(
|
|
167877
|
-
await writeTextIfChanged4(
|
|
167878
|
-
await writeTextIfChanged4(
|
|
167879
|
-
await writeTextIfChanged4(
|
|
167880
|
-
await writeTextIfChanged4(
|
|
168312
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
168313
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
168314
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
168315
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
168316
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
168317
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
167881
168318
|
}
|
|
167882
168319
|
if (enableSecurity) {
|
|
167883
|
-
await writeTextIfMissing4(
|
|
167884
|
-
await writeTextIfChanged4(
|
|
167885
|
-
await writeTextIfChanged4(
|
|
168320
|
+
await writeTextIfMissing4(path322.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
168321
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
168322
|
+
await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
167886
168323
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
167887
168324
|
const warning = await installManagedHookOrWarn(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
167888
168325
|
if (warning)
|
|
@@ -167940,13 +168377,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
167940
168377
|
};
|
|
167941
168378
|
}
|
|
167942
168379
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
167943
|
-
const metaprojectRoot =
|
|
168380
|
+
const metaprojectRoot = path322.join(projectRoot, ".metaproject");
|
|
167944
168381
|
if (!await pathExists(metaprojectRoot)) {
|
|
167945
168382
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
167946
168383
|
}
|
|
167947
168384
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
167948
168385
|
const data = await collectDashboardData(metaprojectRoot);
|
|
167949
|
-
const dashboardPath =
|
|
168386
|
+
const dashboardPath = path322.join(metaprojectRoot, "keryx-dashboard.html");
|
|
167950
168387
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
167951
168388
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
167952
168389
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -167966,7 +168403,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
167966
168403
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
167967
168404
|
return true;
|
|
167968
168405
|
}
|
|
167969
|
-
const hookPath =
|
|
168406
|
+
const hookPath = path322.join(projectRoot, ".git", "hooks", "post-commit");
|
|
167970
168407
|
if (!await pathExists(hookPath)) {
|
|
167971
168408
|
return false;
|
|
167972
168409
|
}
|
|
@@ -167986,11 +168423,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
167986
168423
|
if (testing) {
|
|
167987
168424
|
data.testing = testing;
|
|
167988
168425
|
}
|
|
167989
|
-
const wiki = await collectMarkdownPages(
|
|
168426
|
+
const wiki = await collectMarkdownPages(path322.join(metaprojectRoot, "wiki"), "wiki");
|
|
167990
168427
|
if (wiki.length > 0) {
|
|
167991
168428
|
data.wiki = { pages: wiki };
|
|
167992
168429
|
}
|
|
167993
|
-
const memory = await collectMarkdownPages(
|
|
168430
|
+
const memory = await collectMarkdownPages(path322.join(metaprojectRoot, "memory"), "memory");
|
|
167994
168431
|
if (memory.length > 0) {
|
|
167995
168432
|
data.memory = { entries: memory };
|
|
167996
168433
|
}
|
|
@@ -168005,19 +168442,19 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
168005
168442
|
return data;
|
|
168006
168443
|
}
|
|
168007
168444
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
168008
|
-
const flowsRoot =
|
|
168445
|
+
const flowsRoot = path322.join(metaprojectRoot, "flows");
|
|
168009
168446
|
if (!await pathExists(flowsRoot)) {
|
|
168010
168447
|
return null;
|
|
168011
168448
|
}
|
|
168012
168449
|
let dirEntries;
|
|
168013
168450
|
try {
|
|
168014
|
-
dirEntries = (await
|
|
168451
|
+
dirEntries = (await readdir59(flowsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
|
|
168015
168452
|
} catch {
|
|
168016
168453
|
return null;
|
|
168017
168454
|
}
|
|
168018
168455
|
const flows = [];
|
|
168019
168456
|
for (const dir of dirEntries) {
|
|
168020
|
-
const flowPath =
|
|
168457
|
+
const flowPath = path322.join(flowsRoot, dir, "flow.json");
|
|
168021
168458
|
if (!await pathExists(flowPath)) {
|
|
168022
168459
|
continue;
|
|
168023
168460
|
}
|
|
@@ -168025,7 +168462,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
168025
168462
|
const flow = JSON.parse(await readFile164(flowPath, "utf8"));
|
|
168026
168463
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
168027
168464
|
let acTotal = 0;
|
|
168028
|
-
const acPath =
|
|
168465
|
+
const acPath = path322.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
168029
168466
|
if (await pathExists(acPath)) {
|
|
168030
168467
|
const acContent = await readFile164(acPath, "utf8");
|
|
168031
168468
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -168079,7 +168516,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
168079
168516
|
"data/testing/context.md"
|
|
168080
168517
|
];
|
|
168081
168518
|
for (const href of staticHrefs) {
|
|
168082
|
-
const filePath =
|
|
168519
|
+
const filePath = path322.join(metaprojectRoot, ...href.split("/"));
|
|
168083
168520
|
if (!await pathExists(filePath)) {
|
|
168084
168521
|
continue;
|
|
168085
168522
|
}
|
|
@@ -168096,7 +168533,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
168096
168533
|
return docs;
|
|
168097
168534
|
}
|
|
168098
168535
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
168099
|
-
const reportPath =
|
|
168536
|
+
const reportPath = path322.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
168100
168537
|
if (!await pathExists(reportPath)) {
|
|
168101
168538
|
return;
|
|
168102
168539
|
}
|
|
@@ -168205,8 +168642,8 @@ function metricToScope(metric) {
|
|
|
168205
168642
|
};
|
|
168206
168643
|
}
|
|
168207
168644
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
168208
|
-
const nodesPath =
|
|
168209
|
-
const edgesPath =
|
|
168645
|
+
const nodesPath = path322.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
168646
|
+
const edgesPath = path322.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
168210
168647
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
168211
168648
|
return;
|
|
168212
168649
|
}
|
|
@@ -168257,8 +168694,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
168257
168694
|
};
|
|
168258
168695
|
}
|
|
168259
168696
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
168260
|
-
const reportPath =
|
|
168261
|
-
const contextPath =
|
|
168697
|
+
const reportPath = path322.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
168698
|
+
const contextPath = path322.join(metaprojectRoot, "data", "testing", "context.md");
|
|
168262
168699
|
if (await pathExists(reportPath)) {
|
|
168263
168700
|
const report = JSON.parse(await readFile164(reportPath, "utf8"));
|
|
168264
168701
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -168287,7 +168724,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
168287
168724
|
const files = await listMarkdownFiles(root);
|
|
168288
168725
|
const pages = [];
|
|
168289
168726
|
for (const filePath of files.slice(0, 40)) {
|
|
168290
|
-
const relativePath =
|
|
168727
|
+
const relativePath = path322.relative(root, filePath).split(path322.sep).join("/");
|
|
168291
168728
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
168292
168729
|
continue;
|
|
168293
168730
|
}
|
|
@@ -168305,10 +168742,10 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
168305
168742
|
return pages;
|
|
168306
168743
|
}
|
|
168307
168744
|
async function listMarkdownFiles(root) {
|
|
168308
|
-
const entries = await
|
|
168745
|
+
const entries = await readdir59(root, { withFileTypes: true });
|
|
168309
168746
|
const files = [];
|
|
168310
168747
|
for (const entry of entries) {
|
|
168311
|
-
const fullPath =
|
|
168748
|
+
const fullPath = path322.join(root, entry.name);
|
|
168312
168749
|
if (entry.isDirectory()) {
|
|
168313
168750
|
files.push(...await listMarkdownFiles(fullPath));
|
|
168314
168751
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -168356,7 +168793,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
168356
168793
|
const manifest = {
|
|
168357
168794
|
schemaVersion: 1,
|
|
168358
168795
|
standardVersion: STANDARD_VERSION,
|
|
168359
|
-
name: `${
|
|
168796
|
+
name: `${path322.basename(path322.dirname(metaprojectRoot))}-metaproject`,
|
|
168360
168797
|
createdBy: "keryx",
|
|
168361
168798
|
profiles: computeProfiles(enabledModuleKeys),
|
|
168362
168799
|
paths: {
|
|
@@ -168447,13 +168884,13 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
168447
168884
|
}
|
|
168448
168885
|
};
|
|
168449
168886
|
{
|
|
168450
|
-
const { root, rel } = containFromMetaprojectPath(
|
|
168887
|
+
const { root, rel } = containFromMetaprojectPath(path322.join(metaprojectRoot, "metaproject.json"));
|
|
168451
168888
|
await writeContained(root, rel, `${JSON.stringify(manifest, null, 2)}
|
|
168452
168889
|
`);
|
|
168453
168890
|
}
|
|
168454
168891
|
}
|
|
168455
168892
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
168456
|
-
const manifestPath =
|
|
168893
|
+
const manifestPath = path322.join(metaprojectRoot, "metaproject.json");
|
|
168457
168894
|
if (!await pathExists(manifestPath)) {
|
|
168458
168895
|
return;
|
|
168459
168896
|
}
|
|
@@ -168479,7 +168916,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
168479
168916
|
}
|
|
168480
168917
|
}
|
|
168481
168918
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
168482
|
-
const manifestPath =
|
|
168919
|
+
const manifestPath = path322.join(metaprojectRoot, "metaproject.json");
|
|
168483
168920
|
if (!await pathExists(manifestPath)) {
|
|
168484
168921
|
return;
|
|
168485
168922
|
}
|
|
@@ -168547,61 +168984,61 @@ async function updateRuntime(projectRoot) {
|
|
|
168547
168984
|
}
|
|
168548
168985
|
}
|
|
168549
168986
|
async function findRuntimeRoot(projectRoot) {
|
|
168550
|
-
const projectRuntime =
|
|
168551
|
-
if (await pathExists(
|
|
168987
|
+
const projectRuntime = path322.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
168988
|
+
if (await pathExists(path322.join(projectRuntime, ".git"))) {
|
|
168552
168989
|
return projectRuntime;
|
|
168553
168990
|
}
|
|
168554
168991
|
const home = process.env.HOME;
|
|
168555
168992
|
if (!home) {
|
|
168556
168993
|
return null;
|
|
168557
168994
|
}
|
|
168558
|
-
const globalRuntime =
|
|
168559
|
-
if (await pathExists(
|
|
168995
|
+
const globalRuntime = path322.join(home, ".keryx", "keryx");
|
|
168996
|
+
if (await pathExists(path322.join(globalRuntime, ".git"))) {
|
|
168560
168997
|
return globalRuntime;
|
|
168561
168998
|
}
|
|
168562
168999
|
return null;
|
|
168563
169000
|
}
|
|
168564
169001
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
168565
169002
|
const dirs = [
|
|
168566
|
-
|
|
168567
|
-
|
|
168568
|
-
|
|
168569
|
-
|
|
168570
|
-
|
|
169003
|
+
path322.join(metaprojectRoot, "core"),
|
|
169004
|
+
path322.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
169005
|
+
path322.join(metaprojectRoot, "modules"),
|
|
169006
|
+
path322.join(metaprojectRoot, "rules"),
|
|
169007
|
+
path322.join(metaprojectRoot, "skills", "project-rules"),
|
|
168571
169008
|
...modules.enableGdgraph ? [
|
|
168572
|
-
|
|
168573
|
-
|
|
169009
|
+
path322.join(metaprojectRoot, "core", "gdgraph"),
|
|
169010
|
+
path322.join(metaprojectRoot, "skills", "gdgraph")
|
|
168574
169011
|
] : [],
|
|
168575
169012
|
...modules.enableGdctx ? [
|
|
168576
|
-
|
|
168577
|
-
|
|
169013
|
+
path322.join(metaprojectRoot, "core", "gdctx"),
|
|
169014
|
+
path322.join(metaprojectRoot, "skills", "gdctx")
|
|
168578
169015
|
] : [],
|
|
168579
169016
|
...modules.enableGdwiki ? [
|
|
168580
|
-
|
|
168581
|
-
|
|
169017
|
+
path322.join(metaprojectRoot, "skills", "gdwiki"),
|
|
169018
|
+
path322.join(metaprojectRoot, "wiki", "templates")
|
|
168582
169019
|
] : [],
|
|
168583
169020
|
...modules.enableHealth ? [
|
|
168584
|
-
|
|
168585
|
-
|
|
169021
|
+
path322.join(metaprojectRoot, "core", "health"),
|
|
169022
|
+
path322.join(metaprojectRoot, "skills", "health")
|
|
168586
169023
|
] : [],
|
|
168587
169024
|
...modules.enableTesting ? [
|
|
168588
|
-
|
|
168589
|
-
|
|
169025
|
+
path322.join(metaprojectRoot, "core", "testing"),
|
|
169026
|
+
path322.join(metaprojectRoot, "skills", "testing")
|
|
168590
169027
|
] : [],
|
|
168591
169028
|
...modules.enableMemory ? [
|
|
168592
|
-
|
|
168593
|
-
|
|
168594
|
-
|
|
169029
|
+
path322.join(metaprojectRoot, "core", "memory"),
|
|
169030
|
+
path322.join(metaprojectRoot, "skills", "memory"),
|
|
169031
|
+
path322.join(metaprojectRoot, "memory", "templates")
|
|
168595
169032
|
] : [],
|
|
168596
169033
|
...modules.enableTasks ? [
|
|
168597
|
-
|
|
168598
|
-
|
|
169034
|
+
path322.join(metaprojectRoot, "flows"),
|
|
169035
|
+
path322.join(metaprojectRoot, "skills", "flow")
|
|
168599
169036
|
] : [],
|
|
168600
169037
|
...modules.enableSecurity ? [
|
|
168601
|
-
|
|
169038
|
+
path322.join(metaprojectRoot, "core", "security")
|
|
168602
169039
|
] : [],
|
|
168603
169040
|
...modules.enableSac ? [
|
|
168604
|
-
|
|
169041
|
+
path322.join(metaprojectRoot, "skills", "sac")
|
|
168605
169042
|
] : []
|
|
168606
169043
|
];
|
|
168607
169044
|
await Promise.all(dirs.map((dir) => {
|
|
@@ -168610,22 +169047,22 @@ async function createServiceDirs(metaprojectRoot, modules) {
|
|
|
168610
169047
|
}));
|
|
168611
169048
|
}
|
|
168612
169049
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
168613
|
-
const gdgraphCoreRoot =
|
|
169050
|
+
const gdgraphCoreRoot = path322.join(metaprojectRoot, "core", "gdgraph");
|
|
168614
169051
|
{
|
|
168615
169052
|
const { root, rel } = containFromMetaprojectPath(gdgraphCoreRoot);
|
|
168616
169053
|
await mkdirContained(root, rel);
|
|
168617
169054
|
}
|
|
168618
169055
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
168619
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
169056
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path322.join(gdgraphCoreRoot, file));
|
|
168620
169057
|
}
|
|
168621
|
-
await writeTextIfChanged4(
|
|
169058
|
+
await writeTextIfChanged4(path322.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
168622
169059
|
}
|
|
168623
169060
|
async function prePushHasSecurityBlock2(projectRoot) {
|
|
168624
169061
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
168625
169062
|
if (!hooksRoot) {
|
|
168626
169063
|
return false;
|
|
168627
169064
|
}
|
|
168628
|
-
const hookPath =
|
|
169065
|
+
const hookPath = path322.join(hooksRoot, "pre-push");
|
|
168629
169066
|
if (!await pathExists(hookPath)) {
|
|
168630
169067
|
return false;
|
|
168631
169068
|
}
|
|
@@ -168640,7 +169077,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
168640
169077
|
return (await readFile164(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
168641
169078
|
}
|
|
168642
169079
|
async function readManifest5(metaprojectRoot) {
|
|
168643
|
-
const manifestPath =
|
|
169080
|
+
const manifestPath = path322.join(metaprojectRoot, "metaproject.json");
|
|
168644
169081
|
if (!await pathExists(manifestPath)) {
|
|
168645
169082
|
return {
|
|
168646
169083
|
exists: false,
|
|
@@ -168709,7 +169146,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
168709
169146
|
}
|
|
168710
169147
|
async function anyPathExists(root, candidates) {
|
|
168711
169148
|
for (const candidate of candidates) {
|
|
168712
|
-
if (await pathExists(
|
|
169149
|
+
if (await pathExists(path322.join(root, candidate))) {
|
|
168713
169150
|
return true;
|
|
168714
169151
|
}
|
|
168715
169152
|
}
|
|
@@ -168732,13 +169169,13 @@ function parseUpdateArgs(args) {
|
|
|
168732
169169
|
};
|
|
168733
169170
|
}
|
|
168734
169171
|
async function runPostUpdateHooks(projectRoot) {
|
|
168735
|
-
const hooksDir =
|
|
169172
|
+
const hooksDir = path322.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
168736
169173
|
if (!await pathExists(hooksDir)) {
|
|
168737
169174
|
return;
|
|
168738
169175
|
}
|
|
168739
|
-
const entries = (await
|
|
169176
|
+
const entries = (await readdir59(hooksDir)).sort();
|
|
168740
169177
|
for (const entry of entries) {
|
|
168741
|
-
const hookPath =
|
|
169178
|
+
const hookPath = path322.join(hooksDir, entry);
|
|
168742
169179
|
try {
|
|
168743
169180
|
await accessExecutable(hookPath);
|
|
168744
169181
|
} catch {
|
|
@@ -168799,12 +169236,12 @@ async function copyFileIfChanged2(from, to) {
|
|
|
168799
169236
|
}
|
|
168800
169237
|
function runtimeSourcePath2(relativePath) {
|
|
168801
169238
|
const directPath = fileURLToPath10(new URL(relativePath, import.meta.url));
|
|
168802
|
-
if (
|
|
169239
|
+
if (existsSync51(directPath)) {
|
|
168803
169240
|
return directPath;
|
|
168804
169241
|
}
|
|
168805
169242
|
if (relativePath.startsWith("../")) {
|
|
168806
|
-
const packagedSourcePath =
|
|
168807
|
-
if (
|
|
169243
|
+
const packagedSourcePath = path322.join(path322.dirname(fileURLToPath10(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
169244
|
+
if (existsSync51(packagedSourcePath)) {
|
|
168808
169245
|
return packagedSourcePath;
|
|
168809
169246
|
}
|
|
168810
169247
|
}
|
|
@@ -168835,7 +169272,7 @@ function printHelp24() {
|
|
|
168835
169272
|
|
|
168836
169273
|
// src/commands/dashboard.ts
|
|
168837
169274
|
import { spawn as spawn10 } from "child_process";
|
|
168838
|
-
import
|
|
169275
|
+
import path323 from "path";
|
|
168839
169276
|
init_args();
|
|
168840
169277
|
init_ui();
|
|
168841
169278
|
async function dashboardCommand(args = []) {
|
|
@@ -168847,7 +169284,7 @@ async function dashboardCommand(args = []) {
|
|
|
168847
169284
|
}
|
|
168848
169285
|
if (subcommand === "build") {
|
|
168849
169286
|
const result = await buildDashboard();
|
|
168850
|
-
const rel =
|
|
169287
|
+
const rel = path323.relative(process.cwd(), result.path);
|
|
168851
169288
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
168852
169289
|
note(`Open it: keryx dashboard open`);
|
|
168853
169290
|
return;
|
|
@@ -168855,7 +169292,7 @@ async function dashboardCommand(args = []) {
|
|
|
168855
169292
|
if (subcommand === "open") {
|
|
168856
169293
|
const result = await buildDashboard();
|
|
168857
169294
|
await openFile(result.path);
|
|
168858
|
-
const rel =
|
|
169295
|
+
const rel = path323.relative(process.cwd(), result.path);
|
|
168859
169296
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
168860
169297
|
return;
|
|
168861
169298
|
}
|
|
@@ -168906,7 +169343,7 @@ import { readFileSync as readFileSync27 } from "fs";
|
|
|
168906
169343
|
import { randomUUID as randomUUID46 } from "crypto";
|
|
168907
169344
|
import { mkdir as mkdir69 } from "fs/promises";
|
|
168908
169345
|
import { tmpdir as tmpdir12 } from "os";
|
|
168909
|
-
import
|
|
169346
|
+
import path324 from "path";
|
|
168910
169347
|
import { createInterface as createInterface3 } from "readline";
|
|
168911
169348
|
|
|
168912
169349
|
// src/harness/external-agent-probe.ts
|
|
@@ -169168,7 +169605,7 @@ async function runCommand4(args, deps, log) {
|
|
|
169168
169605
|
const requestApproval = unattended ? undefined : seams.requestApproval ?? terminalApprover();
|
|
169169
169606
|
const timeoutRaw = optionValue(args, "--timeout");
|
|
169170
169607
|
const timeoutMs = timeoutRaw !== undefined && Number.isInteger(Number(timeoutRaw)) && Number(timeoutRaw) > 0 ? Number(timeoutRaw) : gate.config.defaultTimeoutMs;
|
|
169171
|
-
const worktreesDir =
|
|
169608
|
+
const worktreesDir = path324.join(tmpdir12(), "keryx-external-worktrees");
|
|
169172
169609
|
const worktree = seams.worktree ?? createGitWorktreePort({ repoRoot: cwd, worktreesDir });
|
|
169173
169610
|
if (seams.worktree === undefined)
|
|
169174
169611
|
await mkdir69(worktreesDir, { recursive: true });
|
|
@@ -169256,8 +169693,8 @@ init_service8();
|
|
|
169256
169693
|
init_bundled_eval();
|
|
169257
169694
|
init_args();
|
|
169258
169695
|
init_ui();
|
|
169259
|
-
import { existsSync as
|
|
169260
|
-
import
|
|
169696
|
+
import { existsSync as existsSync52, lstatSync as lstatSync7, readFileSync as readFileSync26, writeFileSync as writeFileSync11 } from "fs";
|
|
169697
|
+
import path325 from "path";
|
|
169261
169698
|
function resolveDeps(deps) {
|
|
169262
169699
|
return {
|
|
169263
169700
|
cwd: deps.cwd ?? process.cwd(),
|
|
@@ -169590,11 +170027,11 @@ function generateCommand(args, depsIn) {
|
|
|
169590
170027
|
process.exitCode = 1;
|
|
169591
170028
|
return;
|
|
169592
170029
|
}
|
|
169593
|
-
const bundledAgentsRoot =
|
|
169594
|
-
const stacksRoot =
|
|
169595
|
-
const packDir =
|
|
169596
|
-
const packJsonPath =
|
|
169597
|
-
if (!
|
|
170030
|
+
const bundledAgentsRoot = path325.join(depsIn.bundledRoot ?? defaultBundledRoot(), "agents");
|
|
170031
|
+
const stacksRoot = path325.join(bundledAgentsRoot, "..", "stacks");
|
|
170032
|
+
const packDir = path325.join(stacksRoot, stackId);
|
|
170033
|
+
const packJsonPath = path325.join(packDir, "pack.json");
|
|
170034
|
+
if (!existsSync52(packJsonPath)) {
|
|
169598
170035
|
error(`No stack pack "${stackId}" found at ${packJsonPath}.`);
|
|
169599
170036
|
process.exitCode = 1;
|
|
169600
170037
|
return;
|
|
@@ -169640,9 +170077,9 @@ function generateCommand(args, depsIn) {
|
|
|
169640
170077
|
}
|
|
169641
170078
|
const prepared = [];
|
|
169642
170079
|
for (const file of generatedFiles) {
|
|
169643
|
-
const filePath =
|
|
169644
|
-
const relative =
|
|
169645
|
-
if (relative.startsWith("..") ||
|
|
170080
|
+
const filePath = path325.join(bundledAgentsRoot, file.fileName);
|
|
170081
|
+
const relative = path325.relative(bundledAgentsRoot, filePath);
|
|
170082
|
+
if (relative.startsWith("..") || path325.isAbsolute(relative)) {
|
|
169646
170083
|
error(`Refusing to write "${file.fileName}": it resolves outside the bundled agents directory.`);
|
|
169647
170084
|
process.exitCode = 1;
|
|
169648
170085
|
return;
|
|
@@ -169963,7 +170400,7 @@ function printBootstrapHelp() {
|
|
|
169963
170400
|
// src/commands/metrics.ts
|
|
169964
170401
|
init_args();
|
|
169965
170402
|
import { readFile as readFile165 } from "fs/promises";
|
|
169966
|
-
import
|
|
170403
|
+
import path327 from "path";
|
|
169967
170404
|
|
|
169968
170405
|
// src/metrics/benchmark.ts
|
|
169969
170406
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -170780,7 +171217,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
170780
171217
|
|
|
170781
171218
|
// src/metrics/oracle-runner.ts
|
|
170782
171219
|
import { mkdir as mkdir70, writeFile as writeFile48 } from "fs/promises";
|
|
170783
|
-
import
|
|
171220
|
+
import path326 from "path";
|
|
170784
171221
|
|
|
170785
171222
|
// src/metrics/ir.ts
|
|
170786
171223
|
function toIdSet(ids) {
|
|
@@ -171226,9 +171663,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
171226
171663
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
171227
171664
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
171228
171665
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
171229
|
-
const dir =
|
|
171666
|
+
const dir = path326.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
171230
171667
|
await mkdir70(dir, { recursive: true });
|
|
171231
|
-
const write = (name, value) => writeFile48(
|
|
171668
|
+
const write = (name, value) => writeFile48(path326.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
171232
171669
|
`, "utf8");
|
|
171233
171670
|
await Promise.all([
|
|
171234
171671
|
write("inputs.json", bundle.inputs),
|
|
@@ -171268,7 +171705,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171268
171705
|
console.log("# metrics status");
|
|
171269
171706
|
console.log("");
|
|
171270
171707
|
console.log(`root: ${root}`);
|
|
171271
|
-
console.log(`enabled: ${await Bun.file(
|
|
171708
|
+
console.log(`enabled: ${await Bun.file(path327.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
171272
171709
|
const latest = await readLatestPointer(root);
|
|
171273
171710
|
console.log(`latest: ${latest.status}`);
|
|
171274
171711
|
return;
|
|
@@ -171280,7 +171717,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171280
171717
|
process.exitCode = 1;
|
|
171281
171718
|
return;
|
|
171282
171719
|
}
|
|
171283
|
-
const record = JSON.parse(await readFile165(
|
|
171720
|
+
const record = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171284
171721
|
const result = validateRunRecord(record);
|
|
171285
171722
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
171286
171723
|
for (const error of result.errors)
|
|
@@ -171305,7 +171742,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171305
171742
|
process.exitCode = 1;
|
|
171306
171743
|
return;
|
|
171307
171744
|
}
|
|
171308
|
-
const file =
|
|
171745
|
+
const file = path327.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
171309
171746
|
if (!await Bun.file(file).exists()) {
|
|
171310
171747
|
console.error(`Run not found: ${runId}`);
|
|
171311
171748
|
process.exitCode = 1;
|
|
@@ -171322,8 +171759,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171322
171759
|
process.exitCode = 1;
|
|
171323
171760
|
return;
|
|
171324
171761
|
}
|
|
171325
|
-
const a = JSON.parse(await readFile165(
|
|
171326
|
-
const b = JSON.parse(await readFile165(
|
|
171762
|
+
const a = JSON.parse(await readFile165(path327.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
171763
|
+
const b = JSON.parse(await readFile165(path327.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
171327
171764
|
const comparison = compareExecutionRuns(a, b);
|
|
171328
171765
|
console.log(stableJson(comparison));
|
|
171329
171766
|
return;
|
|
@@ -171357,8 +171794,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171357
171794
|
return;
|
|
171358
171795
|
}
|
|
171359
171796
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
171360
|
-
await Bun.write(
|
|
171361
|
-
console.log(`manifest: ${
|
|
171797
|
+
await Bun.write(path327.resolve(projectRoot, out), stableJson(template));
|
|
171798
|
+
console.log(`manifest: ${path327.relative(projectRoot, path327.resolve(projectRoot, out))}`);
|
|
171362
171799
|
return;
|
|
171363
171800
|
}
|
|
171364
171801
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -171372,7 +171809,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171372
171809
|
process.exitCode = 1;
|
|
171373
171810
|
return;
|
|
171374
171811
|
}
|
|
171375
|
-
const raw = JSON.parse(await readFile165(
|
|
171812
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171376
171813
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
171377
171814
|
const result = validatePairedBenchmark(input);
|
|
171378
171815
|
console.log(stableJson(result));
|
|
@@ -171384,7 +171821,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
171384
171821
|
process.exitCode = 1;
|
|
171385
171822
|
}
|
|
171386
171823
|
async function loadAffectedSets(projectRoot, file) {
|
|
171387
|
-
const raw = JSON.parse(await readFile165(
|
|
171824
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171388
171825
|
const map = new Map;
|
|
171389
171826
|
for (const entry of raw.targets ?? []) {
|
|
171390
171827
|
if (typeof entry.target === "string")
|
|
@@ -171455,7 +171892,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
171455
171892
|
let tasks;
|
|
171456
171893
|
let model;
|
|
171457
171894
|
try {
|
|
171458
|
-
const raw = JSON.parse(await readFile165(
|
|
171895
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
|
|
171459
171896
|
tasks = raw.tasks ?? [];
|
|
171460
171897
|
model = raw.model;
|
|
171461
171898
|
} catch (error) {
|
|
@@ -171486,7 +171923,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
171486
171923
|
let cases;
|
|
171487
171924
|
let model;
|
|
171488
171925
|
try {
|
|
171489
|
-
const raw = JSON.parse(await readFile165(
|
|
171926
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
|
|
171490
171927
|
cases = raw.cases ?? [];
|
|
171491
171928
|
model = raw.model;
|
|
171492
171929
|
} catch (error) {
|
|
@@ -171511,7 +171948,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
171511
171948
|
let cases;
|
|
171512
171949
|
let model;
|
|
171513
171950
|
try {
|
|
171514
|
-
const raw = JSON.parse(await readFile165(
|
|
171951
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
|
|
171515
171952
|
cases = raw.cases ?? [];
|
|
171516
171953
|
model = raw.model;
|
|
171517
171954
|
} catch (error) {
|
|
@@ -171536,7 +171973,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
171536
171973
|
let cases;
|
|
171537
171974
|
let model;
|
|
171538
171975
|
try {
|
|
171539
|
-
const raw = JSON.parse(await readFile165(
|
|
171976
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
|
|
171540
171977
|
cases = raw.cases ?? [];
|
|
171541
171978
|
model = raw.model;
|
|
171542
171979
|
} catch (error) {
|
|
@@ -171598,12 +172035,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
171598
172035
|
}
|
|
171599
172036
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
171600
172037
|
if (outDir) {
|
|
171601
|
-
const resolvedOut =
|
|
172038
|
+
const resolvedOut = path327.resolve(projectRoot, outDir);
|
|
171602
172039
|
for (const input of inputs) {
|
|
171603
172040
|
for (const named of input.golds) {
|
|
171604
172041
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
171605
|
-
const dir = await persistEvidenceBundle(
|
|
171606
|
-
console.error(`bundle[${named.kind}]: ${
|
|
172042
|
+
const dir = await persistEvidenceBundle(path327.join(resolvedOut, named.kind), bundle, ladder);
|
|
172043
|
+
console.error(`bundle[${named.kind}]: ${path327.relative(projectRoot, dir)}`);
|
|
171607
172044
|
}
|
|
171608
172045
|
}
|
|
171609
172046
|
}
|
|
@@ -171627,7 +172064,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
171627
172064
|
return allValid;
|
|
171628
172065
|
}
|
|
171629
172066
|
async function loadCoverageMap2(projectRoot, file) {
|
|
171630
|
-
const raw = JSON.parse(await readFile165(
|
|
172067
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171631
172068
|
return raw.coverageMap ?? {};
|
|
171632
172069
|
}
|
|
171633
172070
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -171664,7 +172101,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
171664
172101
|
return result.valid;
|
|
171665
172102
|
}
|
|
171666
172103
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
171667
|
-
const raw = JSON.parse(await readFile165(
|
|
172104
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171668
172105
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
171669
172106
|
}
|
|
171670
172107
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -171701,11 +172138,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
171701
172138
|
return result.valid;
|
|
171702
172139
|
}
|
|
171703
172140
|
async function loadWikiGoldK(projectRoot, file) {
|
|
171704
|
-
const raw = JSON.parse(await readFile165(
|
|
172141
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171705
172142
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
171706
172143
|
}
|
|
171707
172144
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
171708
|
-
const raw = JSON.parse(await readFile165(
|
|
172145
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171709
172146
|
const map = new Map;
|
|
171710
172147
|
for (const entry of raw.targets ?? []) {
|
|
171711
172148
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -171761,7 +172198,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
171761
172198
|
return result.valid;
|
|
171762
172199
|
}
|
|
171763
172200
|
async function loadGdctxFacts(projectRoot, file) {
|
|
171764
|
-
const raw = JSON.parse(await readFile165(
|
|
172201
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
|
|
171765
172202
|
const inputs = [];
|
|
171766
172203
|
for (const entry of raw.inputs ?? []) {
|
|
171767
172204
|
if (typeof entry.input === "string") {
|
|
@@ -171799,7 +172236,7 @@ async function collect(projectRoot, args) {
|
|
|
171799
172236
|
process.exitCode = 1;
|
|
171800
172237
|
return;
|
|
171801
172238
|
}
|
|
171802
|
-
const raw = JSON.parse(await readFile165(
|
|
172239
|
+
const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, eventFile), "utf8"));
|
|
171803
172240
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
171804
172241
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
171805
172242
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -171815,11 +172252,11 @@ async function collect(projectRoot, args) {
|
|
|
171815
172252
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
171816
172253
|
});
|
|
171817
172254
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
171818
|
-
console.log(`json: ${
|
|
171819
|
-
console.log(`markdown: ${
|
|
172255
|
+
console.log(`json: ${path327.relative(projectRoot, result.jsonPath)}`);
|
|
172256
|
+
console.log(`markdown: ${path327.relative(projectRoot, result.markdownPath)}`);
|
|
171820
172257
|
}
|
|
171821
172258
|
function metricsRoot(projectRoot) {
|
|
171822
|
-
return
|
|
172259
|
+
return path327.join(projectRoot, ".metaproject", "data", "metrics");
|
|
171823
172260
|
}
|
|
171824
172261
|
function printMetricsHelp() {
|
|
171825
172262
|
console.log(`keryx metrics
|
|
@@ -173236,8 +173673,8 @@ init_contained_write();
|
|
|
173236
173673
|
init_prompt();
|
|
173237
173674
|
init_terminal_safe();
|
|
173238
173675
|
init_agent_hooks();
|
|
173239
|
-
import { existsSync as
|
|
173240
|
-
import
|
|
173676
|
+
import { existsSync as existsSync53, readFileSync as readFileSync28 } from "fs";
|
|
173677
|
+
import path328 from "path";
|
|
173241
173678
|
init_hooks();
|
|
173242
173679
|
init_package();
|
|
173243
173680
|
var CLI_VERSION = package_default.version;
|
|
@@ -173253,10 +173690,10 @@ function optConfigDir(deps) {
|
|
|
173253
173690
|
return deps.configDir !== undefined ? { configDir: deps.configDir } : {};
|
|
173254
173691
|
}
|
|
173255
173692
|
function userHooksPath(homeDir) {
|
|
173256
|
-
return
|
|
173693
|
+
return path328.join(homeDir, ".keryx", "hooks.json");
|
|
173257
173694
|
}
|
|
173258
173695
|
function projectHooksPath(projectRoot) {
|
|
173259
|
-
return
|
|
173696
|
+
return path328.join(projectRoot, ".metaproject", "hooks.json");
|
|
173260
173697
|
}
|
|
173261
173698
|
function fail3(message) {
|
|
173262
173699
|
console.error(message);
|
|
@@ -173377,10 +173814,10 @@ async function runList6(args, deps) {
|
|
|
173377
173814
|
function argv0Resolves(argv0) {
|
|
173378
173815
|
if (argv0 === "keryx")
|
|
173379
173816
|
return true;
|
|
173380
|
-
if (
|
|
173381
|
-
return
|
|
173817
|
+
if (path328.isAbsolute(argv0))
|
|
173818
|
+
return existsSync53(argv0);
|
|
173382
173819
|
const pathEnv = process.env.PATH ?? "";
|
|
173383
|
-
return pathEnv.split(
|
|
173820
|
+
return pathEnv.split(path328.delimiter).filter((dir) => dir.length > 0).some((dir) => existsSync53(path328.join(dir, argv0)));
|
|
173384
173821
|
}
|
|
173385
173822
|
function checkArgvResolutions(registrations) {
|
|
173386
173823
|
const seen = new Set;
|
|
@@ -173835,8 +174272,8 @@ async function writeDocAtomic(filePath, doc, scope, cwd, homeDir) {
|
|
|
173835
174272
|
if (scope === "project") {
|
|
173836
174273
|
await writeContained(cwd, PROJECT_HOOKS_REL, body);
|
|
173837
174274
|
} else {
|
|
173838
|
-
const userConfigDir =
|
|
173839
|
-
if (!
|
|
174275
|
+
const userConfigDir = path328.join(homeDir, ".keryx");
|
|
174276
|
+
if (!existsSync53(userConfigDir)) {
|
|
173840
174277
|
await mkdirContained(homeDir, ".keryx");
|
|
173841
174278
|
}
|
|
173842
174279
|
await writeContained(userConfigDir, "hooks.json", body);
|
|
@@ -174093,7 +174530,7 @@ init_ui();
|
|
|
174093
174530
|
init_args();
|
|
174094
174531
|
init_contained_path();
|
|
174095
174532
|
init_package();
|
|
174096
|
-
import
|
|
174533
|
+
import path337 from "path";
|
|
174097
174534
|
|
|
174098
174535
|
// src/bundle/types.ts
|
|
174099
174536
|
var BUNDLE_FORMAT_VERSION = "1.0.0";
|
|
@@ -174147,13 +174584,13 @@ init_contained_path();
|
|
|
174147
174584
|
init_keryx_home();
|
|
174148
174585
|
init_types3();
|
|
174149
174586
|
import { lstat as lstat18, readFile as readFile166 } from "fs/promises";
|
|
174150
|
-
import
|
|
174587
|
+
import path329 from "path";
|
|
174151
174588
|
var MEMORY_FOLDERS = new Set(MEMORY_TYPES.map((entry) => entry.folder));
|
|
174152
174589
|
function scopeRoot(scope, ctx) {
|
|
174153
174590
|
if (scope === "user") {
|
|
174154
174591
|
return userStoreRoot(ctx.env ?? process.env, ctx.homeDir);
|
|
174155
174592
|
}
|
|
174156
|
-
return
|
|
174593
|
+
return path329.join(ctx.projectRoot, ".metaproject");
|
|
174157
174594
|
}
|
|
174158
174595
|
var CONTROL_CHAR_RE = /[\x00-\x1f\x7f\u2028\u2029]/;
|
|
174159
174596
|
var MARKER_FORGING_RE = /[<>`]|<!--|-->/;
|
|
@@ -174342,10 +174779,10 @@ async function targetFor(entry, targetScope, ctx) {
|
|
|
174342
174779
|
return { ok: true, absolutePath: containment.absolutePath };
|
|
174343
174780
|
}
|
|
174344
174781
|
async function resolveContainedPathForWrite(root, relPath) {
|
|
174345
|
-
const absolute =
|
|
174346
|
-
const rootResolved =
|
|
174347
|
-
const relative =
|
|
174348
|
-
if (relative === "" || relative === ".." || relative.startsWith(`..${
|
|
174782
|
+
const absolute = path329.resolve(root, relPath);
|
|
174783
|
+
const rootResolved = path329.resolve(root);
|
|
174784
|
+
const relative = path329.relative(rootResolved, absolute);
|
|
174785
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path329.sep}`) || path329.isAbsolute(relative)) {
|
|
174349
174786
|
return {
|
|
174350
174787
|
ok: false,
|
|
174351
174788
|
refusal: { reason: BUNDLE_REFUSAL.pathEscape, path: relPath, message: `${relPath} resolves outside the scope root` }
|
|
@@ -174354,7 +174791,7 @@ async function resolveContainedPathForWrite(root, relPath) {
|
|
|
174354
174791
|
return { ok: true, absolutePath: absolute };
|
|
174355
174792
|
}
|
|
174356
174793
|
async function refuseSymlinkChain(root, relPath) {
|
|
174357
|
-
const rootResolved =
|
|
174794
|
+
const rootResolved = path329.resolve(root);
|
|
174358
174795
|
try {
|
|
174359
174796
|
const rootStat = await lstat18(rootResolved);
|
|
174360
174797
|
if (rootStat.isSymbolicLink()) {
|
|
@@ -174367,7 +174804,7 @@ async function refuseSymlinkChain(root, relPath) {
|
|
|
174367
174804
|
const segments = relPath.split("/");
|
|
174368
174805
|
let current = rootResolved;
|
|
174369
174806
|
for (const segment of segments) {
|
|
174370
|
-
current =
|
|
174807
|
+
current = path329.join(current, segment);
|
|
174371
174808
|
try {
|
|
174372
174809
|
const stat = await lstat18(current);
|
|
174373
174810
|
if (stat.isSymbolicLink()) {
|
|
@@ -174395,8 +174832,8 @@ async function readTargetFile(absolutePath) {
|
|
|
174395
174832
|
}
|
|
174396
174833
|
}
|
|
174397
174834
|
// src/bundle/archive.ts
|
|
174398
|
-
import { readdir as
|
|
174399
|
-
import
|
|
174835
|
+
import { readdir as readdir60, readFile as readFile167, lstat as lstat19 } from "fs/promises";
|
|
174836
|
+
import path330 from "path";
|
|
174400
174837
|
import { gzipSync, gunzipSync } from "zlib";
|
|
174401
174838
|
var BLOCK = 512;
|
|
174402
174839
|
var BUNDLE_MANIFEST_NAME = "bundle.json";
|
|
@@ -174487,11 +174924,11 @@ function buildBundleArchive(files, manifestBytes) {
|
|
|
174487
174924
|
const dirs = new Set;
|
|
174488
174925
|
const sortedPaths = [...files.keys()].sort();
|
|
174489
174926
|
for (const p of sortedPaths) {
|
|
174490
|
-
let dir =
|
|
174927
|
+
let dir = path330.dirname(p);
|
|
174491
174928
|
const segs = [];
|
|
174492
174929
|
while (dir !== "." && dir !== "/" && dir !== "") {
|
|
174493
174930
|
segs.unshift(dir);
|
|
174494
|
-
dir =
|
|
174931
|
+
dir = path330.dirname(dir);
|
|
174495
174932
|
}
|
|
174496
174933
|
for (const d of segs)
|
|
174497
174934
|
dirs.add(d);
|
|
@@ -174559,7 +174996,7 @@ function parseUstar(tar, limits) {
|
|
|
174559
174996
|
const name = prefix.length > 0 ? `${prefix}/${nameRaw}` : nameRaw;
|
|
174560
174997
|
const typeflag = String.fromCharCode(header[156] ?? 0);
|
|
174561
174998
|
const size = readOctal(header, 124, 12);
|
|
174562
|
-
if (
|
|
174999
|
+
if (path330.isAbsolute(name) || name.split("/").includes("..") || name.length === 0) {
|
|
174563
175000
|
return { ok: false, refusal: { reason: BUNDLE_REFUSAL.archiveInvalid, path: name, message: `unsafe entry name: ${name}` } };
|
|
174564
175001
|
}
|
|
174565
175002
|
const isDir = typeflag === "5";
|
|
@@ -174621,9 +175058,9 @@ async function openBundle(bundlePath, limits) {
|
|
|
174621
175058
|
let entryCount = 0;
|
|
174622
175059
|
let totalBytes = 0;
|
|
174623
175060
|
async function walk(dir, relPrefix) {
|
|
174624
|
-
const entries = await
|
|
175061
|
+
const entries = await readdir60(dir, { withFileTypes: true });
|
|
174625
175062
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
174626
|
-
const abs =
|
|
175063
|
+
const abs = path330.join(dir, entry.name);
|
|
174627
175064
|
const rel = relPrefix.length > 0 ? `${relPrefix}/${entry.name}` : entry.name;
|
|
174628
175065
|
const entryStat = await lstat19(abs);
|
|
174629
175066
|
if (entryStat.isSymbolicLink()) {
|
|
@@ -175010,9 +175447,9 @@ init_validator();
|
|
|
175010
175447
|
init_frontmatter();
|
|
175011
175448
|
init_types3();
|
|
175012
175449
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
175013
|
-
import { mkdir as mkdir71, readdir as
|
|
175014
|
-
import { existsSync as
|
|
175015
|
-
import
|
|
175450
|
+
import { mkdir as mkdir71, readdir as readdir61, readFile as readFile168, writeFile as writeFile50, lstat as lstat20 } from "fs/promises";
|
|
175451
|
+
import { existsSync as existsSync54 } from "fs";
|
|
175452
|
+
import path331 from "path";
|
|
175016
175453
|
// docs/requirements/keryx-agent-platform-expansion/schemas/hook-config.schema.json
|
|
175017
175454
|
var hook_config_schema_default2 = {
|
|
175018
175455
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -175556,12 +175993,12 @@ function contentDigestBundleId(scope, entries) {
|
|
|
175556
175993
|
async function walkFiles2(dir, relPrefix, out, skipped) {
|
|
175557
175994
|
let entries;
|
|
175558
175995
|
try {
|
|
175559
|
-
entries = await
|
|
175996
|
+
entries = await readdir61(dir, { withFileTypes: true });
|
|
175560
175997
|
} catch {
|
|
175561
175998
|
return;
|
|
175562
175999
|
}
|
|
175563
176000
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
175564
|
-
const abs =
|
|
176001
|
+
const abs = path331.join(dir, entry.name);
|
|
175565
176002
|
const rel = relPrefix.length > 0 ? `${relPrefix}/${entry.name}` : entry.name;
|
|
175566
176003
|
const st = await lstat20(abs);
|
|
175567
176004
|
if (st.isSymbolicLink()) {
|
|
@@ -175578,8 +176015,8 @@ async function walkFiles2(dir, relPrefix, out, skipped) {
|
|
|
175578
176015
|
async function collectSkills(root, scope, skipped) {
|
|
175579
176016
|
const candidates = [];
|
|
175580
176017
|
for (const baseDirName of scope === "project" ? ["skills", "project-skills"] : ["skills"]) {
|
|
175581
|
-
const base =
|
|
175582
|
-
if (!
|
|
176018
|
+
const base = path331.join(root, baseDirName);
|
|
176019
|
+
if (!existsSync54(base))
|
|
175583
176020
|
continue;
|
|
175584
176021
|
const files = [];
|
|
175585
176022
|
await walkFiles2(base, baseDirName, files, skipped);
|
|
@@ -175591,26 +176028,26 @@ async function collectSkills(root, scope, skipped) {
|
|
|
175591
176028
|
async function collectRules(root, scope, skipped) {
|
|
175592
176029
|
if (scope === "user")
|
|
175593
176030
|
return [];
|
|
175594
|
-
const base =
|
|
175595
|
-
if (!
|
|
176031
|
+
const base = path331.join(root, "rules");
|
|
176032
|
+
if (!existsSync54(base))
|
|
175596
176033
|
return [];
|
|
175597
176034
|
const files = [];
|
|
175598
176035
|
await walkFiles2(base, "rules", files, skipped);
|
|
175599
176036
|
return files.filter((f) => /\.(md|mdc)$/.test(f.path)).map((f) => ({ bundlePath: f.path, kind: "rule", absolutePath: f.abs }));
|
|
175600
176037
|
}
|
|
175601
176038
|
async function collectAgents(root, skipped) {
|
|
175602
|
-
const base =
|
|
175603
|
-
if (!
|
|
176039
|
+
const base = path331.join(root, "agents");
|
|
176040
|
+
if (!existsSync54(base))
|
|
175604
176041
|
return [];
|
|
175605
176042
|
let entries;
|
|
175606
176043
|
try {
|
|
175607
|
-
entries = await
|
|
176044
|
+
entries = await readdir61(base, { withFileTypes: true });
|
|
175608
176045
|
} catch {
|
|
175609
176046
|
return [];
|
|
175610
176047
|
}
|
|
175611
176048
|
const candidates = [];
|
|
175612
176049
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
175613
|
-
const abs =
|
|
176050
|
+
const abs = path331.join(base, entry.name);
|
|
175614
176051
|
const st = await lstat20(abs);
|
|
175615
176052
|
if (st.isSymbolicLink()) {
|
|
175616
176053
|
skipped.push({ path: `agents/${entry.name}`, reason: "symlink" });
|
|
@@ -175625,17 +176062,17 @@ async function collectAgents(root, skipped) {
|
|
|
175625
176062
|
async function collectMemory(root, skipped) {
|
|
175626
176063
|
const candidates = [];
|
|
175627
176064
|
for (const { folder } of MEMORY_TYPES) {
|
|
175628
|
-
const base =
|
|
175629
|
-
if (!
|
|
176065
|
+
const base = path331.join(root, "memory", folder);
|
|
176066
|
+
if (!existsSync54(base))
|
|
175630
176067
|
continue;
|
|
175631
176068
|
let entries;
|
|
175632
176069
|
try {
|
|
175633
|
-
entries = await
|
|
176070
|
+
entries = await readdir61(base, { withFileTypes: true });
|
|
175634
176071
|
} catch {
|
|
175635
176072
|
continue;
|
|
175636
176073
|
}
|
|
175637
176074
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
175638
|
-
const abs =
|
|
176075
|
+
const abs = path331.join(base, entry.name);
|
|
175639
176076
|
const st = await lstat20(abs);
|
|
175640
176077
|
if (st.isSymbolicLink()) {
|
|
175641
176078
|
skipped.push({ path: `memory/${folder}/${entry.name}`, reason: "symlink" });
|
|
@@ -175649,8 +176086,8 @@ async function collectMemory(root, skipped) {
|
|
|
175649
176086
|
return candidates;
|
|
175650
176087
|
}
|
|
175651
176088
|
async function collectHookConfig(root) {
|
|
175652
|
-
const abs =
|
|
175653
|
-
if (!
|
|
176089
|
+
const abs = path331.join(root, "hooks.json");
|
|
176090
|
+
if (!existsSync54(abs))
|
|
175654
176091
|
return [];
|
|
175655
176092
|
const st = await lstat20(abs);
|
|
175656
176093
|
if (st.isSymbolicLink() || !st.isFile())
|
|
@@ -175659,19 +176096,19 @@ async function collectHookConfig(root) {
|
|
|
175659
176096
|
}
|
|
175660
176097
|
async function collectLearnedPatterns(root, scope) {
|
|
175661
176098
|
const relDir = scope === "user" ? "learning/patterns" : "data/learning/candidates";
|
|
175662
|
-
const base =
|
|
175663
|
-
if (!
|
|
176099
|
+
const base = path331.join(root, ...relDir.split("/"));
|
|
176100
|
+
if (!existsSync54(base))
|
|
175664
176101
|
return [];
|
|
175665
176102
|
let entries;
|
|
175666
176103
|
try {
|
|
175667
|
-
entries = await
|
|
176104
|
+
entries = await readdir61(base, { withFileTypes: true });
|
|
175668
176105
|
} catch {
|
|
175669
176106
|
return [];
|
|
175670
176107
|
}
|
|
175671
176108
|
const candidates = [];
|
|
175672
176109
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
175673
176110
|
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
175674
|
-
candidates.push({ bundlePath: `${relDir}/${entry.name}`, kind: "learned-pattern", absolutePath:
|
|
176111
|
+
candidates.push({ bundlePath: `${relDir}/${entry.name}`, kind: "learned-pattern", absolutePath: path331.join(base, entry.name) });
|
|
175675
176112
|
}
|
|
175676
176113
|
}
|
|
175677
176114
|
return candidates;
|
|
@@ -175704,7 +176141,7 @@ function rewriteAgentOrigin(content, sourceRef) {
|
|
|
175704
176141
|
}
|
|
175705
176142
|
async function outDirIsEmpty(out) {
|
|
175706
176143
|
try {
|
|
175707
|
-
const entries = await
|
|
176144
|
+
const entries = await readdir61(out);
|
|
175708
176145
|
return entries.length === 0;
|
|
175709
176146
|
} catch {
|
|
175710
176147
|
return true;
|
|
@@ -175712,10 +176149,10 @@ async function outDirIsEmpty(out) {
|
|
|
175712
176149
|
}
|
|
175713
176150
|
async function writeDirectoryBundle(out, manifestBytes, files) {
|
|
175714
176151
|
await mkdir71(out, { recursive: true });
|
|
175715
|
-
await writeFile50(
|
|
176152
|
+
await writeFile50(path331.join(out, "bundle.json"), manifestBytes);
|
|
175716
176153
|
for (const [rel, bytes] of files) {
|
|
175717
|
-
const abs =
|
|
175718
|
-
await mkdir71(
|
|
176154
|
+
const abs = path331.join(out, ...rel.split("/"));
|
|
176155
|
+
await mkdir71(path331.dirname(abs), { recursive: true });
|
|
175719
176156
|
await writeFile50(abs, bytes);
|
|
175720
176157
|
}
|
|
175721
176158
|
}
|
|
@@ -175843,16 +176280,16 @@ async function exportBundle(opts) {
|
|
|
175843
176280
|
const manifestBytes = Buffer.from(serializeManifest(manifest), "utf8");
|
|
175844
176281
|
const isArchive = opts.out.endsWith(".tar.gz") || opts.out.endsWith(".tgz");
|
|
175845
176282
|
if (isArchive) {
|
|
175846
|
-
if (
|
|
176283
|
+
if (existsSync54(opts.out)) {
|
|
175847
176284
|
return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.notABundle, message: `${opts.out} already exists` }] };
|
|
175848
176285
|
}
|
|
175849
176286
|
const archive = buildBundleArchive(files, manifestBytes);
|
|
175850
176287
|
if (!archive.ok)
|
|
175851
176288
|
return { ok: false, refusals: [archive.refusal] };
|
|
175852
|
-
await mkdir71(
|
|
176289
|
+
await mkdir71(path331.dirname(opts.out), { recursive: true });
|
|
175853
176290
|
await writeFile50(opts.out, archive.value);
|
|
175854
176291
|
} else {
|
|
175855
|
-
if (
|
|
176292
|
+
if (existsSync54(opts.out) && !await outDirIsEmpty(opts.out)) {
|
|
175856
176293
|
return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.notABundle, message: `${opts.out} already exists and is not empty` }] };
|
|
175857
176294
|
}
|
|
175858
176295
|
await writeDirectoryBundle(opts.out, manifestBytes, files);
|
|
@@ -175899,16 +176336,16 @@ async function verifyBundlePath(bundlePath) {
|
|
|
175899
176336
|
// src/bundle/applied-state.ts
|
|
175900
176337
|
init_contained_write();
|
|
175901
176338
|
import { readFile as readFile169 } from "fs/promises";
|
|
175902
|
-
import
|
|
176339
|
+
import path332 from "path";
|
|
175903
176340
|
var APPLIED_STATE_SCHEMA_VERSION = 2;
|
|
175904
176341
|
function emptyState() {
|
|
175905
176342
|
return { schemaVersion: APPLIED_STATE_SCHEMA_VERSION, entries: {} };
|
|
175906
176343
|
}
|
|
175907
176344
|
function appliedStatePath(scope, ctx) {
|
|
175908
176345
|
if (scope === "user") {
|
|
175909
|
-
return
|
|
176346
|
+
return path332.join(scopeRoot("user", ctx), "bundles", "applied-state.json");
|
|
175910
176347
|
}
|
|
175911
|
-
return
|
|
176348
|
+
return path332.join(scopeRoot(scope, ctx), "data", "bundles", "applied-state.json");
|
|
175912
176349
|
}
|
|
175913
176350
|
function isPlainEntryShape(value) {
|
|
175914
176351
|
if (typeof value !== "object" || value === null)
|
|
@@ -175990,14 +176427,14 @@ async function readAppliedState(ledgerPath) {
|
|
|
175990
176427
|
return { ok: false, reason: "corrupt-ledger", message: `applied-state ledger at ${ledgerPath} has an unrecognized shape` };
|
|
175991
176428
|
}
|
|
175992
176429
|
function containedRootAndRel(ledgerPath) {
|
|
175993
|
-
const marker = `${
|
|
176430
|
+
const marker = `${path332.sep}.metaproject${path332.sep}`;
|
|
175994
176431
|
const idx = ledgerPath.indexOf(marker);
|
|
175995
176432
|
if (idx >= 0) {
|
|
175996
|
-
return { root: ledgerPath.slice(0, idx), rel: ledgerPath.slice(idx +
|
|
176433
|
+
return { root: ledgerPath.slice(0, idx), rel: ledgerPath.slice(idx + path332.sep.length).split(path332.sep).join("/") };
|
|
175997
176434
|
}
|
|
175998
|
-
const bundlesDir =
|
|
175999
|
-
const root =
|
|
176000
|
-
const rel =
|
|
176435
|
+
const bundlesDir = path332.dirname(ledgerPath);
|
|
176436
|
+
const root = path332.dirname(bundlesDir);
|
|
176437
|
+
const rel = path332.join(path332.basename(bundlesDir), path332.basename(ledgerPath)).split(path332.sep).join("/");
|
|
176001
176438
|
return { root, rel };
|
|
176002
176439
|
}
|
|
176003
176440
|
async function writeAppliedState(ledgerPath, state) {
|
|
@@ -176018,17 +176455,17 @@ init_service8();
|
|
|
176018
176455
|
init_fs();
|
|
176019
176456
|
init_symlink_safety();
|
|
176020
176457
|
import { lstat as lstat21, mkdir as mkdir72, open as open8, readFile as readFile170 } from "fs/promises";
|
|
176021
|
-
import
|
|
176458
|
+
import path333 from "path";
|
|
176022
176459
|
var PRIVATE_DIR_GITIGNORE = `# keryx:private-dir \u2014 managed by keryx; this directory stays out of version control
|
|
176023
176460
|
*
|
|
176024
176461
|
!.gitignore
|
|
176025
176462
|
`;
|
|
176026
176463
|
async function checkPrivateDirGitignore(dir, root) {
|
|
176027
176464
|
if (root !== undefined) {
|
|
176028
|
-
const rootResolved =
|
|
176029
|
-
const dirResolved =
|
|
176030
|
-
const relative =
|
|
176031
|
-
const relativePosix = relative.split(
|
|
176465
|
+
const rootResolved = path333.resolve(root);
|
|
176466
|
+
const dirResolved = path333.resolve(dir);
|
|
176467
|
+
const relative = path333.relative(rootResolved, dirResolved);
|
|
176468
|
+
const relativePosix = relative.split(path333.sep).join("/");
|
|
176032
176469
|
const refusal = await refuseEscapingSymlink(rootResolved, relativePosix);
|
|
176033
176470
|
if (refusal !== undefined) {
|
|
176034
176471
|
return {
|
|
@@ -176058,7 +176495,7 @@ async function checkPrivateDirGitignore(dir, root) {
|
|
|
176058
176495
|
message: `${dir} is a symlink; keryx will not manage a private directory reached through a symlink.`
|
|
176059
176496
|
};
|
|
176060
176497
|
}
|
|
176061
|
-
const gitignorePath =
|
|
176498
|
+
const gitignorePath = path333.join(dir, ".gitignore");
|
|
176062
176499
|
let stats;
|
|
176063
176500
|
try {
|
|
176064
176501
|
stats = await lstat21(gitignorePath);
|
|
@@ -176110,7 +176547,7 @@ async function ensurePrivateDirGitignore(dir, root) {
|
|
|
176110
176547
|
if (!check.ok || check.action === "present") {
|
|
176111
176548
|
return check;
|
|
176112
176549
|
}
|
|
176113
|
-
const gitignorePath =
|
|
176550
|
+
const gitignorePath = path333.join(dir, ".gitignore");
|
|
176114
176551
|
await mkdir72(dir, { recursive: true });
|
|
176115
176552
|
try {
|
|
176116
176553
|
const handle = await open8(gitignorePath, "wx");
|
|
@@ -176507,7 +176944,7 @@ async function planBundleImport(opts) {
|
|
|
176507
176944
|
init_service4();
|
|
176508
176945
|
import { mkdtemp as mkdtemp6, mkdir as mkdir73, rm as rm20, writeFile as writeFile51 } from "fs/promises";
|
|
176509
176946
|
import { tmpdir as tmpdir13 } from "os";
|
|
176510
|
-
import
|
|
176947
|
+
import path334 from "path";
|
|
176511
176948
|
function entriesToStage(plan) {
|
|
176512
176949
|
return plan.entries.filter((e) => e.bucket === "new" || e.bucket === "update" || e.bucket === "conflict" && e.forced);
|
|
176513
176950
|
}
|
|
@@ -176517,12 +176954,12 @@ async function auditBundlePlan(plan, opts = {}) {
|
|
|
176517
176954
|
return { ok: true, refusals: [], report: null };
|
|
176518
176955
|
}
|
|
176519
176956
|
const runAudit = opts.runAudit ?? runHarnessAudit;
|
|
176520
|
-
const stagingDir = await mkdtemp6(
|
|
176957
|
+
const stagingDir = await mkdtemp6(path334.join(tmpdir13(), "keryx-bundle-audit-"));
|
|
176521
176958
|
try {
|
|
176522
176959
|
try {
|
|
176523
176960
|
for (const entry of toStage) {
|
|
176524
|
-
const abs =
|
|
176525
|
-
await mkdir73(
|
|
176961
|
+
const abs = path334.join(stagingDir, ...entry.path.split("/"));
|
|
176962
|
+
await mkdir73(path334.dirname(abs), { recursive: true });
|
|
176526
176963
|
await writeFile51(abs, entry.bytes);
|
|
176527
176964
|
}
|
|
176528
176965
|
} catch (err) {
|
|
@@ -176553,7 +176990,7 @@ async function auditBundlePlan(plan, opts = {}) {
|
|
|
176553
176990
|
// src/bundle/apply.ts
|
|
176554
176991
|
init_contained_write();
|
|
176555
176992
|
import { mkdir as mkdir74, rmdir as rmdir2, stat as stat33 } from "fs/promises";
|
|
176556
|
-
import
|
|
176993
|
+
import path335 from "path";
|
|
176557
176994
|
init_keryx_home();
|
|
176558
176995
|
init_checksum();
|
|
176559
176996
|
class ApplyAbort extends Error {
|
|
@@ -176633,7 +177070,7 @@ async function applyBundlePlan(plan, auditResult, opts = {}) {
|
|
|
176633
177070
|
throw new ApplyAbort({ ...previousFile.refusal, path: entry.path });
|
|
176634
177071
|
}
|
|
176635
177072
|
const previousBytes = previousFile.bytes ?? null;
|
|
176636
|
-
const createdDirs = await mkdirRecordingCreated(
|
|
177073
|
+
const createdDirs = await mkdirRecordingCreated(path335.dirname(entry.targetPath));
|
|
176637
177074
|
for (const dir of [...createdDirs].reverse())
|
|
176638
177075
|
rollback.push({ kind: "dir", absolutePath: dir });
|
|
176639
177076
|
rollback.push({ kind: "file", root, rel: entry.targetRelative, previousBytes });
|
|
@@ -176716,14 +177153,14 @@ async function applyBundlePlan(plan, auditResult, opts = {}) {
|
|
|
176716
177153
|
}
|
|
176717
177154
|
async function mkdirRecordingCreated(dir) {
|
|
176718
177155
|
const missing = [];
|
|
176719
|
-
let current =
|
|
177156
|
+
let current = path335.resolve(dir);
|
|
176720
177157
|
for (;; ) {
|
|
176721
177158
|
try {
|
|
176722
177159
|
await stat33(current);
|
|
176723
177160
|
break;
|
|
176724
177161
|
} catch {
|
|
176725
177162
|
missing.push(current);
|
|
176726
|
-
const parent =
|
|
177163
|
+
const parent = path335.dirname(current);
|
|
176727
177164
|
if (parent === current)
|
|
176728
177165
|
break;
|
|
176729
177166
|
current = parent;
|
|
@@ -176738,9 +177175,9 @@ function resolveLedgerPathForEntry(entry, opts) {
|
|
|
176738
177175
|
if (entry.targetScope === "user") {
|
|
176739
177176
|
return userStorePaths(opts.env ?? process.env, opts.homeDir).appliedState;
|
|
176740
177177
|
}
|
|
176741
|
-
const metaprojectIdx = entry.targetPath.indexOf(`${
|
|
176742
|
-
const metaprojectRoot = metaprojectIdx === -1 ?
|
|
176743
|
-
return
|
|
177178
|
+
const metaprojectIdx = entry.targetPath.indexOf(`${path335.sep}.metaproject${path335.sep}`);
|
|
177179
|
+
const metaprojectRoot = metaprojectIdx === -1 ? path335.join(path335.dirname(entry.targetPath), ".metaproject") : entry.targetPath.slice(0, metaprojectIdx + `${path335.sep}.metaproject`.length);
|
|
177180
|
+
return path335.join(metaprojectRoot, "data", "bundles", "applied-state.json");
|
|
176744
177181
|
}
|
|
176745
177182
|
// src/bundle/inspect.ts
|
|
176746
177183
|
init_matrix();
|
|
@@ -176792,28 +177229,28 @@ function defaultCapabilityLookup() {
|
|
|
176792
177229
|
}
|
|
176793
177230
|
// src/bundle/uninstall.ts
|
|
176794
177231
|
init_contained_write();
|
|
176795
|
-
import
|
|
177232
|
+
import path336 from "path";
|
|
176796
177233
|
init_checksum();
|
|
176797
177234
|
var PROTECTED_KIND_ROOTS = new Set(["skills", "project-skills", "rules", "agents", "memory", "learning", "data/learning"]);
|
|
176798
177235
|
function isContained(root, candidate) {
|
|
176799
|
-
const rel =
|
|
176800
|
-
return rel === "" || !rel.startsWith(`..${
|
|
177236
|
+
const rel = path336.relative(root, candidate);
|
|
177237
|
+
return rel === "" || !rel.startsWith(`..${path336.sep}`) && rel !== ".." && !path336.isAbsolute(rel);
|
|
176801
177238
|
}
|
|
176802
177239
|
async function removeNowEmptyParents(root, relFilePath) {
|
|
176803
|
-
let relDir =
|
|
177240
|
+
let relDir = path336.posix.dirname(relFilePath);
|
|
176804
177241
|
while (relDir !== "." && relDir !== "/" && relDir !== "") {
|
|
176805
177242
|
if (PROTECTED_KIND_ROOTS.has(relDir))
|
|
176806
177243
|
break;
|
|
176807
177244
|
const removed = await rmdirIfEmptyContained(root, relDir).catch(() => false);
|
|
176808
177245
|
if (!removed)
|
|
176809
177246
|
break;
|
|
176810
|
-
relDir =
|
|
177247
|
+
relDir = path336.posix.dirname(relDir);
|
|
176811
177248
|
}
|
|
176812
177249
|
}
|
|
176813
177250
|
async function uninstallBundle(opts) {
|
|
176814
177251
|
const ctx = { projectRoot: opts.projectRoot, env: opts.env, homeDir: opts.homeDir };
|
|
176815
177252
|
const root = scopeRoot(opts.targetScope, ctx);
|
|
176816
|
-
const rootResolved =
|
|
177253
|
+
const rootResolved = path336.resolve(root);
|
|
176817
177254
|
const ledgerPath = appliedStatePath(opts.targetScope, ctx);
|
|
176818
177255
|
const ledgerRead = await readAppliedState(ledgerPath);
|
|
176819
177256
|
if (!ledgerRead.ok) {
|
|
@@ -176853,7 +177290,7 @@ async function uninstallBundle(opts) {
|
|
|
176853
177290
|
if (!kindCheck.ok) {
|
|
176854
177291
|
return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.corruptLedger, path: relPath, message: `applied-state ledger record "${relPath}" does not match its recorded kind "${record.kind}": ${kindCheck.refusal.message}` }], removed: [], kept: [], missing: [] };
|
|
176855
177292
|
}
|
|
176856
|
-
const absolute =
|
|
177293
|
+
const absolute = path336.resolve(rootResolved, normalized.path);
|
|
176857
177294
|
if (!isContained(rootResolved, absolute)) {
|
|
176858
177295
|
return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.corruptLedger, path: relPath, message: `applied-state ledger record "${relPath}" resolves outside the scope root` }], removed: [], kept: [], missing: [] };
|
|
176859
177296
|
}
|
|
@@ -177045,7 +177482,7 @@ async function handleExport(args, cwd) {
|
|
|
177045
177482
|
const outcome = await exportBundle({
|
|
177046
177483
|
projectRoot,
|
|
177047
177484
|
scope: scopeArg,
|
|
177048
|
-
out:
|
|
177485
|
+
out: path337.resolve(cwd, out),
|
|
177049
177486
|
include: include.length > 0 ? include : undefined,
|
|
177050
177487
|
kinds: kindsRaw.length > 0 ? kindsRaw : undefined,
|
|
177051
177488
|
bundleId,
|
|
@@ -177092,7 +177529,7 @@ async function handleImport(args, cwd) {
|
|
|
177092
177529
|
}
|
|
177093
177530
|
const projectRoot = resolveBundleProjectRoot(cwd);
|
|
177094
177531
|
if (external) {
|
|
177095
|
-
await handleImportExternal(
|
|
177532
|
+
await handleImportExternal(path337.resolve(cwd, target), projectRoot, { dryRun, json });
|
|
177096
177533
|
return;
|
|
177097
177534
|
}
|
|
177098
177535
|
const targetScopeArg = optionValue([...args], "--target-scope");
|
|
@@ -177119,7 +177556,7 @@ async function handleImport(args, cwd) {
|
|
|
177119
177556
|
return;
|
|
177120
177557
|
}
|
|
177121
177558
|
}
|
|
177122
|
-
const opened = await openBundle(
|
|
177559
|
+
const opened = await openBundle(path337.resolve(cwd, target));
|
|
177123
177560
|
if (!opened.ok) {
|
|
177124
177561
|
printRefusals([opened.refusal], json);
|
|
177125
177562
|
process.exitCode = 1;
|
|
@@ -177297,7 +177734,7 @@ async function handleInspect(args, cwd) {
|
|
|
177297
177734
|
return;
|
|
177298
177735
|
}
|
|
177299
177736
|
const projectRoot = resolveBundleProjectRoot(cwd);
|
|
177300
|
-
const result = await inspectBundle(
|
|
177737
|
+
const result = await inspectBundle(path337.resolve(cwd, bundlePath), {
|
|
177301
177738
|
projectRoot,
|
|
177302
177739
|
targetScope: targetScopeArg
|
|
177303
177740
|
});
|
|
@@ -177341,7 +177778,7 @@ async function handleVerify(args, cwd) {
|
|
|
177341
177778
|
failUsage("keryx bundle verify requires a <bundle> argument (or --external-imports)");
|
|
177342
177779
|
return;
|
|
177343
177780
|
}
|
|
177344
|
-
const outcome = await verifyBundlePath(
|
|
177781
|
+
const outcome = await verifyBundlePath(path337.resolve(cwd, bundlePath));
|
|
177345
177782
|
if (!outcome.ok) {
|
|
177346
177783
|
printRefusals(outcome.refusals, json);
|
|
177347
177784
|
process.exitCode = 1;
|
|
@@ -177469,10 +177906,10 @@ function printUninstallHelp2() {
|
|
|
177469
177906
|
}
|
|
177470
177907
|
|
|
177471
177908
|
// src/commands/learn.ts
|
|
177472
|
-
import { existsSync as
|
|
177909
|
+
import { existsSync as existsSync55 } from "fs";
|
|
177473
177910
|
import { readFile as readFile171 } from "fs/promises";
|
|
177474
177911
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
177475
|
-
import
|
|
177912
|
+
import path338 from "path";
|
|
177476
177913
|
init_contained_path();
|
|
177477
177914
|
init_service9();
|
|
177478
177915
|
var STDIN_DEADLINE_MS2 = 2000;
|
|
@@ -177495,16 +177932,16 @@ function resolveRoot2(deps) {
|
|
|
177495
177932
|
return resolveProjectRoot(deps.cwd ?? process.cwd());
|
|
177496
177933
|
}
|
|
177497
177934
|
function resolveLearnRoot(startDir, env) {
|
|
177498
|
-
const hasMetaproject = (dir) =>
|
|
177935
|
+
const hasMetaproject = (dir) => existsSync55(path338.join(dir, ".metaproject"));
|
|
177499
177936
|
const fromEnv = env.CLAUDE_PROJECT_DIR;
|
|
177500
177937
|
if (typeof fromEnv === "string" && fromEnv.length > 0 && hasMetaproject(fromEnv)) {
|
|
177501
177938
|
return fromEnv;
|
|
177502
177939
|
}
|
|
177503
|
-
let current =
|
|
177940
|
+
let current = path338.resolve(startDir);
|
|
177504
177941
|
for (;; ) {
|
|
177505
177942
|
if (hasMetaproject(current))
|
|
177506
177943
|
return current;
|
|
177507
|
-
const parent =
|
|
177944
|
+
const parent = path338.dirname(current);
|
|
177508
177945
|
if (parent === current)
|
|
177509
177946
|
return;
|
|
177510
177947
|
current = parent;
|
|
@@ -178169,6 +178606,190 @@ async function helpCommand(rest) {
|
|
|
178169
178606
|
process.exitCode = 1;
|
|
178170
178607
|
}
|
|
178171
178608
|
|
|
178609
|
+
// src/lib/group-subcommands.ts
|
|
178610
|
+
var GROUP_SUBCOMMANDS = new Map([
|
|
178611
|
+
["modules", ["status", "list", "enable", "on", "disable", "off", "interactive", "help"]],
|
|
178612
|
+
["projects", ["list", "register", "forget", "help"]],
|
|
178613
|
+
["providers", ["list", "status", "cross-family", "test", "remove"]],
|
|
178614
|
+
["routing", ["list", "set", "unset", "trust", "profile", "stats"]],
|
|
178615
|
+
["external", ["on", "off", "status", "list"]],
|
|
178616
|
+
["auth", ["list", "login", "logout", "status"]],
|
|
178617
|
+
["version", ["check"]],
|
|
178618
|
+
["gdgraph", ["build", "query", "find", "symbol", "symbols", "path", "affected", "repomap", "context", "assets"]],
|
|
178619
|
+
["ctx", ["status", "diff", "rg", "read", "run", "show", "hook", "install-hook", "uninstall-hook"]],
|
|
178620
|
+
[
|
|
178621
|
+
"wiki",
|
|
178622
|
+
[
|
|
178623
|
+
"status",
|
|
178624
|
+
"new",
|
|
178625
|
+
"index",
|
|
178626
|
+
"collect",
|
|
178627
|
+
"check-links",
|
|
178628
|
+
"validate",
|
|
178629
|
+
"freshness",
|
|
178630
|
+
"refresh",
|
|
178631
|
+
"verify",
|
|
178632
|
+
"migrate-markers",
|
|
178633
|
+
"ask",
|
|
178634
|
+
"sections",
|
|
178635
|
+
"enrich",
|
|
178636
|
+
"context",
|
|
178637
|
+
"backlinks"
|
|
178638
|
+
]
|
|
178639
|
+
],
|
|
178640
|
+
["stack", ["detect"]],
|
|
178641
|
+
["health", ["run", "status", "gate", "sources", "explain", "baseline", "trend"]],
|
|
178642
|
+
["metrics", ["status", "validate", "collect", "latest", "show", "compare", "rebuild", "plan", "benchmark"]],
|
|
178643
|
+
["test", ["init", "analyze", "run", "status", "context", "report", "related", "explain", "coverage-map", "suggest"]],
|
|
178644
|
+
["memory", ["new", "index", "search", "supersede", "transition", "assets", "ingest", "check", "reflect", "handoff"]],
|
|
178645
|
+
[
|
|
178646
|
+
"flow",
|
|
178647
|
+
[
|
|
178648
|
+
"init",
|
|
178649
|
+
"list",
|
|
178650
|
+
"status",
|
|
178651
|
+
"freeze",
|
|
178652
|
+
"start",
|
|
178653
|
+
"next",
|
|
178654
|
+
"task",
|
|
178655
|
+
"ac",
|
|
178656
|
+
"check-ac",
|
|
178657
|
+
"owner",
|
|
178658
|
+
"implemented",
|
|
178659
|
+
"complete",
|
|
178660
|
+
"confirm",
|
|
178661
|
+
"recover",
|
|
178662
|
+
"block",
|
|
178663
|
+
"unblock",
|
|
178664
|
+
"check",
|
|
178665
|
+
"renumber",
|
|
178666
|
+
"repair-reviews",
|
|
178667
|
+
"plan",
|
|
178668
|
+
"schema"
|
|
178669
|
+
]
|
|
178670
|
+
],
|
|
178671
|
+
["job", ["init", "status", "step", "document", "complete", "list"]],
|
|
178672
|
+
[
|
|
178673
|
+
"review",
|
|
178674
|
+
[
|
|
178675
|
+
"attach",
|
|
178676
|
+
"start",
|
|
178677
|
+
"ingest",
|
|
178678
|
+
"scope",
|
|
178679
|
+
"floor",
|
|
178680
|
+
"blast-radius",
|
|
178681
|
+
"budget",
|
|
178682
|
+
"tier",
|
|
178683
|
+
"comments",
|
|
178684
|
+
"ci-triage",
|
|
178685
|
+
"conform",
|
|
178686
|
+
"jev-rules",
|
|
178687
|
+
"jev-edit-guard",
|
|
178688
|
+
"jev-risk",
|
|
178689
|
+
"jev-scenarios",
|
|
178690
|
+
"jev-docs",
|
|
178691
|
+
"jev-comments",
|
|
178692
|
+
"jev-contract",
|
|
178693
|
+
"jev-triage",
|
|
178694
|
+
"jev-select",
|
|
178695
|
+
"jev-profile",
|
|
178696
|
+
"learn",
|
|
178697
|
+
"loop",
|
|
178698
|
+
"stack",
|
|
178699
|
+
"reviewers",
|
|
178700
|
+
"import",
|
|
178701
|
+
"status",
|
|
178702
|
+
"complete",
|
|
178703
|
+
"lightweight"
|
|
178704
|
+
]
|
|
178705
|
+
],
|
|
178706
|
+
["rules", ["distill", "sync"]],
|
|
178707
|
+
["standard", ["validate", "doctor", "capabilities", "baseline", "emit"]],
|
|
178708
|
+
[
|
|
178709
|
+
"security",
|
|
178710
|
+
[
|
|
178711
|
+
"status",
|
|
178712
|
+
"scan",
|
|
178713
|
+
"scan-mcp",
|
|
178714
|
+
"audit-harness",
|
|
178715
|
+
"impact-evidence",
|
|
178716
|
+
"check-input",
|
|
178717
|
+
"check-output",
|
|
178718
|
+
"redact",
|
|
178719
|
+
"report",
|
|
178720
|
+
"policy",
|
|
178721
|
+
"incidents",
|
|
178722
|
+
"hooks",
|
|
178723
|
+
"eval"
|
|
178724
|
+
]
|
|
178725
|
+
],
|
|
178726
|
+
["sandbox", ["status"]],
|
|
178727
|
+
["integrations", ["install", "uninstall", "doctor", "matrix"]],
|
|
178728
|
+
[
|
|
178729
|
+
"workspace",
|
|
178730
|
+
[
|
|
178731
|
+
"create",
|
|
178732
|
+
"list",
|
|
178733
|
+
"show",
|
|
178734
|
+
"add-resource",
|
|
178735
|
+
"archive",
|
|
178736
|
+
"remove-resource",
|
|
178737
|
+
"rename",
|
|
178738
|
+
"overview",
|
|
178739
|
+
"read",
|
|
178740
|
+
"propose",
|
|
178741
|
+
"confirm-review",
|
|
178742
|
+
"review",
|
|
178743
|
+
"dismiss-candidate",
|
|
178744
|
+
"handoff",
|
|
178745
|
+
"collaboration",
|
|
178746
|
+
"policy-readiness",
|
|
178747
|
+
"catch-up",
|
|
178748
|
+
"list-proposals",
|
|
178749
|
+
"help"
|
|
178750
|
+
]
|
|
178751
|
+
],
|
|
178752
|
+
["retention", ["status", "sweep"]],
|
|
178753
|
+
["forgetting", ["trail", "lookup"]],
|
|
178754
|
+
["trigger", ["run", "install", "uninstall", "list", "status", "schedule", "resolve"]],
|
|
178755
|
+
["schedule", ["add", "list", "show", "pause", "resume", "run", "remove"]],
|
|
178756
|
+
["governance", ["report", "show"]],
|
|
178757
|
+
["hooks", ["list", "validate", "test", "enable", "disable", "trust", "untrust"]],
|
|
178758
|
+
["bundle", ["export", "import", "inspect", "verify", "uninstall"]],
|
|
178759
|
+
["learn", ["observe", "extract", "list", "review", "accept", "reject", "apply", "promote", "graduate", "prune"]],
|
|
178760
|
+
["sessions", ["list", "fork", "export", "path", "help"]],
|
|
178761
|
+
["session", ["list", "fork", "export", "path", "help"]],
|
|
178762
|
+
["serve", ["status", "token", "config"]],
|
|
178763
|
+
["bus", ["list", "log", "send", "pause", "resume", "prune", "help"]],
|
|
178764
|
+
["dashboard", ["build", "open"]],
|
|
178765
|
+
[
|
|
178766
|
+
"skills",
|
|
178767
|
+
[
|
|
178768
|
+
"catalog",
|
|
178769
|
+
"install",
|
|
178770
|
+
"doctor",
|
|
178771
|
+
"uninstall",
|
|
178772
|
+
"status",
|
|
178773
|
+
"list",
|
|
178774
|
+
"inspect",
|
|
178775
|
+
"route",
|
|
178776
|
+
"create",
|
|
178777
|
+
"generate",
|
|
178778
|
+
"import",
|
|
178779
|
+
"update",
|
|
178780
|
+
"verify",
|
|
178781
|
+
"learn",
|
|
178782
|
+
"export",
|
|
178783
|
+
"sync",
|
|
178784
|
+
"contracts",
|
|
178785
|
+
"scout",
|
|
178786
|
+
"eval",
|
|
178787
|
+
"judge-check",
|
|
178788
|
+
"stocktake"
|
|
178789
|
+
]
|
|
178790
|
+
]
|
|
178791
|
+
]);
|
|
178792
|
+
|
|
178172
178793
|
// src/cli.ts
|
|
178173
178794
|
init_package();
|
|
178174
178795
|
if (import.meta.main) {
|
|
@@ -178178,6 +178799,7 @@ var VERSION2 = package_default.version;
|
|
|
178178
178799
|
var CLI_ROUTES = {
|
|
178179
178800
|
init: initCommand,
|
|
178180
178801
|
help: helpCommand,
|
|
178802
|
+
doctor: doctorCommand2,
|
|
178181
178803
|
status: statusCommand,
|
|
178182
178804
|
modules: modulesCommand,
|
|
178183
178805
|
projects: projectsCommand,
|
|
@@ -178232,6 +178854,15 @@ var CLI_ROUTES = {
|
|
|
178232
178854
|
learn: learnCommand,
|
|
178233
178855
|
"__sandbox-net-forward": sandboxNetForwardCommand
|
|
178234
178856
|
};
|
|
178857
|
+
var MCP_KNOWN_SUBCOMMANDS = [...MCP_CONSUMER_SUBCOMMANDS, "install", "uninstall", "serve"];
|
|
178858
|
+
function knownSubcommandsFor(command) {
|
|
178859
|
+
if (command === "mcp")
|
|
178860
|
+
return MCP_KNOWN_SUBCOMMANDS;
|
|
178861
|
+
return GROUP_SUBCOMMANDS.get(command);
|
|
178862
|
+
}
|
|
178863
|
+
function groupsWithKnownSubcommands() {
|
|
178864
|
+
return ["mcp", ...GROUP_SUBCOMMANDS.keys()];
|
|
178865
|
+
}
|
|
178235
178866
|
async function main() {
|
|
178236
178867
|
setModelTurnPort(async (request) => runModelTurn(request));
|
|
178237
178868
|
const args = process.argv.slice(2);
|
|
@@ -178250,16 +178881,23 @@ async function main() {
|
|
|
178250
178881
|
await printCommandHelp(command);
|
|
178251
178882
|
return;
|
|
178252
178883
|
}
|
|
178884
|
+
const known = knownSubcommandsFor(command);
|
|
178885
|
+
const sub = args[1];
|
|
178886
|
+
if (known !== undefined && sub !== undefined && !sub.startsWith("-") && !known.includes(sub)) {
|
|
178887
|
+
console.error(formatUnknownCommandMessage(sub, known, `keryx ${command} --help`));
|
|
178888
|
+
process.exitCode = 1;
|
|
178889
|
+
return;
|
|
178890
|
+
}
|
|
178253
178891
|
await route(args.slice(1));
|
|
178254
178892
|
return;
|
|
178255
178893
|
}
|
|
178256
|
-
console.error(
|
|
178257
|
-
printHelp30();
|
|
178894
|
+
console.error(formatUnknownCommandMessage(command, Object.keys(CLI_ROUTES).filter((name) => name !== "__sandbox-net-forward")));
|
|
178258
178895
|
process.exitCode = 1;
|
|
178259
178896
|
}
|
|
178260
178897
|
var USAGE_BODY = `Usage:
|
|
178261
178898
|
keryx Show CLI usage
|
|
178262
178899
|
keryx help [group|command] Grouped command help by task (--help/-h keep this flat usage)
|
|
178900
|
+
keryx doctor [--json] One page: version, Bun floor, ripgrep, sandbox, providers, MCP, integrations, standard, worktrees, graph/wiki freshness
|
|
178263
178901
|
keryx shell [-c|--continue] [-r|--resume [id]] [--provider <p>] [--model <m>] [--base-url <url>] [--agent|--chat] [--tui|--no-tui]
|
|
178264
178902
|
Start TUI agent shell (sessions are per-project)
|
|
178265
178903
|
keryx sessions list|fork <id>|export <id>|path
|
|
@@ -178439,6 +179077,7 @@ var USAGE_BODY = `Usage:
|
|
|
178439
179077
|
|
|
178440
179078
|
Commands:
|
|
178441
179079
|
help Grouped command help by task: every verb, in nine onboarding-ordered groups
|
|
179080
|
+
doctor One-page health check with a fix hint per line; --json for {checks:[...]}
|
|
178442
179081
|
shell Start the interactive TUI agent harness. Use --no-tui or --chat to opt out.
|
|
178443
179082
|
Sessions: -c continue last in this project, -r [id] resume (per-project).
|
|
178444
179083
|
sessions List or export per-project shell sessions
|
|
@@ -178587,7 +179226,9 @@ export {
|
|
|
178587
179226
|
USAGE_BODY,
|
|
178588
179227
|
exitCodeForError,
|
|
178589
179228
|
groupUsage,
|
|
179229
|
+
groupsWithKnownSubcommands,
|
|
178590
179230
|
helpRequestedFor,
|
|
179231
|
+
knownSubcommandsFor,
|
|
178591
179232
|
main,
|
|
178592
179233
|
printCommandHelp,
|
|
178593
179234
|
shouldInterceptHelp
|