@mrciphersmith/keryx 0.2.110 → 0.2.111
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 +1171 -769
- package/dist/core.js +1 -1
- package/package.json +1 -1
- package/src/gdskills/bundled/rules/core/model-selection.mdc +30 -13
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/orchestrator-prompt.md +1 -1
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +45 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +26 -38
- package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +1 -1
- package/src/gdskills/contracts/subagent-dispatch.schema.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -589,6 +589,44 @@ var init_grants = __esm(() => {
|
|
|
589
589
|
];
|
|
590
590
|
});
|
|
591
591
|
|
|
592
|
+
// src/lib/caller-session.ts
|
|
593
|
+
function resolveCallerSession(input = {}) {
|
|
594
|
+
const pair = (provider, model, source) => ({
|
|
595
|
+
providerId: (provider ?? "").trim(),
|
|
596
|
+
modelId: (model ?? "").trim(),
|
|
597
|
+
source
|
|
598
|
+
});
|
|
599
|
+
if (input.flagProvider !== undefined || input.flagModel !== undefined) {
|
|
600
|
+
return pair(input.flagProvider, input.flagModel, "flags");
|
|
601
|
+
}
|
|
602
|
+
const env = input.env ?? process.env;
|
|
603
|
+
const envProvider = env[ENV_SESSION_PROVIDER];
|
|
604
|
+
const envModel = env[ENV_SESSION_MODEL];
|
|
605
|
+
if ((envProvider ?? "").trim() !== "" || (envModel ?? "").trim() !== "") {
|
|
606
|
+
return pair(envProvider, envModel, "env");
|
|
607
|
+
}
|
|
608
|
+
if (input.fromShellConfig === true) {
|
|
609
|
+
const config = loadShellConfig(input.configDir);
|
|
610
|
+
return pair(config.provider, config.model, "shell-config");
|
|
611
|
+
}
|
|
612
|
+
return pair(undefined, undefined, "none");
|
|
613
|
+
}
|
|
614
|
+
function exportCallerSession(providerId, modelId, env = process.env) {
|
|
615
|
+
const provider = providerId.trim();
|
|
616
|
+
const model = modelId.trim();
|
|
617
|
+
if (provider === "" || model === "") {
|
|
618
|
+
delete env[ENV_SESSION_PROVIDER];
|
|
619
|
+
delete env[ENV_SESSION_MODEL];
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
env[ENV_SESSION_PROVIDER] = provider;
|
|
623
|
+
env[ENV_SESSION_MODEL] = model;
|
|
624
|
+
}
|
|
625
|
+
var ENV_SESSION_PROVIDER = "KERYX_SESSION_PROVIDER", ENV_SESSION_MODEL = "KERYX_SESSION_MODEL";
|
|
626
|
+
var init_caller_session = __esm(() => {
|
|
627
|
+
init_shell_config();
|
|
628
|
+
});
|
|
629
|
+
|
|
592
630
|
// src/lib/args.ts
|
|
593
631
|
import { parseArgs } from "util";
|
|
594
632
|
function optionValue(args, name) {
|
|
@@ -907,21 +945,23 @@ function runCrossFamily(args) {
|
|
|
907
945
|
const decision = crossFamilyReviewForSession(args.includes("--opt-in"), {
|
|
908
946
|
providerId: optionValue(args, "--session-provider"),
|
|
909
947
|
modelId: optionValue(args, "--session-model")
|
|
910
|
-
});
|
|
948
|
+
}, { fromShellConfig: args.includes("--from-shell-config") });
|
|
911
949
|
if (args.includes("--json")) {
|
|
912
950
|
console.log(JSON.stringify({ cross_family_review: decision }, null, 2));
|
|
913
951
|
return;
|
|
914
952
|
}
|
|
915
953
|
console.log(renderCrossFamilyDecision(decision));
|
|
916
954
|
}
|
|
917
|
-
function crossFamilyReviewForSession(optIn, session = {}) {
|
|
918
|
-
const
|
|
955
|
+
function crossFamilyReviewForSession(optIn, session = {}, options = {}) {
|
|
956
|
+
const resolved = resolveCallerSession({
|
|
957
|
+
flagProvider: session.providerId,
|
|
958
|
+
flagModel: session.modelId,
|
|
959
|
+
fromShellConfig: options.fromShellConfig,
|
|
960
|
+
env: options.env
|
|
961
|
+
});
|
|
919
962
|
return decideCrossFamilyReview({
|
|
920
963
|
optIn,
|
|
921
|
-
session: {
|
|
922
|
-
providerId: (session.providerId ?? config.provider ?? "").trim(),
|
|
923
|
-
modelId: (session.modelId ?? config.model ?? "").trim()
|
|
924
|
-
},
|
|
964
|
+
session: { providerId: resolved.providerId, modelId: resolved.modelId },
|
|
925
965
|
configured: configuredProviders(envWithOAuthAccess(envWithSavedApiKeys()))
|
|
926
966
|
});
|
|
927
967
|
}
|
|
@@ -963,7 +1003,7 @@ function printProvidersHelp() {
|
|
|
963
1003
|
|
|
964
1004
|
Usage:
|
|
965
1005
|
keryx providers list [--json]
|
|
966
|
-
keryx providers cross-family [--opt-in] [--session-provider <id>] [--session-model <id>] [--json]
|
|
1006
|
+
keryx providers cross-family [--opt-in] [--session-provider <id>] [--session-model <id>] [--from-shell-config] [--json]
|
|
967
1007
|
|
|
968
1008
|
Commands:
|
|
969
1009
|
list Providers this operator has configured, and the family of each
|
|
@@ -982,6 +1022,7 @@ var init_providers = __esm(() => {
|
|
|
982
1022
|
init_provider_config();
|
|
983
1023
|
init_catalog();
|
|
984
1024
|
init_grants();
|
|
1025
|
+
init_caller_session();
|
|
985
1026
|
init_shell_config();
|
|
986
1027
|
init_args();
|
|
987
1028
|
OPENAI_COMPAT_PROVIDERS = [
|
|
@@ -8724,6 +8765,7 @@ function parseSkillFrontmatter(content) {
|
|
|
8724
8765
|
let inTriggers = false;
|
|
8725
8766
|
let inMetadata = false;
|
|
8726
8767
|
let metadataCategory;
|
|
8768
|
+
let metadataVersion;
|
|
8727
8769
|
let compatibleHarnesses;
|
|
8728
8770
|
let awaitingHarnessesList = false;
|
|
8729
8771
|
let descriptionBlock = null;
|
|
@@ -8785,6 +8827,11 @@ function parseSkillFrontmatter(content) {
|
|
|
8785
8827
|
metadataCategory = stripSkillFieldQuotes(categoryMatch[1].trim());
|
|
8786
8828
|
continue;
|
|
8787
8829
|
}
|
|
8830
|
+
const versionMatch = /^\s+version:\s*(.+)$/.exec(line);
|
|
8831
|
+
if (versionMatch !== null && versionMatch[1] !== undefined) {
|
|
8832
|
+
metadataVersion = stripSkillFieldQuotes(versionMatch[1].trim());
|
|
8833
|
+
continue;
|
|
8834
|
+
}
|
|
8788
8835
|
const harnessesMatch = /^\s+compatible_harnesses:\s*(.*)$/.exec(line);
|
|
8789
8836
|
if (harnessesMatch !== null) {
|
|
8790
8837
|
const inline = parseInlineHarnesses((harnessesMatch[1] ?? "").trim());
|
|
@@ -8805,6 +8852,7 @@ function parseSkillFrontmatter(content) {
|
|
|
8805
8852
|
...description !== undefined ? { description } : {},
|
|
8806
8853
|
...triggers.length > 0 ? { triggers } : {},
|
|
8807
8854
|
...metadataCategory !== undefined ? { metadataCategory } : {},
|
|
8855
|
+
...metadataVersion !== undefined ? { metadataVersion } : {},
|
|
8808
8856
|
...compatibleHarnesses !== undefined ? { compatibleHarnesses } : {}
|
|
8809
8857
|
};
|
|
8810
8858
|
}
|
|
@@ -25690,7 +25738,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
25690
25738
|
name: skillName,
|
|
25691
25739
|
target: options.target,
|
|
25692
25740
|
path: relativeSkillPath,
|
|
25693
|
-
version: VERSION,
|
|
25741
|
+
version: options.version ?? VERSION,
|
|
25694
25742
|
status: "active",
|
|
25695
25743
|
updatedAt: new Date().toISOString()
|
|
25696
25744
|
});
|
|
@@ -33188,7 +33236,7 @@ function guardGitHubRequest(request) {
|
|
|
33188
33236
|
if (allowed.some((pattern) => pattern.test(request.path))) {
|
|
33189
33237
|
return;
|
|
33190
33238
|
}
|
|
33191
|
-
throw new Error(`Refusing a GitHub ${request.method} to \`${request.path}\`: it is not one of the
|
|
33239
|
+
throw new Error(`Refusing a GitHub ${request.method} to \`${request.path}\`: it is not one of the six endpoints this module may touch. ` + `Reads: pulls/{n}, pulls/{n}/comments, pulls/{n}/reviews, issues/{n}/comments. Writes: pulls/{n}/comments/{id}/replies, issues/{n}/comments. ` + `Resolving, hiding, minimising, editing and dismissing are deliberately unreachable \u2014 replying is ours, resolving is the reviewer's call, and a bot that auto-resolves is a bot that silences a human.`);
|
|
33192
33240
|
}
|
|
33193
33241
|
async function callGitHub(port, request) {
|
|
33194
33242
|
guardGitHubRequest(request);
|
|
@@ -33272,6 +33320,8 @@ function createFixturePort(files, options = {}) {
|
|
|
33272
33320
|
}
|
|
33273
33321
|
function fixtureKey(apiPath) {
|
|
33274
33322
|
const withoutQuery = apiPath.split("?")[0] ?? apiPath;
|
|
33323
|
+
if (/\/pulls\/\d+$/.test(withoutQuery))
|
|
33324
|
+
return "pull";
|
|
33275
33325
|
if (/\/pulls\/\d+\/comments$/.test(withoutQuery))
|
|
33276
33326
|
return "pull-comments";
|
|
33277
33327
|
if (/\/pulls\/\d+\/reviews$/.test(withoutQuery))
|
|
@@ -33280,12 +33330,37 @@ function fixtureKey(apiPath) {
|
|
|
33280
33330
|
return "issue-comments";
|
|
33281
33331
|
return withoutQuery;
|
|
33282
33332
|
}
|
|
33333
|
+
function parsePullRequest(raw) {
|
|
33334
|
+
const object = Array.isArray(raw) ? raw[0] : raw;
|
|
33335
|
+
const state = stringProperty(object, "state");
|
|
33336
|
+
const mergedAt = stringProperty(object, "merged_at") ?? null;
|
|
33337
|
+
const mergedFlag = object !== null && typeof object === "object" && object.merged === true;
|
|
33338
|
+
const head = object !== null && typeof object === "object" ? object.head : undefined;
|
|
33339
|
+
const headSha = stringProperty(head, "sha") ?? null;
|
|
33340
|
+
return {
|
|
33341
|
+
state: state === "open" || state === "closed" ? state : "unknown",
|
|
33342
|
+
merged: mergedFlag || mergedAt !== null,
|
|
33343
|
+
mergedAt,
|
|
33344
|
+
headSha: headSha === null ? null : headSha.toLowerCase()
|
|
33345
|
+
};
|
|
33346
|
+
}
|
|
33347
|
+
function describePullRequest(pull) {
|
|
33348
|
+
const status = pull.merged ? `merged${pull.mergedAt === null ? "" : ` at ${pull.mergedAt}`}` : pull.state;
|
|
33349
|
+
return `${status}, head ${pull.headSha ?? "not reported"}`;
|
|
33350
|
+
}
|
|
33351
|
+
function shaMatchesHead(sha, headSha) {
|
|
33352
|
+
if (headSha === null)
|
|
33353
|
+
return false;
|
|
33354
|
+
const wanted = sha.trim().toLowerCase();
|
|
33355
|
+
return wanted === headSha || wanted.length >= 7 && headSha.startsWith(wanted);
|
|
33356
|
+
}
|
|
33283
33357
|
async function collectPrComments(input) {
|
|
33284
33358
|
if (input.self.trim() === "") {
|
|
33285
33359
|
throw new Error("Refusing to collect PR comments with no `self` identity: without it our own replies are indistinguishable from a reviewer's, and the reply pass answers itself every round. Resolve the acting login (`gh api user --jq .login`) and pass it.");
|
|
33286
33360
|
}
|
|
33287
33361
|
const base = `repos/${input.repo}`;
|
|
33288
|
-
const [rawReviewComments, rawReviews, rawIssueComments] = await Promise.all([
|
|
33362
|
+
const [rawPull, rawReviewComments, rawReviews, rawIssueComments] = await Promise.all([
|
|
33363
|
+
callGitHub(input.port, { method: "GET", path: `${base}/pulls/${input.number}` }),
|
|
33289
33364
|
callGitHub(input.port, { method: "GET", path: `${base}/pulls/${input.number}/comments` }),
|
|
33290
33365
|
callGitHub(input.port, { method: "GET", path: `${base}/pulls/${input.number}/reviews` }),
|
|
33291
33366
|
callGitHub(input.port, { method: "GET", path: `${base}/issues/${input.number}/comments` })
|
|
@@ -33363,6 +33438,7 @@ async function collectPrComments(input) {
|
|
|
33363
33438
|
});
|
|
33364
33439
|
}
|
|
33365
33440
|
return {
|
|
33441
|
+
pull: parsePullRequest(rawPull),
|
|
33366
33442
|
comments,
|
|
33367
33443
|
skipped,
|
|
33368
33444
|
counts: {
|
|
@@ -33884,10 +33960,23 @@ function unansweredComments(state) {
|
|
|
33884
33960
|
const answered = new Set(state.handled_comments.filter((entry) => entry.reply_url !== null).map((entry) => entry.id));
|
|
33885
33961
|
return state.seen.filter((entry) => !answered.has(entry.id));
|
|
33886
33962
|
}
|
|
33963
|
+
function assertReplyTarget(input) {
|
|
33964
|
+
const where = `${input.repo}#${input.number}`;
|
|
33965
|
+
if (input.pull.state === "unknown") {
|
|
33966
|
+
throw new Error(`Refusing to reply on ${where}: the pull request's state could not be read, so nothing shows it is open or that ${input.sha} is its head. Re-run with a reachable \`gh\`, or supply \`pull.json\` with \`--fixtures\`.`);
|
|
33967
|
+
}
|
|
33968
|
+
if ((input.pull.merged || input.pull.state === "closed") && input.allowClosed !== true) {
|
|
33969
|
+
throw new Error(`Refusing to reply on ${where}: the pull request is ${describePullRequest(input.pull)}. Replies are for an open review; posting to a finished one is an outward action nobody gated. Pass \`--allow-closed-pr\` only if answering after close is intended.`);
|
|
33970
|
+
}
|
|
33971
|
+
if (!shaMatchesHead(input.sha, input.pull.headSha)) {
|
|
33972
|
+
throw new Error(`Refusing to reply on ${where}: \`--sha ${input.sha}\` is not the pull request's head (${input.pull.headSha ?? "not reported"}). Every reply would describe a commit the pull request no longer points at. Collect and review the current head, then reply with its SHA.`);
|
|
33973
|
+
}
|
|
33974
|
+
}
|
|
33887
33975
|
async function postReplyPass(input) {
|
|
33888
33976
|
if (!input.round.isFinal) {
|
|
33889
33977
|
throw new Error(`Refusing to post replies during round ${input.round.index}: comments are collected every round and answered ONCE, after the final round and before the completion gate. Replying per round turns one review thread into six, and every reply would state an intention rather than a settled outcome.`);
|
|
33890
33978
|
}
|
|
33979
|
+
assertReplyTarget(input);
|
|
33891
33980
|
const now = (input.now ?? new Date).toISOString();
|
|
33892
33981
|
const alreadyAnswered = new Map(input.state.handled_comments.map((entry) => [entry.id, entry]));
|
|
33893
33982
|
const posted = [];
|
|
@@ -34102,6 +34191,7 @@ var init_pr_comments = __esm(() => {
|
|
|
34102
34191
|
init_fs();
|
|
34103
34192
|
init_types3();
|
|
34104
34193
|
ALLOWED_GITHUB_READS = [
|
|
34194
|
+
/^repos\/[^/]+\/[^/]+\/pulls\/\d+$/,
|
|
34105
34195
|
/^repos\/[^/]+\/[^/]+\/pulls\/\d+\/comments(\?[^\s]*)?$/,
|
|
34106
34196
|
/^repos\/[^/]+\/[^/]+\/pulls\/\d+\/reviews(\?[^\s]*)?$/,
|
|
34107
34197
|
/^repos\/[^/]+\/[^/]+\/issues\/\d+\/comments(\?[^\s]*)?$/
|
|
@@ -40936,9 +41026,9 @@ var init_memory = __esm(() => {
|
|
|
40936
41026
|
|
|
40937
41027
|
// src/lib/serve-config.ts
|
|
40938
41028
|
import { existsSync as existsSync26 } from "fs";
|
|
40939
|
-
import
|
|
41029
|
+
import path177 from "path";
|
|
40940
41030
|
function serveConfigPath(dir) {
|
|
40941
|
-
return
|
|
41031
|
+
return path177.join(keryxConfigDir(dir), "serve.json");
|
|
40942
41032
|
}
|
|
40943
41033
|
function parseIpv4(value) {
|
|
40944
41034
|
const parts = value.split(".");
|
|
@@ -48687,7 +48777,7 @@ import path47 from "path";
|
|
|
48687
48777
|
// package.json
|
|
48688
48778
|
var package_default = {
|
|
48689
48779
|
name: "@mrciphersmith/keryx",
|
|
48690
|
-
version: "0.2.
|
|
48780
|
+
version: "0.2.111",
|
|
48691
48781
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
48692
48782
|
private: false,
|
|
48693
48783
|
publishConfig: {
|
|
@@ -56982,7 +57072,7 @@ init_args();
|
|
|
56982
57072
|
init_fs();
|
|
56983
57073
|
init_json();
|
|
56984
57074
|
import { readdir as readdir25, readFile as readFile83 } from "fs/promises";
|
|
56985
|
-
import
|
|
57075
|
+
import path148 from "path";
|
|
56986
57076
|
|
|
56987
57077
|
// src/lib/route-tokens.ts
|
|
56988
57078
|
var ROUTE_STOPWORDS = new Set([
|
|
@@ -57669,8 +57759,32 @@ init_project_skills();
|
|
|
57669
57759
|
// src/gdskills/import-skills.ts
|
|
57670
57760
|
init_args();
|
|
57671
57761
|
init_fs();
|
|
57672
|
-
import { readdir as readdir24, readFile as readFile81, stat as stat15 } from "fs/promises";
|
|
57762
|
+
import { mkdir as mkdir50, readdir as readdir24, readFile as readFile81, stat as stat15 } from "fs/promises";
|
|
57763
|
+
import { homedir as homedir4 } from "os";
|
|
57764
|
+
import path145 from "path";
|
|
57765
|
+
|
|
57766
|
+
// src/gdskills/rule-references.ts
|
|
57767
|
+
init_fs();
|
|
57673
57768
|
import path144 from "path";
|
|
57769
|
+
function ruleReferences(content) {
|
|
57770
|
+
const refs = new Set;
|
|
57771
|
+
for (const match of content.matchAll(/`(?:\.metaproject\/)?(?:rules\/)?([a-z0-9_-]+\/[a-z0-9._-]+\.mdc)`/gi)) {
|
|
57772
|
+
if (match[1])
|
|
57773
|
+
refs.add(match[1]);
|
|
57774
|
+
}
|
|
57775
|
+
return [...refs].sort();
|
|
57776
|
+
}
|
|
57777
|
+
async function unresolvedRuleReferences(projectRoot, content) {
|
|
57778
|
+
const missing = [];
|
|
57779
|
+
for (const ref of ruleReferences(content)) {
|
|
57780
|
+
if (!await pathExists(path144.join(projectRoot, ".metaproject", "rules", ref))) {
|
|
57781
|
+
missing.push(ref);
|
|
57782
|
+
}
|
|
57783
|
+
}
|
|
57784
|
+
return missing;
|
|
57785
|
+
}
|
|
57786
|
+
|
|
57787
|
+
// src/gdskills/import-skills.ts
|
|
57674
57788
|
init_project_skills();
|
|
57675
57789
|
init_guard2();
|
|
57676
57790
|
var BUNDLED_NAMES = new Set(BUNDLED_GDSKILLS.map((entry) => entry.name));
|
|
@@ -57687,9 +57801,11 @@ async function importProjectSkills(options) {
|
|
|
57687
57801
|
for (const source of sources) {
|
|
57688
57802
|
imported.push(await importOne(options, source));
|
|
57689
57803
|
}
|
|
57804
|
+
const rules = await importReferencedRules(options, sources.filter((source) => options.force === true || !BUNDLED_NAMES.has(source.name)));
|
|
57690
57805
|
return {
|
|
57691
57806
|
from: options.from,
|
|
57692
57807
|
imported,
|
|
57808
|
+
rules,
|
|
57693
57809
|
dryRun: options.dryRun === true,
|
|
57694
57810
|
force: options.force === true
|
|
57695
57811
|
};
|
|
@@ -57707,6 +57823,7 @@ async function updateProjectSkills(options) {
|
|
|
57707
57823
|
return {
|
|
57708
57824
|
from: options.from ?? "(each skill Origin)",
|
|
57709
57825
|
imported,
|
|
57826
|
+
rules: [],
|
|
57710
57827
|
dryRun: options.dryRun === true,
|
|
57711
57828
|
force: true
|
|
57712
57829
|
};
|
|
@@ -57727,6 +57844,14 @@ function renderImportProjectSkillsMarkdown(result) {
|
|
|
57727
57844
|
lines.push(`- ${row.module}/${row.name}: ${row.status}${extra ? ` \u2014 ${extra}` : ""}`);
|
|
57728
57845
|
}
|
|
57729
57846
|
lines.push("");
|
|
57847
|
+
if (result.rules.length > 0) {
|
|
57848
|
+
lines.push("## rules the skills cite", "");
|
|
57849
|
+
for (const rule of result.rules) {
|
|
57850
|
+
const detail = [rule.origin ? `from ${rule.origin}` : undefined, rule.reason].filter(Boolean).join(" \u2014 ");
|
|
57851
|
+
lines.push(`- ${rule.ref}: ${rule.status}${detail ? ` \u2014 ${detail}` : ""} (cited by ${rule.citedBy.join(", ")})`);
|
|
57852
|
+
}
|
|
57853
|
+
lines.push("");
|
|
57854
|
+
}
|
|
57730
57855
|
if (result.imported.some((row) => row.module === "review" && row.status !== "skipped")) {
|
|
57731
57856
|
lines.push("Reviewers: `keryx review reviewers` must list every imported review/* name. That is the same call review-orchestrator makes.");
|
|
57732
57857
|
}
|
|
@@ -57752,6 +57877,21 @@ function githubBlobToRaw(url) {
|
|
|
57752
57877
|
function isHttpsSkillUrl(from) {
|
|
57753
57878
|
return /^https:\/\//i.test(from.trim());
|
|
57754
57879
|
}
|
|
57880
|
+
function isInside4(parent, child) {
|
|
57881
|
+
const relative = path145.relative(parent, child);
|
|
57882
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path145.sep}`) && !path145.isAbsolute(relative);
|
|
57883
|
+
}
|
|
57884
|
+
function portableOriginRef(absolute, projectRoot, home = homedir4()) {
|
|
57885
|
+
const resolved = path145.resolve(absolute);
|
|
57886
|
+
const root = path145.resolve(projectRoot);
|
|
57887
|
+
if (isInside4(root, resolved)) {
|
|
57888
|
+
return toPosix(path145.relative(root, resolved));
|
|
57889
|
+
}
|
|
57890
|
+
if (isInside4(home, resolved)) {
|
|
57891
|
+
return `~/${toPosix(path145.relative(home, resolved))}`;
|
|
57892
|
+
}
|
|
57893
|
+
return resolved;
|
|
57894
|
+
}
|
|
57755
57895
|
async function resolveImportSources(options) {
|
|
57756
57896
|
if (isHttpsSkillUrl(options.from)) {
|
|
57757
57897
|
return [await sourceFromUrl(options)];
|
|
@@ -57762,7 +57902,7 @@ async function resolveImportSources(options) {
|
|
|
57762
57902
|
}
|
|
57763
57903
|
const stats = await stat15(resolved);
|
|
57764
57904
|
if (stats.isFile()) {
|
|
57765
|
-
return [await sourceFromFile(options, resolved
|
|
57905
|
+
return [await sourceFromFile(options, resolved)];
|
|
57766
57906
|
}
|
|
57767
57907
|
if (!stats.isDirectory()) {
|
|
57768
57908
|
throw new Error(`keryx skills import: --from ${options.from} is not a file or directory`);
|
|
@@ -57787,49 +57927,55 @@ async function sourceFromUrl(options) {
|
|
|
57787
57927
|
const moduleName = options.module ?? inferModule2(name, response.text);
|
|
57788
57928
|
return { name, module: moduleName, origin: url, content: response.text };
|
|
57789
57929
|
}
|
|
57790
|
-
async function sourceFromFile(options, absolute
|
|
57930
|
+
async function sourceFromFile(options, absolute) {
|
|
57791
57931
|
const content = await readFile81(absolute, "utf8");
|
|
57792
57932
|
const name = options.name ?? inferNameFromPath(absolute, content);
|
|
57793
57933
|
const moduleName = options.module ?? inferModule2(name, content);
|
|
57794
|
-
return { name, module: moduleName, origin:
|
|
57934
|
+
return { name, module: moduleName, origin: portableOriginRef(absolute, options.projectRoot), content, sourcePath: absolute };
|
|
57795
57935
|
}
|
|
57796
57936
|
async function sourceFromDirectory(options, absolute) {
|
|
57797
57937
|
const roots = await skillPackageDirs(absolute);
|
|
57798
57938
|
const sources = [];
|
|
57799
57939
|
for (const dir of roots) {
|
|
57800
|
-
const name =
|
|
57940
|
+
const name = path145.basename(dir);
|
|
57801
57941
|
if (options.namePrefix && !name.startsWith(options.namePrefix))
|
|
57802
57942
|
continue;
|
|
57803
57943
|
if (options.name && name !== options.name)
|
|
57804
57944
|
continue;
|
|
57805
|
-
const skillMd =
|
|
57945
|
+
const skillMd = path145.join(dir, "SKILL.md");
|
|
57806
57946
|
const content = await readFile81(skillMd, "utf8");
|
|
57807
57947
|
const moduleName = options.module ?? inferModule2(name, content);
|
|
57808
|
-
sources.push({
|
|
57948
|
+
sources.push({
|
|
57949
|
+
name,
|
|
57950
|
+
module: moduleName,
|
|
57951
|
+
origin: portableOriginRef(skillMd, options.projectRoot),
|
|
57952
|
+
content,
|
|
57953
|
+
sourcePath: skillMd
|
|
57954
|
+
});
|
|
57809
57955
|
}
|
|
57810
57956
|
return sources.sort((a, b) => `${a.module}/${a.name}`.localeCompare(`${b.module}/${b.name}`));
|
|
57811
57957
|
}
|
|
57812
57958
|
async function skillPackageDirs(root) {
|
|
57813
|
-
if (await pathExists(
|
|
57959
|
+
if (await pathExists(path145.join(root, "SKILL.md"))) {
|
|
57814
57960
|
return [root];
|
|
57815
57961
|
}
|
|
57816
|
-
const nestedSkills =
|
|
57962
|
+
const nestedSkills = path145.join(root, "skills");
|
|
57817
57963
|
const scan = await pathExists(nestedSkills) ? nestedSkills : root;
|
|
57818
57964
|
const entries = await readdir24(scan, { withFileTypes: true });
|
|
57819
57965
|
const dirs = [];
|
|
57820
57966
|
for (const entry of entries) {
|
|
57821
57967
|
if (!entry.isDirectory())
|
|
57822
57968
|
continue;
|
|
57823
|
-
const dir =
|
|
57824
|
-
if (await pathExists(
|
|
57969
|
+
const dir = path145.join(scan, entry.name);
|
|
57970
|
+
if (await pathExists(path145.join(dir, "SKILL.md"))) {
|
|
57825
57971
|
dirs.push(dir);
|
|
57826
57972
|
}
|
|
57827
57973
|
}
|
|
57828
57974
|
return dirs;
|
|
57829
57975
|
}
|
|
57830
57976
|
async function importOne(options, source) {
|
|
57831
|
-
const dest =
|
|
57832
|
-
const destAbs =
|
|
57977
|
+
const dest = path145.posix.join(".metaproject", "project-skills", source.module, source.name);
|
|
57978
|
+
const destAbs = path145.join(options.projectRoot, dest, "SKILL.md");
|
|
57833
57979
|
const exists = await pathExists(destAbs);
|
|
57834
57980
|
const bundled = BUNDLED_NAMES.has(source.name);
|
|
57835
57981
|
if (bundled && !options.force) {
|
|
@@ -57870,6 +58016,7 @@ async function importOne(options, source) {
|
|
|
57870
58016
|
note: `Imported from ${options.from}`,
|
|
57871
58017
|
origin: source.origin,
|
|
57872
58018
|
originContent: source.content,
|
|
58019
|
+
...versionOption(source.content),
|
|
57873
58020
|
format: "single"
|
|
57874
58021
|
});
|
|
57875
58022
|
await overwriteImportedSkill(options.projectRoot, created, source.content);
|
|
@@ -57883,9 +58030,9 @@ async function importOne(options, source) {
|
|
|
57883
58030
|
};
|
|
57884
58031
|
}
|
|
57885
58032
|
async function updateOne(options, entry) {
|
|
57886
|
-
const skillMd =
|
|
58033
|
+
const skillMd = path145.join(options.projectRoot, entry.path, "SKILL.md");
|
|
57887
58034
|
const current = await readFile81(skillMd, "utf8");
|
|
57888
|
-
|
|
58035
|
+
let origin = options.from ?? metadataLine(current, "Origin");
|
|
57889
58036
|
if (!origin) {
|
|
57890
58037
|
return {
|
|
57891
58038
|
name: entry.name,
|
|
@@ -57924,6 +58071,7 @@ async function updateOne(options, entry) {
|
|
|
57924
58071
|
};
|
|
57925
58072
|
}
|
|
57926
58073
|
content = await readFile81(resolved, "utf8");
|
|
58074
|
+
origin = portableOriginRef(resolved, options.projectRoot);
|
|
57927
58075
|
}
|
|
57928
58076
|
if (options.dryRun) {
|
|
57929
58077
|
return {
|
|
@@ -57942,6 +58090,7 @@ async function updateOne(options, entry) {
|
|
|
57942
58090
|
note: `Updated from origin ${origin}`,
|
|
57943
58091
|
origin,
|
|
57944
58092
|
originContent: content,
|
|
58093
|
+
...versionOption(content),
|
|
57945
58094
|
format: "single"
|
|
57946
58095
|
});
|
|
57947
58096
|
await overwriteImportedSkill(options.projectRoot, created, content);
|
|
@@ -57955,11 +58104,11 @@ async function updateOne(options, entry) {
|
|
|
57955
58104
|
};
|
|
57956
58105
|
}
|
|
57957
58106
|
async function overwriteImportedSkill(projectRoot, created, source) {
|
|
57958
|
-
const skillPath =
|
|
58107
|
+
const skillPath = path145.join(projectRoot, created.skillPath, "SKILL.md");
|
|
57959
58108
|
const scaffold = await readFile81(skillPath, "utf8");
|
|
57960
|
-
const
|
|
57961
|
-
const stamped =
|
|
57962
|
-
const relative = toPosix(
|
|
58109
|
+
const header = extractImportHeader(scaffold, parseSkillFrontmatter(source).metadataVersion);
|
|
58110
|
+
const stamped = stampImportHeader(source, header);
|
|
58111
|
+
const relative = toPosix(path145.join(created.skillPath, "SKILL.md"));
|
|
57963
58112
|
const guard = await guardOutput({
|
|
57964
58113
|
cwd: projectRoot,
|
|
57965
58114
|
content: stamped,
|
|
@@ -57973,15 +58122,26 @@ async function overwriteImportedSkill(projectRoot, created, source) {
|
|
|
57973
58122
|
}
|
|
57974
58123
|
await writeFileAtomic(skillPath, output.content);
|
|
57975
58124
|
}
|
|
57976
|
-
|
|
58125
|
+
var IMPORT_HEADER_LABELS = [
|
|
58126
|
+
"Version",
|
|
58127
|
+
"Target",
|
|
58128
|
+
"Module",
|
|
58129
|
+
"Origin",
|
|
58130
|
+
"Origin Hash",
|
|
58131
|
+
"Imported At",
|
|
58132
|
+
"Status",
|
|
58133
|
+
"Last Verified"
|
|
58134
|
+
];
|
|
58135
|
+
var IMPORT_HEADER_LINE = new RegExp(`^(${IMPORT_HEADER_LABELS.join("|")}):\\s`);
|
|
58136
|
+
function extractImportHeader(scaffold, version) {
|
|
57977
58137
|
const lines = scaffold.split(`
|
|
57978
|
-
`).filter((line) =>
|
|
58138
|
+
`).filter((line) => IMPORT_HEADER_LINE.test(line)).map((line) => version && line.startsWith("Version:") ? `Version: ${version}` : line);
|
|
57979
58139
|
return lines.length > 0 ? `${lines.join(`
|
|
57980
58140
|
`)}
|
|
57981
58141
|
` : "";
|
|
57982
58142
|
}
|
|
57983
|
-
function
|
|
57984
|
-
if (!
|
|
58143
|
+
function stampImportHeader(source, header) {
|
|
58144
|
+
if (!header)
|
|
57985
58145
|
return source;
|
|
57986
58146
|
const stripped = source.replace(/^Origin Hash:.*\n/gm, "").replace(/^Origin:.*\n/gm, "").replace(/^Imported At:.*\n/gm, "");
|
|
57987
58147
|
if (stripped.startsWith("---")) {
|
|
@@ -57992,19 +58152,108 @@ function stampOrigin(source, originBlock) {
|
|
|
57992
58152
|
---`.length;
|
|
57993
58153
|
const insertAt = stripped[afterFence] === `
|
|
57994
58154
|
` ? afterFence + 1 : afterFence;
|
|
57995
|
-
|
|
58155
|
+
let bodyStart = insertAt;
|
|
58156
|
+
while (true) {
|
|
58157
|
+
const lineEnd = stripped.indexOf(`
|
|
58158
|
+
`, bodyStart);
|
|
58159
|
+
const line = stripped.slice(bodyStart, lineEnd === -1 ? stripped.length : lineEnd);
|
|
58160
|
+
if (!IMPORT_HEADER_LINE.test(line))
|
|
58161
|
+
break;
|
|
58162
|
+
bodyStart = lineEnd === -1 ? stripped.length : lineEnd + 1;
|
|
58163
|
+
}
|
|
58164
|
+
return `${stripped.slice(0, insertAt)}${header}${stripped.slice(bodyStart)}`;
|
|
57996
58165
|
}
|
|
57997
58166
|
}
|
|
57998
|
-
return `${
|
|
58167
|
+
return `${header}${stripped}`;
|
|
58168
|
+
}
|
|
58169
|
+
function versionOption(content) {
|
|
58170
|
+
const version = parseSkillFrontmatter(content).metadataVersion;
|
|
58171
|
+
return version ? { version } : {};
|
|
58172
|
+
}
|
|
58173
|
+
async function findOverlayRule(sourcePath, ref) {
|
|
58174
|
+
let dir = path145.dirname(sourcePath);
|
|
58175
|
+
for (let depth = 0;depth <= 3; depth += 1) {
|
|
58176
|
+
const candidate = path145.join(dir, "rules", ref);
|
|
58177
|
+
if (await pathExists(candidate))
|
|
58178
|
+
return candidate;
|
|
58179
|
+
const parent = path145.dirname(dir);
|
|
58180
|
+
if (parent === dir)
|
|
58181
|
+
break;
|
|
58182
|
+
dir = parent;
|
|
58183
|
+
}
|
|
58184
|
+
return;
|
|
58185
|
+
}
|
|
58186
|
+
async function importReferencedRules(options, sources) {
|
|
58187
|
+
const citedBy = new Map;
|
|
58188
|
+
for (const source of sources) {
|
|
58189
|
+
for (const ref of ruleReferences(source.content)) {
|
|
58190
|
+
const entry = citedBy.get(ref) ?? { names: [], sourcePaths: [] };
|
|
58191
|
+
entry.names.push(source.name);
|
|
58192
|
+
if (source.sourcePath)
|
|
58193
|
+
entry.sourcePaths.push(source.sourcePath);
|
|
58194
|
+
citedBy.set(ref, entry);
|
|
58195
|
+
}
|
|
58196
|
+
}
|
|
58197
|
+
const bundledRules = bundledRulesSourcePath();
|
|
58198
|
+
const rules = [];
|
|
58199
|
+
for (const [ref, cited] of [...citedBy.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
58200
|
+
const base = { ref, citedBy: cited.names.sort() };
|
|
58201
|
+
const target = path145.join(options.projectRoot, ".metaproject", "rules", ref);
|
|
58202
|
+
if (await pathExists(target)) {
|
|
58203
|
+
rules.push({ ...base, status: "present" });
|
|
58204
|
+
continue;
|
|
58205
|
+
}
|
|
58206
|
+
if (ref.startsWith("core/") && await pathExists(path145.join(bundledRules, path145.basename(ref)))) {
|
|
58207
|
+
rules.push({ ...base, status: "unresolved", reason: "ships with keryx \u2014 run `keryx install` to restore it" });
|
|
58208
|
+
continue;
|
|
58209
|
+
}
|
|
58210
|
+
let found;
|
|
58211
|
+
for (const sourcePath of cited.sourcePaths) {
|
|
58212
|
+
found = await findOverlayRule(sourcePath, ref);
|
|
58213
|
+
if (found)
|
|
58214
|
+
break;
|
|
58215
|
+
}
|
|
58216
|
+
if (!found) {
|
|
58217
|
+
rules.push({
|
|
58218
|
+
...base,
|
|
58219
|
+
status: "unresolved",
|
|
58220
|
+
reason: cited.sourcePaths.length > 0 ? "no rules/ directory beside the source has it" : "remote source; rules are not fetched"
|
|
58221
|
+
});
|
|
58222
|
+
continue;
|
|
58223
|
+
}
|
|
58224
|
+
const origin = portableOriginRef(found, options.projectRoot);
|
|
58225
|
+
if (options.dryRun) {
|
|
58226
|
+
rules.push({ ...base, status: "would-import", origin });
|
|
58227
|
+
continue;
|
|
58228
|
+
}
|
|
58229
|
+
const content = await readFile81(found, "utf8");
|
|
58230
|
+
const relative = toPosix(path145.relative(options.projectRoot, target));
|
|
58231
|
+
const guard = await guardOutput({
|
|
58232
|
+
cwd: options.projectRoot,
|
|
58233
|
+
content,
|
|
58234
|
+
target: "skill",
|
|
58235
|
+
source: "untrusted-external",
|
|
58236
|
+
path: relative
|
|
58237
|
+
});
|
|
58238
|
+
const output = prepareOutputForPersistence(guard, content);
|
|
58239
|
+
if (!output.allowed) {
|
|
58240
|
+
rules.push({ ...base, status: "unresolved", origin, reason: `blocked by the security gate: ${output.reason}` });
|
|
58241
|
+
continue;
|
|
58242
|
+
}
|
|
58243
|
+
await mkdir50(path145.dirname(target), { recursive: true });
|
|
58244
|
+
await writeFileAtomic(target, output.content);
|
|
58245
|
+
rules.push({ ...base, status: "imported", origin });
|
|
58246
|
+
}
|
|
58247
|
+
return rules;
|
|
57999
58248
|
}
|
|
58000
58249
|
function inferNameFromPath(filePath, content) {
|
|
58001
58250
|
const fromFrontmatter = frontmatterName(content);
|
|
58002
58251
|
if (fromFrontmatter)
|
|
58003
58252
|
return fromFrontmatter;
|
|
58004
|
-
const parent =
|
|
58253
|
+
const parent = path145.basename(path145.dirname(filePath));
|
|
58005
58254
|
if (parent && parent !== "skills" && parent !== ".")
|
|
58006
58255
|
return parent;
|
|
58007
|
-
return
|
|
58256
|
+
return path145.basename(filePath, path145.extname(filePath));
|
|
58008
58257
|
}
|
|
58009
58258
|
function inferNameFromUrl(url) {
|
|
58010
58259
|
const cleaned = url.replace(/\/SKILL\.md$/i, "");
|
|
@@ -58043,7 +58292,7 @@ function metadataLine(content, label) {
|
|
|
58043
58292
|
}
|
|
58044
58293
|
async function readRegistry(projectRoot) {
|
|
58045
58294
|
await Promise.resolve().then(() => init_json());
|
|
58046
|
-
const manifestPath =
|
|
58295
|
+
const manifestPath = path145.join(projectRoot, ".metaproject", "metaproject.json");
|
|
58047
58296
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
58048
58297
|
return manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
58049
58298
|
}
|
|
@@ -58165,10 +58414,10 @@ init_fs();
|
|
|
58165
58414
|
init_json();
|
|
58166
58415
|
init_relevant();
|
|
58167
58416
|
init_service3();
|
|
58168
|
-
import { mkdir as
|
|
58169
|
-
import
|
|
58417
|
+
import { mkdir as mkdir51, readFile as readFile82, writeFile as writeFile46 } from "fs/promises";
|
|
58418
|
+
import path146 from "path";
|
|
58170
58419
|
async function verifyProjectSkill(projectRoot, options) {
|
|
58171
|
-
const metaprojectRoot =
|
|
58420
|
+
const metaprojectRoot = path146.join(projectRoot, ".metaproject");
|
|
58172
58421
|
if (!await pathExists(metaprojectRoot)) {
|
|
58173
58422
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
58174
58423
|
}
|
|
@@ -58178,11 +58427,11 @@ async function verifyProjectSkill(projectRoot, options) {
|
|
|
58178
58427
|
if (!resolved) {
|
|
58179
58428
|
throw new Error(`Project skill not found for: ${options.input}`);
|
|
58180
58429
|
}
|
|
58181
|
-
const skillMdPath =
|
|
58430
|
+
const skillMdPath = path146.join(resolved.packageRoot, "SKILL.md");
|
|
58182
58431
|
const skillMd = await readFile82(skillMdPath, "utf8");
|
|
58183
|
-
const metadata = parseSkillMetadata(skillMd);
|
|
58432
|
+
const metadata = parseSkillMetadata(skillMd, resolved.entry);
|
|
58184
58433
|
const moduleName = metadata.module ?? resolved.entry?.module ?? inferModuleFromPath(resolved.packageRoot);
|
|
58185
|
-
const skillName = resolved.entry?.name ??
|
|
58434
|
+
const skillName = resolved.entry?.name ?? path146.basename(resolved.packageRoot);
|
|
58186
58435
|
const target = metadata.target ?? resolved.entry?.target ?? options.input;
|
|
58187
58436
|
const verifiedAt = new Date().toISOString();
|
|
58188
58437
|
const signals = await collectVerificationSignals({
|
|
@@ -58195,15 +58444,15 @@ async function verifyProjectSkill(projectRoot, options) {
|
|
|
58195
58444
|
});
|
|
58196
58445
|
const status = classifyStatus(signals, metadata);
|
|
58197
58446
|
const recommendations = recommendationsFor(signals, status);
|
|
58198
|
-
const reportPath =
|
|
58199
|
-
const relativeReportPath = toPosix(
|
|
58447
|
+
const reportPath = path146.join(metaprojectRoot, "data", "gdskills", "reports", `${moduleName}-${skillName}-verification.json`);
|
|
58448
|
+
const relativeReportPath = toPosix(path146.relative(projectRoot, reportPath));
|
|
58200
58449
|
const report = {
|
|
58201
58450
|
schemaVersion: 1,
|
|
58202
58451
|
status,
|
|
58203
58452
|
module: moduleName,
|
|
58204
58453
|
name: skillName,
|
|
58205
58454
|
target,
|
|
58206
|
-
skillPath: toPosix(
|
|
58455
|
+
skillPath: toPosix(path146.relative(projectRoot, resolved.packageRoot)),
|
|
58207
58456
|
reportPath: relativeReportPath,
|
|
58208
58457
|
verifiedAt,
|
|
58209
58458
|
signals,
|
|
@@ -58211,16 +58460,16 @@ async function verifyProjectSkill(projectRoot, options) {
|
|
|
58211
58460
|
dryRun: options.dryRun === true
|
|
58212
58461
|
};
|
|
58213
58462
|
if (!options.dryRun) {
|
|
58214
|
-
await
|
|
58463
|
+
await mkdir51(path146.dirname(reportPath), { recursive: true });
|
|
58215
58464
|
await writeFile46(reportPath, `${JSON.stringify(report, null, 2)}
|
|
58216
58465
|
`, "utf8");
|
|
58217
|
-
await writeFile46(
|
|
58466
|
+
await writeFile46(path146.join(resolved.packageRoot, "verification.md"), renderVerificationMarkdown(report), "utf8");
|
|
58218
58467
|
await writeFile46(skillMdPath, updateLastVerified(skillMd, verifiedAt), "utf8");
|
|
58219
58468
|
}
|
|
58220
58469
|
return report;
|
|
58221
58470
|
}
|
|
58222
58471
|
async function readManifest2(projectRoot) {
|
|
58223
|
-
const manifestPath =
|
|
58472
|
+
const manifestPath = path146.join(projectRoot, ".metaproject", "metaproject.json");
|
|
58224
58473
|
if (!await pathExists(manifestPath)) {
|
|
58225
58474
|
return {};
|
|
58226
58475
|
}
|
|
@@ -58240,31 +58489,31 @@ async function collectVerificationSignals({
|
|
|
58240
58489
|
"skill-changelog.md"
|
|
58241
58490
|
];
|
|
58242
58491
|
for (const requiredFile of requiredFiles) {
|
|
58243
|
-
const filePath =
|
|
58492
|
+
const filePath = path146.join(packageRoot, requiredFile);
|
|
58244
58493
|
signals.push({
|
|
58245
58494
|
name: `required:${requiredFile}`,
|
|
58246
58495
|
status: await pathExists(filePath) ? "pass" : "fail",
|
|
58247
58496
|
message: await pathExists(filePath) ? "Required package file exists." : "Required package file is missing.",
|
|
58248
|
-
path: toPosix(
|
|
58497
|
+
path: toPosix(path146.relative(projectRoot, filePath))
|
|
58249
58498
|
});
|
|
58250
58499
|
}
|
|
58251
58500
|
signals.push({
|
|
58252
58501
|
name: "metadata:version",
|
|
58253
58502
|
status: metadata.version ? "pass" : "fail",
|
|
58254
58503
|
message: metadata.version ? `Version ${metadata.version}` : "SKILL.md is missing Version metadata.",
|
|
58255
|
-
path: toPosix(
|
|
58504
|
+
path: toPosix(path146.relative(projectRoot, skillMdPath))
|
|
58256
58505
|
});
|
|
58257
58506
|
signals.push({
|
|
58258
58507
|
name: "metadata:target",
|
|
58259
58508
|
status: metadata.target ? "pass" : "fail",
|
|
58260
58509
|
message: metadata.target ? `Target ${metadata.target}` : "SKILL.md is missing Target metadata.",
|
|
58261
|
-
path: toPosix(
|
|
58510
|
+
path: toPosix(path146.relative(projectRoot, skillMdPath))
|
|
58262
58511
|
});
|
|
58263
58512
|
signals.push({
|
|
58264
58513
|
name: "metadata:last-verified",
|
|
58265
58514
|
status: metadata.lastVerified && metadata.lastVerified !== "never" ? "pass" : "warn",
|
|
58266
58515
|
message: metadata.lastVerified && metadata.lastVerified !== "never" ? `Last verified at ${metadata.lastVerified}` : "Skill has not been verified before.",
|
|
58267
|
-
path: toPosix(
|
|
58516
|
+
path: toPosix(path146.relative(projectRoot, skillMdPath))
|
|
58268
58517
|
});
|
|
58269
58518
|
signals.push({
|
|
58270
58519
|
name: "registry",
|
|
@@ -58272,14 +58521,14 @@ async function collectVerificationSignals({
|
|
|
58272
58521
|
message: registryEntry ? "Skill is registered in metaproject manifest." : "Skill package is not registered in metaproject manifest.",
|
|
58273
58522
|
path: ".metaproject/metaproject.json"
|
|
58274
58523
|
});
|
|
58275
|
-
const targetPath =
|
|
58524
|
+
const targetPath = path146.resolve(projectRoot, target);
|
|
58276
58525
|
const targetLooksLikePath = target.includes("/") || target.includes(".") || target.startsWith(".");
|
|
58277
58526
|
const targetExists = targetLooksLikePath && await pathExists(targetPath);
|
|
58278
58527
|
signals.push({
|
|
58279
58528
|
name: "target",
|
|
58280
58529
|
status: !targetLooksLikePath || targetExists ? "pass" : "fail",
|
|
58281
58530
|
message: targetLooksLikePath ? targetExists ? "Target path exists." : "Target path is missing." : "Target is treated as a symbol or concept.",
|
|
58282
|
-
path: targetLooksLikePath ? toPosix(
|
|
58531
|
+
path: targetLooksLikePath ? toPosix(path146.relative(projectRoot, targetPath)) : undefined
|
|
58283
58532
|
});
|
|
58284
58533
|
await pushArtifactSignal(projectRoot, signals, "gdgraph", [
|
|
58285
58534
|
".metaproject/data/gdgraph/artifacts/summary.md",
|
|
@@ -58309,7 +58558,7 @@ async function collectVerificationSignals({
|
|
|
58309
58558
|
async function pushArtifactSignal(projectRoot, signals, name, candidates) {
|
|
58310
58559
|
const existing = [];
|
|
58311
58560
|
for (const candidate of candidates) {
|
|
58312
|
-
if (await pathExists(
|
|
58561
|
+
if (await pathExists(path146.join(projectRoot, candidate))) {
|
|
58313
58562
|
existing.push(candidate);
|
|
58314
58563
|
}
|
|
58315
58564
|
}
|
|
@@ -58322,7 +58571,7 @@ async function pushArtifactSignal(projectRoot, signals, name, candidates) {
|
|
|
58322
58571
|
}
|
|
58323
58572
|
async function pushGdwikiSignal(projectRoot, signals) {
|
|
58324
58573
|
const indexPath = ".metaproject/wiki/index.md";
|
|
58325
|
-
if (!await pathExists(
|
|
58574
|
+
if (!await pathExists(path146.join(projectRoot, indexPath))) {
|
|
58326
58575
|
signals.push({
|
|
58327
58576
|
name: "evidence:gdwiki",
|
|
58328
58577
|
status: "warn",
|
|
@@ -58420,10 +58669,10 @@ ${signalRows}
|
|
|
58420
58669
|
${recommendations}
|
|
58421
58670
|
`;
|
|
58422
58671
|
}
|
|
58423
|
-
function parseSkillMetadata(skillMd) {
|
|
58672
|
+
function parseSkillMetadata(skillMd, registryEntry) {
|
|
58424
58673
|
return {
|
|
58425
|
-
version: readMetadataLine(skillMd, "Version"),
|
|
58426
|
-
target: readMetadataLine(skillMd, "Target"),
|
|
58674
|
+
version: readMetadataLine(skillMd, "Version") ?? parseSkillFrontmatter(skillMd).metadataVersion,
|
|
58675
|
+
target: readMetadataLine(skillMd, "Target") ?? registryEntry?.target,
|
|
58427
58676
|
module: readMetadataLine(skillMd, "Module"),
|
|
58428
58677
|
status: readMetadataLine(skillMd, "Status"),
|
|
58429
58678
|
lastVerified: readMetadataLine(skillMd, "Last Verified")
|
|
@@ -58441,6 +58690,22 @@ function updateLastVerified(skillMd, verifiedAt) {
|
|
|
58441
58690
|
return skillMd.replace(/^Status:.*$/m, (line) => `${line}
|
|
58442
58691
|
Last Verified: ${verifiedAt}`);
|
|
58443
58692
|
}
|
|
58693
|
+
if (/^Imported At:.*$/m.test(skillMd)) {
|
|
58694
|
+
return skillMd.replace(/^Imported At:.*$/m, (line) => `${line}
|
|
58695
|
+
Last Verified: ${verifiedAt}`);
|
|
58696
|
+
}
|
|
58697
|
+
if (skillMd.startsWith("---")) {
|
|
58698
|
+
const end = skillMd.indexOf(`
|
|
58699
|
+
---`, 3);
|
|
58700
|
+
if (end !== -1) {
|
|
58701
|
+
const afterFence = end + `
|
|
58702
|
+
---`.length;
|
|
58703
|
+
const insertAt = skillMd[afterFence] === `
|
|
58704
|
+
` ? afterFence + 1 : afterFence;
|
|
58705
|
+
return `${skillMd.slice(0, insertAt)}Last Verified: ${verifiedAt}
|
|
58706
|
+
${skillMd.slice(insertAt)}`;
|
|
58707
|
+
}
|
|
58708
|
+
}
|
|
58444
58709
|
return skillMd;
|
|
58445
58710
|
}
|
|
58446
58711
|
function inferModuleFromPath(packageRoot) {
|
|
@@ -58455,7 +58720,7 @@ function escapeRegExp6(value) {
|
|
|
58455
58720
|
|
|
58456
58721
|
// src/gdskills/bundled-eval.ts
|
|
58457
58722
|
import { existsSync as existsSync25, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "fs";
|
|
58458
|
-
import
|
|
58723
|
+
import path147 from "path";
|
|
58459
58724
|
|
|
58460
58725
|
// src/gdskills/model-tier.ts
|
|
58461
58726
|
var MODEL_TIERS = ["light", "standard", "deep"];
|
|
@@ -58785,7 +59050,7 @@ var CEILINGS_BY_KEY = {
|
|
|
58785
59050
|
"review/review-highload": 550,
|
|
58786
59051
|
"review/review-layout": 238,
|
|
58787
59052
|
"review/review-logic": 377,
|
|
58788
|
-
"review/review-orchestrator":
|
|
59053
|
+
"review/review-orchestrator": 1895,
|
|
58789
59054
|
"review/review-performance": 374,
|
|
58790
59055
|
"review/review-pr-feedback": 895,
|
|
58791
59056
|
"review/review-regression": 216,
|
|
@@ -58912,8 +59177,8 @@ function evaluatorSource() {
|
|
|
58912
59177
|
if (here.protocol !== "file:")
|
|
58913
59178
|
return "unknown";
|
|
58914
59179
|
const dir = decodeURIComponent(here.pathname);
|
|
58915
|
-
const rel =
|
|
58916
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
59180
|
+
const rel = path147.relative(process.cwd(), dir);
|
|
59181
|
+
return rel.length > 0 && !rel.startsWith("..") && !path147.isAbsolute(rel) ? rel : dir;
|
|
58917
59182
|
} catch {
|
|
58918
59183
|
return "unknown";
|
|
58919
59184
|
}
|
|
@@ -59315,7 +59580,7 @@ function scanCrossReferences(text, root, known, checkSkillNames, add) {
|
|
|
59315
59580
|
if (GENERATED_PATH_ROOTS.some((entry) => resolved === entry.prefix.replace(/\/$/, "") || resolved.startsWith(entry.prefix))) {
|
|
59316
59581
|
continue;
|
|
59317
59582
|
}
|
|
59318
|
-
if (existsSync25(
|
|
59583
|
+
if (existsSync25(path147.join(root, resolved)))
|
|
59319
59584
|
continue;
|
|
59320
59585
|
add("xref:path", index + 1, `path \`${raw}\` resolves to nothing under the shipped tree (looked for \`${resolved}\`).`);
|
|
59321
59586
|
}
|
|
@@ -59325,7 +59590,7 @@ var SKILL_DOCUMENT_NAMES = new Set(HARNESS_SKILL_RUNTIMES.map((runtime) => skill
|
|
|
59325
59590
|
var KNOWN_SKILL_COMPANION_DOCUMENTS = new Map([
|
|
59326
59591
|
[
|
|
59327
59592
|
"SKILL.detail.md",
|
|
59328
|
-
"overflow reference
|
|
59593
|
+
"overflow reference linked from its skill's SKILL.md (`orchestration/feature-analyzer`, `review/review-orchestrator`); carries no frontmatter and is not addressed by any runtime"
|
|
59329
59594
|
],
|
|
59330
59595
|
[
|
|
59331
59596
|
"orchestrator-prompt.md",
|
|
@@ -59338,7 +59603,7 @@ function walkSkillDocuments(root, accept) {
|
|
|
59338
59603
|
const out = [];
|
|
59339
59604
|
const walk = (dir) => {
|
|
59340
59605
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
59341
|
-
const full =
|
|
59606
|
+
const full = path147.join(dir, entry.name);
|
|
59342
59607
|
if (entry.isDirectory()) {
|
|
59343
59608
|
walk(full);
|
|
59344
59609
|
continue;
|
|
@@ -59360,26 +59625,26 @@ function bundledSkillCompanionDocuments(root) {
|
|
|
59360
59625
|
return walkSkillDocuments(root, (name) => KNOWN_SKILL_COMPANION_DOCUMENTS.has(name));
|
|
59361
59626
|
}
|
|
59362
59627
|
function bundledRuleFiles(root) {
|
|
59363
|
-
const rulesRoot =
|
|
59628
|
+
const rulesRoot = path147.join(root, "rules", "core");
|
|
59364
59629
|
if (!existsSync25(rulesRoot))
|
|
59365
59630
|
return [];
|
|
59366
|
-
return readdirSync2(rulesRoot, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) =>
|
|
59631
|
+
return readdirSync2(rulesRoot, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => path147.join(rulesRoot, entry.name)).sort();
|
|
59367
59632
|
}
|
|
59368
59633
|
function defaultBundledRoot() {
|
|
59369
|
-
const directPath =
|
|
59634
|
+
const directPath = path147.join(import.meta.dir, "bundled");
|
|
59370
59635
|
if (existsSync25(directPath))
|
|
59371
59636
|
return directPath;
|
|
59372
|
-
const packagedPath =
|
|
59637
|
+
const packagedPath = path147.join(import.meta.dir, "..", "src", "gdskills", "bundled");
|
|
59373
59638
|
if (existsSync25(packagedPath))
|
|
59374
59639
|
return packagedPath;
|
|
59375
59640
|
return directPath;
|
|
59376
59641
|
}
|
|
59377
59642
|
function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
59378
|
-
const skillsRoot =
|
|
59643
|
+
const skillsRoot = path147.join(root, "skills");
|
|
59379
59644
|
const canonical = bundledSkillFiles(skillsRoot);
|
|
59380
59645
|
const files = bundledSkillDocuments(skillsRoot);
|
|
59381
59646
|
const companionDocuments = bundledSkillCompanionDocuments(skillsRoot);
|
|
59382
|
-
const skillNames = [...new Set(canonical.map((file) =>
|
|
59647
|
+
const skillNames = [...new Set(canonical.map((file) => path147.basename(path147.dirname(file))))].sort();
|
|
59383
59648
|
const known = new Set(skillNames);
|
|
59384
59649
|
const findings = [];
|
|
59385
59650
|
const declaredNames = new Map;
|
|
@@ -59389,10 +59654,10 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59389
59654
|
const redFlagsByKey = new Map;
|
|
59390
59655
|
const redFlagsFileByKey = new Map;
|
|
59391
59656
|
for (const file of files) {
|
|
59392
|
-
const skillDir =
|
|
59393
|
-
const skill =
|
|
59394
|
-
const rel =
|
|
59395
|
-
const category =
|
|
59657
|
+
const skillDir = path147.dirname(file);
|
|
59658
|
+
const skill = path147.basename(skillDir);
|
|
59659
|
+
const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
|
|
59660
|
+
const category = path147.dirname(rel).split("/")[0] ?? "";
|
|
59396
59661
|
const text = readFileSync7(file, "utf8");
|
|
59397
59662
|
const add = (check, line, message) => {
|
|
59398
59663
|
findings.push({ check, skill, file: rel, line, message });
|
|
@@ -59426,7 +59691,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59426
59691
|
if (served.length === 0) {
|
|
59427
59692
|
add("frontmatter:description", 1, "frontmatter `description` is present but resolves to nothing a harness can match a request against; a block scalar (`description: |`) needs its text on the following indented lines.");
|
|
59428
59693
|
} else {
|
|
59429
|
-
if (
|
|
59694
|
+
if (path147.basename(file) === "SKILL.md") {
|
|
59430
59695
|
const key = `${category}/${skill}`;
|
|
59431
59696
|
descriptionsByKey.set(key, served);
|
|
59432
59697
|
descriptionFileByKey.set(key, rel);
|
|
@@ -59453,14 +59718,14 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59453
59718
|
add("frontmatter:category", 1, `frontmatter \`metadata.category\` is "${declaredCategory}" but this skill ships under \`${category}/${skill}\`; set it to "${category}" or drop the field.`);
|
|
59454
59719
|
}
|
|
59455
59720
|
}
|
|
59456
|
-
if (
|
|
59721
|
+
if (path147.basename(file) === "SKILL.md" && parsed.compatibleHarnesses !== undefined) {
|
|
59457
59722
|
const harnesses = parsed.compatibleHarnesses;
|
|
59458
59723
|
if (!harnesses.includes("claude")) {
|
|
59459
59724
|
add("frontmatter:harness-claude", 1, `frontmatter \`compatible_harnesses\` ("${harnesses.join(",")}") omits \`claude\`, but this file IS the Claude build \u2014 add \`claude\` to the list.`);
|
|
59460
59725
|
}
|
|
59461
59726
|
}
|
|
59462
59727
|
}
|
|
59463
|
-
if (
|
|
59728
|
+
if (path147.basename(file) === "SKILL.md") {
|
|
59464
59729
|
const exemptionKey = `${category}/${skill}`;
|
|
59465
59730
|
redFlagsByKey.set(exemptionKey, redFlagsTableBody(text));
|
|
59466
59731
|
redFlagsFileByKey.set(exemptionKey, rel);
|
|
@@ -59481,7 +59746,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59481
59746
|
add("anatomy:sections", null, `missing ${ANATOMY_SECTION_LABELS[section]}.`);
|
|
59482
59747
|
}
|
|
59483
59748
|
}
|
|
59484
|
-
if (
|
|
59749
|
+
if (path147.basename(file) === "SKILL.md") {
|
|
59485
59750
|
const ceilingKey = `${category}/${skill}`;
|
|
59486
59751
|
const recordedCeiling = SKILL_LENGTH_CEILINGS.get(ceilingKey);
|
|
59487
59752
|
const ceiling = recordedCeiling ?? DEFAULT_SKILL_LENGTH_CEILING;
|
|
@@ -59542,24 +59807,24 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59542
59807
|
}
|
|
59543
59808
|
}
|
|
59544
59809
|
for (const file of bundledRuleFiles(root)) {
|
|
59545
|
-
const rel =
|
|
59810
|
+
const rel = path147.relative(root, file).split(path147.sep).join("/");
|
|
59546
59811
|
const text = readFileSync7(file, "utf8");
|
|
59547
59812
|
const add = (check, line, message) => {
|
|
59548
|
-
findings.push({ check, skill:
|
|
59813
|
+
findings.push({ check, skill: path147.basename(file, path147.extname(file)), file: rel, line, message });
|
|
59549
59814
|
};
|
|
59550
59815
|
scanCrossReferences(text, root, known, false, add);
|
|
59551
59816
|
}
|
|
59552
59817
|
for (const file of companionDocuments) {
|
|
59553
|
-
const skillDir =
|
|
59554
|
-
const skill =
|
|
59555
|
-
const rel =
|
|
59818
|
+
const skillDir = path147.dirname(file);
|
|
59819
|
+
const skill = path147.basename(skillDir);
|
|
59820
|
+
const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
|
|
59556
59821
|
const text = readFileSync7(file, "utf8");
|
|
59557
59822
|
const add = (check, line, message) => {
|
|
59558
59823
|
findings.push({ check, skill, file: rel, line, message });
|
|
59559
59824
|
};
|
|
59560
59825
|
scanCrossReferences(text, root, known, true, add);
|
|
59561
59826
|
}
|
|
59562
|
-
for (const dir of [...new Set(files.map((file) =>
|
|
59827
|
+
for (const dir of [...new Set(files.map((file) => path147.dirname(file)))].sort()) {
|
|
59563
59828
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
59564
59829
|
if (!entry.isFile())
|
|
59565
59830
|
continue;
|
|
@@ -59571,15 +59836,15 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59571
59836
|
continue;
|
|
59572
59837
|
findings.push({
|
|
59573
59838
|
check: "document:addressable",
|
|
59574
|
-
skill:
|
|
59575
|
-
file:
|
|
59839
|
+
skill: path147.basename(dir),
|
|
59840
|
+
file: path147.relative(skillsRoot, path147.join(dir, entry.name)).split(path147.sep).join("/"),
|
|
59576
59841
|
line: null,
|
|
59577
59842
|
message: `\`${entry.name}\` is spelled like a harness build, but no runtime in HARNESS_SKILL_RUNTIMES resolves to it (${[...SKILL_DOCUMENT_NAMES].join(", ")}). Nothing exports it, installs it, or reads it \u2014 delete it, rename it to the build it was meant to be, or register it in KNOWN_SKILL_COMPANION_DOCUMENTS with a reason.`
|
|
59578
59843
|
});
|
|
59579
59844
|
}
|
|
59580
59845
|
}
|
|
59581
59846
|
for (const file of canonical) {
|
|
59582
|
-
const dir =
|
|
59847
|
+
const dir = path147.dirname(file);
|
|
59583
59848
|
let canonicalComparable;
|
|
59584
59849
|
try {
|
|
59585
59850
|
canonicalComparable = buildComparableText(readFileSync7(file, "utf8"));
|
|
@@ -59588,9 +59853,9 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59588
59853
|
}
|
|
59589
59854
|
for (const runtime of HARNESS_SKILL_RUNTIMES) {
|
|
59590
59855
|
const buildName = skillBuildFileName(runtime);
|
|
59591
|
-
if (buildName ===
|
|
59856
|
+
if (buildName === path147.basename(file))
|
|
59592
59857
|
continue;
|
|
59593
|
-
const buildPath =
|
|
59858
|
+
const buildPath = path147.join(dir, buildName);
|
|
59594
59859
|
if (!existsSync25(buildPath))
|
|
59595
59860
|
continue;
|
|
59596
59861
|
let buildComparable;
|
|
@@ -59603,8 +59868,8 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
59603
59868
|
continue;
|
|
59604
59869
|
findings.push({
|
|
59605
59870
|
check: "document:build-parity",
|
|
59606
|
-
skill:
|
|
59607
|
-
file:
|
|
59871
|
+
skill: path147.basename(dir),
|
|
59872
|
+
file: path147.relative(skillsRoot, buildPath).split(path147.sep).join("/"),
|
|
59608
59873
|
line: null,
|
|
59609
59874
|
message: `\`${buildName}\` no longer carries the content of its \`SKILL.md\`, ignoring the fields a build may set for itself (${[...BUILD_DIVERGENCE_ALLOWED_FIELDS].join(", ")}). An agent on that runtime reads this file, not the canonical one \u2014 re-export the build.`
|
|
59610
59875
|
});
|
|
@@ -59673,7 +59938,7 @@ async function skillsCommand(args) {
|
|
|
59673
59938
|
}
|
|
59674
59939
|
if (command === "install") {
|
|
59675
59940
|
const profile = normalizeGdskillsProfile(optionValue(args, "--profile"));
|
|
59676
|
-
const metaprojectRoot =
|
|
59941
|
+
const metaprojectRoot = path148.join(process.cwd(), ".metaproject");
|
|
59677
59942
|
banner("keryx skills install", `Profile: ${profile}`);
|
|
59678
59943
|
if (!await pathExists(metaprojectRoot)) {
|
|
59679
59944
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
@@ -59799,11 +60064,11 @@ async function inspectProjectSkill(args) {
|
|
|
59799
60064
|
process.exitCode = 1;
|
|
59800
60065
|
return;
|
|
59801
60066
|
}
|
|
59802
|
-
const skillRoot =
|
|
59803
|
-
const skillMdPath =
|
|
59804
|
-
const verificationPath =
|
|
59805
|
-
const changelogPath =
|
|
59806
|
-
const reportPath =
|
|
60067
|
+
const skillRoot = path148.resolve(process.cwd(), entry.path);
|
|
60068
|
+
const skillMdPath = path148.join(skillRoot, "SKILL.md");
|
|
60069
|
+
const verificationPath = path148.join(skillRoot, "verification.md");
|
|
60070
|
+
const changelogPath = path148.join(skillRoot, "skill-changelog.md");
|
|
60071
|
+
const reportPath = path148.join(process.cwd(), ".metaproject", "data", "gdskills", "reports", `${entry.module}-${entry.name}-verification.json`);
|
|
59807
60072
|
const metadata = await pathExists(skillMdPath) ? parseProjectSkillMetadata(await readFile83(skillMdPath, "utf8")) : {};
|
|
59808
60073
|
const inspection = {
|
|
59809
60074
|
module: entry.module,
|
|
@@ -59837,7 +60102,7 @@ async function inspectProjectSkill(args) {
|
|
|
59837
60102
|
console.log(`- latest report: ${inspection.files.latestReport}`);
|
|
59838
60103
|
}
|
|
59839
60104
|
async function readProjectSkillRegistryFromManifest() {
|
|
59840
|
-
const manifestPath =
|
|
60105
|
+
const manifestPath = path148.join(process.cwd(), ".metaproject", "metaproject.json");
|
|
59841
60106
|
if (!await pathExists(manifestPath)) {
|
|
59842
60107
|
return [];
|
|
59843
60108
|
}
|
|
@@ -59997,7 +60262,7 @@ function scoreProjectSkillRoute(entry, query) {
|
|
|
59997
60262
|
return { entry, score: 0, reasons: [] };
|
|
59998
60263
|
}
|
|
59999
60264
|
const key = `${entry.module}/${entry.name}`;
|
|
60000
|
-
const targetBase =
|
|
60265
|
+
const targetBase = path148.basename(entry.target).replace(/\.[^.]+$/, "");
|
|
60001
60266
|
const fields = {
|
|
60002
60267
|
key: normalizeRouteText(key),
|
|
60003
60268
|
module: normalizeRouteText(entry.module),
|
|
@@ -60266,7 +60531,7 @@ function verifyBundledSkills(args) {
|
|
|
60266
60531
|
}
|
|
60267
60532
|
if (evaluation.skills === 0) {
|
|
60268
60533
|
console.error(`
|
|
60269
|
-
No SKILL.md found under ${
|
|
60534
|
+
No SKILL.md found under ${path148.join(root, "skills")}. Nothing was evaluated, so nothing was verified.`);
|
|
60270
60535
|
process.exitCode = 1;
|
|
60271
60536
|
return;
|
|
60272
60537
|
}
|
|
@@ -60275,7 +60540,7 @@ No SKILL.md found under ${path147.join(root, "skills")}. Nothing was evaluated,
|
|
|
60275
60540
|
}
|
|
60276
60541
|
}
|
|
60277
60542
|
async function verifyAllProjectSkills(args) {
|
|
60278
|
-
const manifestPath =
|
|
60543
|
+
const manifestPath = path148.join(process.cwd(), ".metaproject", "metaproject.json");
|
|
60279
60544
|
if (!await pathExists(manifestPath)) {
|
|
60280
60545
|
console.error("Metaproject is not initialized. Run: keryx init");
|
|
60281
60546
|
process.exitCode = 1;
|
|
@@ -60379,7 +60644,7 @@ async function contractsCommand(args) {
|
|
|
60379
60644
|
return;
|
|
60380
60645
|
}
|
|
60381
60646
|
try {
|
|
60382
|
-
const result = await validateContractFile(
|
|
60647
|
+
const result = await validateContractFile(path148.resolve(filePath), schemaName);
|
|
60383
60648
|
if (result.valid) {
|
|
60384
60649
|
console.log(`valid: ${result.file}`);
|
|
60385
60650
|
console.log(`schema: ${result.schema}`);
|
|
@@ -60424,10 +60689,10 @@ async function printGdskillsStatus(args) {
|
|
|
60424
60689
|
console.log(`learning proposals: ${summary.learningProposals.total} ` + `(pending ${summary.learningProposals.pending}, applied ${summary.learningProposals.applied})`);
|
|
60425
60690
|
}
|
|
60426
60691
|
async function getGdskillsStatusSummary() {
|
|
60427
|
-
const root =
|
|
60428
|
-
const manifestPath =
|
|
60429
|
-
const catalogPath =
|
|
60430
|
-
const skillsRoot =
|
|
60692
|
+
const root = path148.join(process.cwd(), ".metaproject");
|
|
60693
|
+
const manifestPath = path148.join(root, "metaproject.json");
|
|
60694
|
+
const catalogPath = path148.join(root, "skills", "catalog.md");
|
|
60695
|
+
const skillsRoot = path148.join(root, "skills", "gdskills");
|
|
60431
60696
|
if (!await pathExists(root)) {
|
|
60432
60697
|
return {
|
|
60433
60698
|
initialized: false,
|
|
@@ -60464,9 +60729,9 @@ async function getGdskillsStatusSummary() {
|
|
|
60464
60729
|
profile = normalizeGdskillsProfile(manifest.modules?.gdskills?.profile);
|
|
60465
60730
|
projectSkillRegistry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
60466
60731
|
}
|
|
60467
|
-
const reports = await readVerificationReports(
|
|
60732
|
+
const reports = await readVerificationReports(path148.join(root, "data", "gdskills", "reports"));
|
|
60468
60733
|
const reportKeys = new Set(reports.map((report) => `${report.module}/${report.name}`));
|
|
60469
|
-
const proposals = await readProposalFiles(
|
|
60734
|
+
const proposals = await readProposalFiles(path148.join(root, "data", "gdskills", "proposals"));
|
|
60470
60735
|
const statusCounts = countVerificationStatuses(reports);
|
|
60471
60736
|
return {
|
|
60472
60737
|
initialized: true,
|
|
@@ -60510,8 +60775,8 @@ async function readVerificationReports(reportsRoot) {
|
|
|
60510
60775
|
}
|
|
60511
60776
|
async function readProposalFiles(proposalsRoot) {
|
|
60512
60777
|
const files = await listJsonFiles(proposalsRoot);
|
|
60513
|
-
const appliedIds = new Set(files.filter((filePath) => filePath.endsWith(".applied.json")).map((filePath) =>
|
|
60514
|
-
const proposalIds = files.filter((filePath) => !filePath.endsWith(".applied.json")).map((filePath) =>
|
|
60778
|
+
const appliedIds = new Set(files.filter((filePath) => filePath.endsWith(".applied.json")).map((filePath) => path148.basename(filePath).replace(/\.applied\.json$/, "")));
|
|
60779
|
+
const proposalIds = files.filter((filePath) => !filePath.endsWith(".applied.json")).map((filePath) => path148.basename(filePath).replace(/\.json$/, ""));
|
|
60515
60780
|
return {
|
|
60516
60781
|
total: proposalIds.length,
|
|
60517
60782
|
pending: proposalIds.filter((id) => !appliedIds.has(id)).length,
|
|
@@ -60523,7 +60788,7 @@ async function listJsonFiles(root) {
|
|
|
60523
60788
|
return [];
|
|
60524
60789
|
}
|
|
60525
60790
|
const entries = await readdir25(root, { withFileTypes: true });
|
|
60526
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) =>
|
|
60791
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path148.join(root, entry.name));
|
|
60527
60792
|
}
|
|
60528
60793
|
function countVerificationStatuses(reports) {
|
|
60529
60794
|
return reports.reduce((acc, report) => {
|
|
@@ -60541,7 +60806,7 @@ function latest(values) {
|
|
|
60541
60806
|
return sorted.at(-1) ?? "never";
|
|
60542
60807
|
}
|
|
60543
60808
|
function relativeToCwd(filePath) {
|
|
60544
|
-
return
|
|
60809
|
+
return path148.relative(process.cwd(), filePath) || ".";
|
|
60545
60810
|
}
|
|
60546
60811
|
function printSkillsHelp() {
|
|
60547
60812
|
console.log(`keryx skills
|
|
@@ -60763,9 +61028,9 @@ init_service7();
|
|
|
60763
61028
|
init_fs();
|
|
60764
61029
|
init_util();
|
|
60765
61030
|
import { readdir as readdir26, readFile as readFile84 } from "fs/promises";
|
|
60766
|
-
import
|
|
61031
|
+
import path149 from "path";
|
|
60767
61032
|
async function loadHistory(cwd, limit = 20) {
|
|
60768
|
-
const dir =
|
|
61033
|
+
const dir = path149.join(dataRoot2(cwd), "history");
|
|
60769
61034
|
if (!await pathExists(dir)) {
|
|
60770
61035
|
return [];
|
|
60771
61036
|
}
|
|
@@ -60773,7 +61038,7 @@ async function loadHistory(cwd, limit = 20) {
|
|
|
60773
61038
|
const points = [];
|
|
60774
61039
|
for (const file of files.slice(-limit)) {
|
|
60775
61040
|
try {
|
|
60776
|
-
const report = JSON.parse(await readFile84(
|
|
61041
|
+
const report = JSON.parse(await readFile84(path149.join(dir, file), "utf8"));
|
|
60777
61042
|
const scores = {};
|
|
60778
61043
|
for (const metric of report.metrics ?? []) {
|
|
60779
61044
|
scores[metric.key] = metric.health_score;
|
|
@@ -61508,7 +61773,7 @@ init_github();
|
|
|
61508
61773
|
init_flow_move();
|
|
61509
61774
|
init_service7();
|
|
61510
61775
|
init_guard2();
|
|
61511
|
-
import
|
|
61776
|
+
import path150 from "path";
|
|
61512
61777
|
init_machine();
|
|
61513
61778
|
|
|
61514
61779
|
// src/flow/types.ts
|
|
@@ -62015,7 +62280,7 @@ async function runSchema(args) {
|
|
|
62015
62280
|
`;
|
|
62016
62281
|
const out = optionValue(args, "--out");
|
|
62017
62282
|
if (out) {
|
|
62018
|
-
const target =
|
|
62283
|
+
const target = path150.isAbsolute(out) ? out : path150.join(process.cwd(), out);
|
|
62019
62284
|
await writeFileAtomic(target, json);
|
|
62020
62285
|
console.log(` ${style.green(symbols.ok)} Wrote flow-state schema ${style.cyan(symbols.arrow)} ${out}`);
|
|
62021
62286
|
return;
|
|
@@ -62113,8 +62378,8 @@ init_args();
|
|
|
62113
62378
|
|
|
62114
62379
|
// src/job/service.ts
|
|
62115
62380
|
init_fs();
|
|
62116
|
-
import { copyFile as copyFile5, mkdir as
|
|
62117
|
-
import
|
|
62381
|
+
import { copyFile as copyFile5, mkdir as mkdir53 } from "fs/promises";
|
|
62382
|
+
import path152 from "path";
|
|
62118
62383
|
|
|
62119
62384
|
// src/job/types.ts
|
|
62120
62385
|
var JOB_INTENTS = ["implement", "analyze", "review", "custom"];
|
|
@@ -62252,17 +62517,17 @@ function defaultPlan(intent) {
|
|
|
62252
62517
|
|
|
62253
62518
|
// src/job/store.ts
|
|
62254
62519
|
init_validator();
|
|
62255
|
-
import { appendFile as appendFile6, mkdir as
|
|
62256
|
-
import
|
|
62520
|
+
import { appendFile as appendFile6, mkdir as mkdir52, readFile as readFile85, readdir as readdir27 } from "fs/promises";
|
|
62521
|
+
import path151 from "path";
|
|
62257
62522
|
init_fs();
|
|
62258
62523
|
function jobsRoot(cwd) {
|
|
62259
|
-
return
|
|
62524
|
+
return path151.join(cwd, ".metaproject", "jobs");
|
|
62260
62525
|
}
|
|
62261
62526
|
function jobDir(cwd, name) {
|
|
62262
|
-
return
|
|
62527
|
+
return path151.join(jobsRoot(cwd), name);
|
|
62263
62528
|
}
|
|
62264
62529
|
function jobStatePath(cwd, name) {
|
|
62265
|
-
return
|
|
62530
|
+
return path151.join(jobDir(cwd, name), "state.json");
|
|
62266
62531
|
}
|
|
62267
62532
|
var JOB_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
62268
62533
|
function assertJobName(name) {
|
|
@@ -62299,12 +62564,12 @@ async function writeJob(cwd, name, state) {
|
|
|
62299
62564
|
`));
|
|
62300
62565
|
}
|
|
62301
62566
|
const file = jobStatePath(cwd, name);
|
|
62302
|
-
await
|
|
62567
|
+
await mkdir52(path151.dirname(file), { recursive: true });
|
|
62303
62568
|
await writeFileAtomic(file, `${JSON.stringify(state, null, 2)}
|
|
62304
62569
|
`);
|
|
62305
62570
|
}
|
|
62306
62571
|
async function appendJournal2(cwd, name, at, line) {
|
|
62307
|
-
await appendFile6(
|
|
62572
|
+
await appendFile6(path151.join(jobDir(cwd, name), "journal.md"), `- ${at} - ${line}
|
|
62308
62573
|
`, "utf8");
|
|
62309
62574
|
}
|
|
62310
62575
|
function renderJournal2(name, at) {
|
|
@@ -62325,7 +62590,7 @@ function assertDocumentType(raw) {
|
|
|
62325
62590
|
return raw;
|
|
62326
62591
|
}
|
|
62327
62592
|
function documentFileName(type, sourcePath) {
|
|
62328
|
-
const extension =
|
|
62593
|
+
const extension = path151.extname(sourcePath) || ".md";
|
|
62329
62594
|
return `${type}${extension}`;
|
|
62330
62595
|
}
|
|
62331
62596
|
|
|
@@ -62333,7 +62598,7 @@ function documentFileName(type, sourcePath) {
|
|
|
62333
62598
|
function createJobService(deps) {
|
|
62334
62599
|
const now = () => deps.now().toISOString();
|
|
62335
62600
|
function lockPath(cwd, name) {
|
|
62336
|
-
return
|
|
62601
|
+
return path152.join(jobsRoot(cwd), `.job-lock-${name}`);
|
|
62337
62602
|
}
|
|
62338
62603
|
async function mutate(cwd, name, run) {
|
|
62339
62604
|
assertJobName(name);
|
|
@@ -62371,7 +62636,7 @@ function createJobService(deps) {
|
|
|
62371
62636
|
if (await pathExists(absolute)) {
|
|
62372
62637
|
throw new Error(`Job package already exists: .metaproject/jobs/${name}. ` + `Run: keryx job status ${name}`);
|
|
62373
62638
|
}
|
|
62374
|
-
await
|
|
62639
|
+
await mkdir53(absolute, { recursive: true });
|
|
62375
62640
|
const createdAt = now();
|
|
62376
62641
|
const state = {
|
|
62377
62642
|
phase: "PLAN",
|
|
@@ -62382,20 +62647,20 @@ function createJobService(deps) {
|
|
|
62382
62647
|
},
|
|
62383
62648
|
plan: { steps: defaultPlan(intent) },
|
|
62384
62649
|
documentation: {
|
|
62385
|
-
job_path:
|
|
62650
|
+
job_path: path152.join(".metaproject", "jobs", name),
|
|
62386
62651
|
documents_created: []
|
|
62387
62652
|
},
|
|
62388
62653
|
metrics: { steps: [] },
|
|
62389
|
-
jobs_root:
|
|
62654
|
+
jobs_root: path152.join(".metaproject", "jobs"),
|
|
62390
62655
|
updated_at: createdAt
|
|
62391
62656
|
};
|
|
62392
62657
|
const first = firstOpenStep(state.plan.steps);
|
|
62393
62658
|
if (first) {
|
|
62394
62659
|
state.plan.current_step = first.id;
|
|
62395
62660
|
}
|
|
62396
|
-
await writeFileAtomic(
|
|
62661
|
+
await writeFileAtomic(path152.join(absolute, "journal.md"), renderJournal2(name, createdAt));
|
|
62397
62662
|
await writeJob(input.cwd, name, state);
|
|
62398
|
-
return { state, dir:
|
|
62663
|
+
return { state, dir: path152.relative(input.cwd, absolute) };
|
|
62399
62664
|
},
|
|
62400
62665
|
async status({ cwd, name }) {
|
|
62401
62666
|
assertJobName(name);
|
|
@@ -62407,7 +62672,7 @@ function createJobService(deps) {
|
|
|
62407
62672
|
}
|
|
62408
62673
|
return {
|
|
62409
62674
|
state,
|
|
62410
|
-
dir:
|
|
62675
|
+
dir: path152.join(".metaproject", "jobs", name),
|
|
62411
62676
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
62412
62677
|
stepsTotal: state.plan.steps.length,
|
|
62413
62678
|
nextStep: firstOpenStep(state.plan.steps),
|
|
@@ -62464,23 +62729,23 @@ function createJobService(deps) {
|
|
|
62464
62729
|
if (!input.file) {
|
|
62465
62730
|
throw new Error("Missing --file. Usage: keryx job document <name> --type <type> --file <path>");
|
|
62466
62731
|
}
|
|
62467
|
-
const source =
|
|
62732
|
+
const source = path152.isAbsolute(input.file) ? input.file : path152.join(input.cwd, input.file);
|
|
62468
62733
|
if (!await pathExists(source)) {
|
|
62469
62734
|
throw new Error(`--file not found: ${input.file}. Write the document first, then record it.`);
|
|
62470
62735
|
}
|
|
62471
62736
|
let recorded;
|
|
62472
62737
|
await mutate(input.cwd, input.name, async (state) => {
|
|
62473
62738
|
const fileName = documentFileName(type, source);
|
|
62474
|
-
const target =
|
|
62739
|
+
const target = path152.join(jobDir(input.cwd, state.job_name), fileName);
|
|
62475
62740
|
await copyFile5(source, target);
|
|
62476
62741
|
state.documentation ??= {};
|
|
62477
|
-
state.documentation.job_path ??=
|
|
62742
|
+
state.documentation.job_path ??= path152.join(".metaproject", "jobs", state.job_name);
|
|
62478
62743
|
const created = state.documentation.documents_created ?? [];
|
|
62479
62744
|
state.documentation.documents_created = created.includes(fileName) ? created : [...created, fileName];
|
|
62480
62745
|
recorded = {
|
|
62481
62746
|
state,
|
|
62482
62747
|
document: fileName,
|
|
62483
|
-
path:
|
|
62748
|
+
path: path152.join(".metaproject", "jobs", state.job_name, fileName)
|
|
62484
62749
|
};
|
|
62485
62750
|
return { state, event: "document", detail: `${type} -> ${fileName}` };
|
|
62486
62751
|
});
|
|
@@ -62515,7 +62780,7 @@ function createJobService(deps) {
|
|
|
62515
62780
|
name: state.job_name,
|
|
62516
62781
|
phase: state.phase,
|
|
62517
62782
|
intent: state.intent,
|
|
62518
|
-
dir:
|
|
62783
|
+
dir: path152.join(".metaproject", "jobs", name),
|
|
62519
62784
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
62520
62785
|
stepsTotal: state.plan.steps.length,
|
|
62521
62786
|
nextStep: firstOpenStep(state.plan.steps)?.id ?? null
|
|
@@ -62724,17 +62989,17 @@ function printHelp11() {
|
|
|
62724
62989
|
// src/commands/review.ts
|
|
62725
62990
|
init_args();
|
|
62726
62991
|
init_fs();
|
|
62727
|
-
import { mkdir as
|
|
62728
|
-
import
|
|
62992
|
+
import { mkdir as mkdir55, readdir as readdir30, readFile as readFile88 } from "fs/promises";
|
|
62993
|
+
import path156, { join as join3 } from "path";
|
|
62729
62994
|
|
|
62730
62995
|
// src/review/review-learning.ts
|
|
62731
62996
|
init_fs();
|
|
62732
62997
|
init_json();
|
|
62733
62998
|
init_pr_comments();
|
|
62734
|
-
import
|
|
62999
|
+
import path153 from "path";
|
|
62735
63000
|
var REVIEW_LEARNING_CONFIG_SCHEMA_VERSION = 1;
|
|
62736
63001
|
function reviewLearningConfigPath(cwd) {
|
|
62737
|
-
return
|
|
63002
|
+
return path153.join(cwd, ".metaproject", "review-learning.config.json");
|
|
62738
63003
|
}
|
|
62739
63004
|
async function loadReviewLearningConfig(cwd) {
|
|
62740
63005
|
const file = reviewLearningConfigPath(cwd);
|
|
@@ -62742,7 +63007,7 @@ async function loadReviewLearningConfig(cwd) {
|
|
|
62742
63007
|
return null;
|
|
62743
63008
|
}
|
|
62744
63009
|
const raw = await readJsonFile(file);
|
|
62745
|
-
const where =
|
|
63010
|
+
const where = path153.relative(cwd, file);
|
|
62746
63011
|
if (raw.schemaVersion !== REVIEW_LEARNING_CONFIG_SCHEMA_VERSION) {
|
|
62747
63012
|
throw new Error(`${where}: schemaVersion must be ${REVIEW_LEARNING_CONFIG_SCHEMA_VERSION}, got ${JSON.stringify(raw.schemaVersion)}.`);
|
|
62748
63013
|
}
|
|
@@ -62784,10 +63049,10 @@ function selectLearnableComments(state, authors) {
|
|
|
62784
63049
|
return { kept, unconfigured, bodyless };
|
|
62785
63050
|
}
|
|
62786
63051
|
function learningSourcePath(cwd, repo, number) {
|
|
62787
|
-
return
|
|
63052
|
+
return path153.join(cwd, ".metaproject", "data", "gdskills", "learning-sources", `${slug3(repo)}__${number}.json`);
|
|
62788
63053
|
}
|
|
62789
63054
|
function learningRecordPath(cwd, repo, number) {
|
|
62790
|
-
return
|
|
63055
|
+
return path153.join(cwd, ".metaproject", "data", "gdskills", "learning-sources", `${slug3(repo)}__${number}.md`);
|
|
62791
63056
|
}
|
|
62792
63057
|
function slug3(repo) {
|
|
62793
63058
|
return repo.replace(/\//g, "__");
|
|
@@ -62849,8 +63114,8 @@ ${learningSourceLessons(selection).map((lesson) => `- ${lesson}
|
|
|
62849
63114
|
|
|
62850
63115
|
// src/review/managed.ts
|
|
62851
63116
|
init_validator();
|
|
62852
|
-
import { mkdir as
|
|
62853
|
-
import
|
|
63117
|
+
import { mkdir as mkdir54, readFile as readFile86, readdir as readdir28, realpath as realpath8, rm as rm11, stat as stat16 } from "fs/promises";
|
|
63118
|
+
import path154 from "path";
|
|
62854
63119
|
init_fs();
|
|
62855
63120
|
init_store2();
|
|
62856
63121
|
|
|
@@ -64962,7 +65227,7 @@ init_review_notes();
|
|
|
64962
65227
|
init_types3();
|
|
64963
65228
|
var REQUIRED_ARTIFACTS = ["scope", "coverage", "report", "findings", "learning", "decisions"];
|
|
64964
65229
|
function reviewsRoot(cwd) {
|
|
64965
|
-
return
|
|
65230
|
+
return path154.join(cwd, ".metaproject", "reviews");
|
|
64966
65231
|
}
|
|
64967
65232
|
async function resolveGitHead2(cwd) {
|
|
64968
65233
|
try {
|
|
@@ -65095,27 +65360,27 @@ async function createManagedReviewPackage(input) {
|
|
|
65095
65360
|
if (contractErrors.length > 0) {
|
|
65096
65361
|
throw new Error(`Refusing to record findings that do not satisfy review-finding.schema.json: ${contractErrors.map((error) => `${error.path} ${error.message}`).join("; ")}`);
|
|
65097
65362
|
}
|
|
65098
|
-
await
|
|
65099
|
-
await writeFileAtomic(
|
|
65363
|
+
await mkdir54(packageDir, { recursive: true });
|
|
65364
|
+
await writeFileAtomic(path154.join(packageDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
65100
65365
|
`);
|
|
65101
|
-
await writeFileAtomic(
|
|
65102
|
-
await writeFileAtomic(
|
|
65103
|
-
await writeFileAtomic(
|
|
65104
|
-
await writeFileAtomic(
|
|
65366
|
+
await writeFileAtomic(path154.join(packageDir, "scope.md"), renderScope(input, flowMatch, at, verification, carried, caps, externalReclaims, scopeB.record, filterStats));
|
|
65367
|
+
await writeFileAtomic(path154.join(packageDir, "coverage.md"), renderCoverage(coverage));
|
|
65368
|
+
await writeFileAtomic(path154.join(packageDir, "report.md"), renderReport(report, input.mode));
|
|
65369
|
+
await writeFileAtomic(path154.join(packageDir, "findings.json"), `${JSON.stringify(findings.map(toContractFinding), null, 2)}
|
|
65105
65370
|
`);
|
|
65106
|
-
await writeFileAtomic(
|
|
65107
|
-
await writeFileAtomic(
|
|
65371
|
+
await writeFileAtomic(path154.join(packageDir, "learning.md"), renderLearning(findings));
|
|
65372
|
+
await writeFileAtomic(path154.join(packageDir, "decisions.md"), renderDecisions(findings));
|
|
65108
65373
|
await persistScreenRadius(packageDir, scopeB.resolved);
|
|
65109
65374
|
const reviewNotes = await writeReviewNotes(findings, {
|
|
65110
65375
|
cwd: input.cwd,
|
|
65111
65376
|
reviewId,
|
|
65112
65377
|
head,
|
|
65113
|
-
packagePath:
|
|
65378
|
+
packagePath: path154.relative(input.cwd, packageDir),
|
|
65114
65379
|
...input.now === undefined ? {} : { now: input.now }
|
|
65115
65380
|
});
|
|
65116
65381
|
return {
|
|
65117
65382
|
reviewId,
|
|
65118
|
-
path:
|
|
65383
|
+
path: path154.relative(input.cwd, packageDir),
|
|
65119
65384
|
manifest,
|
|
65120
65385
|
verification: verification.counts,
|
|
65121
65386
|
verificationRejections: verification.rejections,
|
|
@@ -65132,7 +65397,7 @@ async function screenScopeBFindings(input, packageDir, findings) {
|
|
|
65132
65397
|
const scopeB = findings.filter((finding) => isBlastRadiusScopedFinding(finding, reviewers));
|
|
65133
65398
|
if (resolved === undefined) {
|
|
65134
65399
|
if (scopeB.length > 0) {
|
|
65135
|
-
throw new Error(`Refusing to record ${scopeB.length} finding(s) raised under scope B (${scopeB.map((finding) => `${finding.id} by ${finding.reviewer}`).join(", ")}) with no blast-radius record: AC3 is enforced in code, and the screen cannot be applied without the computed set the round was dispatched over. Two ways out, and the first is the one that always works: (1) compute the set with \`keryx review blast-radius --json > <file>\` and pass \`--blast-radius <file>\` to this ingest; (2) write that record to \`${
|
|
65400
|
+
throw new Error(`Refusing to record ${scopeB.length} finding(s) raised under scope B (${scopeB.map((finding) => `${finding.id} by ${finding.reviewer}`).join(", ")}) with no blast-radius record: AC3 is enforced in code, and the screen cannot be applied without the computed set the round was dispatched over. Two ways out, and the first is the one that always works: (1) compute the set with \`keryx review blast-radius --json > <file>\` and pass \`--blast-radius <file>\` to this ingest; (2) write that record to \`${path154.relative(input.cwd, handoffBlastRadiusPath(packageDir))}\` and ingest again \u2014 the ingest reads it from there and consumes it. Do NOT write it to \`${path154.relative(input.cwd, path154.join(packageDir, BLAST_RADIUS_ARTIFACT))}\`: a round that names no --review-id has not allocated that directory yet, so creating it makes the next ingest take the NEXT free name and find nothing there either.`);
|
|
65136
65401
|
}
|
|
65137
65402
|
return { retained: [...findings], record: { source: "none", scopeBFindings: 0 }, resolved: undefined };
|
|
65138
65403
|
}
|
|
@@ -65149,13 +65414,13 @@ async function screenScopeBFindings(input, packageDir, findings) {
|
|
|
65149
65414
|
};
|
|
65150
65415
|
}
|
|
65151
65416
|
function handoffBlastRadiusPath(packageDir) {
|
|
65152
|
-
return
|
|
65417
|
+
return path154.join(path154.dirname(packageDir), BLAST_RADIUS_ARTIFACT);
|
|
65153
65418
|
}
|
|
65154
65419
|
async function resolveScreenRadius(input, packageDir) {
|
|
65155
65420
|
if (input.blastRadius !== undefined) {
|
|
65156
65421
|
return { radius: input.blastRadius, source: "input" };
|
|
65157
65422
|
}
|
|
65158
|
-
const inPackage =
|
|
65423
|
+
const inPackage = path154.join(packageDir, BLAST_RADIUS_ARTIFACT);
|
|
65159
65424
|
const fromPackage = await readBlastRadiusRecordFile(inPackage);
|
|
65160
65425
|
if (fromPackage !== undefined) {
|
|
65161
65426
|
return { radius: fromPackage, source: "package", file: inPackage };
|
|
@@ -65181,7 +65446,7 @@ async function persistScreenRadius(packageDir, resolved) {
|
|
|
65181
65446
|
if (resolved === undefined) {
|
|
65182
65447
|
return;
|
|
65183
65448
|
}
|
|
65184
|
-
const inPackage =
|
|
65449
|
+
const inPackage = path154.join(packageDir, BLAST_RADIUS_ARTIFACT);
|
|
65185
65450
|
if (resolved.source !== "package") {
|
|
65186
65451
|
await writeFileAtomic(inPackage, `${JSON.stringify(resolved.radius, null, 2)}
|
|
65187
65452
|
`);
|
|
@@ -65212,12 +65477,12 @@ function fromVerifierRefutations(findings) {
|
|
|
65212
65477
|
}));
|
|
65213
65478
|
}
|
|
65214
65479
|
async function getManagedReviewStatus(cwd, ref) {
|
|
65215
|
-
const manifestPath = ref.endsWith("manifest.json") ?
|
|
65480
|
+
const manifestPath = ref.endsWith("manifest.json") ? path154.resolve(cwd, ref) : path154.join(await resolveReviewPackagePath(cwd, ref), "manifest.json");
|
|
65216
65481
|
return JSON.parse(await readFile86(manifestPath, "utf8"));
|
|
65217
65482
|
}
|
|
65218
65483
|
async function completeManagedReview(cwd, ref, options = {}) {
|
|
65219
65484
|
const packageDir = await resolveReviewPackagePath(cwd, ref);
|
|
65220
|
-
const manifestPath =
|
|
65485
|
+
const manifestPath = path154.join(packageDir, "manifest.json");
|
|
65221
65486
|
const manifest = JSON.parse(await readFile86(manifestPath, "utf8"));
|
|
65222
65487
|
const missing = await missingArtifacts(packageDir);
|
|
65223
65488
|
if (missing.length > 0) {
|
|
@@ -65238,12 +65503,12 @@ async function completeManagedReview(cwd, ref, options = {}) {
|
|
|
65238
65503
|
}
|
|
65239
65504
|
await writeFileAtomic(manifestPath, `${JSON.stringify(updated, null, 2)}
|
|
65240
65505
|
`);
|
|
65241
|
-
const recorded = JSON.parse(await readFile86(
|
|
65506
|
+
const recorded = JSON.parse(await readFile86(path154.join(packageDir, "findings.json"), "utf8"));
|
|
65242
65507
|
const reviewNotes = await writeReviewNotes(recorded, {
|
|
65243
65508
|
cwd,
|
|
65244
65509
|
reviewId: updated.reviewId,
|
|
65245
65510
|
head: updated.target.head ?? null,
|
|
65246
|
-
packagePath:
|
|
65511
|
+
packagePath: path154.relative(cwd, packageDir),
|
|
65247
65512
|
...options.now === undefined ? {} : { now: options.now }
|
|
65248
65513
|
});
|
|
65249
65514
|
return { manifest: updated, reviewNotes, dispositions: tallyDispositions(recorded) };
|
|
@@ -65309,16 +65574,16 @@ async function resolvePackageFlow(input) {
|
|
|
65309
65574
|
}
|
|
65310
65575
|
function packagePath(cwd, mode, reviewId, flowMatch) {
|
|
65311
65576
|
if ((mode === "attach-review" || mode === "ingest") && flowMatch) {
|
|
65312
|
-
return
|
|
65577
|
+
return path154.join(flowsRoot(cwd), flowMatch.dir, "reviews", reviewId);
|
|
65313
65578
|
}
|
|
65314
65579
|
if (mode === "attach-review") {
|
|
65315
65580
|
throw new Error("attach-review requires an explicit or matched flow");
|
|
65316
65581
|
}
|
|
65317
|
-
return
|
|
65582
|
+
return path154.join(reviewsRoot(cwd), reviewId);
|
|
65318
65583
|
}
|
|
65319
65584
|
function buildManifest2(args) {
|
|
65320
65585
|
const crossFamily = args.input.crossFamilyReview;
|
|
65321
|
-
const artifactPath = (name) =>
|
|
65586
|
+
const artifactPath = (name) => path154.relative(args.input.cwd, path154.join(args.packageDir, name));
|
|
65322
65587
|
const manifest = {
|
|
65323
65588
|
schemaVersion: 1,
|
|
65324
65589
|
reviewId: args.reviewId,
|
|
@@ -65365,7 +65630,7 @@ async function readReport(input) {
|
|
|
65365
65630
|
return input.reportText;
|
|
65366
65631
|
}
|
|
65367
65632
|
if (input.reportPath) {
|
|
65368
|
-
return readFile86(
|
|
65633
|
+
return readFile86(path154.resolve(input.cwd, input.reportPath), "utf8");
|
|
65369
65634
|
}
|
|
65370
65635
|
if (input.mode === "ingest") {
|
|
65371
65636
|
throw new Error("ingest requires --report or reportText");
|
|
@@ -65376,7 +65641,7 @@ No reviewer findings recorded yet.
|
|
|
65376
65641
|
`;
|
|
65377
65642
|
}
|
|
65378
65643
|
function reportLabel(input) {
|
|
65379
|
-
return input.reportPath ?
|
|
65644
|
+
return input.reportPath ? path154.resolve(input.cwd, input.reportPath) : "the report text passed to keryx review";
|
|
65380
65645
|
}
|
|
65381
65646
|
async function normalizeFindings(args) {
|
|
65382
65647
|
if (args.source !== undefined) {
|
|
@@ -65431,16 +65696,16 @@ async function anchorFindings(findings, input, head) {
|
|
|
65431
65696
|
}
|
|
65432
65697
|
}
|
|
65433
65698
|
async function containedRelativePath(cwd, relative) {
|
|
65434
|
-
const root = await realpath8(cwd).catch(() =>
|
|
65435
|
-
const resolved =
|
|
65699
|
+
const root = await realpath8(cwd).catch(() => path154.resolve(cwd));
|
|
65700
|
+
const resolved = path154.resolve(root, relative);
|
|
65436
65701
|
const real = await realpath8(resolved).catch(() => null);
|
|
65437
|
-
if (real === null || real !== root && !real.startsWith(`${root}${
|
|
65702
|
+
if (real === null || real !== root && !real.startsWith(`${root}${path154.sep}`)) {
|
|
65438
65703
|
return null;
|
|
65439
65704
|
}
|
|
65440
65705
|
return real;
|
|
65441
65706
|
}
|
|
65442
65707
|
async function readFileAtCommit(cwd, commit, absolute) {
|
|
65443
|
-
const relative =
|
|
65708
|
+
const relative = path154.relative(await realpath8(cwd).catch(() => path154.resolve(cwd)), absolute);
|
|
65444
65709
|
try {
|
|
65445
65710
|
const proc = Bun.spawn(["git", "show", `${commit}:${relative}`], {
|
|
65446
65711
|
cwd,
|
|
@@ -65459,10 +65724,10 @@ async function readFileAtCommit(cwd, commit, absolute) {
|
|
|
65459
65724
|
}
|
|
65460
65725
|
function readFromWorkingTree(input) {
|
|
65461
65726
|
return async (relative) => {
|
|
65462
|
-
const root = await realpath8(input.cwd).catch(() =>
|
|
65463
|
-
const resolved =
|
|
65727
|
+
const root = await realpath8(input.cwd).catch(() => path154.resolve(input.cwd));
|
|
65728
|
+
const resolved = path154.resolve(root, relative);
|
|
65464
65729
|
const real = await realpath8(resolved).catch(() => null);
|
|
65465
|
-
if (real === null || real !== root && !real.startsWith(`${root}${
|
|
65730
|
+
if (real === null || real !== root && !real.startsWith(`${root}${path154.sep}`)) {
|
|
65466
65731
|
return null;
|
|
65467
65732
|
}
|
|
65468
65733
|
try {
|
|
@@ -65594,7 +65859,7 @@ function refutedClassification(state) {
|
|
|
65594
65859
|
return state === "dismissed-incorrect" ? "false_positive" : "out_of_scope";
|
|
65595
65860
|
}
|
|
65596
65861
|
async function recordDispositions(packageDir, records) {
|
|
65597
|
-
const findingsPath =
|
|
65862
|
+
const findingsPath = path154.join(packageDir, "findings.json");
|
|
65598
65863
|
const findings = JSON.parse(await readFile86(findingsPath, "utf8"));
|
|
65599
65864
|
const schema = await loadSchema("review-finding");
|
|
65600
65865
|
const dispositionSchema = schema.properties?.["disposition"];
|
|
@@ -65930,7 +66195,7 @@ function upsertPreFilterScopeBlock(text, block) {
|
|
|
65930
66195
|
${body}`;
|
|
65931
66196
|
}
|
|
65932
66197
|
async function readPreFilterScopeBlock(packageDir) {
|
|
65933
|
-
const scopePath =
|
|
66198
|
+
const scopePath = path154.join(packageDir, "scope.md");
|
|
65934
66199
|
if (!await pathExists(scopePath)) {
|
|
65935
66200
|
return;
|
|
65936
66201
|
}
|
|
@@ -66111,39 +66376,39 @@ function matchReason(flow, dir, kind, ref) {
|
|
|
66111
66376
|
async function missingArtifacts(packageDir) {
|
|
66112
66377
|
const missing = [];
|
|
66113
66378
|
for (const artifact of ["scope.md", "coverage.md", "report.md", "findings.json", "learning.md", "decisions.md"]) {
|
|
66114
|
-
if (!await pathExists(
|
|
66379
|
+
if (!await pathExists(path154.join(packageDir, artifact))) {
|
|
66115
66380
|
missing.push(artifact);
|
|
66116
66381
|
}
|
|
66117
66382
|
}
|
|
66118
66383
|
return missing;
|
|
66119
66384
|
}
|
|
66120
66385
|
async function resolveReviewPackagePath(cwd, ref) {
|
|
66121
|
-
const absolute =
|
|
66122
|
-
if (await pathExists(
|
|
66386
|
+
const absolute = path154.resolve(cwd, ref);
|
|
66387
|
+
if (await pathExists(path154.join(absolute, "manifest.json"))) {
|
|
66123
66388
|
return absolute;
|
|
66124
66389
|
}
|
|
66125
66390
|
if (await pathExists(absolute) && absolute.endsWith("manifest.json")) {
|
|
66126
|
-
return
|
|
66391
|
+
return path154.dirname(absolute);
|
|
66127
66392
|
}
|
|
66128
|
-
const standalone =
|
|
66129
|
-
if (await pathExists(
|
|
66393
|
+
const standalone = path154.join(reviewsRoot(cwd), ref);
|
|
66394
|
+
if (await pathExists(path154.join(standalone, "manifest.json"))) {
|
|
66130
66395
|
return standalone;
|
|
66131
66396
|
}
|
|
66132
66397
|
for (const flowDir of await listFlowDirs(cwd)) {
|
|
66133
|
-
const reviewsDir =
|
|
66398
|
+
const reviewsDir = path154.join(flowsRoot(cwd), flowDir, "reviews");
|
|
66134
66399
|
if (!await pathExists(reviewsDir)) {
|
|
66135
66400
|
continue;
|
|
66136
66401
|
}
|
|
66137
66402
|
for (const entry of await readdir28(reviewsDir, { withFileTypes: true })) {
|
|
66138
66403
|
if (entry.isDirectory() && entry.name === ref) {
|
|
66139
|
-
return
|
|
66404
|
+
return path154.join(reviewsDir, entry.name);
|
|
66140
66405
|
}
|
|
66141
66406
|
}
|
|
66142
66407
|
}
|
|
66143
66408
|
throw new Error(`Managed review package not found: ${ref}`);
|
|
66144
66409
|
}
|
|
66145
66410
|
async function loadDocpackSchema(cwd) {
|
|
66146
|
-
const schemaPath =
|
|
66411
|
+
const schemaPath = path154.join(cwd, "docs", "requirements", "managed-review-feedback-loop", "schemas", "managed-review-package.schema.json");
|
|
66147
66412
|
if (!await pathExists(schemaPath)) {
|
|
66148
66413
|
return null;
|
|
66149
66414
|
}
|
|
@@ -66221,7 +66486,190 @@ function renderCostPerFinding(cost, retained) {
|
|
|
66221
66486
|
// src/review/reviewers.ts
|
|
66222
66487
|
init_project_skills();
|
|
66223
66488
|
import { readFile as readFile87, readdir as readdir29 } from "fs/promises";
|
|
66224
|
-
import
|
|
66489
|
+
import path155 from "path";
|
|
66490
|
+
// src/review/stack.ts
|
|
66491
|
+
var STACK_TAGS = ["nestjs", "react", "mobx", "prisma", "playwright", "sql", "http-server"];
|
|
66492
|
+
function isStackTag(value) {
|
|
66493
|
+
return STACK_TAGS.includes(value);
|
|
66494
|
+
}
|
|
66495
|
+
var TAG_MARKERS = {
|
|
66496
|
+
react: ["react", "react-dom"],
|
|
66497
|
+
mobx: ["mobx", "mobx-react", "mobx-react-lite"],
|
|
66498
|
+
prisma: ["prisma", "@prisma/client"],
|
|
66499
|
+
playwright: ["@playwright/test", "playwright"],
|
|
66500
|
+
sql: ["prisma", "@prisma/client", "typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql2", "better-sqlite3", "kysely"],
|
|
66501
|
+
"http-server": ["express", "fastify", "koa", "hono", "@nestjs/core", "@hapi/hapi"]
|
|
66502
|
+
};
|
|
66503
|
+
var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
|
66504
|
+
async function detectProjectStack(cwd, options = {}) {
|
|
66505
|
+
const readFile = options.readFile ?? defaultReadFile;
|
|
66506
|
+
const manifestPath = `${cwd.replace(/\/+$/, "")}/package.json`;
|
|
66507
|
+
let raw;
|
|
66508
|
+
try {
|
|
66509
|
+
raw = await readFile(manifestPath);
|
|
66510
|
+
} catch {
|
|
66511
|
+
return uncertainStack(`package.json not found at ${manifestPath}`);
|
|
66512
|
+
}
|
|
66513
|
+
let parsed;
|
|
66514
|
+
try {
|
|
66515
|
+
parsed = JSON.parse(raw);
|
|
66516
|
+
} catch (error) {
|
|
66517
|
+
return uncertainStack(`package.json at ${manifestPath} did not parse as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
66518
|
+
}
|
|
66519
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
66520
|
+
return uncertainStack(`package.json at ${manifestPath} is not a JSON object`);
|
|
66521
|
+
}
|
|
66522
|
+
const record = parsed;
|
|
66523
|
+
const names = new Set;
|
|
66524
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
66525
|
+
const value = record[field];
|
|
66526
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
66527
|
+
for (const name of Object.keys(value)) {
|
|
66528
|
+
names.add(name);
|
|
66529
|
+
}
|
|
66530
|
+
}
|
|
66531
|
+
}
|
|
66532
|
+
const workspaceGlobs = workspacePatterns(record);
|
|
66533
|
+
if (workspaceGlobs.length > 0) {
|
|
66534
|
+
return uncertainStack(`package.json at ${manifestPath} declares workspaces (${workspaceGlobs.join(", ")}); the dependencies that decide the stack live in the sub-packages, which this module does not walk`);
|
|
66535
|
+
}
|
|
66536
|
+
if (names.size === 0) {
|
|
66537
|
+
return uncertainStack(`package.json at ${manifestPath} declares no dependencies in ${DEPENDENCY_FIELDS.join("/")} \u2014 a manifest that declares nothing has not said the repository uses nothing`);
|
|
66538
|
+
}
|
|
66539
|
+
const matched = [];
|
|
66540
|
+
const nestjsMatches = [...names].filter((name) => name.startsWith("@nestjs/")).sort();
|
|
66541
|
+
matched.push(...nestjsMatches);
|
|
66542
|
+
const tags = { nestjs: nestjsMatches.length > 0 };
|
|
66543
|
+
for (const tag of Object.keys(TAG_MARKERS)) {
|
|
66544
|
+
const hit = TAG_MARKERS[tag].filter((marker) => names.has(marker));
|
|
66545
|
+
tags[tag] = hit.length > 0;
|
|
66546
|
+
matched.push(...hit);
|
|
66547
|
+
}
|
|
66548
|
+
return {
|
|
66549
|
+
tags,
|
|
66550
|
+
uncertain: false,
|
|
66551
|
+
reason: `detected from ${manifestPath} (${names.size} declared dependenc${names.size === 1 ? "y" : "ies"})`,
|
|
66552
|
+
matched
|
|
66553
|
+
};
|
|
66554
|
+
}
|
|
66555
|
+
function workspacePatterns(record) {
|
|
66556
|
+
const declared = record["workspaces"];
|
|
66557
|
+
const list = Array.isArray(declared) ? declared : typeof declared === "object" && declared !== null ? declared["packages"] : undefined;
|
|
66558
|
+
return Array.isArray(list) ? list.filter((item) => typeof item === "string" && item !== "") : [];
|
|
66559
|
+
}
|
|
66560
|
+
function uncertainStack(reason) {
|
|
66561
|
+
const tags = Object.fromEntries(STACK_TAGS.map((tag) => [tag, true]));
|
|
66562
|
+
return { tags, uncertain: true, reason, matched: [] };
|
|
66563
|
+
}
|
|
66564
|
+
async function defaultReadFile(path) {
|
|
66565
|
+
return await Bun.file(path).text();
|
|
66566
|
+
}
|
|
66567
|
+
function parseStackRequires(value) {
|
|
66568
|
+
if (value === undefined) {
|
|
66569
|
+
return [];
|
|
66570
|
+
}
|
|
66571
|
+
const seen = new Set;
|
|
66572
|
+
for (const token of value.split(",")) {
|
|
66573
|
+
const trimmed = token.trim().toLowerCase();
|
|
66574
|
+
if (isStackTag(trimmed)) {
|
|
66575
|
+
seen.add(trimmed);
|
|
66576
|
+
}
|
|
66577
|
+
}
|
|
66578
|
+
return [...seen];
|
|
66579
|
+
}
|
|
66580
|
+
function extractStackRequiresField(skillMdContent) {
|
|
66581
|
+
if (!skillMdContent.startsWith("---")) {
|
|
66582
|
+
return;
|
|
66583
|
+
}
|
|
66584
|
+
const end = skillMdContent.indexOf(`
|
|
66585
|
+
---`, 3);
|
|
66586
|
+
if (end === -1) {
|
|
66587
|
+
return;
|
|
66588
|
+
}
|
|
66589
|
+
const lines = skillMdContent.slice(3, end).split(`
|
|
66590
|
+
`);
|
|
66591
|
+
let inMetadata = false;
|
|
66592
|
+
for (const line of lines) {
|
|
66593
|
+
const topMatch = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
|
|
66594
|
+
if (topMatch) {
|
|
66595
|
+
inMetadata = topMatch[1] === "metadata";
|
|
66596
|
+
continue;
|
|
66597
|
+
}
|
|
66598
|
+
if (!inMetadata) {
|
|
66599
|
+
continue;
|
|
66600
|
+
}
|
|
66601
|
+
const fieldMatch = /^\s+stack_requires:\s*(.+)$/.exec(line);
|
|
66602
|
+
if (fieldMatch && fieldMatch[1] !== undefined) {
|
|
66603
|
+
return stripQuotes(fieldMatch[1].trim());
|
|
66604
|
+
}
|
|
66605
|
+
}
|
|
66606
|
+
return;
|
|
66607
|
+
}
|
|
66608
|
+
function stripQuotes(value) {
|
|
66609
|
+
if (value.length >= 2) {
|
|
66610
|
+
const first = value[0];
|
|
66611
|
+
const last = value[value.length - 1];
|
|
66612
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
66613
|
+
return value.slice(1, -1);
|
|
66614
|
+
}
|
|
66615
|
+
}
|
|
66616
|
+
return value;
|
|
66617
|
+
}
|
|
66618
|
+
function scopeReviewerByStack(reviewer, requires, detected) {
|
|
66619
|
+
const requiresCopy = [...requires];
|
|
66620
|
+
if (requiresCopy.length === 0) {
|
|
66621
|
+
return { reviewer, requires: requiresCopy, include: true, reason: "no stack requirement declared \u2014 generic reviewer" };
|
|
66622
|
+
}
|
|
66623
|
+
if (detected.uncertain) {
|
|
66624
|
+
return {
|
|
66625
|
+
reviewer,
|
|
66626
|
+
requires: requiresCopy,
|
|
66627
|
+
include: true,
|
|
66628
|
+
reason: `stack detection uncertain (${detected.reason}); failing open`
|
|
66629
|
+
};
|
|
66630
|
+
}
|
|
66631
|
+
const present = requiresCopy.filter((tag) => detected.tags[tag]);
|
|
66632
|
+
if (present.length > 0) {
|
|
66633
|
+
return { reviewer, requires: requiresCopy, include: true, reason: `detected: ${present.join(", ")}` };
|
|
66634
|
+
}
|
|
66635
|
+
return {
|
|
66636
|
+
reviewer,
|
|
66637
|
+
requires: requiresCopy,
|
|
66638
|
+
include: false,
|
|
66639
|
+
reason: `none of [${requiresCopy.join(", ")}] detected in package.json (${detected.reason})`
|
|
66640
|
+
};
|
|
66641
|
+
}
|
|
66642
|
+
function renderStackScopingMarkdown(detected, decisions) {
|
|
66643
|
+
const lines = [];
|
|
66644
|
+
lines.push("## Stack scoping");
|
|
66645
|
+
lines.push("");
|
|
66646
|
+
lines.push(detected.uncertain ? `stack detection: uncertain \u2014 ${detected.reason}. Every stack-gated reviewer is included; uncertain never excludes.` : `stack detection: ${detected.reason}`);
|
|
66647
|
+
if (!detected.uncertain) {
|
|
66648
|
+
const present = STACK_TAGS.filter((tag) => detected.tags[tag]);
|
|
66649
|
+
lines.push(`tags present: ${present.length > 0 ? present.join(", ") : "none"}`);
|
|
66650
|
+
if (detected.matched.length > 0) {
|
|
66651
|
+
lines.push(`matched dependencies: ${detected.matched.join(", ")}`);
|
|
66652
|
+
}
|
|
66653
|
+
}
|
|
66654
|
+
lines.push("");
|
|
66655
|
+
if (decisions.length === 0) {
|
|
66656
|
+
lines.push("_no stack-gated reviewers were evaluated_");
|
|
66657
|
+
return lines.join(`
|
|
66658
|
+
`);
|
|
66659
|
+
}
|
|
66660
|
+
lines.push("| reviewer | requires | include | reason |");
|
|
66661
|
+
lines.push("|---|---|---|---|");
|
|
66662
|
+
for (const decision of decisions) {
|
|
66663
|
+
lines.push(`| ${decision.reviewer} | ${decision.requires.length > 0 ? decision.requires.join(", ") : "-"} | ${decision.include ? "yes" : "no"} | ${decision.reason} |`);
|
|
66664
|
+
}
|
|
66665
|
+
const excluded = decisions.filter((decision) => !decision.include);
|
|
66666
|
+
lines.push("");
|
|
66667
|
+
lines.push(excluded.length === 0 ? "No reviewer was excluded by stack scoping." : `Excluded (never run this round): ${excluded.map((decision) => decision.reviewer).join(", ")}.`);
|
|
66668
|
+
return lines.join(`
|
|
66669
|
+
`);
|
|
66670
|
+
}
|
|
66671
|
+
|
|
66672
|
+
// src/review/reviewers.ts
|
|
66225
66673
|
var PROJECT_REVIEWER_MODULE = "review";
|
|
66226
66674
|
function escapeRegexLiteral(literal) {
|
|
66227
66675
|
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -66242,12 +66690,60 @@ async function skillDirs(root) {
|
|
|
66242
66690
|
if (!entry.isDirectory())
|
|
66243
66691
|
continue;
|
|
66244
66692
|
try {
|
|
66245
|
-
await readFile87(
|
|
66693
|
+
await readFile87(path155.join(root, entry.name, "SKILL.md"), "utf8");
|
|
66246
66694
|
names.push(entry.name);
|
|
66247
66695
|
} catch {}
|
|
66248
66696
|
}
|
|
66249
66697
|
return names.sort();
|
|
66250
66698
|
}
|
|
66699
|
+
function metadataList(content, key) {
|
|
66700
|
+
if (!content.startsWith("---"))
|
|
66701
|
+
return [];
|
|
66702
|
+
const end = content.indexOf(`
|
|
66703
|
+
---`, 3);
|
|
66704
|
+
if (end === -1)
|
|
66705
|
+
return [];
|
|
66706
|
+
let inMetadata = false;
|
|
66707
|
+
for (const line of content.slice(3, end).split(`
|
|
66708
|
+
`)) {
|
|
66709
|
+
const top = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
|
|
66710
|
+
if (top) {
|
|
66711
|
+
inMetadata = top[1] === "metadata";
|
|
66712
|
+
continue;
|
|
66713
|
+
}
|
|
66714
|
+
if (!inMetadata)
|
|
66715
|
+
continue;
|
|
66716
|
+
const field = new RegExp(`^\\s+${escapeRegexLiteral(key)}:\\s*(.+)$`).exec(line);
|
|
66717
|
+
if (field?.[1]) {
|
|
66718
|
+
return field[1].trim().replace(/^["'[]|["'\]]$/g, "").split(",").map((entry) => entry.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
66719
|
+
}
|
|
66720
|
+
}
|
|
66721
|
+
return [];
|
|
66722
|
+
}
|
|
66723
|
+
function descriptionPathTriggers(description) {
|
|
66724
|
+
const globs = new Set;
|
|
66725
|
+
for (const raw of description.split(/\s+/)) {
|
|
66726
|
+
const token = raw.replace(/^[("'`]+/, "").replace(/[,.;:"'`]+$/, "");
|
|
66727
|
+
if (!token.includes("*") || !(token.includes("/") || token.startsWith("*.")))
|
|
66728
|
+
continue;
|
|
66729
|
+
const optional = /^(.*)\(([a-z0-9]+)\)$/i.exec(token);
|
|
66730
|
+
if (optional?.[1] && optional[2]) {
|
|
66731
|
+
globs.add(optional[1]);
|
|
66732
|
+
globs.add(`${optional[1]}${optional[2]}`);
|
|
66733
|
+
} else {
|
|
66734
|
+
globs.add(token.replace(/\)+$/, ""));
|
|
66735
|
+
}
|
|
66736
|
+
}
|
|
66737
|
+
return [...globs];
|
|
66738
|
+
}
|
|
66739
|
+
function descriptionFlags(description) {
|
|
66740
|
+
const flags = new Set;
|
|
66741
|
+
for (const match of description.matchAll(/(?:^|[\s(,])(--[a-z][a-z0-9-]*)/g)) {
|
|
66742
|
+
if (match[1] && match[1] !== "--all")
|
|
66743
|
+
flags.add(match[1]);
|
|
66744
|
+
}
|
|
66745
|
+
return [...flags];
|
|
66746
|
+
}
|
|
66251
66747
|
async function driftFor(projectRoot, origin, recordedHash) {
|
|
66252
66748
|
if (!origin || !recordedHash) {
|
|
66253
66749
|
return "none";
|
|
@@ -66263,24 +66759,34 @@ async function driftFor(projectRoot, origin, recordedHash) {
|
|
|
66263
66759
|
}
|
|
66264
66760
|
}
|
|
66265
66761
|
async function collectReviewers(projectRoot) {
|
|
66266
|
-
const bundledRoot =
|
|
66762
|
+
const bundledRoot = path155.join(projectRoot, ".metaproject", "skills", "gdskills", "review");
|
|
66267
66763
|
const bundled = (await skillDirs(bundledRoot)).map((name) => ({
|
|
66268
66764
|
name,
|
|
66269
66765
|
source: "bundled",
|
|
66270
|
-
path:
|
|
66766
|
+
path: path155.posix.join(".metaproject", "skills", "gdskills", "review", name)
|
|
66271
66767
|
}));
|
|
66272
|
-
const projectRoot_ =
|
|
66768
|
+
const projectRoot_ = path155.join(projectRoot, ".metaproject", "project-skills", PROJECT_REVIEWER_MODULE);
|
|
66273
66769
|
const project = [];
|
|
66274
66770
|
for (const name of await skillDirs(projectRoot_)) {
|
|
66275
|
-
const relative =
|
|
66276
|
-
const content = await readFile87(
|
|
66771
|
+
const relative = path155.posix.join(".metaproject", "project-skills", PROJECT_REVIEWER_MODULE, name);
|
|
66772
|
+
const content = await readFile87(path155.join(projectRoot_, name, "SKILL.md"), "utf8");
|
|
66277
66773
|
const origin = metadataLine2(content, "Origin");
|
|
66278
66774
|
const originHash = metadataLine2(content, "Origin Hash");
|
|
66279
66775
|
const importedAt = metadataLine2(content, "Imported At");
|
|
66776
|
+
const description = parseSkillFrontmatter(content).description;
|
|
66777
|
+
const declaredPaths = metadataList(content, "paths");
|
|
66778
|
+
const describedPaths = description ? descriptionPathTriggers(description) : [];
|
|
66779
|
+
const pathsSource = declaredPaths.length > 0 ? "metadata" : describedPaths.length > 0 ? "description" : "none";
|
|
66280
66780
|
project.push({
|
|
66281
66781
|
name,
|
|
66282
66782
|
source: "project-skill",
|
|
66283
66783
|
path: relative,
|
|
66784
|
+
...description ? { description } : {},
|
|
66785
|
+
paths: pathsSource === "metadata" ? declaredPaths : describedPaths,
|
|
66786
|
+
pathsSource,
|
|
66787
|
+
flags: description ? descriptionFlags(description) : [],
|
|
66788
|
+
stackRequires: parseStackRequires(extractStackRequiresField(content)),
|
|
66789
|
+
unresolvedRules: await unresolvedRuleReferences(projectRoot, content),
|
|
66284
66790
|
...origin ? { origin } : {},
|
|
66285
66791
|
...originHash ? { originHash } : {},
|
|
66286
66792
|
...importedAt ? { importedAt } : {},
|
|
@@ -66312,6 +66818,18 @@ function renderReviewerInventoryMarkdown(inventory) {
|
|
|
66312
66818
|
for (const reviewer of inventory.project) {
|
|
66313
66819
|
const provenance = reviewer.drift === "none" ? "no recorded origin" : `${reviewer.origin ?? "?"} \u2014 ${reviewer.drift}`;
|
|
66314
66820
|
lines.push(`- ${reviewer.name} (${provenance})`);
|
|
66821
|
+
lines.push(` - paths: ${reviewer.paths.length > 0 ? reviewer.paths.join(", ") : "none \u2014 dispatched on every round"} [${reviewer.pathsSource}]`);
|
|
66822
|
+
if (reviewer.flags.length > 0) {
|
|
66823
|
+
lines.push(` - flags: ${reviewer.flags.join(", ")}`);
|
|
66824
|
+
}
|
|
66825
|
+
}
|
|
66826
|
+
const unresolved = inventory.project.filter((reviewer) => reviewer.unresolvedRules.length > 0);
|
|
66827
|
+
if (unresolved.length > 0) {
|
|
66828
|
+
lines.push("", "## rules cited but not in .metaproject/rules", "");
|
|
66829
|
+
for (const reviewer of unresolved) {
|
|
66830
|
+
lines.push(`- ${reviewer.name}: ${reviewer.unresolvedRules.join(", ")}`);
|
|
66831
|
+
}
|
|
66832
|
+
lines.push("", "The reviewer names these as its standard and the project does not have them. Re-run", "`keryx review import --from <overlay>` to copy them from the overlay, or add them by hand.");
|
|
66315
66833
|
}
|
|
66316
66834
|
const changed = inventory.project.filter((reviewer) => reviewer.drift === "changed");
|
|
66317
66835
|
const missing = inventory.project.filter((reviewer) => reviewer.drift === "missing");
|
|
@@ -67187,194 +67705,11 @@ async function pickProviderModel(io, detected, deps = {}) {
|
|
|
67187
67705
|
}
|
|
67188
67706
|
|
|
67189
67707
|
// src/commands/review.ts
|
|
67708
|
+
init_caller_session();
|
|
67190
67709
|
init_shell_config();
|
|
67191
67710
|
init_selection();
|
|
67192
67711
|
init_pr_comments();
|
|
67193
67712
|
init_loop();
|
|
67194
|
-
|
|
67195
|
-
// src/review/stack.ts
|
|
67196
|
-
var STACK_TAGS = ["nestjs", "react", "mobx", "prisma", "playwright", "sql", "http-server"];
|
|
67197
|
-
function isStackTag(value) {
|
|
67198
|
-
return STACK_TAGS.includes(value);
|
|
67199
|
-
}
|
|
67200
|
-
var TAG_MARKERS = {
|
|
67201
|
-
react: ["react", "react-dom"],
|
|
67202
|
-
mobx: ["mobx", "mobx-react", "mobx-react-lite"],
|
|
67203
|
-
prisma: ["prisma", "@prisma/client"],
|
|
67204
|
-
playwright: ["@playwright/test", "playwright"],
|
|
67205
|
-
sql: ["prisma", "@prisma/client", "typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql2", "better-sqlite3", "kysely"],
|
|
67206
|
-
"http-server": ["express", "fastify", "koa", "hono", "@nestjs/core", "@hapi/hapi"]
|
|
67207
|
-
};
|
|
67208
|
-
var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
|
|
67209
|
-
async function detectProjectStack(cwd, options = {}) {
|
|
67210
|
-
const readFile = options.readFile ?? defaultReadFile;
|
|
67211
|
-
const manifestPath = `${cwd.replace(/\/+$/, "")}/package.json`;
|
|
67212
|
-
let raw;
|
|
67213
|
-
try {
|
|
67214
|
-
raw = await readFile(manifestPath);
|
|
67215
|
-
} catch {
|
|
67216
|
-
return uncertainStack(`package.json not found at ${manifestPath}`);
|
|
67217
|
-
}
|
|
67218
|
-
let parsed;
|
|
67219
|
-
try {
|
|
67220
|
-
parsed = JSON.parse(raw);
|
|
67221
|
-
} catch (error) {
|
|
67222
|
-
return uncertainStack(`package.json at ${manifestPath} did not parse as JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
67223
|
-
}
|
|
67224
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
67225
|
-
return uncertainStack(`package.json at ${manifestPath} is not a JSON object`);
|
|
67226
|
-
}
|
|
67227
|
-
const record = parsed;
|
|
67228
|
-
const names = new Set;
|
|
67229
|
-
for (const field of DEPENDENCY_FIELDS) {
|
|
67230
|
-
const value = record[field];
|
|
67231
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
67232
|
-
for (const name of Object.keys(value)) {
|
|
67233
|
-
names.add(name);
|
|
67234
|
-
}
|
|
67235
|
-
}
|
|
67236
|
-
}
|
|
67237
|
-
const workspaceGlobs = workspacePatterns(record);
|
|
67238
|
-
if (workspaceGlobs.length > 0) {
|
|
67239
|
-
return uncertainStack(`package.json at ${manifestPath} declares workspaces (${workspaceGlobs.join(", ")}); the dependencies that decide the stack live in the sub-packages, which this module does not walk`);
|
|
67240
|
-
}
|
|
67241
|
-
if (names.size === 0) {
|
|
67242
|
-
return uncertainStack(`package.json at ${manifestPath} declares no dependencies in ${DEPENDENCY_FIELDS.join("/")} \u2014 a manifest that declares nothing has not said the repository uses nothing`);
|
|
67243
|
-
}
|
|
67244
|
-
const matched = [];
|
|
67245
|
-
const nestjsMatches = [...names].filter((name) => name.startsWith("@nestjs/")).sort();
|
|
67246
|
-
matched.push(...nestjsMatches);
|
|
67247
|
-
const tags = { nestjs: nestjsMatches.length > 0 };
|
|
67248
|
-
for (const tag of Object.keys(TAG_MARKERS)) {
|
|
67249
|
-
const hit = TAG_MARKERS[tag].filter((marker) => names.has(marker));
|
|
67250
|
-
tags[tag] = hit.length > 0;
|
|
67251
|
-
matched.push(...hit);
|
|
67252
|
-
}
|
|
67253
|
-
return {
|
|
67254
|
-
tags,
|
|
67255
|
-
uncertain: false,
|
|
67256
|
-
reason: `detected from ${manifestPath} (${names.size} declared dependenc${names.size === 1 ? "y" : "ies"})`,
|
|
67257
|
-
matched
|
|
67258
|
-
};
|
|
67259
|
-
}
|
|
67260
|
-
function workspacePatterns(record) {
|
|
67261
|
-
const declared = record["workspaces"];
|
|
67262
|
-
const list = Array.isArray(declared) ? declared : typeof declared === "object" && declared !== null ? declared["packages"] : undefined;
|
|
67263
|
-
return Array.isArray(list) ? list.filter((item) => typeof item === "string" && item !== "") : [];
|
|
67264
|
-
}
|
|
67265
|
-
function uncertainStack(reason) {
|
|
67266
|
-
const tags = Object.fromEntries(STACK_TAGS.map((tag) => [tag, true]));
|
|
67267
|
-
return { tags, uncertain: true, reason, matched: [] };
|
|
67268
|
-
}
|
|
67269
|
-
async function defaultReadFile(path) {
|
|
67270
|
-
return await Bun.file(path).text();
|
|
67271
|
-
}
|
|
67272
|
-
function parseStackRequires(value) {
|
|
67273
|
-
if (value === undefined) {
|
|
67274
|
-
return [];
|
|
67275
|
-
}
|
|
67276
|
-
const seen = new Set;
|
|
67277
|
-
for (const token of value.split(",")) {
|
|
67278
|
-
const trimmed = token.trim().toLowerCase();
|
|
67279
|
-
if (isStackTag(trimmed)) {
|
|
67280
|
-
seen.add(trimmed);
|
|
67281
|
-
}
|
|
67282
|
-
}
|
|
67283
|
-
return [...seen];
|
|
67284
|
-
}
|
|
67285
|
-
function extractStackRequiresField(skillMdContent) {
|
|
67286
|
-
if (!skillMdContent.startsWith("---")) {
|
|
67287
|
-
return;
|
|
67288
|
-
}
|
|
67289
|
-
const end = skillMdContent.indexOf(`
|
|
67290
|
-
---`, 3);
|
|
67291
|
-
if (end === -1) {
|
|
67292
|
-
return;
|
|
67293
|
-
}
|
|
67294
|
-
const lines = skillMdContent.slice(3, end).split(`
|
|
67295
|
-
`);
|
|
67296
|
-
let inMetadata = false;
|
|
67297
|
-
for (const line of lines) {
|
|
67298
|
-
const topMatch = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
|
|
67299
|
-
if (topMatch) {
|
|
67300
|
-
inMetadata = topMatch[1] === "metadata";
|
|
67301
|
-
continue;
|
|
67302
|
-
}
|
|
67303
|
-
if (!inMetadata) {
|
|
67304
|
-
continue;
|
|
67305
|
-
}
|
|
67306
|
-
const fieldMatch = /^\s+stack_requires:\s*(.+)$/.exec(line);
|
|
67307
|
-
if (fieldMatch && fieldMatch[1] !== undefined) {
|
|
67308
|
-
return stripQuotes(fieldMatch[1].trim());
|
|
67309
|
-
}
|
|
67310
|
-
}
|
|
67311
|
-
return;
|
|
67312
|
-
}
|
|
67313
|
-
function stripQuotes(value) {
|
|
67314
|
-
if (value.length >= 2) {
|
|
67315
|
-
const first = value[0];
|
|
67316
|
-
const last = value[value.length - 1];
|
|
67317
|
-
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
67318
|
-
return value.slice(1, -1);
|
|
67319
|
-
}
|
|
67320
|
-
}
|
|
67321
|
-
return value;
|
|
67322
|
-
}
|
|
67323
|
-
function scopeReviewerByStack(reviewer, requires, detected) {
|
|
67324
|
-
const requiresCopy = [...requires];
|
|
67325
|
-
if (requiresCopy.length === 0) {
|
|
67326
|
-
return { reviewer, requires: requiresCopy, include: true, reason: "no stack requirement declared \u2014 generic reviewer" };
|
|
67327
|
-
}
|
|
67328
|
-
if (detected.uncertain) {
|
|
67329
|
-
return {
|
|
67330
|
-
reviewer,
|
|
67331
|
-
requires: requiresCopy,
|
|
67332
|
-
include: true,
|
|
67333
|
-
reason: `stack detection uncertain (${detected.reason}); failing open`
|
|
67334
|
-
};
|
|
67335
|
-
}
|
|
67336
|
-
const present = requiresCopy.filter((tag) => detected.tags[tag]);
|
|
67337
|
-
if (present.length > 0) {
|
|
67338
|
-
return { reviewer, requires: requiresCopy, include: true, reason: `detected: ${present.join(", ")}` };
|
|
67339
|
-
}
|
|
67340
|
-
return {
|
|
67341
|
-
reviewer,
|
|
67342
|
-
requires: requiresCopy,
|
|
67343
|
-
include: false,
|
|
67344
|
-
reason: `none of [${requiresCopy.join(", ")}] detected in package.json (${detected.reason})`
|
|
67345
|
-
};
|
|
67346
|
-
}
|
|
67347
|
-
function renderStackScopingMarkdown(detected, decisions) {
|
|
67348
|
-
const lines = [];
|
|
67349
|
-
lines.push("## Stack scoping");
|
|
67350
|
-
lines.push("");
|
|
67351
|
-
lines.push(detected.uncertain ? `stack detection: uncertain \u2014 ${detected.reason}. Every stack-gated reviewer is included; uncertain never excludes.` : `stack detection: ${detected.reason}`);
|
|
67352
|
-
if (!detected.uncertain) {
|
|
67353
|
-
const present = STACK_TAGS.filter((tag) => detected.tags[tag]);
|
|
67354
|
-
lines.push(`tags present: ${present.length > 0 ? present.join(", ") : "none"}`);
|
|
67355
|
-
if (detected.matched.length > 0) {
|
|
67356
|
-
lines.push(`matched dependencies: ${detected.matched.join(", ")}`);
|
|
67357
|
-
}
|
|
67358
|
-
}
|
|
67359
|
-
lines.push("");
|
|
67360
|
-
if (decisions.length === 0) {
|
|
67361
|
-
lines.push("_no stack-gated reviewers were evaluated_");
|
|
67362
|
-
return lines.join(`
|
|
67363
|
-
`);
|
|
67364
|
-
}
|
|
67365
|
-
lines.push("| reviewer | requires | include | reason |");
|
|
67366
|
-
lines.push("|---|---|---|---|");
|
|
67367
|
-
for (const decision of decisions) {
|
|
67368
|
-
lines.push(`| ${decision.reviewer} | ${decision.requires.length > 0 ? decision.requires.join(", ") : "-"} | ${decision.include ? "yes" : "no"} | ${decision.reason} |`);
|
|
67369
|
-
}
|
|
67370
|
-
const excluded = decisions.filter((decision) => !decision.include);
|
|
67371
|
-
lines.push("");
|
|
67372
|
-
lines.push(excluded.length === 0 ? "No reviewer was excluded by stack scoping." : `Excluded (never run this round): ${excluded.map((decision) => decision.reviewer).join(", ")}.`);
|
|
67373
|
-
return lines.join(`
|
|
67374
|
-
`);
|
|
67375
|
-
}
|
|
67376
|
-
|
|
67377
|
-
// src/commands/review.ts
|
|
67378
67713
|
init_types3();
|
|
67379
67714
|
var CREATE_FLAGS = [
|
|
67380
67715
|
"--target",
|
|
@@ -67416,6 +67751,7 @@ var COMMENTS_REPLY_FLAGS = [
|
|
|
67416
67751
|
"--self",
|
|
67417
67752
|
"--outcomes",
|
|
67418
67753
|
"--result",
|
|
67754
|
+
"--review",
|
|
67419
67755
|
"--sha",
|
|
67420
67756
|
"--round",
|
|
67421
67757
|
"--final",
|
|
@@ -67424,7 +67760,8 @@ var COMMENTS_REPLY_FLAGS = [
|
|
|
67424
67760
|
"--max-sentences",
|
|
67425
67761
|
"--max-chars",
|
|
67426
67762
|
"--flow-link",
|
|
67427
|
-
"--fixtures"
|
|
67763
|
+
"--fixtures",
|
|
67764
|
+
"--allow-closed-pr"
|
|
67428
67765
|
];
|
|
67429
67766
|
var LOOP_FLAGS = ["--flow", "--task"];
|
|
67430
67767
|
var LEARN_FLAGS = ["--pr", "--dry-run", "--json"];
|
|
@@ -67473,6 +67810,7 @@ var TIER_FLAGS = [
|
|
|
67473
67810
|
"--security",
|
|
67474
67811
|
"--session-provider",
|
|
67475
67812
|
"--session-model",
|
|
67813
|
+
"--from-shell-config",
|
|
67476
67814
|
"--catalog",
|
|
67477
67815
|
"--json"
|
|
67478
67816
|
];
|
|
@@ -67752,7 +68090,7 @@ async function runBudget(args) {
|
|
|
67752
68090
|
async function runTier(args) {
|
|
67753
68091
|
rejectUnknownFlags(args, TIER_FLAGS, "tier");
|
|
67754
68092
|
const signals = tierSignalsFromArgs(args);
|
|
67755
|
-
const session = sessionModelFromArgs(args);
|
|
68093
|
+
const { session, source } = sessionModelFromArgs(args);
|
|
67756
68094
|
const catalog = await tierCatalog(args, session);
|
|
67757
68095
|
const decision = decideDispatchModel(session, signals, catalog);
|
|
67758
68096
|
const block = dispatchModelBlock(decision);
|
|
@@ -67765,8 +68103,13 @@ async function runTier(args) {
|
|
|
67765
68103
|
console.log("");
|
|
67766
68104
|
console.log(`tier: ${decision.tier}`);
|
|
67767
68105
|
console.log(`tier_reasons: ${decision.tier_reasons.join(", ")}`);
|
|
67768
|
-
console.log(`
|
|
67769
|
-
|
|
68106
|
+
console.log(`session_source: ${SESSION_SOURCE_LABELS[source]}`);
|
|
68107
|
+
if (pinsModel(decision)) {
|
|
68108
|
+
console.log(`provider: ${decision.provider}`);
|
|
68109
|
+
console.log(`model: ${decision.model}`);
|
|
68110
|
+
} else {
|
|
68111
|
+
console.log(`model: inherit \u2014 ${ADAPTIVE_GUIDANCE[decision.tier]}`);
|
|
68112
|
+
}
|
|
67770
68113
|
console.log(`tier_resolution: ${decision.tier_resolution}`);
|
|
67771
68114
|
console.log(`model_discovery: provider=${discovery.provider === "" ? "none" : discovery.provider} candidates=${discovery.candidates.length} ranked=${discovery.ranked.length} session_rank=${discovery.session_rank ?? "none"}`);
|
|
67772
68115
|
console.log(`fallback_reason: ${discovery.fallback_reason ?? "none (ranking was not refused)"}`);
|
|
@@ -67795,12 +68138,25 @@ function tierSignalsFromArgs(args) {
|
|
|
67795
68138
|
hasSecurityFinding: args.includes("--security")
|
|
67796
68139
|
};
|
|
67797
68140
|
}
|
|
67798
|
-
|
|
67799
|
-
|
|
67800
|
-
|
|
67801
|
-
|
|
67802
|
-
|
|
67803
|
-
|
|
68141
|
+
var SESSION_SOURCE_LABELS = {
|
|
68142
|
+
flags: "flags (--session-provider/--session-model)",
|
|
68143
|
+
env: "environment (KERYX_SESSION_PROVIDER/KERYX_SESSION_MODEL)",
|
|
68144
|
+
"shell-config": "the selection `keryx shell` persisted (--from-shell-config)",
|
|
68145
|
+
none: "none \u2014 the caller named no model, so the block is adaptive"
|
|
68146
|
+
};
|
|
68147
|
+
var ADAPTIVE_GUIDANCE = {
|
|
68148
|
+
light: "dispatch on a lighter model your own runtime offers (its fast/small class), or on your session model if it offers no choice",
|
|
68149
|
+
standard: "dispatch on your session model",
|
|
68150
|
+
deep: "dispatch on the most capable model your own runtime offers, or on your session model if it offers no choice"
|
|
68151
|
+
};
|
|
68152
|
+
function sessionModelFromArgs(args, env = process.env) {
|
|
68153
|
+
const resolved = resolveCallerSession({
|
|
68154
|
+
flagProvider: optionValue(args, "--session-provider"),
|
|
68155
|
+
flagModel: optionValue(args, "--session-model"),
|
|
68156
|
+
fromShellConfig: args.includes("--from-shell-config"),
|
|
68157
|
+
env
|
|
68158
|
+
});
|
|
68159
|
+
return { session: { providerId: resolved.providerId, modelId: resolved.modelId }, source: resolved.source };
|
|
67804
68160
|
}
|
|
67805
68161
|
async function tierCatalog(args, session) {
|
|
67806
68162
|
const file = optionValue(args, "--catalog");
|
|
@@ -67833,12 +68189,14 @@ async function tierCatalog(args, session) {
|
|
|
67833
68189
|
}
|
|
67834
68190
|
return await detectProviders({ fetch, env: envWithSavedApiKeys() });
|
|
67835
68191
|
}
|
|
68192
|
+
function pinsModel(decision) {
|
|
68193
|
+
return decision.tier_resolution === "discovered" && decision.provider !== "" && decision.model !== "";
|
|
68194
|
+
}
|
|
67836
68195
|
function dispatchModelBlock(decision) {
|
|
67837
|
-
const named = decision.provider !== "" && decision.model !== "";
|
|
67838
68196
|
return {
|
|
67839
68197
|
tier: decision.tier,
|
|
67840
68198
|
tier_reasons: decision.tier_reasons,
|
|
67841
|
-
...
|
|
68199
|
+
...pinsModel(decision) ? { provider: decision.provider, model: decision.model } : { inherit: true },
|
|
67842
68200
|
tier_resolution: decision.tier_resolution,
|
|
67843
68201
|
model_discovery: decision.model_discovery
|
|
67844
68202
|
};
|
|
@@ -67887,6 +68245,15 @@ async function runCommentsCollect(args) {
|
|
|
67887
68245
|
console.log(renderPrCommentsMarkdown({ repo, number, round, result }));
|
|
67888
68246
|
console.log(`findings: ${findings.length}${out === undefined ? "" : ` (written to ${out})`}`);
|
|
67889
68247
|
console.log(`collected against: ${sha} (round ${round})`);
|
|
68248
|
+
console.log(`pull request: ${describePullRequest(result.pull)}`);
|
|
68249
|
+
if (result.pull.state === "unknown") {
|
|
68250
|
+
console.log("WARNING: the pull request's state could not be read \u2014 `comments reply` will refuse this pull request.");
|
|
68251
|
+
} else if (result.pull.merged || result.pull.state === "closed") {
|
|
68252
|
+
console.log("WARNING: the pull request is no longer open \u2014 `comments reply` will refuse it unless --allow-closed-pr is passed.");
|
|
68253
|
+
}
|
|
68254
|
+
if (result.pull.headSha !== null && !shaMatchesHead(sha, result.pull.headSha)) {
|
|
68255
|
+
console.log(`WARNING: --sha ${sha} is not the pull request's head (${result.pull.headSha}) \u2014 this collection is stale, and \`comments reply\` refuses a SHA that is not the head.`);
|
|
68256
|
+
}
|
|
67890
68257
|
console.log(`unanswered so far: ${unansweredComments(recorded).length} \u2014 replies are posted ONCE, after the final round.`);
|
|
67891
68258
|
}
|
|
67892
68259
|
async function runCommentsReply(args) {
|
|
@@ -67897,6 +68264,7 @@ async function runCommentsReply(args) {
|
|
|
67897
68264
|
const isFinal = args.includes("--final");
|
|
67898
68265
|
const dryRun = args.includes("--dry-run");
|
|
67899
68266
|
await refuseInvalidResult(optionValue(args, "--result"), optionValue(args, "--outcomes"));
|
|
68267
|
+
await refuseUnmanagedReply(args, repo, number, dryRun || !isFinal);
|
|
67900
68268
|
const cwd = process.cwd();
|
|
67901
68269
|
const port = await resolvePort(args);
|
|
67902
68270
|
const state = await readPrCommentState(cwd, repo, number);
|
|
@@ -67924,7 +68292,9 @@ async function runCommentsReply(args) {
|
|
|
67924
68292
|
repo,
|
|
67925
68293
|
number,
|
|
67926
68294
|
pass,
|
|
67927
|
-
sha:
|
|
68295
|
+
sha: requiredSha(args, "--sha", "comments reply"),
|
|
68296
|
+
pull: collected.pull,
|
|
68297
|
+
allowClosed: args.includes("--allow-closed-pr"),
|
|
67928
68298
|
round: { index: round, isFinal },
|
|
67929
68299
|
state,
|
|
67930
68300
|
dryRun
|
|
@@ -67950,7 +68320,7 @@ async function resolvePort(args) {
|
|
|
67950
68320
|
return createGhPort();
|
|
67951
68321
|
}
|
|
67952
68322
|
const files = {};
|
|
67953
|
-
for (const key of ["pull-comments", "pull-reviews", "issue-comments"]) {
|
|
68323
|
+
for (const key of ["pull", "pull-comments", "pull-reviews", "issue-comments"]) {
|
|
67954
68324
|
const file = join3(fixtures, `${key}.json`);
|
|
67955
68325
|
try {
|
|
67956
68326
|
files[key] = JSON.parse(await readFile88(file, "utf8"));
|
|
@@ -67976,6 +68346,28 @@ async function resolveSelfLogin(args) {
|
|
|
67976
68346
|
}
|
|
67977
68347
|
return login;
|
|
67978
68348
|
}
|
|
68349
|
+
async function refuseUnmanagedReply(args, repo, number, postsNothing) {
|
|
68350
|
+
if (postsNothing || optionValue(args, "--result") !== undefined) {
|
|
68351
|
+
return;
|
|
68352
|
+
}
|
|
68353
|
+
const where = `${repo}#${number}`;
|
|
68354
|
+
const ref = optionValue(args, "--review");
|
|
68355
|
+
if (ref === undefined) {
|
|
68356
|
+
throw new Error(`Refusing to reply on ${where}: posting is for a MANAGED review, and nothing here shows one. Pass \`--review <review-id-or-path>\` (the package \`keryx review start/attach\` wrote for this pull request) or \`--result <file>\` (a review-pr-feedback run). A lightweight review is report-only \u2014 it answers nobody. \`--dry-run\` still shows what would be posted.`);
|
|
68357
|
+
}
|
|
68358
|
+
let manifest;
|
|
68359
|
+
try {
|
|
68360
|
+
manifest = await getManagedReviewStatus(process.cwd(), ref);
|
|
68361
|
+
} catch (error) {
|
|
68362
|
+
throw new Error(`Refusing to reply on ${where}: \`--review ${ref}\` is not a readable managed review package (${error instanceof Error ? error.message : String(error)}).`);
|
|
68363
|
+
}
|
|
68364
|
+
const target = manifest.target;
|
|
68365
|
+
const refNumber = /(?:^|[/#])(\d+)\/?$/.exec(target.ref.trim())?.[1];
|
|
68366
|
+
const repoMismatch = target.repository !== undefined && target.repository !== "" && !target.repository.toLowerCase().endsWith(repo.toLowerCase());
|
|
68367
|
+
if (target.kind !== "pr" || refNumber !== String(number) || repoMismatch) {
|
|
68368
|
+
throw new Error(`Refusing to reply on ${where}: \`--review ${ref}\` reviewed ${target.kind} \`${target.ref}\`${target.repository ? ` in ${target.repository}` : ""}, not this pull request. A reply pass answers the review that produced its outcomes.`);
|
|
68369
|
+
}
|
|
68370
|
+
}
|
|
67979
68371
|
async function refuseInvalidResult(source, outcomesSource) {
|
|
67980
68372
|
if (source === undefined) {
|
|
67981
68373
|
return;
|
|
@@ -68035,20 +68427,20 @@ async function runLearn(args) {
|
|
|
68035
68427
|
const cwd = process.cwd();
|
|
68036
68428
|
const config = await loadReviewLearningConfig(cwd);
|
|
68037
68429
|
if (config === null) {
|
|
68038
|
-
console.log(`no review learning configured: ${
|
|
68430
|
+
console.log(`no review learning configured: ${path156.relative(cwd, reviewLearningConfigPath(cwd))} is absent, so this project does not learn from pull-request comments.`);
|
|
68039
68431
|
return;
|
|
68040
68432
|
}
|
|
68041
68433
|
const number = requiredInteger(args, "--pr");
|
|
68042
68434
|
const statePath = prCommentsStatePath(cwd, config.repo, number);
|
|
68043
68435
|
if (!await pathExists(statePath)) {
|
|
68044
|
-
throw new Error(`No collected comments for ${config.repo}#${number}: ${
|
|
68436
|
+
throw new Error(`No collected comments for ${config.repo}#${number}: ${path156.relative(cwd, statePath)} does not exist. Run \`keryx review comments collect --repo ${config.repo} --pr ${number} --sha <head-sha>\` first \u2014 this command reads that record and never fetches from GitHub.`);
|
|
68045
68437
|
}
|
|
68046
68438
|
const state = await readPrCommentState(cwd, config.repo, number);
|
|
68047
68439
|
const selection = selectLearnableComments(state, config.authors);
|
|
68048
68440
|
const sourcePath = learningSourcePath(cwd, config.repo, number);
|
|
68049
68441
|
const recordPath = learningRecordPath(cwd, config.repo, number);
|
|
68050
|
-
const relativeSourcePath = toPosix(
|
|
68051
|
-
await
|
|
68442
|
+
const relativeSourcePath = toPosix(path156.relative(cwd, sourcePath));
|
|
68443
|
+
await mkdir55(path156.dirname(sourcePath), { recursive: true });
|
|
68052
68444
|
await writeFileAtomic(sourcePath, renderLearningSource(selection));
|
|
68053
68445
|
await writeFileAtomic(recordPath, renderLearningRecord({
|
|
68054
68446
|
repo: config.repo,
|
|
@@ -68071,7 +68463,7 @@ async function runLearn(args) {
|
|
|
68071
68463
|
}
|
|
68072
68464
|
console.log(`nothing to learn from ${config.repo}#${number} \u2014 ${detail}.`);
|
|
68073
68465
|
console.log(`Source: ${relativeSourcePath}`);
|
|
68074
|
-
console.log(`Record: ${toPosix(
|
|
68466
|
+
console.log(`Record: ${toPosix(path156.relative(cwd, recordPath))}`);
|
|
68075
68467
|
return;
|
|
68076
68468
|
}
|
|
68077
68469
|
const proposal = await learnProjectSkill(cwd, {
|
|
@@ -68087,7 +68479,7 @@ async function runLearn(args) {
|
|
|
68087
68479
|
console.log(`${proposal.dryRun ? "Would create" : "Created"} learning proposal: ${proposal.proposalId}`);
|
|
68088
68480
|
console.log(`Skill: ${proposal.skill.module}/${proposal.skill.name}`);
|
|
68089
68481
|
console.log(`Source: ${relativeSourcePath}`);
|
|
68090
|
-
console.log(`Record: ${toPosix(
|
|
68482
|
+
console.log(`Record: ${toPosix(path156.relative(cwd, recordPath))}`);
|
|
68091
68483
|
console.log(`Authors: ${config.authors.join(", ")}`);
|
|
68092
68484
|
console.log(`Comments: ${selection.kept.length} used, ${selection.unconfigured.length} excluded as unconfigured, ${selection.bodyless.length} with no recorded body`);
|
|
68093
68485
|
console.log(`Confidence: ${proposal.confidence}`);
|
|
@@ -68775,7 +69167,9 @@ comments:
|
|
|
68775
69167
|
UNOBSERVED, on the same rule as no record at all.
|
|
68776
69168
|
\`reply\` refuses without --final: replying per round turns one review thread
|
|
68777
69169
|
into six, and a reply written mid-flow states an intention rather than an
|
|
68778
|
-
outcome.
|
|
69170
|
+
outcome. It also refuses a pull request that is closed or merged (override:
|
|
69171
|
+
--allow-closed-pr), one whose state could not be read, and a --sha that is not
|
|
69172
|
+
the pull request's head \u2014 the answers must describe the commit it points at. Each reply is at most ${DEFAULT_MAX_SENTENCES_PER_REPLY} sentences (--max-sentences) AND ${DEFAULT_MAX_REPLY_CHARS} characters
|
|
68779
69173
|
(--max-chars) \u2014 CUT to both in code, with the remainder replaced by a link,
|
|
68780
69174
|
because a sentence budget alone lets one 4,000-character sentence through \u2014
|
|
68781
69175
|
threaded where GitHub gives a thread, and capped
|
|
@@ -68805,11 +69199,13 @@ tier:
|
|
|
68805
69199
|
that produced it, the resolved provider/model, \`tier_resolution\`, and what was
|
|
68806
69200
|
on the table when it resolved; --json prints the block alone.
|
|
68807
69201
|
NO MODEL NAME EXISTS IN THIS COMMAND. The session's provider/model come from
|
|
68808
|
-
the
|
|
68809
|
-
|
|
68810
|
-
|
|
68811
|
-
|
|
68812
|
-
|
|
69202
|
+
the caller: --session-provider / --session-model, else KERYX_SESSION_PROVIDER /
|
|
69203
|
+
KERYX_SESSION_MODEL, else (only with --from-shell-config) the selection
|
|
69204
|
+
\`keryx shell\` persisted. The candidate set comes from live provider detection
|
|
69205
|
+
(override with --catalog, the \`detectProviders()\` shape). A model is named
|
|
69206
|
+
only when discovery assigned one other than the session's; otherwise the block
|
|
69207
|
+
says \`inherit\` with the tier, and the host picks its own model for that tier
|
|
69208
|
+
(standard: the session model). Never a non-zero exit. Detection is skipped
|
|
68813
69209
|
entirely when the session names no provider and model, because ranking is
|
|
68814
69210
|
refused without an anchor whatever the catalogue holds.
|
|
68815
69211
|
|
|
@@ -68852,8 +69248,8 @@ stack:
|
|
|
68852
69248
|
|
|
68853
69249
|
// src/commands/rules.ts
|
|
68854
69250
|
init_fs();
|
|
68855
|
-
import { mkdir as
|
|
68856
|
-
import
|
|
69251
|
+
import { mkdir as mkdir56, readFile as readFile89, writeFile as writeFile47 } from "fs/promises";
|
|
69252
|
+
import path157 from "path";
|
|
68857
69253
|
async function rulesCommand(args = [], projectRoot = process.cwd()) {
|
|
68858
69254
|
const subcommand = args[0];
|
|
68859
69255
|
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
@@ -68871,11 +69267,11 @@ async function rulesCommand(args = [], projectRoot = process.cwd()) {
|
|
|
68871
69267
|
printHelp13();
|
|
68872
69268
|
return;
|
|
68873
69269
|
}
|
|
68874
|
-
const metaprojectRoot =
|
|
69270
|
+
const metaprojectRoot = path157.join(projectRoot, ".metaproject");
|
|
68875
69271
|
if (!await pathExists(metaprojectRoot)) {
|
|
68876
69272
|
throw new Error("Metaproject is not initialized. Run `keryx init` first.");
|
|
68877
69273
|
}
|
|
68878
|
-
const manifestPath =
|
|
69274
|
+
const manifestPath = path157.join(metaprojectRoot, "metaproject.json");
|
|
68879
69275
|
const manifest = await readManifest3(manifestPath);
|
|
68880
69276
|
const enableTasks = moduleEnabled(manifest, "tasks");
|
|
68881
69277
|
if (options.preview) {
|
|
@@ -68926,7 +69322,7 @@ async function rulesCommand(args = [], projectRoot = process.cwd()) {
|
|
|
68926
69322
|
createDefault: true
|
|
68927
69323
|
});
|
|
68928
69324
|
const ruleSources = syncedRules.map((rule) => rule.source);
|
|
68929
|
-
await
|
|
69325
|
+
await mkdir56(path157.join(metaprojectRoot, "rules"), { recursive: true });
|
|
68930
69326
|
await persistManifestEntrypoints(manifestPath, manifest, ruleSources);
|
|
68931
69327
|
await refreshRoutingEntrypoints(metaprojectRoot, manifest, ruleSources, await hasDistilledEntrypoints(metaprojectRoot), { intent: "rules sync", resolution: options.resolution });
|
|
68932
69328
|
console.log(`# rules sync`);
|
|
@@ -69013,17 +69409,17 @@ Options:
|
|
|
69013
69409
|
}
|
|
69014
69410
|
|
|
69015
69411
|
// src/commands/standard.ts
|
|
69016
|
-
import { mkdir as
|
|
69017
|
-
import
|
|
69412
|
+
import { mkdir as mkdir57, writeFile as writeFile48 } from "fs/promises";
|
|
69413
|
+
import path161 from "path";
|
|
69018
69414
|
|
|
69019
69415
|
// src/standard/service.ts
|
|
69020
69416
|
init_fs();
|
|
69021
|
-
import
|
|
69417
|
+
import path159 from "path";
|
|
69022
69418
|
import { readFile as readFile91 } from "fs/promises";
|
|
69023
69419
|
|
|
69024
69420
|
// src/standard/validate.ts
|
|
69025
69421
|
init_fs();
|
|
69026
|
-
import
|
|
69422
|
+
import path158 from "path";
|
|
69027
69423
|
import { readFile as readFile90 } from "fs/promises";
|
|
69028
69424
|
|
|
69029
69425
|
// src/standard/schemas.ts
|
|
@@ -69296,20 +69692,20 @@ var MANIFEST_TOP_LEVEL_SCHEMA = {
|
|
|
69296
69692
|
async function validateWorkspace2(cwd) {
|
|
69297
69693
|
const errors = [];
|
|
69298
69694
|
const warnings = [];
|
|
69299
|
-
const metaprojectRoot =
|
|
69695
|
+
const metaprojectRoot = path158.join(cwd, ".metaproject");
|
|
69300
69696
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
69301
69697
|
for (const file of requiredFiles) {
|
|
69302
|
-
if (!await pathExists(
|
|
69698
|
+
if (!await pathExists(path158.join(metaprojectRoot, file))) {
|
|
69303
69699
|
errors.push(issue("missing-required-file", `Required file .metaproject/${file} is missing`, `Run \`keryx init\` to scaffold .metaproject/${file}.`));
|
|
69304
69700
|
}
|
|
69305
69701
|
}
|
|
69306
69702
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
69307
69703
|
for (const dir of requiredDirs) {
|
|
69308
|
-
if (!await pathExists(
|
|
69704
|
+
if (!await pathExists(path158.join(metaprojectRoot, dir))) {
|
|
69309
69705
|
errors.push(issue("missing-required-dir", `Required directory .metaproject/${dir}/ is missing`, `Run \`keryx init\` to create .metaproject/${dir}/.`));
|
|
69310
69706
|
}
|
|
69311
69707
|
}
|
|
69312
|
-
const manifestPath =
|
|
69708
|
+
const manifestPath = path158.join(metaprojectRoot, "metaproject.json");
|
|
69313
69709
|
if (!await pathExists(manifestPath)) {
|
|
69314
69710
|
return { ok: errors.length === 0, errors, warnings };
|
|
69315
69711
|
}
|
|
@@ -69341,7 +69737,7 @@ async function validateWorkspace2(cwd) {
|
|
|
69341
69737
|
if (typeof value !== "string") {
|
|
69342
69738
|
continue;
|
|
69343
69739
|
}
|
|
69344
|
-
if (!await pathExists(
|
|
69740
|
+
if (!await pathExists(path158.join(cwd, value))) {
|
|
69345
69741
|
errors.push(issue("missing-declared-path", `Declared path paths.${key} -> ${value} does not exist`, `Create ${value} or remove paths.${key} from metaproject.json.`));
|
|
69346
69742
|
}
|
|
69347
69743
|
}
|
|
@@ -69351,7 +69747,7 @@ async function validateWorkspace2(cwd) {
|
|
|
69351
69747
|
continue;
|
|
69352
69748
|
}
|
|
69353
69749
|
if (typeof entry.manifest === "string") {
|
|
69354
|
-
if (!await pathExists(
|
|
69750
|
+
if (!await pathExists(path158.join(cwd, entry.manifest))) {
|
|
69355
69751
|
errors.push(issue("missing-module-manifest", `Enabled module "${key}" manifest ${entry.manifest} is missing`, `Create ${entry.manifest} or run \`keryx update\` to regenerate module manifests.`));
|
|
69356
69752
|
}
|
|
69357
69753
|
}
|
|
@@ -69360,14 +69756,14 @@ async function validateWorkspace2(cwd) {
|
|
|
69360
69756
|
if (typeof value !== "string") {
|
|
69361
69757
|
continue;
|
|
69362
69758
|
}
|
|
69363
|
-
if (!await pathExists(
|
|
69759
|
+
if (!await pathExists(path158.join(cwd, value))) {
|
|
69364
69760
|
const sink = field === "data" ? warnings : errors;
|
|
69365
69761
|
sink.push(issue("missing-module-path", `Module "${key}" declares ${String(field)} ${value}, which does not exist`, field === "data" ? `Run the module (e.g. \`keryx ${key} ...\`) to generate ${value}, or update modules.${key}.data in metaproject.json.` : `Create ${value} or update modules.${key}.${String(field)} in metaproject.json.`));
|
|
69366
69762
|
}
|
|
69367
69763
|
}
|
|
69368
69764
|
}
|
|
69369
69765
|
for (const name of ROOT_ENTRYPOINTS2) {
|
|
69370
|
-
const filePath =
|
|
69766
|
+
const filePath = path158.join(cwd, name);
|
|
69371
69767
|
if (!await pathExists(filePath)) {
|
|
69372
69768
|
continue;
|
|
69373
69769
|
}
|
|
@@ -69421,7 +69817,7 @@ async function runDoctor(cwd) {
|
|
|
69421
69817
|
return validateWorkspace2(cwd);
|
|
69422
69818
|
}
|
|
69423
69819
|
async function readManifest4(cwd) {
|
|
69424
|
-
const manifestPath =
|
|
69820
|
+
const manifestPath = path159.join(cwd, ".metaproject", "metaproject.json");
|
|
69425
69821
|
if (!await pathExists(manifestPath)) {
|
|
69426
69822
|
throw new Error("Metaproject is not initialized (missing .metaproject/metaproject.json). Run `keryx init` first.");
|
|
69427
69823
|
}
|
|
@@ -69443,10 +69839,10 @@ async function runCapabilities(cwd) {
|
|
|
69443
69839
|
// src/standard/emit-llms.ts
|
|
69444
69840
|
init_fs();
|
|
69445
69841
|
init_json();
|
|
69446
|
-
import
|
|
69842
|
+
import path160 from "path";
|
|
69447
69843
|
import { readdir as readdir31 } from "fs/promises";
|
|
69448
69844
|
function llmsPath(cwd) {
|
|
69449
|
-
return
|
|
69845
|
+
return path160.join(cwd, ".metaproject", "llms.txt");
|
|
69450
69846
|
}
|
|
69451
69847
|
function renderLlms(manifest, artifacts) {
|
|
69452
69848
|
const name = manifest.name ?? "metaproject";
|
|
@@ -69489,7 +69885,7 @@ function renderLlms(manifest, artifacts) {
|
|
|
69489
69885
|
`;
|
|
69490
69886
|
}
|
|
69491
69887
|
async function collectArtifactIndex(cwd) {
|
|
69492
|
-
const dataRoot =
|
|
69888
|
+
const dataRoot = path160.join(cwd, ".metaproject", "data");
|
|
69493
69889
|
if (!await pathExists(dataRoot)) {
|
|
69494
69890
|
return [];
|
|
69495
69891
|
}
|
|
@@ -69502,11 +69898,11 @@ async function collectArtifactIndex(cwd) {
|
|
|
69502
69898
|
return;
|
|
69503
69899
|
}
|
|
69504
69900
|
for (const entry of entries) {
|
|
69505
|
-
const full =
|
|
69901
|
+
const full = path160.join(dir, entry.name);
|
|
69506
69902
|
if (entry.isDirectory()) {
|
|
69507
69903
|
await walk(full);
|
|
69508
69904
|
} else if (entry.isFile()) {
|
|
69509
|
-
const rel = toPosix(
|
|
69905
|
+
const rel = toPosix(path160.relative(cwd, full));
|
|
69510
69906
|
if (rel.includes("/artifacts/")) {
|
|
69511
69907
|
out.push(rel);
|
|
69512
69908
|
}
|
|
@@ -69517,7 +69913,7 @@ async function collectArtifactIndex(cwd) {
|
|
|
69517
69913
|
return out.sort();
|
|
69518
69914
|
}
|
|
69519
69915
|
async function emitLlms(cwd) {
|
|
69520
|
-
const manifestPath =
|
|
69916
|
+
const manifestPath = path160.join(cwd, ".metaproject", "metaproject.json");
|
|
69521
69917
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
69522
69918
|
const artifacts = await collectArtifactIndex(cwd);
|
|
69523
69919
|
const content = renderLlms(manifest, artifacts);
|
|
@@ -69696,11 +70092,11 @@ async function handleEmit(projectRoot, args) {
|
|
|
69696
70092
|
if (args.includes("--stdout")) {
|
|
69697
70093
|
process.stdout.write(result.content);
|
|
69698
70094
|
} else {
|
|
69699
|
-
await
|
|
70095
|
+
await mkdir57(path161.dirname(result.path), { recursive: true });
|
|
69700
70096
|
await writeFile48(result.path, result.content, "utf8");
|
|
69701
70097
|
heading("keryx standard emit llms");
|
|
69702
70098
|
if (problems.length === 0) {
|
|
69703
|
-
console.log(` ${style.green(symbols.ok)} wrote ${
|
|
70099
|
+
console.log(` ${style.green(symbols.ok)} wrote ${path161.relative(projectRoot, result.path)} (valid llms.txt)`);
|
|
69704
70100
|
} else {
|
|
69705
70101
|
for (const problem of problems) {
|
|
69706
70102
|
console.log(` ${style.red(symbols.cross)} ${problem}`);
|
|
@@ -70503,8 +70899,9 @@ var COMMAND_DESCRIPTORS = [
|
|
|
70503
70899
|
],
|
|
70504
70900
|
args: [
|
|
70505
70901
|
{ name: "opt-in", type: "bool", required: false, desc: "request cross-family review; without it the answer is single-family" },
|
|
70506
|
-
{ name: "session-provider", type: "string", required: false, desc: "provider that authored the change; defaults to
|
|
70507
|
-
{ name: "session-model", type: "string", required: false, desc: "model that authored the change; defaults to
|
|
70902
|
+
{ name: "session-provider", type: "string", required: false, desc: "provider that authored the change; defaults to KERYX_SESSION_PROVIDER" },
|
|
70903
|
+
{ name: "session-model", type: "string", required: false, desc: "model that authored the change; defaults to KERYX_SESSION_MODEL" },
|
|
70904
|
+
{ name: "from-shell-config", type: "bool", required: false, desc: "use the selection keryx shell persisted as the session (off by default)" },
|
|
70508
70905
|
{ name: "json", type: "bool", required: false, desc: "the record a round should carry" }
|
|
70509
70906
|
],
|
|
70510
70907
|
json: true,
|
|
@@ -70699,8 +71096,8 @@ Usage:
|
|
|
70699
71096
|
}
|
|
70700
71097
|
|
|
70701
71098
|
// src/commands/security.ts
|
|
70702
|
-
import { mkdir as
|
|
70703
|
-
import
|
|
71099
|
+
import { mkdir as mkdir58, readdir as readdir32, readFile as readFile93, writeFile as writeFile49 } from "fs/promises";
|
|
71100
|
+
import path163 from "path";
|
|
70704
71101
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
70705
71102
|
init_args();
|
|
70706
71103
|
init_contained_path();
|
|
@@ -70870,11 +71267,11 @@ init_detect();
|
|
|
70870
71267
|
init_fs();
|
|
70871
71268
|
init_config();
|
|
70872
71269
|
init_detect();
|
|
70873
|
-
import
|
|
71270
|
+
import path162 from "path";
|
|
70874
71271
|
import { readFile as readFile92 } from "fs/promises";
|
|
70875
71272
|
var DEFAULT_CORPORA = ["injection", "exfil", "structured-pii", "secret"];
|
|
70876
71273
|
async function loadEvalCases(dir) {
|
|
70877
|
-
const file =
|
|
71274
|
+
const file = path162.join(dir, "cases.json");
|
|
70878
71275
|
if (!await pathExists(file)) {
|
|
70879
71276
|
return [];
|
|
70880
71277
|
}
|
|
@@ -70916,7 +71313,7 @@ async function runEval(opts) {
|
|
|
70916
71313
|
const corporaSeen = [];
|
|
70917
71314
|
let totalCases = 0;
|
|
70918
71315
|
for (const corpus of opts.corpora) {
|
|
70919
|
-
const dir =
|
|
71316
|
+
const dir = path162.join(opts.fixturesRoot, corpus);
|
|
70920
71317
|
const cases = await loadEvalCases(dir);
|
|
70921
71318
|
if (cases.length === 0) {
|
|
70922
71319
|
continue;
|
|
@@ -71155,7 +71552,7 @@ async function handleScan(cwd, args) {
|
|
|
71155
71552
|
ownerRoot: projectRoot,
|
|
71156
71553
|
targetPath: contained.path,
|
|
71157
71554
|
source,
|
|
71158
|
-
path:
|
|
71555
|
+
path: path163.relative(projectRoot, contained.path) || ".",
|
|
71159
71556
|
...exclusions.length > 0 ? { exclusions } : {},
|
|
71160
71557
|
...recursive !== undefined ? { recursive } : {},
|
|
71161
71558
|
...maxFiles !== undefined ? { limits: { maxFiles } } : {},
|
|
@@ -71231,7 +71628,7 @@ function scanPathArgument(args) {
|
|
|
71231
71628
|
return optionValue(args, "--file");
|
|
71232
71629
|
}
|
|
71233
71630
|
function mcpBaselinePath(cwd) {
|
|
71234
|
-
return
|
|
71631
|
+
return path163.join(securityDataRoot(cwd), "mcp-baseline.json");
|
|
71235
71632
|
}
|
|
71236
71633
|
async function readMcpBaseline(file) {
|
|
71237
71634
|
if (!await pathExists(file)) {
|
|
@@ -71259,7 +71656,7 @@ async function collectManifestFiles(target) {
|
|
|
71259
71656
|
}
|
|
71260
71657
|
const files = [];
|
|
71261
71658
|
for (const entry of entries) {
|
|
71262
|
-
const full =
|
|
71659
|
+
const full = path163.join(target, entry.name);
|
|
71263
71660
|
if (entry.isDirectory()) {
|
|
71264
71661
|
files.push(...await collectManifestFiles(full));
|
|
71265
71662
|
} else if (entry.isFile() && entry.name.endsWith(".json") && entry.name !== "cases.json") {
|
|
@@ -71294,7 +71691,7 @@ async function handleScanMcp(cwd, args) {
|
|
|
71294
71691
|
const { manifest } = extractManifestAndBaseline(read.value, {});
|
|
71295
71692
|
const baseline = { schemaVersion: 1, tools: buildMcpBaseline(manifest) };
|
|
71296
71693
|
const outPath = mcpBaselinePath(cwd);
|
|
71297
|
-
await
|
|
71694
|
+
await mkdir58(path163.dirname(outPath), { recursive: true });
|
|
71298
71695
|
await writeFile49(outPath, `${JSON.stringify(baseline, null, 2)}
|
|
71299
71696
|
`, "utf8");
|
|
71300
71697
|
if (asJson) {
|
|
@@ -71338,7 +71735,7 @@ async function handleScanMcp(cwd, args) {
|
|
|
71338
71735
|
coverage,
|
|
71339
71736
|
baseline: baselineState.state,
|
|
71340
71737
|
files: perFile.map((entry) => ({
|
|
71341
|
-
file:
|
|
71738
|
+
file: path163.relative(cwd, entry.file),
|
|
71342
71739
|
readable: entry.readable,
|
|
71343
71740
|
findings: entry.matches.map((m) => ({
|
|
71344
71741
|
category: m.category,
|
|
@@ -71355,7 +71752,7 @@ async function handleScanMcp(cwd, args) {
|
|
|
71355
71752
|
console.log("");
|
|
71356
71753
|
console.log(` ${style.bold(`${unreadable} manifest(s) could not be read and were NOT scanned:`)}`);
|
|
71357
71754
|
for (const entry of perFile.filter((e) => !e.readable)) {
|
|
71358
|
-
console.log(` ${symbols.cross} ${
|
|
71755
|
+
console.log(` ${symbols.cross} ${path163.relative(cwd, entry.file)}`);
|
|
71359
71756
|
}
|
|
71360
71757
|
}
|
|
71361
71758
|
if (baselineState.state === "unreadable") {
|
|
@@ -71364,7 +71761,7 @@ async function handleScanMcp(cwd, args) {
|
|
|
71364
71761
|
}
|
|
71365
71762
|
for (const entry of flagged) {
|
|
71366
71763
|
console.log("");
|
|
71367
|
-
console.log(` ${style.bold(
|
|
71764
|
+
console.log(` ${style.bold(path163.relative(cwd, entry.file))}`);
|
|
71368
71765
|
for (const m of entry.matches) {
|
|
71369
71766
|
console.log(` ${severityMarker(m.severity)} ${m.category}/${m.policyId} ${style.dim(`(conf ${m.confidence})`)}`);
|
|
71370
71767
|
}
|
|
@@ -71525,9 +71922,9 @@ async function handleHooks(cwd, args) {
|
|
|
71525
71922
|
await installRuntimeHooks(cwd, runtime);
|
|
71526
71923
|
const errors = runtime.validate(JSON.parse(await readFile93(runtime.settingsPath(cwd), "utf8")));
|
|
71527
71924
|
if (errors.length === 0) {
|
|
71528
|
-
console.log(` ${style.green(symbols.ok)} ${runtime.id} \u2192 ${
|
|
71925
|
+
console.log(` ${style.green(symbols.ok)} ${runtime.id} \u2192 ${path163.relative(cwd, runtime.settingsPath(cwd))}`);
|
|
71529
71926
|
if (await modeOf(cwd) === "advisory") {
|
|
71530
|
-
note(`advisory mode: ${runtime.id} will report findings and allow the call. Set \`mode\` to \`enforced\`, \`ci\` or \`gateway\` in ${
|
|
71927
|
+
note(`advisory mode: ${runtime.id} will report findings and allow the call. Set \`mode\` to \`enforced\`, \`ci\` or \`gateway\` in ${path163.join(".metaproject", "security.config.json")} to make it refuse.`);
|
|
71531
71928
|
}
|
|
71532
71929
|
} else {
|
|
71533
71930
|
for (const e of errors) {
|
|
@@ -71542,7 +71939,7 @@ async function handleHooks(cwd, args) {
|
|
|
71542
71939
|
}
|
|
71543
71940
|
}
|
|
71544
71941
|
function fixturesRoot() {
|
|
71545
|
-
return
|
|
71942
|
+
return path163.join(path163.dirname(fileURLToPath7(import.meta.url)), "..", "..", "fixtures");
|
|
71546
71943
|
}
|
|
71547
71944
|
async function handleEval(cwd, args) {
|
|
71548
71945
|
const corpusArg = optionValue(args, "--corpus") ?? "all";
|
|
@@ -71564,7 +71961,7 @@ async function handleEval(cwd, args) {
|
|
|
71564
71961
|
detect = await pureDetect(cwd);
|
|
71565
71962
|
}
|
|
71566
71963
|
const report = await runEval({ fixturesRoot: root, corpora, detect });
|
|
71567
|
-
const thresholds = await loadThresholds(
|
|
71964
|
+
const thresholds = await loadThresholds(path163.join(root, "thresholds.json"));
|
|
71568
71965
|
const gate = gateEval(report, thresholds);
|
|
71569
71966
|
if (asJson) {
|
|
71570
71967
|
console.log(JSON.stringify({ report, gate }, null, 2));
|
|
@@ -71660,7 +72057,7 @@ async function policyIsUnderstood(cwd) {
|
|
|
71660
72057
|
const config = await loadSecurityConfig(cwd);
|
|
71661
72058
|
const errors = validateSecurityConfig(config);
|
|
71662
72059
|
if (errors.length > 0) {
|
|
71663
|
-
process.stderr.write(`keryx security: ${
|
|
72060
|
+
process.stderr.write(`keryx security: ${path163.relative(cwd, configPath(cwd))} does not match the policy schema, so no approval is emitted \u2014 ${errors[0]}
|
|
71664
72061
|
`);
|
|
71665
72062
|
return false;
|
|
71666
72063
|
}
|
|
@@ -71710,7 +72107,7 @@ function printSecurityHelp() {
|
|
|
71710
72107
|
|
|
71711
72108
|
// src/harness/process/sandbox/detect.ts
|
|
71712
72109
|
import { existsSync as realExistsSync } from "fs";
|
|
71713
|
-
import
|
|
72110
|
+
import path164 from "path";
|
|
71714
72111
|
|
|
71715
72112
|
// src/harness/process/sandbox/seatbelt.ts
|
|
71716
72113
|
var SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
|
|
@@ -71904,9 +72301,9 @@ function detectSandboxLauncher(opts = {}) {
|
|
|
71904
72301
|
}
|
|
71905
72302
|
if (platform === "linux") {
|
|
71906
72303
|
const env = opts.env ?? process.env;
|
|
71907
|
-
const dirs = (env.PATH ?? "").split(
|
|
72304
|
+
const dirs = (env.PATH ?? "").split(path164.delimiter).filter(Boolean);
|
|
71908
72305
|
for (const dir of dirs) {
|
|
71909
|
-
const candidate =
|
|
72306
|
+
const candidate = path164.join(dir, BWRAP_PROGRAM);
|
|
71910
72307
|
if (exists(candidate)) {
|
|
71911
72308
|
return { available: true, platform, path: candidate };
|
|
71912
72309
|
}
|
|
@@ -72045,7 +72442,7 @@ async function sandboxCommand(args = [], deps = {}) {
|
|
|
72045
72442
|
init_args();
|
|
72046
72443
|
|
|
72047
72444
|
// src/commands/integrate.ts
|
|
72048
|
-
import
|
|
72445
|
+
import path165 from "path";
|
|
72049
72446
|
var EDITOR_USAGE = `<cursor|claude|opencode|vscode|generic|all>`;
|
|
72050
72447
|
var DEFAULT_EDITORS = ["all"];
|
|
72051
72448
|
async function integrateCommand(args = [], cwd = process.cwd(), label) {
|
|
@@ -72078,7 +72475,7 @@ async function addIntegration(cwd, editors, dryRun, label) {
|
|
|
72078
72475
|
console.log(outcome.snippet ?? "");
|
|
72079
72476
|
continue;
|
|
72080
72477
|
}
|
|
72081
|
-
const rel =
|
|
72478
|
+
const rel = path165.relative(cwd, outcome.filePath);
|
|
72082
72479
|
if (outcome.errors.length > 0) {
|
|
72083
72480
|
for (const e of outcome.errors) {
|
|
72084
72481
|
console.log(` ${style.red(symbols.cross)} ${e}`);
|
|
@@ -72117,7 +72514,7 @@ async function removeIntegration(cwd, editors, dryRun, label) {
|
|
|
72117
72514
|
console.log(` ${style.gray(symbols.off)} ${outcome.id} ${style.dim("no file to change")}`);
|
|
72118
72515
|
continue;
|
|
72119
72516
|
}
|
|
72120
|
-
const rel =
|
|
72517
|
+
const rel = path165.relative(cwd, outcome.filePath);
|
|
72121
72518
|
console.log(` ${outcome.removed ? style.green(symbols.ok) : style.gray(symbols.off)} ${outcome.id} ${style.dim(outcome.removed ? `${dryRun ? "would be removed from" : "removed from"} ${rel}` : "nothing to remove")}`);
|
|
72122
72519
|
}
|
|
72123
72520
|
}
|
|
@@ -72144,21 +72541,21 @@ init_contained_path();
|
|
|
72144
72541
|
|
|
72145
72542
|
// src/mcp-servers/config.ts
|
|
72146
72543
|
init_config_dir();
|
|
72147
|
-
import
|
|
72544
|
+
import path167 from "path";
|
|
72148
72545
|
|
|
72149
72546
|
// src/mcp-servers/compat.ts
|
|
72150
72547
|
init_config_dir();
|
|
72151
|
-
import
|
|
72548
|
+
import path166 from "path";
|
|
72152
72549
|
import os2 from "os";
|
|
72153
72550
|
function compatFiles(cwd, home) {
|
|
72154
72551
|
const base = home ?? os2.homedir();
|
|
72155
72552
|
return [
|
|
72156
|
-
{ source: "grok", file:
|
|
72157
|
-
{ source: "grok", file:
|
|
72158
|
-
{ source: "mcp.json", file:
|
|
72159
|
-
{ source: "cursor", file:
|
|
72160
|
-
{ source: "cursor", file:
|
|
72161
|
-
{ source: "claude", file:
|
|
72553
|
+
{ source: "grok", file: path166.join(base, ".grok", "config.toml"), projectLocal: false },
|
|
72554
|
+
{ source: "grok", file: path166.join(cwd, ".grok", "config.toml"), projectLocal: true },
|
|
72555
|
+
{ source: "mcp.json", file: path166.join(cwd, ".mcp.json"), projectLocal: true },
|
|
72556
|
+
{ source: "cursor", file: path166.join(base, ".cursor", "mcp.json"), projectLocal: false },
|
|
72557
|
+
{ source: "cursor", file: path166.join(cwd, ".cursor", "mcp.json"), projectLocal: true },
|
|
72558
|
+
{ source: "claude", file: path166.join(base, ".claude.json"), projectLocal: false }
|
|
72162
72559
|
];
|
|
72163
72560
|
}
|
|
72164
72561
|
function readMcpServersShape(file, text) {
|
|
@@ -72554,25 +72951,25 @@ function parseConfigFile(file) {
|
|
|
72554
72951
|
}
|
|
72555
72952
|
function projectConfigFiles(cwd, gitRoot) {
|
|
72556
72953
|
const files = [];
|
|
72557
|
-
let dir =
|
|
72558
|
-
const stop = gitRoot === undefined ? undefined :
|
|
72954
|
+
let dir = path167.resolve(cwd);
|
|
72955
|
+
const stop = gitRoot === undefined ? undefined : path167.resolve(gitRoot);
|
|
72559
72956
|
for (;; ) {
|
|
72560
|
-
files.push(
|
|
72957
|
+
files.push(path167.join(dir, ".keryx", "mcp-servers.json"));
|
|
72561
72958
|
if (stop !== undefined && dir === stop)
|
|
72562
72959
|
break;
|
|
72563
|
-
const parent =
|
|
72960
|
+
const parent = path167.dirname(dir);
|
|
72564
72961
|
if (parent === dir)
|
|
72565
72962
|
break;
|
|
72566
72963
|
dir = parent;
|
|
72567
72964
|
}
|
|
72568
72965
|
return files.reverse();
|
|
72569
72966
|
}
|
|
72570
|
-
var NO_HOME =
|
|
72967
|
+
var NO_HOME = path167.join(path167.sep, "\x00keryx-no-home");
|
|
72571
72968
|
function loadMcpServers(options) {
|
|
72572
72969
|
const env = options.env ?? process.env;
|
|
72573
72970
|
const configDir = options.configDir ?? keryxConfigDir();
|
|
72574
72971
|
const problems = [];
|
|
72575
|
-
const userFile =
|
|
72972
|
+
const userFile = path167.join(configDir, "mcp-servers.json");
|
|
72576
72973
|
const user = parseConfigFile(userFile);
|
|
72577
72974
|
problems.push(...user.problems);
|
|
72578
72975
|
const winner = new Map;
|
|
@@ -72607,7 +73004,7 @@ function loadMcpServers(options) {
|
|
|
72607
73004
|
winner.set(name, { entry, source: "project", file, projectLocal: true });
|
|
72608
73005
|
}
|
|
72609
73006
|
}
|
|
72610
|
-
const overlayFile =
|
|
73007
|
+
const overlayFile = path167.join(configDir, "mcp-servers-disabled.json");
|
|
72611
73008
|
const overlay = readOverlay(overlayFile);
|
|
72612
73009
|
problems.push(...overlay.problems);
|
|
72613
73010
|
const servers = [...winner.entries()].map(([name, { entry, source, file, projectLocal }]) => {
|
|
@@ -73042,11 +73439,11 @@ function createMcpInteractiveTools(deps) {
|
|
|
73042
73439
|
|
|
73043
73440
|
// src/mcp-servers/credentials.ts
|
|
73044
73441
|
init_config_dir();
|
|
73045
|
-
import
|
|
73442
|
+
import path168 from "path";
|
|
73046
73443
|
var FORGET_CODE_VERIFIER = { code_verifier: undefined };
|
|
73047
73444
|
var CREDENTIALS_SCHEMA_VERSION = 1;
|
|
73048
73445
|
function credentialsFile(configDir) {
|
|
73049
|
-
return
|
|
73446
|
+
return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-credentials.json");
|
|
73050
73447
|
}
|
|
73051
73448
|
function credentialKey(serverName, serverUrl) {
|
|
73052
73449
|
return `${serverName}:${serverUrl}`;
|
|
@@ -73945,7 +74342,7 @@ async function connectStdioMcpServer(argv, options, handshakeTimeoutMs, signal,
|
|
|
73945
74342
|
}
|
|
73946
74343
|
|
|
73947
74344
|
// src/mcp-servers/runtime.ts
|
|
73948
|
-
import
|
|
74345
|
+
import path171 from "path";
|
|
73949
74346
|
|
|
73950
74347
|
// src/mcp-servers/manager.ts
|
|
73951
74348
|
var DEFAULT_CONNECT_CONCURRENCY = 4;
|
|
@@ -74037,9 +74434,9 @@ async function closeServers(states) {
|
|
|
74037
74434
|
// src/mcp-servers/trust.ts
|
|
74038
74435
|
init_config_dir();
|
|
74039
74436
|
import { createHash as createHash32 } from "crypto";
|
|
74040
|
-
import
|
|
74437
|
+
import path169 from "path";
|
|
74041
74438
|
function trustFile(configDir) {
|
|
74042
|
-
return
|
|
74439
|
+
return path169.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-trust.json");
|
|
74043
74440
|
}
|
|
74044
74441
|
function serverFingerprint(server) {
|
|
74045
74442
|
const material = JSON.stringify({
|
|
@@ -74136,16 +74533,16 @@ function credentialSummary(raw) {
|
|
|
74136
74533
|
// src/mcp-servers/store.ts
|
|
74137
74534
|
init_config_dir();
|
|
74138
74535
|
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
74139
|
-
import
|
|
74536
|
+
import path170 from "path";
|
|
74140
74537
|
var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
74141
74538
|
function userConfigFile(configDir) {
|
|
74142
|
-
return
|
|
74539
|
+
return path170.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers.json");
|
|
74143
74540
|
}
|
|
74144
74541
|
function projectConfigFile(projectRoot) {
|
|
74145
|
-
return
|
|
74542
|
+
return path170.join(projectRoot, ".keryx", "mcp-servers.json");
|
|
74146
74543
|
}
|
|
74147
74544
|
function overlayFile(configDir) {
|
|
74148
|
-
return
|
|
74545
|
+
return path170.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-disabled.json");
|
|
74149
74546
|
}
|
|
74150
74547
|
function readForWrite(file) {
|
|
74151
74548
|
const read = readConfigFile(file);
|
|
@@ -74184,7 +74581,7 @@ function writeDocument(file, doc, scope) {
|
|
|
74184
74581
|
writeOwnerOnlyFileAtomic(file, body);
|
|
74185
74582
|
return;
|
|
74186
74583
|
}
|
|
74187
|
-
mkdirSync6(
|
|
74584
|
+
mkdirSync6(path170.dirname(file), { recursive: true });
|
|
74188
74585
|
writeFileSync7(file, body, { encoding: "utf8" });
|
|
74189
74586
|
}
|
|
74190
74587
|
function addServer(options) {
|
|
@@ -74733,7 +75130,7 @@ async function defaultConnect(server, env, signal, kills, configDir) {
|
|
|
74733
75130
|
function defaultServerCwd(server, fallbackCwd = process.cwd()) {
|
|
74734
75131
|
if (server.source !== "project")
|
|
74735
75132
|
return fallbackCwd;
|
|
74736
|
-
return
|
|
75133
|
+
return path171.dirname(path171.dirname(server.file));
|
|
74737
75134
|
}
|
|
74738
75135
|
|
|
74739
75136
|
// src/commands/mcp-servers.ts
|
|
@@ -75347,12 +75744,12 @@ function positional3(args) {
|
|
|
75347
75744
|
|
|
75348
75745
|
// src/commands/serve-mcp.ts
|
|
75349
75746
|
init_args();
|
|
75350
|
-
import
|
|
75747
|
+
import path178 from "path";
|
|
75351
75748
|
|
|
75352
75749
|
// src/mcp/discovery.ts
|
|
75353
75750
|
init_fs();
|
|
75354
75751
|
init_json();
|
|
75355
|
-
import
|
|
75752
|
+
import path172 from "path";
|
|
75356
75753
|
var MODULE_MANIFEST_KEY = {
|
|
75357
75754
|
gdgraph: "gdgraph",
|
|
75358
75755
|
security: "security",
|
|
@@ -75414,7 +75811,7 @@ function buildDiscovery(manifest) {
|
|
|
75414
75811
|
};
|
|
75415
75812
|
}
|
|
75416
75813
|
async function loadDiscovery(cwd) {
|
|
75417
|
-
const manifestPath =
|
|
75814
|
+
const manifestPath = path172.join(cwd, ".metaproject", "metaproject.json");
|
|
75418
75815
|
if (!await pathExists(manifestPath)) {
|
|
75419
75816
|
return buildDiscovery({});
|
|
75420
75817
|
}
|
|
@@ -75486,8 +75883,8 @@ init_workspace_service();
|
|
|
75486
75883
|
init_store2();
|
|
75487
75884
|
init_fs();
|
|
75488
75885
|
import { createHash as createHash35, randomUUID as randomUUID17 } from "crypto";
|
|
75489
|
-
import { appendFile as appendFile7, mkdir as
|
|
75490
|
-
import
|
|
75886
|
+
import { appendFile as appendFile7, mkdir as mkdir59, open as open2, readFile as readFile94, rename as rename7, rm as rm12, stat as stat17, writeFile as writeFile50 } from "fs/promises";
|
|
75887
|
+
import path173 from "path";
|
|
75491
75888
|
|
|
75492
75889
|
// src/sac/policy-experiment.ts
|
|
75493
75890
|
import { createHash as createHash34, createHmac as createHmac2 } from "crypto";
|
|
@@ -75843,7 +76240,7 @@ function hasReportIntegrity(report) {
|
|
|
75843
76240
|
return reportHashPattern.test(reportDigest) && sha2565(body) === reportDigest;
|
|
75844
76241
|
}
|
|
75845
76242
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
75846
|
-
const configPath =
|
|
76243
|
+
const configPath = path173.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
75847
76244
|
let configJson;
|
|
75848
76245
|
try {
|
|
75849
76246
|
const raw = await readFile94(configPath, "utf8").catch((error) => {
|
|
@@ -75945,7 +76342,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
|
|
|
75945
76342
|
const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
|
|
75946
76343
|
return Object.freeze({ configPresent, enabled, killSwitch, integrityReady, candidateWouldActivate: integrityReady && enabled && !killSwitch, steps: Object.freeze([...steps]) });
|
|
75947
76344
|
};
|
|
75948
|
-
const configPath =
|
|
76345
|
+
const configPath = path173.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
75949
76346
|
let configJson;
|
|
75950
76347
|
try {
|
|
75951
76348
|
const raw = await readFile94(configPath, "utf8").catch((error) => {
|
|
@@ -76200,9 +76597,9 @@ class FwkReadService {
|
|
|
76200
76597
|
const recordedAt = nowIso2(now);
|
|
76201
76598
|
const id = `receipt-${randomUUID17().replace(/-/g, "").slice(0, 16)}`;
|
|
76202
76599
|
const base = { schemaVersion: "1.0", id, workspaceId, actor, action, decision, recordedAt, cost: { tokens: 0, toolCalls: 1, elapsedMs: 0 }, contextAssembly: { traceRef: assembly.traceRef, configurationRevision: assembly.configurationRevision, selected: assembly.selected.map((id) => `./ids/${id}`), omittedOptional: assembly.omittedOptional.map((id) => `./ids/${id}`) }, policy: { ref: assembly.policyRef, revision: assembly.policyRevision }, ...action === "resource" ? { resourceRef: `./ids/${resourceId ?? "unknown"}` } : {} };
|
|
76203
|
-
const ledger =
|
|
76204
|
-
const checkpointPath =
|
|
76205
|
-
await
|
|
76600
|
+
const ledger = path173.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
|
|
76601
|
+
const checkpointPath = path173.join(path173.dirname(ledger), "access-receipts.checkpoint.json");
|
|
76602
|
+
await mkdir59(path173.dirname(ledger), { recursive: true, mode: 448 });
|
|
76206
76603
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
76207
76604
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
76208
76605
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -76312,9 +76709,9 @@ init_store3();
|
|
|
76312
76709
|
// src/sac/collaboration-service.ts
|
|
76313
76710
|
init_workspace_service();
|
|
76314
76711
|
init_sac();
|
|
76315
|
-
import { appendFile as appendFile8, mkdir as
|
|
76712
|
+
import { appendFile as appendFile8, mkdir as mkdir60, readFile as readFile95 } from "fs/promises";
|
|
76316
76713
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
76317
|
-
import
|
|
76714
|
+
import path174 from "path";
|
|
76318
76715
|
|
|
76319
76716
|
class CollaborationServiceError extends Error {
|
|
76320
76717
|
code;
|
|
@@ -76330,7 +76727,7 @@ class CollaborationService {
|
|
|
76330
76727
|
this.input = input;
|
|
76331
76728
|
}
|
|
76332
76729
|
file(id) {
|
|
76333
|
-
return
|
|
76730
|
+
return path174.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
76334
76731
|
}
|
|
76335
76732
|
async overview(input) {
|
|
76336
76733
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -76367,7 +76764,7 @@ class CollaborationService {
|
|
|
76367
76764
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
76368
76765
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
76369
76766
|
});
|
|
76370
|
-
await
|
|
76767
|
+
await mkdir60(path174.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
76371
76768
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
76372
76769
|
`, { mode: 384 });
|
|
76373
76770
|
return normalizeCollaborationResult(activity);
|
|
@@ -76421,10 +76818,10 @@ init_workspace_service();
|
|
|
76421
76818
|
// src/session/external-slate.ts
|
|
76422
76819
|
init_fs();
|
|
76423
76820
|
init_machine_wrap_up();
|
|
76424
|
-
import { mkdir as
|
|
76425
|
-
import
|
|
76821
|
+
import { mkdir as mkdir61, readdir as readdir33, readFile as readFile96 } from "fs/promises";
|
|
76822
|
+
import path175 from "path";
|
|
76426
76823
|
function externalSlatesDir(cwd) {
|
|
76427
|
-
return
|
|
76824
|
+
return path175.join(cwd, ".keryx", "external-slates");
|
|
76428
76825
|
}
|
|
76429
76826
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
76430
76827
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -76434,14 +76831,14 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
76434
76831
|
}
|
|
76435
76832
|
function externalSlatePath(cwd, externalSessionId) {
|
|
76436
76833
|
assertValidExternalSessionId(externalSessionId);
|
|
76437
|
-
return
|
|
76834
|
+
return path175.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
76438
76835
|
}
|
|
76439
76836
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
76440
76837
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
76441
76838
|
}
|
|
76442
76839
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
76443
76840
|
assertValidExternalSessionId(externalSessionId);
|
|
76444
|
-
return
|
|
76841
|
+
return path175.join(externalSlatesDir(cwd), externalSessionId);
|
|
76445
76842
|
}
|
|
76446
76843
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
76447
76844
|
try {
|
|
@@ -76454,7 +76851,7 @@ async function readExternalSlate(cwd, externalSessionId) {
|
|
|
76454
76851
|
}
|
|
76455
76852
|
}
|
|
76456
76853
|
async function writeExternalSlate(cwd, externalSessionId, update) {
|
|
76457
|
-
await
|
|
76854
|
+
await mkdir61(externalSlatesDir(cwd), { recursive: true });
|
|
76458
76855
|
return withFileLock(externalSlateLockPath(cwd, externalSessionId), async () => {
|
|
76459
76856
|
const prev = await readExternalSlate(cwd, externalSessionId);
|
|
76460
76857
|
const next = update(prev);
|
|
@@ -76480,7 +76877,7 @@ function isExternalSlateStale(slate, now = () => new Date) {
|
|
|
76480
76877
|
return now().getTime() - lastWriteMs > DEFAULT_LOCK_STALE_MS;
|
|
76481
76878
|
}
|
|
76482
76879
|
async function closeExternalSlate(cwd, externalSessionId, trigger, now = () => new Date) {
|
|
76483
|
-
await
|
|
76880
|
+
await mkdir61(externalSlatesDir(cwd), { recursive: true });
|
|
76484
76881
|
return withFileLock(externalSlateLockPath(cwd, externalSessionId), async () => {
|
|
76485
76882
|
const external = await readExternalSlate(cwd, externalSessionId);
|
|
76486
76883
|
if (!external || external.closedAt !== undefined)
|
|
@@ -77086,7 +77483,7 @@ function buildToolRegistry() {
|
|
|
77086
77483
|
// src/mcp/resources.ts
|
|
77087
77484
|
init_fs();
|
|
77088
77485
|
init_contained_read();
|
|
77089
|
-
import
|
|
77486
|
+
import path176 from "path";
|
|
77090
77487
|
import { lstat as lstat5, readdir as readdir34, realpath as realpath9, stat as stat18 } from "fs/promises";
|
|
77091
77488
|
var URI_PREFIX = "metaproject://";
|
|
77092
77489
|
function mimeForPath(filePath) {
|
|
@@ -77099,13 +77496,13 @@ function mimeForPath(filePath) {
|
|
|
77099
77496
|
return "text/plain";
|
|
77100
77497
|
}
|
|
77101
77498
|
function dataRoot3(cwd) {
|
|
77102
|
-
return
|
|
77499
|
+
return path176.join(cwd, ".metaproject", "data");
|
|
77103
77500
|
}
|
|
77104
77501
|
function wikiRoot(cwd) {
|
|
77105
|
-
return
|
|
77502
|
+
return path176.join(cwd, ".metaproject", "wiki");
|
|
77106
77503
|
}
|
|
77107
77504
|
function memoryRoot2(cwd) {
|
|
77108
|
-
return
|
|
77505
|
+
return path176.join(cwd, ".metaproject", "memory");
|
|
77109
77506
|
}
|
|
77110
77507
|
var MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
|
|
77111
77508
|
async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
@@ -77131,7 +77528,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
|
|
|
77131
77528
|
return [];
|
|
77132
77529
|
}
|
|
77133
77530
|
for (const entry of entries) {
|
|
77134
|
-
const full =
|
|
77531
|
+
const full = path176.join(root, entry.name);
|
|
77135
77532
|
const info = await stat18(full).catch(() => null);
|
|
77136
77533
|
if (!info || !isPathInside(ownerReal, await realpath9(full).catch(() => "")))
|
|
77137
77534
|
continue;
|
|
@@ -77159,9 +77556,9 @@ async function listArtifacts(cwd) {
|
|
|
77159
77556
|
if (!moduleEntry.isDirectory()) {
|
|
77160
77557
|
continue;
|
|
77161
77558
|
}
|
|
77162
|
-
const artifactsDir =
|
|
77559
|
+
const artifactsDir = path176.join(base, moduleEntry.name, "artifacts");
|
|
77163
77560
|
for (const file of await walkFiles(artifactsDir)) {
|
|
77164
|
-
const rel = toPosix(
|
|
77561
|
+
const rel = toPosix(path176.relative(artifactsDir, file));
|
|
77165
77562
|
const relPath = `${moduleEntry.name}/${rel}`;
|
|
77166
77563
|
listings.push({
|
|
77167
77564
|
uri: `${URI_PREFIX}artifacts/${relPath}`,
|
|
@@ -77175,7 +77572,7 @@ async function listArtifacts(cwd) {
|
|
|
77175
77572
|
async function listUnderRoot(cwd, cls, root) {
|
|
77176
77573
|
const listings = [];
|
|
77177
77574
|
for (const file of await walkFiles(root)) {
|
|
77178
|
-
const rel = toPosix(
|
|
77575
|
+
const rel = toPosix(path176.relative(root, file));
|
|
77179
77576
|
listings.push({
|
|
77180
77577
|
uri: `${URI_PREFIX}${cls}/${rel}`,
|
|
77181
77578
|
name: rel,
|
|
@@ -77225,12 +77622,12 @@ function resolveConfined(cwd, cls, relPath) {
|
|
|
77225
77622
|
if (moduleName.includes("..") || moduleName.length === 0) {
|
|
77226
77623
|
return null;
|
|
77227
77624
|
}
|
|
77228
|
-
const root =
|
|
77229
|
-
const absolute =
|
|
77625
|
+
const root = path176.join(dataRoot3(cwd), moduleName, "artifacts");
|
|
77626
|
+
const absolute = path176.resolve(root, rest);
|
|
77230
77627
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
77231
77628
|
}
|
|
77232
77629
|
const root = cls === "wiki" ? wikiRoot(cwd) : memoryRoot2(cwd);
|
|
77233
|
-
const absolute =
|
|
77630
|
+
const absolute = path176.resolve(root, relPath);
|
|
77234
77631
|
return isPathInside(root, absolute) ? { root, absolute } : null;
|
|
77235
77632
|
}
|
|
77236
77633
|
async function readResource(cwd, roots, uri) {
|
|
@@ -77441,7 +77838,7 @@ async function serveMcpCommand(args = [], cwd = process.cwd()) {
|
|
|
77441
77838
|
return;
|
|
77442
77839
|
}
|
|
77443
77840
|
const http = args.includes("--http");
|
|
77444
|
-
const projectRoot =
|
|
77841
|
+
const projectRoot = path178.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
|
|
77445
77842
|
try {
|
|
77446
77843
|
await serveMcp({ cwd: projectRoot, http });
|
|
77447
77844
|
} catch (error) {
|
|
@@ -77545,14 +77942,14 @@ function printMcpHelp() {
|
|
|
77545
77942
|
// src/commands/status.ts
|
|
77546
77943
|
init_fs();
|
|
77547
77944
|
init_json();
|
|
77548
|
-
import
|
|
77945
|
+
import path179 from "path";
|
|
77549
77946
|
async function statusCommand(args = []) {
|
|
77550
77947
|
if (args.includes("--help") || args.includes("-h")) {
|
|
77551
77948
|
printHelp16();
|
|
77552
77949
|
return;
|
|
77553
77950
|
}
|
|
77554
|
-
const root =
|
|
77555
|
-
const manifestPath =
|
|
77951
|
+
const root = path179.join(process.cwd(), ".metaproject");
|
|
77952
|
+
const manifestPath = path179.join(root, "metaproject.json");
|
|
77556
77953
|
if (!await pathExists(root)) {
|
|
77557
77954
|
console.log("Metaproject: not initialized");
|
|
77558
77955
|
console.log("Run: keryx init");
|
|
@@ -77592,7 +77989,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
|
|
|
77592
77989
|
|
|
77593
77990
|
// src/commands/harness.ts
|
|
77594
77991
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
|
|
77595
|
-
import
|
|
77992
|
+
import path182 from "path";
|
|
77596
77993
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
77597
77994
|
|
|
77598
77995
|
// src/security/harness-scan.ts
|
|
@@ -78665,7 +79062,7 @@ ${command.argv.join(" ")}`);
|
|
|
78665
79062
|
}
|
|
78666
79063
|
|
|
78667
79064
|
// src/harness/process/sandbox/profile.ts
|
|
78668
|
-
import
|
|
79065
|
+
import path180 from "path";
|
|
78669
79066
|
var DEFAULT_SECRET_SUBPATHS = [
|
|
78670
79067
|
".ssh",
|
|
78671
79068
|
".gnupg",
|
|
@@ -78690,7 +79087,7 @@ function defaultReadDenyList(home) {
|
|
|
78690
79087
|
if (!home) {
|
|
78691
79088
|
return [];
|
|
78692
79089
|
}
|
|
78693
|
-
return DEFAULT_SECRET_SUBPATHS.map((sub) =>
|
|
79090
|
+
return DEFAULT_SECRET_SUBPATHS.map((sub) => path180.join(home, sub));
|
|
78694
79091
|
}
|
|
78695
79092
|
function defaultSandboxProfile(cwd, tmpDir, home) {
|
|
78696
79093
|
return {
|
|
@@ -78710,10 +79107,10 @@ function dedupe3(values) {
|
|
|
78710
79107
|
init_config_dir();
|
|
78711
79108
|
init_shell_config();
|
|
78712
79109
|
import { existsSync as existsSync27 } from "fs";
|
|
78713
|
-
import
|
|
79110
|
+
import path181 from "path";
|
|
78714
79111
|
var SECRET_KEY_PATTERN2 = /api[_-]?key|secret|token|password|credential/i;
|
|
78715
79112
|
function sandboxConfigPath(dir) {
|
|
78716
|
-
return
|
|
79113
|
+
return path181.join(path181.dirname(shellConfigPath(dir)), "sandbox.json");
|
|
78717
79114
|
}
|
|
78718
79115
|
function isShellDefault(v) {
|
|
78719
79116
|
return v === "off" || v === "workspace" || v === "strict" || v === "1";
|
|
@@ -79039,7 +79436,7 @@ init_slate();
|
|
|
79039
79436
|
init_paths();
|
|
79040
79437
|
init_shell_config();
|
|
79041
79438
|
import { realpathSync as realpathSync3 } from "fs";
|
|
79042
|
-
import { homedir as
|
|
79439
|
+
import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
|
|
79043
79440
|
|
|
79044
79441
|
// src/harness/extension/registry.ts
|
|
79045
79442
|
function registerExtension(input) {
|
|
@@ -79297,7 +79694,7 @@ function canonicalPath(p) {
|
|
|
79297
79694
|
}
|
|
79298
79695
|
function buildDefaultShellAdapter(cwd, env, profileOverride) {
|
|
79299
79696
|
const real = new RealProcessAdapter({ allowRealSubprocess: true });
|
|
79300
|
-
let profile = profileOverride ?? defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()),
|
|
79697
|
+
let profile = profileOverride ?? defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()), homedir5());
|
|
79301
79698
|
if (profileOverride === undefined && env.KERYX_DANGEROUSLY_DISABLE_SANDBOX === "1") {
|
|
79302
79699
|
profile = { ...profile, mode: "danger-full-access", required: false };
|
|
79303
79700
|
}
|
|
@@ -79811,7 +80208,7 @@ async function harnessExec(args, deps) {
|
|
|
79811
80208
|
let closeNetwork = async () => {};
|
|
79812
80209
|
let netDecisions;
|
|
79813
80210
|
if (restrictedDomains !== undefined && deps?.processAdapter === undefined) {
|
|
79814
|
-
const baseProfile = defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()),
|
|
80211
|
+
const baseProfile = defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()), homedir5());
|
|
79815
80212
|
const net = await setupNetworkRun({
|
|
79816
80213
|
...baseProfile,
|
|
79817
80214
|
network: "restricted",
|
|
@@ -79832,7 +80229,7 @@ async function harnessExec(args, deps) {
|
|
|
79832
80229
|
env: commandEnv,
|
|
79833
80230
|
cwd
|
|
79834
80231
|
};
|
|
79835
|
-
const worktreeRoot =
|
|
80232
|
+
const worktreeRoot = path182.parse(path182.resolve(cwd, commandPath)).root || cwd;
|
|
79836
80233
|
const budget = {
|
|
79837
80234
|
reservationId: idSeq(),
|
|
79838
80235
|
maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
|
|
@@ -80433,7 +80830,7 @@ function createAskUserTool(ask) {
|
|
|
80433
80830
|
|
|
80434
80831
|
// src/harness/process/shell-spawn.ts
|
|
80435
80832
|
import { realpathSync as realpathSync5 } from "fs";
|
|
80436
|
-
import { homedir as
|
|
80833
|
+
import { homedir as homedir6, tmpdir as tmpdir3 } from "os";
|
|
80437
80834
|
init_providers();
|
|
80438
80835
|
function resolveShellSandboxMode(env, sandboxConfigDir) {
|
|
80439
80836
|
if (env.KERYX_DANGEROUSLY_DISABLE_SANDBOX === "1")
|
|
@@ -80463,7 +80860,7 @@ function rootsFromEnv(name, env) {
|
|
|
80463
80860
|
const raw = env[name];
|
|
80464
80861
|
if (!raw)
|
|
80465
80862
|
return [];
|
|
80466
|
-
return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0).map((p) => canonical(p.startsWith("~/") ? p.replace(/^~/,
|
|
80863
|
+
return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0).map((p) => canonical(p.startsWith("~/") ? p.replace(/^~/, homedir6()) : p));
|
|
80467
80864
|
}
|
|
80468
80865
|
function extraWritableRoots(env) {
|
|
80469
80866
|
return rootsFromEnv("KERYX_SANDBOX_ALLOW_WRITE", env);
|
|
@@ -80472,7 +80869,7 @@ function extraReadDenyRoots(env) {
|
|
|
80472
80869
|
return rootsFromEnv("KERYX_SANDBOX_READ_DENY", env);
|
|
80473
80870
|
}
|
|
80474
80871
|
function shellSandboxProfile(root, mode, env) {
|
|
80475
|
-
const base = defaultSandboxProfile(canonical(root), canonical(tmpdir3()), canonical(
|
|
80872
|
+
const base = defaultSandboxProfile(canonical(root), canonical(tmpdir3()), canonical(homedir6()));
|
|
80476
80873
|
const writableRoots = [...base.writableRoots, ...extraWritableRoots(env)];
|
|
80477
80874
|
const readDenyList = [...base.readDenyList, ...extraReadDenyRoots(env)];
|
|
80478
80875
|
const domains = resolveAllowedDomains(env, root);
|
|
@@ -81900,7 +82297,7 @@ class SandboxedWebTransport {
|
|
|
81900
82297
|
|
|
81901
82298
|
// src/harness/web/web-worker-runner.ts
|
|
81902
82299
|
import { existsSync as existsSync29 } from "fs";
|
|
81903
|
-
import { homedir as
|
|
82300
|
+
import { homedir as homedir7 } from "os";
|
|
81904
82301
|
var WORKER_TIMEOUT_MS = 1e4;
|
|
81905
82302
|
var MAX_WORKER_OUTPUT_BYTES = 192000;
|
|
81906
82303
|
var WORKER_SOURCE = String.raw`
|
|
@@ -81998,7 +82395,7 @@ class SystemWebWorkerRunner {
|
|
|
81998
82395
|
platform;
|
|
81999
82396
|
constructor(options = {}) {
|
|
82000
82397
|
this.workspace = options.workspace ?? process.cwd();
|
|
82001
|
-
this.home = options.home ??
|
|
82398
|
+
this.home = options.home ?? homedir7();
|
|
82002
82399
|
this.platform = options.platform ?? process.platform;
|
|
82003
82400
|
}
|
|
82004
82401
|
async run(request, signal) {
|
|
@@ -82064,7 +82461,7 @@ class SystemWebWorkerRunner {
|
|
|
82064
82461
|
}
|
|
82065
82462
|
}
|
|
82066
82463
|
function createSystemWebWorkerRunner() {
|
|
82067
|
-
return new SystemWebWorkerRunner({ workspace: process.cwd(), home:
|
|
82464
|
+
return new SystemWebWorkerRunner({ workspace: process.cwd(), home: homedir7() });
|
|
82068
82465
|
}
|
|
82069
82466
|
|
|
82070
82467
|
// src/harness/tool/builtin/web-fetch-tool.ts
|
|
@@ -82216,7 +82613,7 @@ function workspaceReadTool(cwd) {
|
|
|
82216
82613
|
// src/harness/tool/builtin/workspace-lifecycle-tool.ts
|
|
82217
82614
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
82218
82615
|
import { writeFile as writeFile52 } from "fs/promises";
|
|
82219
|
-
import
|
|
82616
|
+
import path183 from "path";
|
|
82220
82617
|
init_slate();
|
|
82221
82618
|
init_store3();
|
|
82222
82619
|
init_guard2();
|
|
@@ -82362,7 +82759,7 @@ function workspaceProposeTool(cwd, getSessionDir) {
|
|
|
82362
82759
|
return { output: `workspace_propose: unrecognized 'kind' \u2014 expected one of: ${PROPOSAL_KINDS.join(", ")}`, isError: true };
|
|
82363
82760
|
}
|
|
82364
82761
|
const explicitSessionId = typeof input.sessionId === "string" && input.sessionId.length > 0 ? input.sessionId : undefined;
|
|
82365
|
-
const sessionRef = explicitSessionId ??
|
|
82762
|
+
const sessionRef = explicitSessionId ?? path183.basename(getSessionDir() ?? "");
|
|
82366
82763
|
if (sessionRef.length === 0) {
|
|
82367
82764
|
return { output: "workspace_propose: no 'sessionId' given and no active session in this run", isError: true };
|
|
82368
82765
|
}
|
|
@@ -82540,7 +82937,7 @@ init_shell_config();
|
|
|
82540
82937
|
init_command_risk();
|
|
82541
82938
|
init_shell_syntax();
|
|
82542
82939
|
import { existsSync as existsSync31 } from "fs";
|
|
82543
|
-
import
|
|
82940
|
+
import path184 from "path";
|
|
82544
82941
|
import { createHash as createHash42 } from "crypto";
|
|
82545
82942
|
var PREFIX_BANNED = new Set([
|
|
82546
82943
|
"sh",
|
|
@@ -82745,7 +83142,7 @@ function emptyShellPermissions() {
|
|
|
82745
83142
|
return { allow: [] };
|
|
82746
83143
|
}
|
|
82747
83144
|
function shellPermissionsPath(dir) {
|
|
82748
|
-
return
|
|
83145
|
+
return path184.join(path184.dirname(shellConfigPath(dir)), "permissions.json");
|
|
82749
83146
|
}
|
|
82750
83147
|
function loadShellPermissionsWithAudit(dir) {
|
|
82751
83148
|
try {
|
|
@@ -82783,7 +83180,7 @@ function loadShellPermissions(dir) {
|
|
|
82783
83180
|
function saveShellPermissions(perms, dir, options = {}) {
|
|
82784
83181
|
try {
|
|
82785
83182
|
const file = shellPermissionsPath(dir);
|
|
82786
|
-
ensureKeryxConfigDir(
|
|
83183
|
+
ensureKeryxConfigDir(path184.dirname(file));
|
|
82787
83184
|
const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
|
|
82788
83185
|
const body = {
|
|
82789
83186
|
allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
|
|
@@ -83222,12 +83619,12 @@ function remoteRequest(providerId, endpoint, query, key) {
|
|
|
83222
83619
|
// src/lib/search-config.ts
|
|
83223
83620
|
init_config_dir();
|
|
83224
83621
|
import { existsSync as existsSync32 } from "fs";
|
|
83225
|
-
import
|
|
83622
|
+
import path185 from "path";
|
|
83226
83623
|
function searchConfigPath(dir) {
|
|
83227
|
-
return
|
|
83624
|
+
return path185.join(keryxConfigDir(dir), "search-providers.json");
|
|
83228
83625
|
}
|
|
83229
83626
|
function searchCredentialPath(dir) {
|
|
83230
|
-
return
|
|
83627
|
+
return path185.join(keryxConfigDir(dir), "search-credentials.json");
|
|
83231
83628
|
}
|
|
83232
83629
|
function readJson5(file) {
|
|
83233
83630
|
try {
|
|
@@ -83356,7 +83753,7 @@ function createDefaultSearchProviderController(configDir) {
|
|
|
83356
83753
|
import { createHash as createHash43, randomUUID as randomUUID24 } from "crypto";
|
|
83357
83754
|
import { mkdtemp, rm as rm13 } from "fs/promises";
|
|
83358
83755
|
import { tmpdir as tmpdir4 } from "os";
|
|
83359
|
-
import
|
|
83756
|
+
import path186 from "path";
|
|
83360
83757
|
init_metaproject_adapter();
|
|
83361
83758
|
init_ledger();
|
|
83362
83759
|
init_orchestrate();
|
|
@@ -83619,8 +84016,8 @@ function createSpawnSubagentTool(deps) {
|
|
|
83619
84016
|
let dispatchId = "";
|
|
83620
84017
|
let closing = false;
|
|
83621
84018
|
try {
|
|
83622
|
-
ephemeralDir = await mkdtemp(
|
|
83623
|
-
dispatchId =
|
|
84019
|
+
ephemeralDir = await mkdtemp(path186.join(tmpdir4(), "keryx-subagent-slate-"));
|
|
84020
|
+
dispatchId = path186.basename(ephemeralDir);
|
|
83624
84021
|
openedChildSlate = await openSlate({
|
|
83625
84022
|
dir: ephemeralDir,
|
|
83626
84023
|
cwd,
|
|
@@ -83861,9 +84258,9 @@ ${boundSummary(folded.text)}`,
|
|
|
83861
84258
|
// src/harness/run-external-factory.ts
|
|
83862
84259
|
import { randomUUID as randomUUID25 } from "crypto";
|
|
83863
84260
|
import { execFile as execFile4 } from "child_process";
|
|
83864
|
-
import { mkdir as
|
|
84261
|
+
import { mkdir as mkdir62 } from "fs/promises";
|
|
83865
84262
|
import { tmpdir as tmpdir6 } from "os";
|
|
83866
|
-
import
|
|
84263
|
+
import path188 from "path";
|
|
83867
84264
|
import { promisify as promisify4 } from "util";
|
|
83868
84265
|
|
|
83869
84266
|
// src/harness/child/git-worktree-port.ts
|
|
@@ -84115,7 +84512,7 @@ function readRuntimeBlock(dispatch) {
|
|
|
84115
84512
|
// src/harness/external/runtime.ts
|
|
84116
84513
|
import { mkdtemp as mkdtemp2, rm as rm14, writeFile as writeFile53 } from "fs/promises";
|
|
84117
84514
|
import { tmpdir as tmpdir5 } from "os";
|
|
84118
|
-
import
|
|
84515
|
+
import path187 from "path";
|
|
84119
84516
|
|
|
84120
84517
|
// src/harness/external/prompt.ts
|
|
84121
84518
|
var EXTERNAL_RUNTIME_DIRECTIVE = [
|
|
@@ -85158,8 +85555,8 @@ async function prepareResultSchema() {
|
|
|
85158
85555
|
try {
|
|
85159
85556
|
const schema = await loadSchema("subagent-result");
|
|
85160
85557
|
const schemaText = JSON.stringify(schema, null, 2);
|
|
85161
|
-
schemaDir = await mkdtemp2(
|
|
85162
|
-
const schemaPath =
|
|
85558
|
+
schemaDir = await mkdtemp2(path187.join(tmpdir5(), "keryx-external-result-schema-"));
|
|
85559
|
+
const schemaPath = path187.join(schemaDir, "subagent-result.schema.json");
|
|
85163
85560
|
await writeFile53(schemaPath, schemaText, "utf8");
|
|
85164
85561
|
return { ok: true, schema, schemaText, schemaPath, schemaDir };
|
|
85165
85562
|
} catch (error) {
|
|
@@ -85386,7 +85783,7 @@ async function defaultWorkingDiff(cwd) {
|
|
|
85386
85783
|
function ensuringParentDir(port, worktreesDir) {
|
|
85387
85784
|
return {
|
|
85388
85785
|
async create(worktreeId) {
|
|
85389
|
-
await
|
|
85786
|
+
await mkdir62(worktreesDir, { recursive: true });
|
|
85390
85787
|
return port.create(worktreeId);
|
|
85391
85788
|
},
|
|
85392
85789
|
remove: (worktreeId) => port.remove(worktreeId),
|
|
@@ -85416,7 +85813,7 @@ async function createRunExternal(options) {
|
|
|
85416
85813
|
}
|
|
85417
85814
|
const config = gate.config;
|
|
85418
85815
|
const idSeq = options.idSeq ?? (() => randomUUID25());
|
|
85419
|
-
const worktreesDir = options.worktreesDir ??
|
|
85816
|
+
const worktreesDir = options.worktreesDir ?? path188.join(tmpdir6(), "keryx-external-worktrees");
|
|
85420
85817
|
const spawn = options.spawn ?? createBunSpawnPort();
|
|
85421
85818
|
const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
|
|
85422
85819
|
const readWorkingDiff = options.readWorkingDiff ?? (() => defaultWorkingDiff(options.cwd));
|
|
@@ -85611,12 +86008,15 @@ async function approveExternalSpawn(request) {
|
|
|
85611
86008
|
}
|
|
85612
86009
|
}
|
|
85613
86010
|
|
|
86011
|
+
// src/commands/shell.ts
|
|
86012
|
+
init_caller_session();
|
|
86013
|
+
|
|
85614
86014
|
// src/lib/statusbar.ts
|
|
85615
|
-
import { homedir as
|
|
86015
|
+
import { homedir as homedir8 } from "os";
|
|
85616
86016
|
var ESC = "\x1B";
|
|
85617
86017
|
var CSI = `${ESC}[`;
|
|
85618
86018
|
function collapseHome(path) {
|
|
85619
|
-
const home =
|
|
86019
|
+
const home = homedir8();
|
|
85620
86020
|
if (home.length > 0 && (path === home || path.startsWith(`${home}/`))) {
|
|
85621
86021
|
return `~${path.slice(home.length)}`;
|
|
85622
86022
|
}
|
|
@@ -85726,7 +86126,7 @@ class LiveMarkdownBlock {
|
|
|
85726
86126
|
// src/tui/theme.ts
|
|
85727
86127
|
init_config_dir();
|
|
85728
86128
|
import { existsSync as existsSync33 } from "fs";
|
|
85729
|
-
import
|
|
86129
|
+
import path189 from "path";
|
|
85730
86130
|
var THEME_NAMES = ["groknight", "grokday", "tokyonight", "keryx"];
|
|
85731
86131
|
var THEME_IDS = ["auto", ...THEME_NAMES];
|
|
85732
86132
|
var DEFAULT_THEME_ID = "groknight";
|
|
@@ -85856,7 +86256,7 @@ function onThemeChange(listener) {
|
|
|
85856
86256
|
};
|
|
85857
86257
|
}
|
|
85858
86258
|
function tuiConfigPath(dir) {
|
|
85859
|
-
return
|
|
86259
|
+
return path189.join(keryxConfigDir(dir), "tui.json");
|
|
85860
86260
|
}
|
|
85861
86261
|
function loadPersistedThemeId(dir) {
|
|
85862
86262
|
try {
|
|
@@ -85913,7 +86313,7 @@ init_workspace_service();
|
|
|
85913
86313
|
init_service8();
|
|
85914
86314
|
init_store2();
|
|
85915
86315
|
init_fs();
|
|
85916
|
-
import
|
|
86316
|
+
import path190 from "path";
|
|
85917
86317
|
import { readFile as readFile98 } from "fs/promises";
|
|
85918
86318
|
var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
|
|
85919
86319
|
var DEFAULT_AUTO_GOAL_ROUNDS = 8;
|
|
@@ -85989,7 +86389,7 @@ function getFlowService() {
|
|
|
85989
86389
|
async function autoProvisionFlow(cwd, goalText) {
|
|
85990
86390
|
const service = getFlowService();
|
|
85991
86391
|
const result = await service.init({ cwd, title: goalText });
|
|
85992
|
-
const acFile =
|
|
86392
|
+
const acFile = path190.join(cwd, result.dir, "acceptance-criteria.md");
|
|
85993
86393
|
await writeFileAtomic(acFile, [
|
|
85994
86394
|
"# Acceptance Criteria",
|
|
85995
86395
|
"",
|
|
@@ -87540,7 +87940,7 @@ init_proposal_lifecycle();
|
|
|
87540
87940
|
init_workspace_service();
|
|
87541
87941
|
import { randomUUID as randomUUID27 } from "crypto";
|
|
87542
87942
|
import { readdir as readdir36, stat as stat19 } from "fs/promises";
|
|
87543
|
-
import
|
|
87943
|
+
import path191 from "path";
|
|
87544
87944
|
|
|
87545
87945
|
// src/sac/lifecycle-flag.ts
|
|
87546
87946
|
init_store();
|
|
@@ -87699,7 +88099,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
87699
88099
|
}
|
|
87700
88100
|
return groups;
|
|
87701
88101
|
}, []));
|
|
87702
|
-
const metaPath =
|
|
88102
|
+
const metaPath = path191.join(extDir, `${id}.json`);
|
|
87703
88103
|
if (await pathExists(unboundDismissedReceiptPath(metaPath)))
|
|
87704
88104
|
continue;
|
|
87705
88105
|
candidates.push({
|
|
@@ -87713,7 +88113,7 @@ async function readExternalUnboundCandidates(cwd) {
|
|
|
87713
88113
|
return candidates;
|
|
87714
88114
|
}
|
|
87715
88115
|
async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
87716
|
-
const evidenceDir =
|
|
88116
|
+
const evidenceDir = path191.join(externalSlatesDir(cwd), externalSessionId);
|
|
87717
88117
|
let entries;
|
|
87718
88118
|
try {
|
|
87719
88119
|
entries = (await readdir36(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -87722,7 +88122,7 @@ async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
|
87722
88122
|
}
|
|
87723
88123
|
entries.sort();
|
|
87724
88124
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
87725
|
-
const evidencePath =
|
|
88125
|
+
const evidencePath = path191.join(evidenceDir, entries[i]);
|
|
87726
88126
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
87727
88127
|
continue;
|
|
87728
88128
|
const result = readConfigFile(evidencePath);
|
|
@@ -87765,7 +88165,7 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
|
|
|
87765
88165
|
var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
|
|
87766
88166
|
async function collectReceiptTargets(cwd, owner) {
|
|
87767
88167
|
const targets = new Set;
|
|
87768
|
-
const workspacesDir =
|
|
88168
|
+
const workspacesDir = path191.join(cwd, ".metaproject", "workspaces");
|
|
87769
88169
|
let workspaceIds;
|
|
87770
88170
|
try {
|
|
87771
88171
|
workspaceIds = (await readdir36(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -87773,7 +88173,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
87773
88173
|
return targets;
|
|
87774
88174
|
}
|
|
87775
88175
|
for (const workspaceId of workspaceIds) {
|
|
87776
|
-
const receiptsDir =
|
|
88176
|
+
const receiptsDir = path191.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
|
|
87777
88177
|
let files;
|
|
87778
88178
|
try {
|
|
87779
88179
|
files = (await readdir36(receiptsDir)).filter((name) => name.endsWith(".json"));
|
|
@@ -87781,7 +88181,7 @@ async function collectReceiptTargets(cwd, owner) {
|
|
|
87781
88181
|
continue;
|
|
87782
88182
|
}
|
|
87783
88183
|
for (const file of files) {
|
|
87784
|
-
const result = readConfigFile(
|
|
88184
|
+
const result = readConfigFile(path191.join(receiptsDir, file));
|
|
87785
88185
|
if (!result.ok)
|
|
87786
88186
|
continue;
|
|
87787
88187
|
try {
|
|
@@ -87824,7 +88224,7 @@ async function findSkillFiles(dir) {
|
|
|
87824
88224
|
}
|
|
87825
88225
|
const found = [];
|
|
87826
88226
|
for (const entry of entries) {
|
|
87827
|
-
const full =
|
|
88227
|
+
const full = path191.join(dir, entry.name);
|
|
87828
88228
|
if (entry.isDirectory()) {
|
|
87829
88229
|
found.push(...await findSkillFiles(full));
|
|
87830
88230
|
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
@@ -87883,10 +88283,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
87883
88283
|
changedAt: attribution.changedAt
|
|
87884
88284
|
});
|
|
87885
88285
|
}
|
|
87886
|
-
const sacSkillsDir =
|
|
88286
|
+
const sacSkillsDir = path191.join(cwd, ".metaproject", "project-skills", "sac");
|
|
87887
88287
|
const skillFiles = await findSkillFiles(sacSkillsDir);
|
|
87888
88288
|
for (const absolutePath of skillFiles) {
|
|
87889
|
-
const targetRef =
|
|
88289
|
+
const targetRef = path191.relative(path191.join(cwd, ".metaproject"), absolutePath).split(path191.sep).join("/");
|
|
87890
88290
|
if (skillReceipts.has(targetRef))
|
|
87891
88291
|
continue;
|
|
87892
88292
|
const attribution = await attributeToSession(absolutePath, scoped);
|
|
@@ -87904,12 +88304,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
|
87904
88304
|
return items;
|
|
87905
88305
|
}
|
|
87906
88306
|
async function isSlateEngaged(dir) {
|
|
87907
|
-
if (await pathExists(
|
|
88307
|
+
if (await pathExists(path191.join(dir, "slate.json")))
|
|
87908
88308
|
return true;
|
|
87909
|
-
if (await pathExists(
|
|
88309
|
+
if (await pathExists(path191.join(dir, "terminal-state.json")))
|
|
87910
88310
|
return true;
|
|
87911
88311
|
try {
|
|
87912
|
-
const entries = await readdir36(
|
|
88312
|
+
const entries = await readdir36(path191.join(dir, "slate-archive"));
|
|
87913
88313
|
return entries.length > 0;
|
|
87914
88314
|
} catch {
|
|
87915
88315
|
return false;
|
|
@@ -87923,7 +88323,7 @@ async function safeReadSlate(dir) {
|
|
|
87923
88323
|
}
|
|
87924
88324
|
}
|
|
87925
88325
|
async function readTerminalState(dir) {
|
|
87926
|
-
const result = readConfigFile(
|
|
88326
|
+
const result = readConfigFile(path191.join(dir, "terminal-state.json"));
|
|
87927
88327
|
if (!result.ok) {
|
|
87928
88328
|
return;
|
|
87929
88329
|
}
|
|
@@ -87947,7 +88347,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
87947
88347
|
if (session === undefined) {
|
|
87948
88348
|
throw new Error(`No session or evidence path matches "${target}"`);
|
|
87949
88349
|
}
|
|
87950
|
-
const archiveDir =
|
|
88350
|
+
const archiveDir = path191.join(sessionDir(session.projectPath, session.id), "slate-archive");
|
|
87951
88351
|
let entries;
|
|
87952
88352
|
try {
|
|
87953
88353
|
entries = (await readdir36(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -87957,7 +88357,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
|
|
|
87957
88357
|
if (entries.length === 0)
|
|
87958
88358
|
throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
|
|
87959
88359
|
entries.sort();
|
|
87960
|
-
return
|
|
88360
|
+
return path191.join(archiveDir, entries[entries.length - 1]);
|
|
87961
88361
|
}
|
|
87962
88362
|
async function dismissUnboundByTarget(cwd, target, reason) {
|
|
87963
88363
|
const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
|
|
@@ -87983,7 +88383,7 @@ async function dismissUnboundCandidate(candidatePath, reason) {
|
|
|
87983
88383
|
return { removed: candidatePath, receipt: receiptPath };
|
|
87984
88384
|
}
|
|
87985
88385
|
async function readNewestUnboundCandidate(dir) {
|
|
87986
|
-
const archiveDir =
|
|
88386
|
+
const archiveDir = path191.join(dir, "slate-archive");
|
|
87987
88387
|
let entries;
|
|
87988
88388
|
try {
|
|
87989
88389
|
entries = (await readdir36(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
@@ -87992,7 +88392,7 @@ async function readNewestUnboundCandidate(dir) {
|
|
|
87992
88392
|
}
|
|
87993
88393
|
entries.sort();
|
|
87994
88394
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
87995
|
-
const evidencePath =
|
|
88395
|
+
const evidencePath = path191.join(archiveDir, entries[i]);
|
|
87996
88396
|
if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
|
|
87997
88397
|
continue;
|
|
87998
88398
|
const result = readConfigFile(evidencePath);
|
|
@@ -88022,7 +88422,7 @@ function isFailureOutcome(g) {
|
|
|
88022
88422
|
return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
|
|
88023
88423
|
}
|
|
88024
88424
|
async function readNewestWrapUpOutcome(dir) {
|
|
88025
|
-
const archiveDir =
|
|
88425
|
+
const archiveDir = path191.join(dir, "slate-archive");
|
|
88026
88426
|
let entries;
|
|
88027
88427
|
try {
|
|
88028
88428
|
entries = (await readdir36(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
|
|
@@ -88031,7 +88431,7 @@ async function readNewestWrapUpOutcome(dir) {
|
|
|
88031
88431
|
}
|
|
88032
88432
|
entries.sort();
|
|
88033
88433
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
88034
|
-
const evidencePath =
|
|
88434
|
+
const evidencePath = path191.join(archiveDir, entries[i]);
|
|
88035
88435
|
const result = readConfigFile(evidencePath);
|
|
88036
88436
|
if (!result.ok) {
|
|
88037
88437
|
continue;
|
|
@@ -91787,10 +92187,10 @@ function describeElicitationPrompt(tool, inputJson) {
|
|
|
91787
92187
|
// src/lib/permission-mode-config.ts
|
|
91788
92188
|
init_permission_mode();
|
|
91789
92189
|
init_config_dir();
|
|
91790
|
-
import
|
|
92190
|
+
import path192 from "path";
|
|
91791
92191
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
91792
92192
|
function permissionModeConfigPath(dir) {
|
|
91793
|
-
return
|
|
92193
|
+
return path192.join(keryxConfigDir(dir), "permission-mode.json");
|
|
91794
92194
|
}
|
|
91795
92195
|
function withRegistryLock2(dir, fn) {
|
|
91796
92196
|
return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -92308,7 +92708,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
92308
92708
|
// src/lib/version-check.ts
|
|
92309
92709
|
init_config_dir();
|
|
92310
92710
|
init_fs();
|
|
92311
|
-
import
|
|
92711
|
+
import path193 from "path";
|
|
92312
92712
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
92313
92713
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
92314
92714
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -92510,7 +92910,7 @@ async function checkVersion(options) {
|
|
|
92510
92910
|
const now = options.now ?? Date.now;
|
|
92511
92911
|
const timestamp = now();
|
|
92512
92912
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
92513
|
-
const cacheFile =
|
|
92913
|
+
const cacheFile = path193.join(configDir, "version-check.json");
|
|
92514
92914
|
const cache = parseCache(cacheFile);
|
|
92515
92915
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
92516
92916
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -100895,6 +101295,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
100895
101295
|
};
|
|
100896
101296
|
const makeAgentDeps = async (sel, getSlateSession) => {
|
|
100897
101297
|
const getSessionDir = () => getSlateSession()?.dir;
|
|
101298
|
+
exportCallerSession(sel.provider, sel.model);
|
|
100898
101299
|
const agentProvider = tuiProviderFactory(sel.provider, sel.model, sel.baseUrl);
|
|
100899
101300
|
let orient;
|
|
100900
101301
|
try {
|
|
@@ -101095,6 +101496,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
101095
101496
|
const cwdLabel = collapseHome(process.cwd());
|
|
101096
101497
|
printHeader("keryx", `${provider}/${model}${baseUrl !== undefined ? ` (${baseUrl})` : ""}${modeLabel} \xB7 ${cwdLabel}`);
|
|
101097
101498
|
if (agentMode) {
|
|
101499
|
+
exportCallerSession(provider, model);
|
|
101098
101500
|
const agentProvider = baseFactory(provider, model, baseUrl);
|
|
101099
101501
|
let orient;
|
|
101100
101502
|
try {
|
|
@@ -101337,7 +101739,7 @@ Shell:
|
|
|
101337
101739
|
init_fs();
|
|
101338
101740
|
import { readFile as readFile99 } from "fs/promises";
|
|
101339
101741
|
import { stdin } from "process";
|
|
101340
|
-
import
|
|
101742
|
+
import path194 from "path";
|
|
101341
101743
|
var MODULES = [
|
|
101342
101744
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
101343
101745
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -101383,8 +101785,8 @@ async function modulesCommand(args = []) {
|
|
|
101383
101785
|
return;
|
|
101384
101786
|
}
|
|
101385
101787
|
const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
101386
|
-
const metaprojectRoot =
|
|
101387
|
-
const manifestPath =
|
|
101788
|
+
const metaprojectRoot = path194.join(process.cwd(), ".metaproject");
|
|
101789
|
+
const manifestPath = path194.join(metaprojectRoot, "metaproject.json");
|
|
101388
101790
|
if (!await pathExists(manifestPath)) {
|
|
101389
101791
|
if (wantsJson) {
|
|
101390
101792
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -101515,9 +101917,9 @@ import {
|
|
|
101515
101917
|
unlinkSync as unlinkSync3,
|
|
101516
101918
|
writeFileSync as writeFileSync9
|
|
101517
101919
|
} from "fs";
|
|
101518
|
-
import
|
|
101920
|
+
import path195 from "path";
|
|
101519
101921
|
function serveCredentialPath(dir) {
|
|
101520
|
-
return
|
|
101922
|
+
return path195.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
101521
101923
|
}
|
|
101522
101924
|
function constantTimeEqual(a, b) {
|
|
101523
101925
|
const width = Math.max(a.length, b.length);
|
|
@@ -101751,22 +102153,22 @@ class AuthFailureThrottle {
|
|
|
101751
102153
|
init_config_dir();
|
|
101752
102154
|
import { createHash as createHash45 } from "crypto";
|
|
101753
102155
|
import { existsSync as existsSync35, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
|
|
101754
|
-
import
|
|
102156
|
+
import path196 from "path";
|
|
101755
102157
|
var MAX_TURN_EVENTS = 1e4;
|
|
101756
102158
|
function turnsRoot(dir) {
|
|
101757
|
-
return
|
|
102159
|
+
return path196.join(keryxConfigDir(dir), "turns");
|
|
101758
102160
|
}
|
|
101759
102161
|
function turnDir(turnId, dir) {
|
|
101760
|
-
return
|
|
102162
|
+
return path196.join(turnsRoot(dir), turnId);
|
|
101761
102163
|
}
|
|
101762
102164
|
function keyPath(project, idempotencyKey, dir) {
|
|
101763
102165
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
101764
102166
|
const digest = createHash45("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
101765
|
-
return
|
|
102167
|
+
return path196.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
101766
102168
|
}
|
|
101767
102169
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
101768
102170
|
const digest = createHash45("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
101769
|
-
return
|
|
102171
|
+
return path196.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
101770
102172
|
}
|
|
101771
102173
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
101772
102174
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -101830,7 +102232,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
101830
102232
|
}
|
|
101831
102233
|
function createTurnRecord(record, dir) {
|
|
101832
102234
|
ensureTurnDir(record.turnId, dir);
|
|
101833
|
-
writeOwnerOnlyFile(
|
|
102235
|
+
writeOwnerOnlyFile(path196.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
101834
102236
|
`);
|
|
101835
102237
|
}
|
|
101836
102238
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -101839,12 +102241,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
101839
102241
|
}
|
|
101840
102242
|
const line = JSON.stringify(event);
|
|
101841
102243
|
try {
|
|
101842
|
-
appendOwnerOnlyLine(
|
|
102244
|
+
appendOwnerOnlyLine(path196.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
101843
102245
|
} catch (error) {
|
|
101844
102246
|
if (error?.code !== "ENOENT") {
|
|
101845
102247
|
throw error;
|
|
101846
102248
|
}
|
|
101847
|
-
appendOwnerOnlyLine(
|
|
102249
|
+
appendOwnerOnlyLine(path196.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
101848
102250
|
}
|
|
101849
102251
|
return true;
|
|
101850
102252
|
}
|
|
@@ -101852,7 +102254,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
101852
102254
|
if (!isTurnId(turnId)) {
|
|
101853
102255
|
return { ok: false, reason: "not-a-turn-id" };
|
|
101854
102256
|
}
|
|
101855
|
-
const read = readTurnFile(
|
|
102257
|
+
const read = readTurnFile(path196.join(turnDir(turnId, dir), "events.jsonl"));
|
|
101856
102258
|
if (!read.ok) {
|
|
101857
102259
|
if (isDefiniteAbsence2(read.reason)) {
|
|
101858
102260
|
return { ok: true, value: [] };
|
|
@@ -101880,7 +102282,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
101880
102282
|
if (!isTurnId(turnId)) {
|
|
101881
102283
|
return { ok: false, reason: "not-a-turn-id" };
|
|
101882
102284
|
}
|
|
101883
|
-
const read = readTurnFile(
|
|
102285
|
+
const read = readTurnFile(path196.join(turnDir(turnId, dir), "turn.json"));
|
|
101884
102286
|
if (!read.ok) {
|
|
101885
102287
|
return { ok: false, reason: read.reason };
|
|
101886
102288
|
}
|
|
@@ -101899,7 +102301,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
101899
102301
|
if (!record.ok) {
|
|
101900
102302
|
return false;
|
|
101901
102303
|
}
|
|
101902
|
-
writeOwnerOnlyFile(
|
|
102304
|
+
writeOwnerOnlyFile(path196.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
101903
102305
|
`);
|
|
101904
102306
|
return true;
|
|
101905
102307
|
}
|
|
@@ -101945,7 +102347,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
101945
102347
|
|
|
101946
102348
|
// src/lib/serve-turn.ts
|
|
101947
102349
|
import { randomUUID as randomUUID31 } from "crypto";
|
|
101948
|
-
import
|
|
102350
|
+
import path197 from "path";
|
|
101949
102351
|
init_service();
|
|
101950
102352
|
var REMOTE_ORIGIN = "remote:http";
|
|
101951
102353
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -102014,9 +102416,9 @@ function isUuid(value) {
|
|
|
102014
102416
|
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);
|
|
102015
102417
|
}
|
|
102016
102418
|
function resolveProject(declared, dir) {
|
|
102017
|
-
const wanted =
|
|
102419
|
+
const wanted = path197.resolve(declared);
|
|
102018
102420
|
for (const entry of listProjects(dir, () => {})) {
|
|
102019
|
-
if (
|
|
102421
|
+
if (path197.resolve(entry.path) === wanted) {
|
|
102020
102422
|
return { ok: true, project: entry.path };
|
|
102021
102423
|
}
|
|
102022
102424
|
}
|
|
@@ -103050,9 +103452,9 @@ function printHelp19() {
|
|
|
103050
103452
|
|
|
103051
103453
|
// src/commands/update.ts
|
|
103052
103454
|
import { spawn as spawn6 } from "child_process";
|
|
103053
|
-
import { chmod as chmod4, mkdir as
|
|
103455
|
+
import { chmod as chmod4, mkdir as mkdir63, readFile as readFile100, readdir as readdir37, writeFile as writeFile54 } from "fs/promises";
|
|
103054
103456
|
import { access as access4, constants as constants2, existsSync as existsSync36 } from "fs";
|
|
103055
|
-
import
|
|
103457
|
+
import path198 from "path";
|
|
103056
103458
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
103057
103459
|
init_config2();
|
|
103058
103460
|
init_config();
|
|
@@ -103069,8 +103471,8 @@ async function updateCommand(args = []) {
|
|
|
103069
103471
|
return;
|
|
103070
103472
|
}
|
|
103071
103473
|
const projectRoot = process.cwd();
|
|
103072
|
-
const metaprojectRoot =
|
|
103073
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
103474
|
+
const metaprojectRoot = path198.join(projectRoot, ".metaproject");
|
|
103475
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path198.basename(projectRoot)}/`);
|
|
103074
103476
|
if (!await pathExists(metaprojectRoot)) {
|
|
103075
103477
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
103076
103478
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -103143,7 +103545,7 @@ async function updateCommand(args = []) {
|
|
|
103143
103545
|
nextSteps(steps);
|
|
103144
103546
|
}
|
|
103145
103547
|
async function previewServiceFiles(projectRoot, options) {
|
|
103146
|
-
const metaprojectRoot =
|
|
103548
|
+
const metaprojectRoot = path198.join(projectRoot, ".metaproject");
|
|
103147
103549
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
103148
103550
|
const manifest = manifestState.manifest;
|
|
103149
103551
|
const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
|
|
@@ -103172,12 +103574,12 @@ async function previewServiceFiles(projectRoot, options) {
|
|
|
103172
103574
|
});
|
|
103173
103575
|
}
|
|
103174
103576
|
async function refreshServiceFiles(projectRoot, options) {
|
|
103175
|
-
const metaprojectRoot =
|
|
103577
|
+
const metaprojectRoot = path198.join(projectRoot, ".metaproject");
|
|
103176
103578
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
103177
103579
|
const manifest = manifestState.manifest;
|
|
103178
103580
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
103179
103581
|
if (manifestState.migrated) {
|
|
103180
|
-
await writeFile54(
|
|
103582
|
+
await writeFile54(path198.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
103181
103583
|
`, "utf8");
|
|
103182
103584
|
}
|
|
103183
103585
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -103214,9 +103616,9 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103214
103616
|
enableSecurity,
|
|
103215
103617
|
enableSac
|
|
103216
103618
|
});
|
|
103217
|
-
await writeTextIfChanged4(
|
|
103218
|
-
await writeTextIfChanged4(
|
|
103219
|
-
await writeTextIfChanged4(
|
|
103619
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
103620
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
103621
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
103220
103622
|
await writeRoutingEntrypointPair(metaprojectRoot, {
|
|
103221
103623
|
enableGdgraph,
|
|
103222
103624
|
enableGdctx,
|
|
@@ -103239,7 +103641,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103239
103641
|
}
|
|
103240
103642
|
}
|
|
103241
103643
|
});
|
|
103242
|
-
await writeTextIfChanged4(
|
|
103644
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
103243
103645
|
enableGdgraph,
|
|
103244
103646
|
enableGdctx,
|
|
103245
103647
|
enableGdwiki,
|
|
@@ -103251,7 +103653,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103251
103653
|
enableSecurity,
|
|
103252
103654
|
data: dashboardData
|
|
103253
103655
|
}));
|
|
103254
|
-
await writeTextIfMissing4(
|
|
103656
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
103255
103657
|
enableGdgraph,
|
|
103256
103658
|
enableGdctx,
|
|
103257
103659
|
enableGdwiki,
|
|
@@ -103264,31 +103666,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103264
103666
|
}));
|
|
103265
103667
|
if (enableGdgraph) {
|
|
103266
103668
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
103267
|
-
await writeTextIfChanged4(
|
|
103268
|
-
await writeTextIfChanged4(
|
|
103269
|
-
await writeTextIfChanged4(
|
|
103669
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
103670
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
103671
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
103270
103672
|
await seedAssetsLock(metaprojectRoot);
|
|
103271
103673
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
103272
103674
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
103273
103675
|
}
|
|
103274
103676
|
}
|
|
103275
103677
|
if (enableGdctx) {
|
|
103276
|
-
await writeTextIfMissing4(
|
|
103277
|
-
await writeTextIfChanged4(
|
|
103278
|
-
await writeTextIfChanged4(
|
|
103279
|
-
await writeTextIfChanged4(
|
|
103678
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
103679
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
103680
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
103681
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
103280
103682
|
}
|
|
103281
103683
|
if (enableGdwiki) {
|
|
103282
|
-
await writeTextIfMissing4(
|
|
103283
|
-
await writeTextIfChanged4(
|
|
103284
|
-
await writeTextIfChanged4(
|
|
103684
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
103685
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
103686
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
103285
103687
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
103286
103688
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
103287
103689
|
}
|
|
103288
103690
|
}
|
|
103289
103691
|
if (enableSac) {
|
|
103290
|
-
await writeTextIfMissing4(
|
|
103291
|
-
await writeTextIfChanged4(
|
|
103692
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
103693
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
103292
103694
|
}
|
|
103293
103695
|
let gdskillsWarnings = [];
|
|
103294
103696
|
let gdskillsNotices = [];
|
|
@@ -103301,25 +103703,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103301
103703
|
}
|
|
103302
103704
|
}
|
|
103303
103705
|
if (enableHealth) {
|
|
103304
|
-
await writeTextIfMissing4(
|
|
103305
|
-
await writeTextIfChanged4(
|
|
103306
|
-
await writeTextIfChanged4(
|
|
103307
|
-
await writeTextIfChanged4(
|
|
103706
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
103707
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
103708
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
103709
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
103308
103710
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
103309
103711
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
103310
103712
|
}
|
|
103311
103713
|
}
|
|
103312
103714
|
if (enableTesting) {
|
|
103313
|
-
await writeTextIfMissing4(
|
|
103715
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
103314
103716
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
103315
103717
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
103316
103718
|
}));
|
|
103317
|
-
await writeTextIfChanged4(
|
|
103318
|
-
await writeTextIfChanged4(
|
|
103319
|
-
await writeTextIfChanged4(
|
|
103719
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
103720
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
103721
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
103320
103722
|
if (enableGdwiki) {
|
|
103321
|
-
await writeTextIfMissing4(
|
|
103322
|
-
await writeTextIfMissing4(
|
|
103723
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
103724
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
103323
103725
|
}
|
|
103324
103726
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
103325
103727
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -103332,24 +103734,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103332
103734
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
103333
103735
|
}
|
|
103334
103736
|
if (enableMemory) {
|
|
103335
|
-
await writeTextIfMissing4(
|
|
103336
|
-
await writeTextIfMissing4(
|
|
103337
|
-
await writeTextIfChanged4(
|
|
103338
|
-
await writeTextIfChanged4(
|
|
103339
|
-
await writeTextIfChanged4(
|
|
103737
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
103738
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
103739
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
103740
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
103741
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
103340
103742
|
}
|
|
103341
103743
|
if (enableTasks) {
|
|
103342
|
-
await writeTextIfChanged4(
|
|
103343
|
-
await writeTextIfChanged4(
|
|
103344
|
-
await writeTextIfChanged4(
|
|
103345
|
-
await writeTextIfChanged4(
|
|
103346
|
-
await writeTextIfChanged4(
|
|
103347
|
-
await writeTextIfChanged4(
|
|
103744
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
103745
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
103746
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
103747
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
103748
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
103749
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
103348
103750
|
}
|
|
103349
103751
|
if (enableSecurity) {
|
|
103350
|
-
await writeTextIfMissing4(
|
|
103351
|
-
await writeTextIfChanged4(
|
|
103352
|
-
await writeTextIfChanged4(
|
|
103752
|
+
await writeTextIfMissing4(path198.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
103753
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
103754
|
+
await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
103353
103755
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
103354
103756
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
103355
103757
|
}
|
|
@@ -103402,13 +103804,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
103402
103804
|
};
|
|
103403
103805
|
}
|
|
103404
103806
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
103405
|
-
const metaprojectRoot =
|
|
103807
|
+
const metaprojectRoot = path198.join(projectRoot, ".metaproject");
|
|
103406
103808
|
if (!await pathExists(metaprojectRoot)) {
|
|
103407
103809
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
103408
103810
|
}
|
|
103409
103811
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
103410
103812
|
const data = await collectDashboardData(metaprojectRoot);
|
|
103411
|
-
const dashboardPath =
|
|
103813
|
+
const dashboardPath = path198.join(metaprojectRoot, "keryx-dashboard.html");
|
|
103412
103814
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
103413
103815
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
103414
103816
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -103428,7 +103830,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
103428
103830
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
103429
103831
|
return true;
|
|
103430
103832
|
}
|
|
103431
|
-
const hookPath =
|
|
103833
|
+
const hookPath = path198.join(projectRoot, ".git", "hooks", "post-commit");
|
|
103432
103834
|
if (!await pathExists(hookPath)) {
|
|
103433
103835
|
return false;
|
|
103434
103836
|
}
|
|
@@ -103448,11 +103850,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
103448
103850
|
if (testing) {
|
|
103449
103851
|
data.testing = testing;
|
|
103450
103852
|
}
|
|
103451
|
-
const wiki = await collectMarkdownPages(
|
|
103853
|
+
const wiki = await collectMarkdownPages(path198.join(metaprojectRoot, "wiki"), "wiki");
|
|
103452
103854
|
if (wiki.length > 0) {
|
|
103453
103855
|
data.wiki = { pages: wiki };
|
|
103454
103856
|
}
|
|
103455
|
-
const memory = await collectMarkdownPages(
|
|
103857
|
+
const memory = await collectMarkdownPages(path198.join(metaprojectRoot, "memory"), "memory");
|
|
103456
103858
|
if (memory.length > 0) {
|
|
103457
103859
|
data.memory = { entries: memory };
|
|
103458
103860
|
}
|
|
@@ -103467,7 +103869,7 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
103467
103869
|
return data;
|
|
103468
103870
|
}
|
|
103469
103871
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
103470
|
-
const flowsRoot =
|
|
103872
|
+
const flowsRoot = path198.join(metaprojectRoot, "flows");
|
|
103471
103873
|
if (!await pathExists(flowsRoot)) {
|
|
103472
103874
|
return null;
|
|
103473
103875
|
}
|
|
@@ -103479,7 +103881,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
103479
103881
|
}
|
|
103480
103882
|
const flows = [];
|
|
103481
103883
|
for (const dir of dirEntries) {
|
|
103482
|
-
const flowPath =
|
|
103884
|
+
const flowPath = path198.join(flowsRoot, dir, "flow.json");
|
|
103483
103885
|
if (!await pathExists(flowPath)) {
|
|
103484
103886
|
continue;
|
|
103485
103887
|
}
|
|
@@ -103487,7 +103889,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
103487
103889
|
const flow = JSON.parse(await readFile100(flowPath, "utf8"));
|
|
103488
103890
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
103489
103891
|
let acTotal = 0;
|
|
103490
|
-
const acPath =
|
|
103892
|
+
const acPath = path198.join(flowsRoot, dir, "acceptance-criteria.md");
|
|
103491
103893
|
if (await pathExists(acPath)) {
|
|
103492
103894
|
const acContent = await readFile100(acPath, "utf8");
|
|
103493
103895
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -103541,7 +103943,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
103541
103943
|
"data/testing/context.md"
|
|
103542
103944
|
];
|
|
103543
103945
|
for (const href of staticHrefs) {
|
|
103544
|
-
const filePath =
|
|
103946
|
+
const filePath = path198.join(metaprojectRoot, ...href.split("/"));
|
|
103545
103947
|
if (!await pathExists(filePath)) {
|
|
103546
103948
|
continue;
|
|
103547
103949
|
}
|
|
@@ -103558,7 +103960,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
103558
103960
|
return docs;
|
|
103559
103961
|
}
|
|
103560
103962
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
103561
|
-
const reportPath =
|
|
103963
|
+
const reportPath = path198.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
103562
103964
|
if (!await pathExists(reportPath)) {
|
|
103563
103965
|
return;
|
|
103564
103966
|
}
|
|
@@ -103667,8 +104069,8 @@ function metricToScope(metric) {
|
|
|
103667
104069
|
};
|
|
103668
104070
|
}
|
|
103669
104071
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
103670
|
-
const nodesPath =
|
|
103671
|
-
const edgesPath =
|
|
104072
|
+
const nodesPath = path198.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
104073
|
+
const edgesPath = path198.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
103672
104074
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
103673
104075
|
return;
|
|
103674
104076
|
}
|
|
@@ -103719,8 +104121,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
103719
104121
|
};
|
|
103720
104122
|
}
|
|
103721
104123
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
103722
|
-
const reportPath =
|
|
103723
|
-
const contextPath =
|
|
104124
|
+
const reportPath = path198.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
104125
|
+
const contextPath = path198.join(metaprojectRoot, "data", "testing", "context.md");
|
|
103724
104126
|
if (await pathExists(reportPath)) {
|
|
103725
104127
|
const report = JSON.parse(await readFile100(reportPath, "utf8"));
|
|
103726
104128
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -103749,7 +104151,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
103749
104151
|
const files = await listMarkdownFiles(root);
|
|
103750
104152
|
const pages = [];
|
|
103751
104153
|
for (const filePath of files.slice(0, 40)) {
|
|
103752
|
-
const relativePath =
|
|
104154
|
+
const relativePath = path198.relative(root, filePath).split(path198.sep).join("/");
|
|
103753
104155
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
103754
104156
|
continue;
|
|
103755
104157
|
}
|
|
@@ -103770,7 +104172,7 @@ async function listMarkdownFiles(root) {
|
|
|
103770
104172
|
const entries = await readdir37(root, { withFileTypes: true });
|
|
103771
104173
|
const files = [];
|
|
103772
104174
|
for (const entry of entries) {
|
|
103773
|
-
const fullPath =
|
|
104175
|
+
const fullPath = path198.join(root, entry.name);
|
|
103774
104176
|
if (entry.isDirectory()) {
|
|
103775
104177
|
files.push(...await listMarkdownFiles(fullPath));
|
|
103776
104178
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -103818,7 +104220,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
103818
104220
|
const manifest = {
|
|
103819
104221
|
schemaVersion: 1,
|
|
103820
104222
|
standardVersion: STANDARD_VERSION,
|
|
103821
|
-
name: `${
|
|
104223
|
+
name: `${path198.basename(path198.dirname(metaprojectRoot))}-metaproject`,
|
|
103822
104224
|
createdBy: "keryx",
|
|
103823
104225
|
profiles: computeProfiles(enabledModuleKeys),
|
|
103824
104226
|
paths: {
|
|
@@ -103908,11 +104310,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
103908
104310
|
metaproject: ".metaproject/index.md"
|
|
103909
104311
|
}
|
|
103910
104312
|
};
|
|
103911
|
-
await writeFile54(
|
|
104313
|
+
await writeFile54(path198.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
103912
104314
|
`, "utf8");
|
|
103913
104315
|
}
|
|
103914
104316
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
103915
|
-
const manifestPath =
|
|
104317
|
+
const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
|
|
103916
104318
|
if (!await pathExists(manifestPath)) {
|
|
103917
104319
|
return;
|
|
103918
104320
|
}
|
|
@@ -103935,7 +104337,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
103935
104337
|
`, "utf8");
|
|
103936
104338
|
}
|
|
103937
104339
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
103938
|
-
const manifestPath =
|
|
104340
|
+
const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
|
|
103939
104341
|
if (!await pathExists(manifestPath)) {
|
|
103940
104342
|
return;
|
|
103941
104343
|
}
|
|
@@ -103971,80 +104373,80 @@ async function updateRuntime(projectRoot) {
|
|
|
103971
104373
|
}
|
|
103972
104374
|
}
|
|
103973
104375
|
async function findRuntimeRoot(projectRoot) {
|
|
103974
|
-
const projectRuntime =
|
|
103975
|
-
if (await pathExists(
|
|
104376
|
+
const projectRuntime = path198.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
104377
|
+
if (await pathExists(path198.join(projectRuntime, ".git"))) {
|
|
103976
104378
|
return projectRuntime;
|
|
103977
104379
|
}
|
|
103978
104380
|
const home = process.env.HOME;
|
|
103979
104381
|
if (!home) {
|
|
103980
104382
|
return null;
|
|
103981
104383
|
}
|
|
103982
|
-
const globalRuntime =
|
|
103983
|
-
if (await pathExists(
|
|
104384
|
+
const globalRuntime = path198.join(home, ".keryx", "keryx");
|
|
104385
|
+
if (await pathExists(path198.join(globalRuntime, ".git"))) {
|
|
103984
104386
|
return globalRuntime;
|
|
103985
104387
|
}
|
|
103986
104388
|
return null;
|
|
103987
104389
|
}
|
|
103988
104390
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
103989
104391
|
const dirs = [
|
|
103990
|
-
|
|
103991
|
-
|
|
103992
|
-
|
|
103993
|
-
|
|
103994
|
-
|
|
104392
|
+
path198.join(metaprojectRoot, "core"),
|
|
104393
|
+
path198.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
104394
|
+
path198.join(metaprojectRoot, "modules"),
|
|
104395
|
+
path198.join(metaprojectRoot, "rules"),
|
|
104396
|
+
path198.join(metaprojectRoot, "skills", "project-rules"),
|
|
103995
104397
|
...modules.enableGdgraph ? [
|
|
103996
|
-
|
|
103997
|
-
|
|
104398
|
+
path198.join(metaprojectRoot, "core", "gdgraph"),
|
|
104399
|
+
path198.join(metaprojectRoot, "skills", "gdgraph")
|
|
103998
104400
|
] : [],
|
|
103999
104401
|
...modules.enableGdctx ? [
|
|
104000
|
-
|
|
104001
|
-
|
|
104402
|
+
path198.join(metaprojectRoot, "core", "gdctx"),
|
|
104403
|
+
path198.join(metaprojectRoot, "skills", "gdctx")
|
|
104002
104404
|
] : [],
|
|
104003
104405
|
...modules.enableGdwiki ? [
|
|
104004
|
-
|
|
104005
|
-
|
|
104406
|
+
path198.join(metaprojectRoot, "skills", "gdwiki"),
|
|
104407
|
+
path198.join(metaprojectRoot, "wiki", "templates")
|
|
104006
104408
|
] : [],
|
|
104007
104409
|
...modules.enableHealth ? [
|
|
104008
|
-
|
|
104009
|
-
|
|
104410
|
+
path198.join(metaprojectRoot, "core", "health"),
|
|
104411
|
+
path198.join(metaprojectRoot, "skills", "health")
|
|
104010
104412
|
] : [],
|
|
104011
104413
|
...modules.enableTesting ? [
|
|
104012
|
-
|
|
104013
|
-
|
|
104414
|
+
path198.join(metaprojectRoot, "core", "testing"),
|
|
104415
|
+
path198.join(metaprojectRoot, "skills", "testing")
|
|
104014
104416
|
] : [],
|
|
104015
104417
|
...modules.enableMemory ? [
|
|
104016
|
-
|
|
104017
|
-
|
|
104018
|
-
|
|
104418
|
+
path198.join(metaprojectRoot, "core", "memory"),
|
|
104419
|
+
path198.join(metaprojectRoot, "skills", "memory"),
|
|
104420
|
+
path198.join(metaprojectRoot, "memory", "templates")
|
|
104019
104421
|
] : [],
|
|
104020
104422
|
...modules.enableTasks ? [
|
|
104021
|
-
|
|
104022
|
-
|
|
104423
|
+
path198.join(metaprojectRoot, "flows"),
|
|
104424
|
+
path198.join(metaprojectRoot, "skills", "flow")
|
|
104023
104425
|
] : [],
|
|
104024
104426
|
...modules.enableSecurity ? [
|
|
104025
|
-
|
|
104427
|
+
path198.join(metaprojectRoot, "core", "security")
|
|
104026
104428
|
] : [],
|
|
104027
104429
|
...modules.enableSac ? [
|
|
104028
|
-
|
|
104430
|
+
path198.join(metaprojectRoot, "skills", "sac")
|
|
104029
104431
|
] : []
|
|
104030
104432
|
];
|
|
104031
|
-
await Promise.all(dirs.map((dir) =>
|
|
104433
|
+
await Promise.all(dirs.map((dir) => mkdir63(dir, { recursive: true })));
|
|
104032
104434
|
}
|
|
104033
104435
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
104034
|
-
const gdgraphCoreRoot =
|
|
104035
|
-
await
|
|
104436
|
+
const gdgraphCoreRoot = path198.join(metaprojectRoot, "core", "gdgraph");
|
|
104437
|
+
await mkdir63(gdgraphCoreRoot, { recursive: true });
|
|
104036
104438
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
104037
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
104439
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path198.join(gdgraphCoreRoot, file));
|
|
104038
104440
|
}
|
|
104039
|
-
await writeTextIfChanged4(
|
|
104441
|
+
await writeTextIfChanged4(path198.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
104040
104442
|
}
|
|
104041
104443
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
104042
104444
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
104043
104445
|
if (!hooksRoot) {
|
|
104044
104446
|
return;
|
|
104045
104447
|
}
|
|
104046
|
-
await
|
|
104047
|
-
const hookPath =
|
|
104448
|
+
await mkdir63(hooksRoot, { recursive: true });
|
|
104449
|
+
const hookPath = path198.join(hooksRoot, hookName);
|
|
104048
104450
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
104049
104451
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
104050
104452
|
const managedBlock = `${blockStart}
|
|
@@ -104065,7 +104467,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
104065
104467
|
if (!hooksRoot) {
|
|
104066
104468
|
return;
|
|
104067
104469
|
}
|
|
104068
|
-
const hookPath =
|
|
104470
|
+
const hookPath = path198.join(hooksRoot, hookName);
|
|
104069
104471
|
if (!await pathExists(hookPath)) {
|
|
104070
104472
|
return;
|
|
104071
104473
|
}
|
|
@@ -104087,7 +104489,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
104087
104489
|
if (!hooksRoot) {
|
|
104088
104490
|
return false;
|
|
104089
104491
|
}
|
|
104090
|
-
const hookPath =
|
|
104492
|
+
const hookPath = path198.join(hooksRoot, "pre-push");
|
|
104091
104493
|
if (!await pathExists(hookPath)) {
|
|
104092
104494
|
return false;
|
|
104093
104495
|
}
|
|
@@ -104102,7 +104504,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
104102
104504
|
return (await readFile100(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
104103
104505
|
}
|
|
104104
104506
|
async function readManifest5(metaprojectRoot) {
|
|
104105
|
-
const manifestPath =
|
|
104507
|
+
const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
|
|
104106
104508
|
if (!await pathExists(manifestPath)) {
|
|
104107
104509
|
return {
|
|
104108
104510
|
exists: false,
|
|
@@ -104171,7 +104573,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
104171
104573
|
}
|
|
104172
104574
|
async function anyPathExists(root, candidates) {
|
|
104173
104575
|
for (const candidate of candidates) {
|
|
104174
|
-
if (await pathExists(
|
|
104576
|
+
if (await pathExists(path198.join(root, candidate))) {
|
|
104175
104577
|
return true;
|
|
104176
104578
|
}
|
|
104177
104579
|
}
|
|
@@ -104194,13 +104596,13 @@ function parseUpdateArgs(args) {
|
|
|
104194
104596
|
};
|
|
104195
104597
|
}
|
|
104196
104598
|
async function runPostUpdateHooks(projectRoot) {
|
|
104197
|
-
const hooksDir =
|
|
104599
|
+
const hooksDir = path198.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
104198
104600
|
if (!await pathExists(hooksDir)) {
|
|
104199
104601
|
return;
|
|
104200
104602
|
}
|
|
104201
104603
|
const entries = (await readdir37(hooksDir)).sort();
|
|
104202
104604
|
for (const entry of entries) {
|
|
104203
|
-
const hookPath =
|
|
104605
|
+
const hookPath = path198.join(hooksDir, entry);
|
|
104204
104606
|
try {
|
|
104205
104607
|
await accessExecutable(hookPath);
|
|
104206
104608
|
} catch {
|
|
@@ -104241,14 +104643,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
104241
104643
|
if (await pathExists(filePath) && await readFile100(filePath, "utf8") === content) {
|
|
104242
104644
|
return;
|
|
104243
104645
|
}
|
|
104244
|
-
await
|
|
104646
|
+
await mkdir63(path198.dirname(filePath), { recursive: true });
|
|
104245
104647
|
await writeFile54(filePath, content, "utf8");
|
|
104246
104648
|
}
|
|
104247
104649
|
async function writeTextIfMissing4(filePath, content) {
|
|
104248
104650
|
if (await pathExists(filePath)) {
|
|
104249
104651
|
return;
|
|
104250
104652
|
}
|
|
104251
|
-
await
|
|
104653
|
+
await mkdir63(path198.dirname(filePath), { recursive: true });
|
|
104252
104654
|
await writeFile54(filePath, content, "utf8");
|
|
104253
104655
|
}
|
|
104254
104656
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -104256,7 +104658,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
104256
104658
|
if (await pathExists(to) && await readFile100(to, "utf8") === next) {
|
|
104257
104659
|
return;
|
|
104258
104660
|
}
|
|
104259
|
-
await
|
|
104661
|
+
await mkdir63(path198.dirname(to), { recursive: true });
|
|
104260
104662
|
await writeFile54(to, next, "utf8");
|
|
104261
104663
|
}
|
|
104262
104664
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -104265,7 +104667,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
104265
104667
|
return directPath;
|
|
104266
104668
|
}
|
|
104267
104669
|
if (relativePath.startsWith("../")) {
|
|
104268
|
-
const packagedSourcePath =
|
|
104670
|
+
const packagedSourcePath = path198.join(path198.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
104269
104671
|
if (existsSync36(packagedSourcePath)) {
|
|
104270
104672
|
return packagedSourcePath;
|
|
104271
104673
|
}
|
|
@@ -104300,7 +104702,7 @@ function printHelp20() {
|
|
|
104300
104702
|
|
|
104301
104703
|
// src/commands/dashboard.ts
|
|
104302
104704
|
import { spawn as spawn7 } from "child_process";
|
|
104303
|
-
import
|
|
104705
|
+
import path199 from "path";
|
|
104304
104706
|
init_args();
|
|
104305
104707
|
async function dashboardCommand(args = []) {
|
|
104306
104708
|
const options = parseOptions(args);
|
|
@@ -104311,7 +104713,7 @@ async function dashboardCommand(args = []) {
|
|
|
104311
104713
|
}
|
|
104312
104714
|
if (subcommand === "build") {
|
|
104313
104715
|
const result = await buildDashboard();
|
|
104314
|
-
const rel =
|
|
104716
|
+
const rel = path199.relative(process.cwd(), result.path);
|
|
104315
104717
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
104316
104718
|
note(`Open it: keryx dashboard open`);
|
|
104317
104719
|
return;
|
|
@@ -104319,7 +104721,7 @@ async function dashboardCommand(args = []) {
|
|
|
104319
104721
|
if (subcommand === "open") {
|
|
104320
104722
|
const result = await buildDashboard();
|
|
104321
104723
|
await openFile(result.path);
|
|
104322
|
-
const rel =
|
|
104724
|
+
const rel = path199.relative(process.cwd(), result.path);
|
|
104323
104725
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
104324
104726
|
return;
|
|
104325
104727
|
}
|
|
@@ -104366,9 +104768,9 @@ function printHelp21() {
|
|
|
104366
104768
|
import { readFileSync as readFileSync9 } from "fs";
|
|
104367
104769
|
|
|
104368
104770
|
// src/agents/bootstrap.ts
|
|
104369
|
-
import { mkdir as
|
|
104370
|
-
import { homedir as
|
|
104371
|
-
import
|
|
104771
|
+
import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
|
|
104772
|
+
import { homedir as homedir9 } from "os";
|
|
104773
|
+
import path200 from "path";
|
|
104372
104774
|
init_fs();
|
|
104373
104775
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
104374
104776
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -104378,35 +104780,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
104378
104780
|
aliases: ["claude-code"],
|
|
104379
104781
|
label: "Claude Code",
|
|
104380
104782
|
fileName: "CLAUDE.md",
|
|
104381
|
-
filePath: (homeRoot) =>
|
|
104783
|
+
filePath: (homeRoot) => path200.join(homeRoot, ".claude", "CLAUDE.md")
|
|
104382
104784
|
},
|
|
104383
104785
|
{
|
|
104384
104786
|
id: "opencode",
|
|
104385
104787
|
aliases: ["open-code"],
|
|
104386
104788
|
label: "OpenCode",
|
|
104387
104789
|
fileName: "AGENTS.md",
|
|
104388
|
-
filePath: (homeRoot) =>
|
|
104790
|
+
filePath: (homeRoot) => path200.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
104389
104791
|
},
|
|
104390
104792
|
{
|
|
104391
104793
|
id: "zcode",
|
|
104392
104794
|
aliases: ["zed", "zed-code"],
|
|
104393
104795
|
label: "ZCode",
|
|
104394
104796
|
fileName: "AGENTS.md",
|
|
104395
|
-
filePath: (homeRoot) =>
|
|
104797
|
+
filePath: (homeRoot) => path200.join(homeRoot, ".zcode", "AGENTS.md")
|
|
104396
104798
|
},
|
|
104397
104799
|
{
|
|
104398
104800
|
id: "codex",
|
|
104399
104801
|
aliases: [],
|
|
104400
104802
|
label: "Codex",
|
|
104401
104803
|
fileName: "AGENTS.md",
|
|
104402
|
-
filePath: (homeRoot) =>
|
|
104804
|
+
filePath: (homeRoot) => path200.join(homeRoot, ".codex", "AGENTS.md")
|
|
104403
104805
|
},
|
|
104404
104806
|
{
|
|
104405
104807
|
id: "antigravity",
|
|
104406
104808
|
aliases: ["antigravuty", "antigravity-code"],
|
|
104407
104809
|
label: "Antigravity",
|
|
104408
104810
|
fileName: "AGENTS.md",
|
|
104409
|
-
filePath: (homeRoot) =>
|
|
104811
|
+
filePath: (homeRoot) => path200.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
104410
104812
|
}
|
|
104411
104813
|
];
|
|
104412
104814
|
function agentBootstrapRuntimeIds() {
|
|
@@ -104436,7 +104838,7 @@ function resolveAgentBootstrapRuntimes(ids) {
|
|
|
104436
104838
|
}
|
|
104437
104839
|
return { runtimes, unknown };
|
|
104438
104840
|
}
|
|
104439
|
-
async function agentBootstrapStatus(runtime, homeRoot =
|
|
104841
|
+
async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
|
|
104440
104842
|
const filePath = runtime.filePath(homeRoot);
|
|
104441
104843
|
const exists = await pathExists(filePath);
|
|
104442
104844
|
const content = exists ? await readFile101(filePath, "utf8") : "";
|
|
@@ -104446,7 +104848,7 @@ async function agentBootstrapStatus(runtime, homeRoot = homedir8()) {
|
|
|
104446
104848
|
return { runtime: runtime.id, label: runtime.label, filePath, exists, installed, current };
|
|
104447
104849
|
}
|
|
104448
104850
|
async function installAgentBootstrap(runtime, options = {}) {
|
|
104449
|
-
const homeRoot = options.homeRoot ??
|
|
104851
|
+
const homeRoot = options.homeRoot ?? homedir9();
|
|
104450
104852
|
const filePath = runtime.filePath(homeRoot);
|
|
104451
104853
|
const exists = await pathExists(filePath);
|
|
104452
104854
|
const current = exists ? await readFile101(filePath, "utf8") : "";
|
|
@@ -104454,14 +104856,14 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
104454
104856
|
const dryRun = options.dryRun === true;
|
|
104455
104857
|
const wrote = next !== current;
|
|
104456
104858
|
if (wrote && !dryRun) {
|
|
104457
|
-
await
|
|
104859
|
+
await mkdir64(path200.dirname(filePath), { recursive: true });
|
|
104458
104860
|
await writeFile55(filePath, next, "utf8");
|
|
104459
104861
|
}
|
|
104460
104862
|
const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
104461
104863
|
return { ...status, wrote, dryRun };
|
|
104462
104864
|
}
|
|
104463
104865
|
async function uninstallAgentBootstrap(runtime, options = {}) {
|
|
104464
|
-
const homeRoot = options.homeRoot ??
|
|
104866
|
+
const homeRoot = options.homeRoot ?? homedir9();
|
|
104465
104867
|
const filePath = runtime.filePath(homeRoot);
|
|
104466
104868
|
const exists = await pathExists(filePath);
|
|
104467
104869
|
const current = exists ? await readFile101(filePath, "utf8") : "";
|
|
@@ -104991,7 +105393,7 @@ function printBootstrapHelp() {
|
|
|
104991
105393
|
// src/commands/metrics.ts
|
|
104992
105394
|
init_args();
|
|
104993
105395
|
import { readFile as readFile102 } from "fs/promises";
|
|
104994
|
-
import
|
|
105396
|
+
import path202 from "path";
|
|
104995
105397
|
|
|
104996
105398
|
// src/metrics/benchmark.ts
|
|
104997
105399
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -105807,8 +106209,8 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
105807
106209
|
}
|
|
105808
106210
|
|
|
105809
106211
|
// src/metrics/oracle-runner.ts
|
|
105810
|
-
import { mkdir as
|
|
105811
|
-
import
|
|
106212
|
+
import { mkdir as mkdir65, writeFile as writeFile56 } from "fs/promises";
|
|
106213
|
+
import path201 from "path";
|
|
105812
106214
|
|
|
105813
106215
|
// src/metrics/ir.ts
|
|
105814
106216
|
function toIdSet(ids) {
|
|
@@ -106254,9 +106656,9 @@ function buildEvidenceBundle(input, options = {}) {
|
|
|
106254
106656
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
106255
106657
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
106256
106658
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
106257
|
-
const dir =
|
|
106258
|
-
await
|
|
106259
|
-
const write = (name, value) => writeFile56(
|
|
106659
|
+
const dir = path201.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
106660
|
+
await mkdir65(dir, { recursive: true });
|
|
106661
|
+
const write = (name, value) => writeFile56(path201.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
106260
106662
|
`, "utf8");
|
|
106261
106663
|
await Promise.all([
|
|
106262
106664
|
write("inputs.json", bundle.inputs),
|
|
@@ -106296,7 +106698,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106296
106698
|
console.log("# metrics status");
|
|
106297
106699
|
console.log("");
|
|
106298
106700
|
console.log(`root: ${root}`);
|
|
106299
|
-
console.log(`enabled: ${await Bun.file(
|
|
106701
|
+
console.log(`enabled: ${await Bun.file(path202.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
106300
106702
|
const latest = await readLatestPointer(root);
|
|
106301
106703
|
console.log(`latest: ${latest.status}`);
|
|
106302
106704
|
return;
|
|
@@ -106308,7 +106710,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106308
106710
|
process.exitCode = 1;
|
|
106309
106711
|
return;
|
|
106310
106712
|
}
|
|
106311
|
-
const record = JSON.parse(await readFile102(
|
|
106713
|
+
const record = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106312
106714
|
const result = validateRunRecord(record);
|
|
106313
106715
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
106314
106716
|
for (const error of result.errors)
|
|
@@ -106333,7 +106735,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106333
106735
|
process.exitCode = 1;
|
|
106334
106736
|
return;
|
|
106335
106737
|
}
|
|
106336
|
-
const file =
|
|
106738
|
+
const file = path202.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
106337
106739
|
if (!await Bun.file(file).exists()) {
|
|
106338
106740
|
console.error(`Run not found: ${runId}`);
|
|
106339
106741
|
process.exitCode = 1;
|
|
@@ -106350,8 +106752,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106350
106752
|
process.exitCode = 1;
|
|
106351
106753
|
return;
|
|
106352
106754
|
}
|
|
106353
|
-
const a = JSON.parse(await readFile102(
|
|
106354
|
-
const b = JSON.parse(await readFile102(
|
|
106755
|
+
const a = JSON.parse(await readFile102(path202.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
106756
|
+
const b = JSON.parse(await readFile102(path202.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
106355
106757
|
const comparison = compareExecutionRuns(a, b);
|
|
106356
106758
|
console.log(stableJson(comparison));
|
|
106357
106759
|
return;
|
|
@@ -106385,8 +106787,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106385
106787
|
return;
|
|
106386
106788
|
}
|
|
106387
106789
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
106388
|
-
await Bun.write(
|
|
106389
|
-
console.log(`manifest: ${
|
|
106790
|
+
await Bun.write(path202.resolve(projectRoot, out), stableJson(template));
|
|
106791
|
+
console.log(`manifest: ${path202.relative(projectRoot, path202.resolve(projectRoot, out))}`);
|
|
106390
106792
|
return;
|
|
106391
106793
|
}
|
|
106392
106794
|
if (subcommand === "benchmark" && args[1] === "run") {
|
|
@@ -106400,7 +106802,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106400
106802
|
process.exitCode = 1;
|
|
106401
106803
|
return;
|
|
106402
106804
|
}
|
|
106403
|
-
const raw = JSON.parse(await readFile102(
|
|
106805
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106404
106806
|
const input = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
106405
106807
|
const result = validatePairedBenchmark(input);
|
|
106406
106808
|
console.log(stableJson(result));
|
|
@@ -106412,7 +106814,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
|
|
|
106412
106814
|
process.exitCode = 1;
|
|
106413
106815
|
}
|
|
106414
106816
|
async function loadAffectedSets(projectRoot, file) {
|
|
106415
|
-
const raw = JSON.parse(await readFile102(
|
|
106817
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106416
106818
|
const map = new Map;
|
|
106417
106819
|
for (const entry of raw.targets ?? []) {
|
|
106418
106820
|
if (typeof entry.target === "string")
|
|
@@ -106483,7 +106885,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
|
|
|
106483
106885
|
let tasks;
|
|
106484
106886
|
let model;
|
|
106485
106887
|
try {
|
|
106486
|
-
const raw = JSON.parse(await readFile102(
|
|
106888
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
|
|
106487
106889
|
tasks = raw.tasks ?? [];
|
|
106488
106890
|
model = raw.model;
|
|
106489
106891
|
} catch (error) {
|
|
@@ -106514,7 +106916,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
|
|
|
106514
106916
|
let cases;
|
|
106515
106917
|
let model;
|
|
106516
106918
|
try {
|
|
106517
|
-
const raw = JSON.parse(await readFile102(
|
|
106919
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
|
|
106518
106920
|
cases = raw.cases ?? [];
|
|
106519
106921
|
model = raw.model;
|
|
106520
106922
|
} catch (error) {
|
|
@@ -106539,7 +106941,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
|
|
|
106539
106941
|
let cases;
|
|
106540
106942
|
let model;
|
|
106541
106943
|
try {
|
|
106542
|
-
const raw = JSON.parse(await readFile102(
|
|
106944
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
|
|
106543
106945
|
cases = raw.cases ?? [];
|
|
106544
106946
|
model = raw.model;
|
|
106545
106947
|
} catch (error) {
|
|
@@ -106564,7 +106966,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
|
|
|
106564
106966
|
let cases;
|
|
106565
106967
|
let model;
|
|
106566
106968
|
try {
|
|
106567
|
-
const raw = JSON.parse(await readFile102(
|
|
106969
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
|
|
106568
106970
|
cases = raw.cases ?? [];
|
|
106569
106971
|
model = raw.model;
|
|
106570
106972
|
} catch (error) {
|
|
@@ -106626,12 +107028,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
106626
107028
|
}
|
|
106627
107029
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
106628
107030
|
if (outDir) {
|
|
106629
|
-
const resolvedOut =
|
|
107031
|
+
const resolvedOut = path202.resolve(projectRoot, outDir);
|
|
106630
107032
|
for (const input of inputs) {
|
|
106631
107033
|
for (const named of input.golds) {
|
|
106632
107034
|
const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
106633
|
-
const dir = await persistEvidenceBundle(
|
|
106634
|
-
console.error(`bundle[${named.kind}]: ${
|
|
107035
|
+
const dir = await persistEvidenceBundle(path202.join(resolvedOut, named.kind), bundle, ladder);
|
|
107036
|
+
console.error(`bundle[${named.kind}]: ${path202.relative(projectRoot, dir)}`);
|
|
106635
107037
|
}
|
|
106636
107038
|
}
|
|
106637
107039
|
}
|
|
@@ -106655,7 +107057,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
|
|
|
106655
107057
|
return allValid;
|
|
106656
107058
|
}
|
|
106657
107059
|
async function loadCoverageMap2(projectRoot, file) {
|
|
106658
|
-
const raw = JSON.parse(await readFile102(
|
|
107060
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106659
107061
|
return raw.coverageMap ?? {};
|
|
106660
107062
|
}
|
|
106661
107063
|
async function runTestingLayer(projectRoot, args, ladder) {
|
|
@@ -106692,7 +107094,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
|
|
|
106692
107094
|
return result.valid;
|
|
106693
107095
|
}
|
|
106694
107096
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
106695
|
-
const raw = JSON.parse(await readFile102(
|
|
107097
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106696
107098
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
106697
107099
|
}
|
|
106698
107100
|
async function runMemoryLayer(projectRoot, args, ladder) {
|
|
@@ -106729,11 +107131,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
|
|
|
106729
107131
|
return result.valid;
|
|
106730
107132
|
}
|
|
106731
107133
|
async function loadWikiGoldK(projectRoot, file) {
|
|
106732
|
-
const raw = JSON.parse(await readFile102(
|
|
107134
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106733
107135
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
106734
107136
|
}
|
|
106735
107137
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
106736
|
-
const raw = JSON.parse(await readFile102(
|
|
107138
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106737
107139
|
const map = new Map;
|
|
106738
107140
|
for (const entry of raw.targets ?? []) {
|
|
106739
107141
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -106789,7 +107191,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
|
|
|
106789
107191
|
return result.valid;
|
|
106790
107192
|
}
|
|
106791
107193
|
async function loadGdctxFacts(projectRoot, file) {
|
|
106792
|
-
const raw = JSON.parse(await readFile102(
|
|
107194
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
|
|
106793
107195
|
const inputs = [];
|
|
106794
107196
|
for (const entry of raw.inputs ?? []) {
|
|
106795
107197
|
if (typeof entry.input === "string") {
|
|
@@ -106827,7 +107229,7 @@ async function collect(projectRoot, args) {
|
|
|
106827
107229
|
process.exitCode = 1;
|
|
106828
107230
|
return;
|
|
106829
107231
|
}
|
|
106830
|
-
const raw = JSON.parse(await readFile102(
|
|
107232
|
+
const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, eventFile), "utf8"));
|
|
106831
107233
|
const events = Array.isArray(raw) ? raw : raw.events;
|
|
106832
107234
|
const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
|
|
106833
107235
|
const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -106843,11 +107245,11 @@ async function collect(projectRoot, args) {
|
|
|
106843
107245
|
parentRunId: optionValue(args, "--parent-run-id") ?? null
|
|
106844
107246
|
});
|
|
106845
107247
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
|
|
106846
|
-
console.log(`json: ${
|
|
106847
|
-
console.log(`markdown: ${
|
|
107248
|
+
console.log(`json: ${path202.relative(projectRoot, result.jsonPath)}`);
|
|
107249
|
+
console.log(`markdown: ${path202.relative(projectRoot, result.markdownPath)}`);
|
|
106848
107250
|
}
|
|
106849
107251
|
function metricsRoot(projectRoot) {
|
|
106850
|
-
return
|
|
107252
|
+
return path202.join(projectRoot, ".metaproject", "data", "metrics");
|
|
106851
107253
|
}
|
|
106852
107254
|
function printMetricsHelp() {
|
|
106853
107255
|
console.log(`keryx metrics
|