@mrciphersmith/keryx 0.3.44 → 0.3.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -8
- package/dist/cli.js +132183 -128470
- package/dist/core.js +919 -516
- package/package.json +1 -1
- package/src/gdskills/bundled/rules/core/mobx-store-template.mdc +12 -2
- package/src/gdskills/bundled/rules/core/rule-management-workflow.mdc +5 -2
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.detail.md +669 -0
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.md +20 -20
- package/src/gdskills/bundled/skills/platform/agent-entrypoint-distiller/SKILL.md +3 -3
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +7 -7
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +208 -4
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +10 -10
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +7 -0
package/dist/core.js
CHANGED
|
@@ -7067,6 +7067,9 @@ async function assertContained(root, rel, opts = {}) {
|
|
|
7067
7067
|
const resolvedPath = targetReal ?? targetPath;
|
|
7068
7068
|
return { targetPath, dir: path31.dirname(targetPath), resolvedPath, resolvedDir: path31.dirname(resolvedPath) };
|
|
7069
7069
|
}
|
|
7070
|
+
async function assertWritableContained(root, rel, kind = "file") {
|
|
7071
|
+
await assertContained(root, rel, kind === "file" ? { requireRegularIfExists: true } : { requireDirectoryIfExists: true });
|
|
7072
|
+
}
|
|
7070
7073
|
async function pathExistsLstat(p) {
|
|
7071
7074
|
try {
|
|
7072
7075
|
await lstat5(p);
|
|
@@ -7369,9 +7372,212 @@ var init_codecs = __esm(() => {
|
|
|
7369
7372
|
allow: (_runtimeId) => ({ exitCode: 0 })
|
|
7370
7373
|
};
|
|
7371
7374
|
});
|
|
7375
|
+
// src/lib/git-worktrees.ts
|
|
7376
|
+
var init_git_worktrees = () => {};
|
|
7372
7377
|
|
|
7373
|
-
// src/
|
|
7378
|
+
// src/rules/entrypoint-targets.ts
|
|
7379
|
+
function localRootEntry(runtime, source = DEFAULT_CODEX_SOURCE) {
|
|
7380
|
+
return runtime === "claude" ? { runtime, path: LOCAL_ROOT_PATH.claude, scope: "local" } : { runtime, path: LOCAL_ROOT_PATH.codex, scope: "local", mode: "override", source };
|
|
7381
|
+
}
|
|
7382
|
+
function sharedRootEntry(runtime, entryPath = SHARED_ROOT_PATH[runtime]) {
|
|
7383
|
+
return { runtime, path: entryPath, scope: "shared" };
|
|
7384
|
+
}
|
|
7385
|
+
function localClaudeSettingsTarget() {
|
|
7386
|
+
return { path: LOCAL_SETTINGS_PATH, scope: "local" };
|
|
7387
|
+
}
|
|
7388
|
+
function sharedClaudeSettingsTarget() {
|
|
7389
|
+
return { path: SHARED_SETTINGS_PATH, scope: "shared" };
|
|
7390
|
+
}
|
|
7391
|
+
function normalizeEntrypointTargets(agentEntrypoints) {
|
|
7392
|
+
const raw = isRecord(agentEntrypoints) ? agentEntrypoints : {};
|
|
7393
|
+
const rawRoot = Array.isArray(raw.root) ? raw.root : [];
|
|
7394
|
+
const root = [];
|
|
7395
|
+
const legacy = [];
|
|
7396
|
+
const ignored = [];
|
|
7397
|
+
const seen = new Set;
|
|
7398
|
+
for (const item of rawRoot) {
|
|
7399
|
+
const fromString = typeof item === "string" ? rootEntryFromLegacyString(item) : undefined;
|
|
7400
|
+
const entry = fromString ?? (isRecord(item) ? rootEntryFromRecord(item) : undefined);
|
|
7401
|
+
if (entry === undefined || seen.has(entry.runtime)) {
|
|
7402
|
+
ignored.push(item);
|
|
7403
|
+
continue;
|
|
7404
|
+
}
|
|
7405
|
+
seen.add(entry.runtime);
|
|
7406
|
+
root.push(entry);
|
|
7407
|
+
if (fromString !== undefined && entry.scope === "shared") {
|
|
7408
|
+
legacy.push({ kind: "root", runtime: entry.runtime, path: entry.path });
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7411
|
+
for (const runtime of RUNTIMES) {
|
|
7412
|
+
if (seen.has(runtime))
|
|
7413
|
+
continue;
|
|
7414
|
+
const entry = sharedRootEntry(runtime);
|
|
7415
|
+
root.push(entry);
|
|
7416
|
+
legacy.push({ kind: "root", runtime, path: entry.path });
|
|
7417
|
+
}
|
|
7418
|
+
let claudeSettings = claudeSettingsFromValue(raw.claudeSettings);
|
|
7419
|
+
if (claudeSettings === undefined) {
|
|
7420
|
+
claudeSettings = sharedClaudeSettingsTarget();
|
|
7421
|
+
legacy.push({ kind: "claudeSettings", path: claudeSettings.path });
|
|
7422
|
+
}
|
|
7423
|
+
const targets = { root, claudeSettings };
|
|
7424
|
+
const needsRewrite = JSON.stringify(raw.root) !== JSON.stringify(targets.root) || JSON.stringify(raw.claudeSettings) !== JSON.stringify(targets.claudeSettings);
|
|
7425
|
+
return { targets, legacy, ignored, needsRewrite };
|
|
7426
|
+
}
|
|
7427
|
+
function isRecord(value) {
|
|
7428
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7429
|
+
}
|
|
7430
|
+
function safeRelativePath(value) {
|
|
7431
|
+
if (typeof value !== "string" || value.length === 0 || /[\u0000-\u001f\u007f]/.test(value))
|
|
7432
|
+
return;
|
|
7433
|
+
if (value.startsWith("/") || value.startsWith("\\") || /^[A-Za-z]:/.test(value))
|
|
7434
|
+
return;
|
|
7435
|
+
if (value.startsWith("!") || value.startsWith("#"))
|
|
7436
|
+
return;
|
|
7437
|
+
if (value.split(/[\\/]/).includes(".."))
|
|
7438
|
+
return;
|
|
7439
|
+
return value;
|
|
7440
|
+
}
|
|
7441
|
+
function scopeOf(value) {
|
|
7442
|
+
return value === "local" || value === "shared" ? value : undefined;
|
|
7443
|
+
}
|
|
7444
|
+
function runtimeOf(value) {
|
|
7445
|
+
return value === "claude" || value === "codex" ? value : undefined;
|
|
7446
|
+
}
|
|
7447
|
+
function rootEntryFromLegacyString(value) {
|
|
7448
|
+
const lower = value.toLowerCase();
|
|
7449
|
+
for (const runtime of RUNTIMES) {
|
|
7450
|
+
if (lower === SHARED_ROOT_PATH[runtime].toLowerCase())
|
|
7451
|
+
return sharedRootEntry(runtime, value);
|
|
7452
|
+
if (lower === LOCAL_ROOT_PATH[runtime].toLowerCase())
|
|
7453
|
+
return localRootEntry(runtime);
|
|
7454
|
+
}
|
|
7455
|
+
return;
|
|
7456
|
+
}
|
|
7457
|
+
function rootEntryFromRecord(value) {
|
|
7458
|
+
const runtime = runtimeOf(value.runtime);
|
|
7459
|
+
if (runtime === undefined)
|
|
7460
|
+
return;
|
|
7461
|
+
const statedPath = safeRelativePath(value.path);
|
|
7462
|
+
if (value.path !== undefined && statedPath === undefined)
|
|
7463
|
+
return;
|
|
7464
|
+
const statedScope = scopeOf(value.scope);
|
|
7465
|
+
if (statedPath === undefined && statedScope === undefined)
|
|
7466
|
+
return;
|
|
7467
|
+
const scope = statedScope ?? (statedPath?.toLowerCase() === LOCAL_ROOT_PATH[runtime].toLowerCase() ? "local" : "shared");
|
|
7468
|
+
if (scope === "shared")
|
|
7469
|
+
return sharedRootEntry(runtime, statedPath ?? SHARED_ROOT_PATH[runtime]);
|
|
7470
|
+
if (runtime === "claude")
|
|
7471
|
+
return localRootEntry(runtime);
|
|
7472
|
+
return {
|
|
7473
|
+
...localRootEntry(runtime, safeRelativePath(value.source) ?? DEFAULT_CODEX_SOURCE),
|
|
7474
|
+
mode: value.mode === "skip" ? "skip" : "override"
|
|
7475
|
+
};
|
|
7476
|
+
}
|
|
7477
|
+
function claudeSettingsFromValue(value) {
|
|
7478
|
+
if (!isRecord(value))
|
|
7479
|
+
return;
|
|
7480
|
+
const statedPath = safeRelativePath(value.path);
|
|
7481
|
+
if (value.path !== undefined && statedPath === undefined)
|
|
7482
|
+
return;
|
|
7483
|
+
const statedScope = scopeOf(value.scope);
|
|
7484
|
+
if (statedPath === undefined && statedScope === undefined)
|
|
7485
|
+
return;
|
|
7486
|
+
const scope = statedScope ?? (statedPath === LOCAL_SETTINGS_PATH ? "local" : "shared");
|
|
7487
|
+
return scope === "local" ? localClaudeSettingsTarget() : sharedClaudeSettingsTarget();
|
|
7488
|
+
}
|
|
7489
|
+
var RUNTIMES, SHARED_ROOT_PATH, LOCAL_ROOT_PATH, SHARED_SETTINGS_PATH = ".claude/settings.json", LOCAL_SETTINGS_PATH = ".claude/settings.local.json", DEFAULT_CODEX_SOURCE;
|
|
7490
|
+
var init_entrypoint_targets = __esm(() => {
|
|
7491
|
+
RUNTIMES = ["claude", "codex"];
|
|
7492
|
+
SHARED_ROOT_PATH = { claude: "CLAUDE.md", codex: "AGENTS.md" };
|
|
7493
|
+
LOCAL_ROOT_PATH = { claude: "CLAUDE.local.md", codex: "AGENTS.override.md" };
|
|
7494
|
+
DEFAULT_CODEX_SOURCE = SHARED_ROOT_PATH.codex;
|
|
7495
|
+
});
|
|
7496
|
+
// src/rules/managed-index-block.ts
|
|
7497
|
+
var init_managed_index_block = () => {};
|
|
7498
|
+
|
|
7499
|
+
// src/rules/entrypoint-migration.ts
|
|
7500
|
+
function settingsTextHasManagedHooks(text) {
|
|
7501
|
+
let parsed;
|
|
7502
|
+
try {
|
|
7503
|
+
parsed = JSON.parse(text);
|
|
7504
|
+
} catch {
|
|
7505
|
+
return false;
|
|
7506
|
+
}
|
|
7507
|
+
if (!isRecord2(parsed) || !isRecord2(parsed.hooks))
|
|
7508
|
+
return false;
|
|
7509
|
+
return Object.values(parsed.hooks).some((groups) => Array.isArray(groups) && groups.some((group) => isRecord2(group) && typeof group[MANAGED_KEY] === "string"));
|
|
7510
|
+
}
|
|
7511
|
+
function isRecord2(value) {
|
|
7512
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7513
|
+
}
|
|
7514
|
+
var init_entrypoint_migration = __esm(() => {
|
|
7515
|
+
init_settings_json();
|
|
7516
|
+
init_git_worktrees();
|
|
7517
|
+
init_entrypoint_targets();
|
|
7518
|
+
init_managed_index_block();
|
|
7519
|
+
});
|
|
7520
|
+
|
|
7521
|
+
// src/integrations/claude-settings.ts
|
|
7522
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
7374
7523
|
import path32 from "path";
|
|
7524
|
+
function readTextSync(file) {
|
|
7525
|
+
try {
|
|
7526
|
+
return readFileSync2(file, "utf8");
|
|
7527
|
+
} catch {
|
|
7528
|
+
return;
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
function statedClaudeSettingsTarget(projectRoot) {
|
|
7532
|
+
const text = readTextSync(path32.join(projectRoot, ...MANIFEST_REL.split("/")));
|
|
7533
|
+
if (text === undefined)
|
|
7534
|
+
return;
|
|
7535
|
+
let manifest;
|
|
7536
|
+
try {
|
|
7537
|
+
manifest = JSON.parse(text);
|
|
7538
|
+
} catch {
|
|
7539
|
+
return;
|
|
7540
|
+
}
|
|
7541
|
+
const agentEntrypoints = typeof manifest === "object" && manifest !== null ? manifest.agentEntrypoints : undefined;
|
|
7542
|
+
const normalized = normalizeEntrypointTargets(agentEntrypoints);
|
|
7543
|
+
if (normalized.legacy.some((entry) => entry.kind === "claudeSettings"))
|
|
7544
|
+
return;
|
|
7545
|
+
return normalized.targets.claudeSettings.scope === "shared" ? sharedClaudeSettingsTarget() : localClaudeSettingsTarget();
|
|
7546
|
+
}
|
|
7547
|
+
function claudeSettingsFileHoldsManagedHooks(projectRoot, relativePath) {
|
|
7548
|
+
const text = readTextSync(path32.join(projectRoot, ...relativePath.split("/")));
|
|
7549
|
+
return text !== undefined && settingsTextHasManagedHooks(text);
|
|
7550
|
+
}
|
|
7551
|
+
function resolveClaudeSettingsTarget(projectRoot) {
|
|
7552
|
+
const stated = statedClaudeSettingsTarget(projectRoot);
|
|
7553
|
+
if (stated?.scope === "shared")
|
|
7554
|
+
return stated;
|
|
7555
|
+
if (claudeSettingsFileHoldsManagedHooks(projectRoot, CLAUDE_SHARED_SETTINGS_PATH))
|
|
7556
|
+
return sharedClaudeSettingsTarget();
|
|
7557
|
+
return localClaudeSettingsTarget();
|
|
7558
|
+
}
|
|
7559
|
+
function claudeSettingsRelativePath(projectRoot) {
|
|
7560
|
+
return resolveClaudeSettingsTarget(projectRoot).path;
|
|
7561
|
+
}
|
|
7562
|
+
function claudeSettingsLocation() {
|
|
7563
|
+
return {
|
|
7564
|
+
relativePath: CLAUDE_LOCAL_SETTINGS_PATH,
|
|
7565
|
+
relativePathCandidates: CLAUDE_SETTINGS_PATHS,
|
|
7566
|
+
relativePathFor: claudeSettingsRelativePath,
|
|
7567
|
+
settingsFile: (projectRoot) => path32.join(projectRoot, ...claudeSettingsRelativePath(projectRoot).split("/"))
|
|
7568
|
+
};
|
|
7569
|
+
}
|
|
7570
|
+
var CLAUDE_LOCAL_SETTINGS_PATH, CLAUDE_SHARED_SETTINGS_PATH, CLAUDE_SETTINGS_PATHS, MANIFEST_REL = ".metaproject/metaproject.json";
|
|
7571
|
+
var init_claude_settings = __esm(() => {
|
|
7572
|
+
init_entrypoint_migration();
|
|
7573
|
+
init_entrypoint_targets();
|
|
7574
|
+
CLAUDE_LOCAL_SETTINGS_PATH = localClaudeSettingsTarget().path;
|
|
7575
|
+
CLAUDE_SHARED_SETTINGS_PATH = sharedClaudeSettingsTarget().path;
|
|
7576
|
+
CLAUDE_SETTINGS_PATHS = [CLAUDE_LOCAL_SETTINGS_PATH, CLAUDE_SHARED_SETTINGS_PATH];
|
|
7577
|
+
});
|
|
7578
|
+
|
|
7579
|
+
// src/integrations/surfaces.ts
|
|
7580
|
+
import path33 from "path";
|
|
7375
7581
|
function ctxHookCommand(id) {
|
|
7376
7582
|
return `keryx ctx hook ${id}`;
|
|
7377
7583
|
}
|
|
@@ -7416,7 +7622,7 @@ function nestedCtxSurface(opts) {
|
|
|
7416
7622
|
confidence,
|
|
7417
7623
|
sourceDocs,
|
|
7418
7624
|
...riskNotes !== undefined ? { riskNotes } : {},
|
|
7419
|
-
settingsFile: (root) =>
|
|
7625
|
+
settingsFile: (root) => path33.join(root, ...relativePath.split("/")),
|
|
7420
7626
|
relativePath,
|
|
7421
7627
|
slots: [
|
|
7422
7628
|
{ key: container ?? "hooks", type: "object", access: "owns" },
|
|
@@ -7530,7 +7736,7 @@ function flatSecuritySurface(harnessId, on, relativePath) {
|
|
|
7530
7736
|
"`securityHooks` matches no documented hook contract for this harness; it is keryx's own invention (tracked as OQ-3)."
|
|
7531
7737
|
],
|
|
7532
7738
|
sourceDocs: ["src/security/agent-hooks/runtimes.ts"],
|
|
7533
|
-
settingsFile: (root) =>
|
|
7739
|
+
settingsFile: (root) => path33.join(root, ...relativePath.split("/")),
|
|
7534
7740
|
relativePath,
|
|
7535
7741
|
slots: [
|
|
7536
7742
|
{ key: SECURITY_HOOKS_KEY, type: "array", access: "owns" },
|
|
@@ -7587,14 +7793,18 @@ export const KeryxCtxGuard = async () => ({
|
|
|
7587
7793
|
var init_surfaces = __esm(() => {
|
|
7588
7794
|
init_settings_json();
|
|
7589
7795
|
init_codecs();
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7796
|
+
init_claude_settings();
|
|
7797
|
+
CTX_GUARD_CLAUDE = {
|
|
7798
|
+
...nestedCtxSurface({
|
|
7799
|
+
id: "claude",
|
|
7800
|
+
relativePath: CLAUDE_LOCAL_SETTINGS_PATH,
|
|
7801
|
+
confidence: "verified",
|
|
7802
|
+
nativeSearchTools: ["Grep"],
|
|
7803
|
+
key: "PreToolUse",
|
|
7804
|
+
sourceDocs: ["src/ctx/runtimes.ts"]
|
|
7805
|
+
}),
|
|
7806
|
+
...claudeSettingsLocation()
|
|
7807
|
+
};
|
|
7598
7808
|
CTX_GUARD_CODEX = nestedCtxSurface({
|
|
7599
7809
|
id: "codex",
|
|
7600
7810
|
relativePath: ".codex/hooks.json",
|
|
@@ -7609,7 +7819,7 @@ var init_surfaces = __esm(() => {
|
|
|
7609
7819
|
sentinel: CTX_HOOK_SENTINEL,
|
|
7610
7820
|
confidence: "verified",
|
|
7611
7821
|
sourceDocs: ["src/ctx/runtimes.ts"],
|
|
7612
|
-
settingsFile: (root) =>
|
|
7822
|
+
settingsFile: (root) => path33.join(root, ".cursor", "hooks.json"),
|
|
7613
7823
|
relativePath: ".cursor/hooks.json",
|
|
7614
7824
|
slots: [
|
|
7615
7825
|
{ key: "hooks", type: "object", access: "owns" },
|
|
@@ -7636,7 +7846,7 @@ var init_surfaces = __esm(() => {
|
|
|
7636
7846
|
sentinel: CTX_HOOK_SENTINEL,
|
|
7637
7847
|
confidence: "verified",
|
|
7638
7848
|
sourceDocs: ["src/ctx/runtimes.ts"],
|
|
7639
|
-
settingsFile: (root) =>
|
|
7849
|
+
settingsFile: (root) => path33.join(root, ".windsurf", "hooks.json"),
|
|
7640
7850
|
relativePath: ".windsurf/hooks.json",
|
|
7641
7851
|
slots: [
|
|
7642
7852
|
{ key: "hooks", type: "object", access: "owns" },
|
|
@@ -7660,7 +7870,7 @@ var init_surfaces = __esm(() => {
|
|
|
7660
7870
|
confidence: "experimental",
|
|
7661
7871
|
riskNotes: ["No first-party docs confirm this hook contract; verify on a live install."],
|
|
7662
7872
|
sourceDocs: ["src/ctx/runtimes.ts"],
|
|
7663
|
-
settingsFile: (root) =>
|
|
7873
|
+
settingsFile: (root) => path33.join(root, ".agents", "hooks.json"),
|
|
7664
7874
|
relativePath: ".agents/hooks.json",
|
|
7665
7875
|
slots: [
|
|
7666
7876
|
{ key: ANTIGRAVITY_CONTAINER, type: "object", access: "owns" },
|
|
@@ -7713,7 +7923,7 @@ var init_surfaces = __esm(() => {
|
|
|
7713
7923
|
confidence: "experimental",
|
|
7714
7924
|
riskNotes: ["Bridged via a generated plugin file rather than a documented hook config."],
|
|
7715
7925
|
sourceDocs: ["src/ctx/runtimes.ts"],
|
|
7716
|
-
settingsFile: (root) =>
|
|
7926
|
+
settingsFile: (root) => path33.join(root, ...OPENCODE_PLUGIN_RELATIVE_PATH.split("/")),
|
|
7717
7927
|
relativePath: OPENCODE_PLUGIN_RELATIVE_PATH,
|
|
7718
7928
|
slots: [],
|
|
7719
7929
|
label: OPENCODE_PLUGIN_RELATIVE_PATH,
|
|
@@ -7750,8 +7960,7 @@ var init_surfaces = __esm(() => {
|
|
|
7750
7960
|
sentinel: ORIENT_SENTINEL,
|
|
7751
7961
|
confidence: "verified",
|
|
7752
7962
|
sourceDocs: ["src/ctx/orient-runtimes.ts"],
|
|
7753
|
-
|
|
7754
|
-
relativePath: ".claude/settings.json",
|
|
7963
|
+
...claudeSettingsLocation(),
|
|
7755
7964
|
slots: [
|
|
7756
7965
|
{ key: "hooks", type: "object", access: "owns" },
|
|
7757
7966
|
{ key: "_keryxManaged", type: "array", access: "owns" },
|
|
@@ -7760,7 +7969,7 @@ var init_surfaces = __esm(() => {
|
|
|
7760
7969
|
merge: (s) => mergeIntoHookArray(s, "UserPromptSubmit", { hooks: [{ type: "command", command: orientHookCommand("claude") }], [MANAGED_KEY]: ORIENT_SENTINEL }, ORIENT_SENTINEL),
|
|
7761
7970
|
strip: (s) => stripFromHookArray(s, "UserPromptSubmit", ORIENT_SENTINEL),
|
|
7762
7971
|
validate: orientValidate("claude", "UserPromptSubmit", "claude: missing UserPromptSubmit orientation hook"),
|
|
7763
|
-
label:
|
|
7972
|
+
label: `${CLAUDE_LOCAL_SETTINGS_PATH} (UserPromptSubmit)`,
|
|
7764
7973
|
groupKey: "UserPromptSubmit",
|
|
7765
7974
|
groupContainer: "hooks"
|
|
7766
7975
|
};
|
|
@@ -7771,7 +7980,7 @@ var init_surfaces = __esm(() => {
|
|
|
7771
7980
|
sentinel: ORIENT_SENTINEL,
|
|
7772
7981
|
confidence: "verified",
|
|
7773
7982
|
sourceDocs: ["src/ctx/orient-runtimes.ts"],
|
|
7774
|
-
settingsFile: (root) =>
|
|
7983
|
+
settingsFile: (root) => path33.join(root, ".codex", "hooks.json"),
|
|
7775
7984
|
relativePath: ".codex/hooks.json",
|
|
7776
7985
|
slots: [
|
|
7777
7986
|
{ key: "hooks", type: "object", access: "owns" },
|
|
@@ -7792,7 +8001,7 @@ var init_surfaces = __esm(() => {
|
|
|
7792
8001
|
sentinel: ORIENT_SENTINEL,
|
|
7793
8002
|
confidence: "verified",
|
|
7794
8003
|
sourceDocs: ["src/ctx/orient-runtimes.ts"],
|
|
7795
|
-
settingsFile: (root) =>
|
|
8004
|
+
settingsFile: (root) => path33.join(root, ".cursor", "hooks.json"),
|
|
7796
8005
|
relativePath: ".cursor/hooks.json",
|
|
7797
8006
|
slots: [
|
|
7798
8007
|
{ key: "hooks", type: "object", access: "owns" },
|
|
@@ -7823,8 +8032,7 @@ var init_surfaces = __esm(() => {
|
|
|
7823
8032
|
sentinel: AGENT_HOOKS_SENTINEL,
|
|
7824
8033
|
confidence: "verified",
|
|
7825
8034
|
sourceDocs: ["src/security/agent-hooks/runtimes.ts"],
|
|
7826
|
-
|
|
7827
|
-
relativePath: ".claude/settings.json",
|
|
8035
|
+
...claudeSettingsLocation(),
|
|
7828
8036
|
slots: [
|
|
7829
8037
|
{ key: "hooks", type: "object", access: "owns" },
|
|
7830
8038
|
{ key: "_keryxManaged", type: "array", access: "owns" },
|
|
@@ -7843,8 +8051,7 @@ var init_surfaces = __esm(() => {
|
|
|
7843
8051
|
sentinel: AGENT_HOOKS_SENTINEL,
|
|
7844
8052
|
confidence: "verified",
|
|
7845
8053
|
sourceDocs: ["src/security/agent-hooks/runtimes.ts"],
|
|
7846
|
-
|
|
7847
|
-
relativePath: ".claude/settings.json",
|
|
8054
|
+
...claudeSettingsLocation(),
|
|
7848
8055
|
slots: [
|
|
7849
8056
|
{ key: "hooks", type: "object", access: "owns" },
|
|
7850
8057
|
{ key: "_keryxManaged", type: "array", access: "owns" },
|
|
@@ -7870,7 +8077,7 @@ var init_surfaces = __esm(() => {
|
|
|
7870
8077
|
|
|
7871
8078
|
// src/integrations/markdown-block.ts
|
|
7872
8079
|
import { readFile as readFile15 } from "fs/promises";
|
|
7873
|
-
import
|
|
8080
|
+
import path34 from "path";
|
|
7874
8081
|
function dominantEol(content) {
|
|
7875
8082
|
const crlf = (content.match(/\r\n/g) ?? []).length;
|
|
7876
8083
|
const totalLf = (content.match(/\n/g) ?? []).length;
|
|
@@ -7922,7 +8129,7 @@ function allIndices(haystack, needle) {
|
|
|
7922
8129
|
}
|
|
7923
8130
|
return out;
|
|
7924
8131
|
}
|
|
7925
|
-
function
|
|
8132
|
+
function computeFencedRanges2(content) {
|
|
7926
8133
|
const ranges = [];
|
|
7927
8134
|
const eolRe = /\r\n|\n/g;
|
|
7928
8135
|
let lineStart = 0;
|
|
@@ -7952,7 +8159,7 @@ function isWithinRanges(offset, ranges) {
|
|
|
7952
8159
|
return ranges.some(([s, e]) => offset >= s && offset < e);
|
|
7953
8160
|
}
|
|
7954
8161
|
function endsInsideOpenFence(content) {
|
|
7955
|
-
return
|
|
8162
|
+
return computeFencedRanges2(content).endsInOpenFence;
|
|
7956
8163
|
}
|
|
7957
8164
|
function unclosedFenceMessage(relativePath) {
|
|
7958
8165
|
return `${relativePath}: ends inside an unclosed code fence \u2014 close it (or add the Keryx block by hand) before installing`;
|
|
@@ -7961,7 +8168,7 @@ function parseBlocks(content, relativePath, spec) {
|
|
|
7961
8168
|
const fail = () => {
|
|
7962
8169
|
throw new UnterminatedInstructionsBlockError(`${relativePath}: unterminated ${spec.startMarker} block \u2014 fix it by hand`);
|
|
7963
8170
|
};
|
|
7964
|
-
const { ranges: fenced } =
|
|
8171
|
+
const { ranges: fenced } = computeFencedRanges2(content);
|
|
7965
8172
|
const starts = allIndices(content, spec.startMarker);
|
|
7966
8173
|
const ends = allIndices(content, spec.endMarker);
|
|
7967
8174
|
if (starts.some((i) => isWithinRanges(i, fenced)) || ends.some((i) => isWithinRanges(i, fenced)))
|
|
@@ -8012,7 +8219,7 @@ function appendBlock(content, block, eol) {
|
|
|
8012
8219
|
return endsWithEol(content) ? `${content}${eol}${block}` : `${content}${eol}${eol}${block}`;
|
|
8013
8220
|
}
|
|
8014
8221
|
function fileFor(root, relativePath) {
|
|
8015
|
-
return
|
|
8222
|
+
return path34.join(root, ...relativePath.split("/"));
|
|
8016
8223
|
}
|
|
8017
8224
|
async function installMarkdownBlock(root, relativePath, frontMatter, spec = INSTRUCTIONS_BLOCK_SPEC) {
|
|
8018
8225
|
const symlinkRefusal = await refuseEscapingSymlink(root, relativePath);
|
|
@@ -8124,7 +8331,7 @@ var init_markdown_block = __esm(() => {
|
|
|
8124
8331
|
});
|
|
8125
8332
|
|
|
8126
8333
|
// src/integrations/surfaces-w5b.ts
|
|
8127
|
-
import
|
|
8334
|
+
import path35 from "path";
|
|
8128
8335
|
function kiroHookEntry() {
|
|
8129
8336
|
return {
|
|
8130
8337
|
name: KIRO_HOOK_ID,
|
|
@@ -8151,13 +8358,36 @@ function copilotHookEntry() {
|
|
|
8151
8358
|
};
|
|
8152
8359
|
}
|
|
8153
8360
|
async function agentsMdHasKeryxBlock(root) {
|
|
8154
|
-
const file =
|
|
8361
|
+
const file = path35.join(root, "AGENTS.md");
|
|
8155
8362
|
if (!await pathExists(file))
|
|
8156
8363
|
return false;
|
|
8157
8364
|
const { readFile } = await import("fs/promises");
|
|
8158
8365
|
const content = await readFile(file, "utf8");
|
|
8159
8366
|
return content.includes("<!-- keryx:index -->");
|
|
8160
8367
|
}
|
|
8368
|
+
async function localCodexEntryPath(root) {
|
|
8369
|
+
const { readFile } = await import("fs/promises");
|
|
8370
|
+
let manifest;
|
|
8371
|
+
try {
|
|
8372
|
+
manifest = JSON.parse(await readFile(path35.join(root, ".metaproject", "metaproject.json"), "utf8"));
|
|
8373
|
+
} catch {
|
|
8374
|
+
return;
|
|
8375
|
+
}
|
|
8376
|
+
const agentEntrypoints = typeof manifest === "object" && manifest !== null ? manifest.agentEntrypoints : undefined;
|
|
8377
|
+
const codex = normalizeEntrypointTargets(agentEntrypoints).targets.root.find((entry) => entry.runtime === "codex");
|
|
8378
|
+
return codex?.scope === "local" ? codex.path : undefined;
|
|
8379
|
+
}
|
|
8380
|
+
async function probeZedInstructions(root) {
|
|
8381
|
+
if (await agentsMdHasKeryxBlock(root))
|
|
8382
|
+
return [];
|
|
8383
|
+
const localCodex = await localCodexEntryPath(root);
|
|
8384
|
+
if (localCodex !== undefined) {
|
|
8385
|
+
return [
|
|
8386
|
+
`zed: AGENTS.md carries no Keryx block \u2014 the codex entry is local, so the block goes to ${localCodex}, which Zed does not read. To route Zed through Keryx, set the codex entry's scope to "shared" under agentEntrypoints.root in .metaproject/metaproject.json and run \`keryx update\`.`
|
|
8387
|
+
];
|
|
8388
|
+
}
|
|
8389
|
+
return ["zed: AGENTS.md is missing or missing Keryx's block \u2014 run `keryx update`"];
|
|
8390
|
+
}
|
|
8161
8391
|
function shellHookSurface(id, description) {
|
|
8162
8392
|
return {
|
|
8163
8393
|
id,
|
|
@@ -8179,6 +8409,7 @@ inclusion: always
|
|
|
8179
8409
|
|
|
8180
8410
|
`, INSTRUCTIONS_KIRO, COPILOT_SOURCE_DOCS, COPILOT_KEY = "preToolUse", CTX_GUARD_GITHUB_COPILOT_AGENT, INSTRUCTIONS_GITHUB_COPILOT_AGENT, ACP_PERMISSION_ZED, ZED_SHADOWING_RULES_FILES, INSTRUCTIONS_ZED, KERYX_SHELL_UNSUPPORTED_REASON = "Registered by W6's keryx shell hook runtime; not installed by keryx integrations yet.", KERYX_SHELL_UNSUPPORTED_FLAGS, KERYX_SHELL_UNSUPPORTED, KERYX_SHELL_SOURCE_DOCS, SHELL_HOOKS_BLOCK, SHELL_HOOKS_PROMPT_GATE, SHELL_HOOKS_PRE_TOOL_CONTEXT, SHELL_HOOKS_INJECT_CONTEXT, SHELL_HOOKS_OBSERVE, SHELL_HOOKS_POST_TOOL, SHELL_HOOKS_SESSION_START, SHELL_HOOKS_STOP, KERYX_SHELL_SURFACES;
|
|
8181
8411
|
var init_surfaces_w5b = __esm(() => {
|
|
8412
|
+
init_entrypoint_targets();
|
|
8182
8413
|
init_settings_json();
|
|
8183
8414
|
init_codecs();
|
|
8184
8415
|
init_surfaces();
|
|
@@ -8209,7 +8440,7 @@ var init_surfaces_w5b = __esm(() => {
|
|
|
8209
8440
|
confidence: "experimental",
|
|
8210
8441
|
riskNotes: ["Whether Gemini CLI actually reads GEMINI.md end-to-end (beyond the documented context.fileName option) is not independently confirmed here."],
|
|
8211
8442
|
sourceDocs: ["https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/gemini-md.md"],
|
|
8212
|
-
settingsFile: (root) =>
|
|
8443
|
+
settingsFile: (root) => path35.join(root, "GEMINI.md"),
|
|
8213
8444
|
relativePath: "GEMINI.md",
|
|
8214
8445
|
slots: [],
|
|
8215
8446
|
customInstall: (root) => installMarkdownBlock(root, "GEMINI.md"),
|
|
@@ -8233,7 +8464,7 @@ var init_surfaces_w5b = __esm(() => {
|
|
|
8233
8464
|
"Kiro's hook stdin field names and its shell tool's name are third-party-reported only, not confirmed by first-party docs \u2014 the matcher above is a best guess and may gate nothing on a real install; verify before relying on it."
|
|
8234
8465
|
],
|
|
8235
8466
|
sourceDocs: KIRO_SOURCE_DOCS,
|
|
8236
|
-
settingsFile: (root) =>
|
|
8467
|
+
settingsFile: (root) => path35.join(root, ".kiro", "hooks", "keryx-ctx-guard.json"),
|
|
8237
8468
|
relativePath: ".kiro/hooks/keryx-ctx-guard.json",
|
|
8238
8469
|
slots: [
|
|
8239
8470
|
{ key: "version", type: "string", access: "owns" },
|
|
@@ -8278,7 +8509,7 @@ var init_surfaces_w5b = __esm(() => {
|
|
|
8278
8509
|
"Open Kiro issues report that steering file `inclusion` modes are not always honoured, even with `inclusion: always` set \u2014 verify on a live install."
|
|
8279
8510
|
],
|
|
8280
8511
|
sourceDocs: ["https://kiro.dev/docs/steering/"],
|
|
8281
|
-
settingsFile: (root) =>
|
|
8512
|
+
settingsFile: (root) => path35.join(root, ".kiro", "steering", "keryx.md"),
|
|
8282
8513
|
relativePath: ".kiro/steering/keryx.md",
|
|
8283
8514
|
slots: [],
|
|
8284
8515
|
customInstall: (root) => installMarkdownBlock(root, ".kiro/steering/keryx.md", KIRO_STEERING_FRONT_MATTER),
|
|
@@ -8301,7 +8532,7 @@ var init_surfaces_w5b = __esm(() => {
|
|
|
8301
8532
|
"The shell tool's name Copilot's hook payload carries is not documented; this guard parses any tool call whose payload carries `toolArgs.command`, whatever `toolName` is."
|
|
8302
8533
|
],
|
|
8303
8534
|
sourceDocs: COPILOT_SOURCE_DOCS,
|
|
8304
|
-
settingsFile: (root) =>
|
|
8535
|
+
settingsFile: (root) => path35.join(root, ".github", "hooks", "keryx-ctx-guard.json"),
|
|
8305
8536
|
relativePath: ".github/hooks/keryx-ctx-guard.json",
|
|
8306
8537
|
slots: [
|
|
8307
8538
|
{ key: "version", type: "number", access: "owns" },
|
|
@@ -8345,7 +8576,7 @@ var init_surfaces_w5b = __esm(() => {
|
|
|
8345
8576
|
"https://docs.github.com/en/copilot/how-tos/configure-custom-instructions-in-your-ide/add-repository-instructions-in-your-ide",
|
|
8346
8577
|
"https://github.blog/changelog/2025-08-28-copilot-coding-agent-now-supports-agents-md-custom-instructions/"
|
|
8347
8578
|
],
|
|
8348
|
-
settingsFile: (root) =>
|
|
8579
|
+
settingsFile: (root) => path35.join(root, ".github", "copilot-instructions.md"),
|
|
8349
8580
|
relativePath: ".github/copilot-instructions.md",
|
|
8350
8581
|
slots: [],
|
|
8351
8582
|
customInstall: (root) => installMarkdownBlock(root, ".github/copilot-instructions.md"),
|
|
@@ -8384,10 +8615,10 @@ var init_surfaces_w5b = __esm(() => {
|
|
|
8384
8615
|
`Zed uses the first matching rules file among ${ZED_SHADOWING_RULES_FILES.join(", ")}, AGENT.md, AGENTS.md, CLAUDE.md, GEMINI.md, ... \u2014 an earlier file in that list shadows AGENTS.md even when AGENTS.md itself carries Keryx's block.`
|
|
8385
8616
|
],
|
|
8386
8617
|
sourceDocs: ["https://github.com/zed-industries/zed/blob/main/docs/src/ai/rules.md"],
|
|
8387
|
-
settingsFile: (root) =>
|
|
8618
|
+
settingsFile: (root) => path35.join(root, "AGENTS.md"),
|
|
8388
8619
|
relativePath: "AGENTS.md",
|
|
8389
8620
|
slots: [],
|
|
8390
|
-
probe:
|
|
8621
|
+
probe: probeZedInstructions
|
|
8391
8622
|
};
|
|
8392
8623
|
KERYX_SHELL_UNSUPPORTED_FLAGS = ["skills", "agents", "instructions", "mcp"];
|
|
8393
8624
|
KERYX_SHELL_UNSUPPORTED = Object.fromEntries(KERYX_SHELL_UNSUPPORTED_FLAGS.map((flag) => [flag, KERYX_SHELL_UNSUPPORTED_REASON]));
|
|
@@ -8465,142 +8696,185 @@ var init_route_tokens = __esm(() => {
|
|
|
8465
8696
|
});
|
|
8466
8697
|
|
|
8467
8698
|
// src/gdskills/skill-frontmatter.ts
|
|
8468
|
-
function
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
}
|
|
8476
|
-
return value;
|
|
8699
|
+
function frontmatterLines(content) {
|
|
8700
|
+
const text = content.startsWith("\uFEFF") ? content.slice(1) : content;
|
|
8701
|
+
const lines = text.split(/\r?\n/);
|
|
8702
|
+
if (lines[0]?.trimEnd() !== "---")
|
|
8703
|
+
return;
|
|
8704
|
+
const close = lines.findIndex((line, index) => index > 0 && line.trimEnd() === "---");
|
|
8705
|
+
return close === -1 ? undefined : lines.slice(1, close);
|
|
8477
8706
|
}
|
|
8478
|
-
function
|
|
8479
|
-
|
|
8707
|
+
function readFrontmatter(content) {
|
|
8708
|
+
const lines = frontmatterLines(content);
|
|
8709
|
+
return lines === undefined ? undefined : readMapping(lines, 0);
|
|
8480
8710
|
}
|
|
8481
|
-
function
|
|
8482
|
-
|
|
8483
|
-
return;
|
|
8484
|
-
const flowList = /^\[(.*)\]$/.exec(value);
|
|
8485
|
-
if (flowList !== null) {
|
|
8486
|
-
return splitHarnessScalar((flowList[1] ?? "").replace(/["']/g, ""));
|
|
8487
|
-
}
|
|
8488
|
-
return splitHarnessScalar(stripSkillFieldQuotes(value));
|
|
8711
|
+
function scalarText(value) {
|
|
8712
|
+
return value?.kind === "scalar" || value?.kind === "block" ? value.text : undefined;
|
|
8489
8713
|
}
|
|
8490
|
-
function
|
|
8491
|
-
if (
|
|
8492
|
-
return
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
let
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
let descriptionBlock = null;
|
|
8512
|
-
const foldBlock = (block) => block.join(" ").replace(/\s+/g, " ").trim();
|
|
8513
|
-
for (const line of lines) {
|
|
8514
|
-
if (descriptionBlock !== null) {
|
|
8515
|
-
if (line.trim() === "" || /^\s/.test(line)) {
|
|
8516
|
-
descriptionBlock.push(line.trim());
|
|
8517
|
-
continue;
|
|
8518
|
-
}
|
|
8519
|
-
description = foldBlock(descriptionBlock);
|
|
8520
|
-
descriptionBlock = null;
|
|
8521
|
-
}
|
|
8522
|
-
const nameMatch = /^name:\s*(.*)$/.exec(line);
|
|
8523
|
-
if (nameMatch !== null && nameMatch[1] !== undefined) {
|
|
8524
|
-
name = stripSkillFieldQuotes(nameMatch[1].trim());
|
|
8525
|
-
inTriggers = false;
|
|
8526
|
-
inMetadata = false;
|
|
8527
|
-
awaitingHarnessesList = false;
|
|
8714
|
+
function listItems(value) {
|
|
8715
|
+
if (value?.kind === "list")
|
|
8716
|
+
return [...value.items];
|
|
8717
|
+
if (value?.kind === "scalar")
|
|
8718
|
+
return splitOutsideQuotes(value.text).map(unquote).filter(Boolean);
|
|
8719
|
+
return [];
|
|
8720
|
+
}
|
|
8721
|
+
function indentOf(line) {
|
|
8722
|
+
return /^[ \t]*/.exec(line)?.[0].length ?? 0;
|
|
8723
|
+
}
|
|
8724
|
+
function isFiller(line) {
|
|
8725
|
+
const trimmed = line.trim();
|
|
8726
|
+
return trimmed === "" || trimmed.startsWith("#");
|
|
8727
|
+
}
|
|
8728
|
+
function readMapping(lines, indent) {
|
|
8729
|
+
const fields = new Map;
|
|
8730
|
+
let index = 0;
|
|
8731
|
+
while (index < lines.length) {
|
|
8732
|
+
const line = lines[index];
|
|
8733
|
+
index += 1;
|
|
8734
|
+
if (isFiller(line) || indentOf(line) !== indent)
|
|
8528
8735
|
continue;
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8736
|
+
const match = KEY_LINE.exec(line.slice(indent));
|
|
8737
|
+
if (match === null)
|
|
8738
|
+
continue;
|
|
8739
|
+
const start = index;
|
|
8740
|
+
while (index < lines.length) {
|
|
8741
|
+
const next = lines[index];
|
|
8742
|
+
const nextIndent = indentOf(next);
|
|
8743
|
+
if (isFiller(next) || nextIndent > indent || nextIndent === indent && LIST_ITEM.test(next.slice(nextIndent))) {
|
|
8744
|
+
index += 1;
|
|
8535
8745
|
} else {
|
|
8536
|
-
|
|
8746
|
+
break;
|
|
8537
8747
|
}
|
|
8538
|
-
inTriggers = false;
|
|
8539
|
-
inMetadata = false;
|
|
8540
|
-
awaitingHarnessesList = false;
|
|
8541
|
-
continue;
|
|
8542
8748
|
}
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8749
|
+
const key = unquote(match[1]);
|
|
8750
|
+
if (!fields.has(key))
|
|
8751
|
+
fields.set(key, readValue(match[2] ?? "", lines.slice(start, index), indent));
|
|
8752
|
+
}
|
|
8753
|
+
return fields;
|
|
8754
|
+
}
|
|
8755
|
+
function readValue(rest, children, keyIndent) {
|
|
8756
|
+
const text = stripTrailingComment(rest);
|
|
8757
|
+
if (BLOCK_SCALAR.test(text)) {
|
|
8758
|
+
const body = children.filter((line) => line.trim() === "" || indentOf(line) > keyIndent);
|
|
8759
|
+
return { kind: "block", text: body.map((line) => line.trim()).join(" ").replace(/\s+/g, " ").trim() };
|
|
8760
|
+
}
|
|
8761
|
+
if (text.startsWith("["))
|
|
8762
|
+
return readFlowList(text);
|
|
8763
|
+
if (text.startsWith("{") || text.startsWith("&") || text.startsWith("*") || text.startsWith("!")) {
|
|
8764
|
+
return { kind: "unsupported" };
|
|
8765
|
+
}
|
|
8766
|
+
if (text !== "")
|
|
8767
|
+
return { kind: "scalar", text: unquote(text) };
|
|
8768
|
+
const first = children.find((line) => !isFiller(line));
|
|
8769
|
+
if (first === undefined)
|
|
8770
|
+
return { kind: "scalar", text: "" };
|
|
8771
|
+
const firstIndent = indentOf(first);
|
|
8772
|
+
if (LIST_ITEM.test(first.slice(firstIndent)))
|
|
8773
|
+
return readBlockList(children, keyIndent);
|
|
8774
|
+
if (!KEY_LINE.test(first.slice(firstIndent)))
|
|
8775
|
+
return { kind: "unsupported" };
|
|
8776
|
+
return { kind: "mapping", fields: readMapping(children, firstIndent) };
|
|
8777
|
+
}
|
|
8778
|
+
function readBlockList(children, keyIndent) {
|
|
8779
|
+
const items = [];
|
|
8780
|
+
for (const line of children) {
|
|
8781
|
+
if (isFiller(line))
|
|
8547
8782
|
continue;
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
}
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8783
|
+
const lineIndent = indentOf(line);
|
|
8784
|
+
const item = lineIndent >= keyIndent ? LIST_ITEM.exec(line.slice(lineIndent)) : null;
|
|
8785
|
+
if (item === null)
|
|
8786
|
+
break;
|
|
8787
|
+
const text = stripTrailingComment(item[1] ?? "");
|
|
8788
|
+
if (text.startsWith("[") || text.startsWith("{"))
|
|
8789
|
+
return { kind: "unsupported" };
|
|
8790
|
+
const entry = unquote(text);
|
|
8791
|
+
if (entry !== "")
|
|
8792
|
+
items.push(entry);
|
|
8793
|
+
}
|
|
8794
|
+
return { kind: "list", items };
|
|
8795
|
+
}
|
|
8796
|
+
function readFlowList(text) {
|
|
8797
|
+
if (!text.endsWith("]"))
|
|
8798
|
+
return { kind: "unsupported" };
|
|
8799
|
+
const inner = text.slice(1, -1);
|
|
8800
|
+
const entries = splitOutsideQuotes(inner);
|
|
8801
|
+
if (entries.some((entry) => /^[[{]/.test(entry.trim())))
|
|
8802
|
+
return { kind: "unsupported" };
|
|
8803
|
+
return { kind: "list", items: entries.map(unquote).filter(Boolean) };
|
|
8804
|
+
}
|
|
8805
|
+
function atTokenStart(text, index) {
|
|
8806
|
+
return index === 0 || /[\s[{,]/.test(text[index - 1]);
|
|
8807
|
+
}
|
|
8808
|
+
function stripTrailingComment(value) {
|
|
8809
|
+
let quote;
|
|
8810
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
8811
|
+
const char = value[index];
|
|
8812
|
+
if (quote !== undefined) {
|
|
8813
|
+
if (quote === '"' && char === "\\")
|
|
8814
|
+
index += 1;
|
|
8815
|
+
else if (char === quote) {
|
|
8816
|
+
if (quote === "'" && value[index + 1] === "'")
|
|
8817
|
+
index += 1;
|
|
8818
|
+
else
|
|
8819
|
+
quote = undefined;
|
|
8562
8820
|
}
|
|
8563
|
-
awaitingHarnessesList = false;
|
|
8564
|
-
}
|
|
8565
|
-
if (/^metadata:\s*$/.test(line)) {
|
|
8566
|
-
inMetadata = true;
|
|
8567
8821
|
continue;
|
|
8568
8822
|
}
|
|
8569
|
-
if (
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
if (inline !== undefined) {
|
|
8592
|
-
compatibleHarnesses = inline;
|
|
8593
|
-
} else {
|
|
8594
|
-
awaitingHarnessesList = true;
|
|
8595
|
-
}
|
|
8596
|
-
continue;
|
|
8597
|
-
}
|
|
8598
|
-
}
|
|
8823
|
+
if ((char === '"' || char === "'") && atTokenStart(value, index))
|
|
8824
|
+
quote = char;
|
|
8825
|
+
else if (char === "#" && (index === 0 || /\s/.test(value[index - 1])))
|
|
8826
|
+
return value.slice(0, index).trim();
|
|
8827
|
+
}
|
|
8828
|
+
return value.trim();
|
|
8829
|
+
}
|
|
8830
|
+
function splitOutsideQuotes(text) {
|
|
8831
|
+
const parts = [];
|
|
8832
|
+
let quote;
|
|
8833
|
+
let current = "";
|
|
8834
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
8835
|
+
const char = text[index];
|
|
8836
|
+
if (quote !== undefined) {
|
|
8837
|
+
if (char === quote)
|
|
8838
|
+
quote = undefined;
|
|
8839
|
+
} else if ((char === '"' || char === "'") && current.trim() === "") {
|
|
8840
|
+
quote = char;
|
|
8841
|
+
} else if (char === ",") {
|
|
8842
|
+
parts.push(current);
|
|
8843
|
+
current = "";
|
|
8844
|
+
continue;
|
|
8599
8845
|
}
|
|
8846
|
+
current += char;
|
|
8600
8847
|
}
|
|
8601
|
-
|
|
8602
|
-
|
|
8848
|
+
parts.push(current);
|
|
8849
|
+
return parts;
|
|
8850
|
+
}
|
|
8851
|
+
function unquote(value) {
|
|
8852
|
+
const trimmed = value.trim();
|
|
8853
|
+
if (trimmed.length >= 2) {
|
|
8854
|
+
const first = trimmed[0];
|
|
8855
|
+
if ((first === '"' || first === "'") && trimmed.endsWith(first))
|
|
8856
|
+
return trimmed.slice(1, -1);
|
|
8603
8857
|
}
|
|
8858
|
+
return trimmed;
|
|
8859
|
+
}
|
|
8860
|
+
function nonEmpty(value) {
|
|
8861
|
+
return value === undefined || value === "" ? undefined : value;
|
|
8862
|
+
}
|
|
8863
|
+
function parseSkillFrontmatter(content) {
|
|
8864
|
+
const top = readFrontmatter(content);
|
|
8865
|
+
if (top === undefined)
|
|
8866
|
+
return {};
|
|
8867
|
+
const metadataValue = top.get("metadata");
|
|
8868
|
+
const metadata = metadataValue?.kind === "mapping" ? metadataValue.fields : new Map;
|
|
8869
|
+
const name = scalarText(top.get("name"));
|
|
8870
|
+
const description = scalarText(top.get("description"));
|
|
8871
|
+
const triggersValue = top.get("triggers");
|
|
8872
|
+
const triggers = triggersValue?.kind === "list" ? [...triggersValue.items] : [];
|
|
8873
|
+
const metadataCategory = nonEmpty(scalarText(metadata.get("category")));
|
|
8874
|
+
const metadataVersion = nonEmpty(scalarText(metadata.get("version")));
|
|
8875
|
+
const metadataOrigin = nonEmpty(scalarText(metadata.get("origin")));
|
|
8876
|
+
const harnessesValue = metadata.get("compatible_harnesses");
|
|
8877
|
+
const compatibleHarnesses = harnessesValue?.kind === "list" || harnessesValue?.kind === "scalar" && harnessesValue.text !== "" ? listItems(harnessesValue) : undefined;
|
|
8604
8878
|
return {
|
|
8605
8879
|
...name !== undefined ? { name } : {},
|
|
8606
8880
|
...description !== undefined ? { description } : {},
|
|
@@ -8611,10 +8885,16 @@ function parseSkillFrontmatter(content) {
|
|
|
8611
8885
|
...compatibleHarnesses !== undefined ? { compatibleHarnesses } : {}
|
|
8612
8886
|
};
|
|
8613
8887
|
}
|
|
8888
|
+
var KEY_LINE, LIST_ITEM, BLOCK_SCALAR;
|
|
8889
|
+
var init_skill_frontmatter = __esm(() => {
|
|
8890
|
+
KEY_LINE = /^("[^"]*"|'[^']*'|[^\s#"'\-[\]{},:][^:]*?):(?:[ \t]+(.*))?$/;
|
|
8891
|
+
LIST_ITEM = /^-(?:[ \t]+(.*))?$/;
|
|
8892
|
+
BLOCK_SCALAR = /^[|>][-+]?[0-9]?$/;
|
|
8893
|
+
});
|
|
8614
8894
|
|
|
8615
8895
|
// src/gdskills/catalog.ts
|
|
8616
|
-
import { existsSync as existsSync2, readFileSync as
|
|
8617
|
-
import
|
|
8896
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
8897
|
+
import path36 from "path";
|
|
8618
8898
|
import { fileURLToPath } from "url";
|
|
8619
8899
|
function skill(name, category, profiles, purpose, workflow) {
|
|
8620
8900
|
let routing;
|
|
@@ -8637,10 +8917,10 @@ function renderedSkill(name, category, profiles, purpose, workflow, triggers, de
|
|
|
8637
8917
|
return { name, category, description, purpose, workflow, triggers, profiles };
|
|
8638
8918
|
}
|
|
8639
8919
|
function bundledSkillMarkdownPath(category, name) {
|
|
8640
|
-
const here =
|
|
8920
|
+
const here = path36.dirname(fileURLToPath(import.meta.url));
|
|
8641
8921
|
return [
|
|
8642
|
-
|
|
8643
|
-
|
|
8922
|
+
path36.join(here, "bundled", "skills", category, name, "SKILL.md"),
|
|
8923
|
+
path36.join(here, "..", "src", "gdskills", "bundled", "skills", category, name, "SKILL.md")
|
|
8644
8924
|
].find((candidate) => existsSync2(candidate));
|
|
8645
8925
|
}
|
|
8646
8926
|
function readBundledSkillRouting(category, name) {
|
|
@@ -8648,7 +8928,7 @@ function readBundledSkillRouting(category, name) {
|
|
|
8648
8928
|
if (file === undefined) {
|
|
8649
8929
|
throw new Error(`bundled skill "${category}/${name}" ships no SKILL.md under src/gdskills/bundled/skills, and BUNDLED_GDSKILLS reads its description and triggers from there. Declare it with renderedSkill() if it has no file.`);
|
|
8650
8930
|
}
|
|
8651
|
-
const { description, triggers } = parseSkillFrontmatter(
|
|
8931
|
+
const { description, triggers } = parseSkillFrontmatter(readFileSync3(file, "utf8"));
|
|
8652
8932
|
if (description === undefined || description.length === 0 || triggers === undefined || triggers.length === 0) {
|
|
8653
8933
|
throw new Error(`${file}: frontmatter must declare a non-empty \`description\` and \`triggers:\` list; the skill router scores both.`);
|
|
8654
8934
|
}
|
|
@@ -8656,6 +8936,7 @@ function readBundledSkillRouting(category, name) {
|
|
|
8656
8936
|
}
|
|
8657
8937
|
var BUNDLED_GDSKILLS, BUNDLED_TREE_MARKER;
|
|
8658
8938
|
var init_catalog = __esm(() => {
|
|
8939
|
+
init_skill_frontmatter();
|
|
8659
8940
|
BUNDLED_GDSKILLS = [
|
|
8660
8941
|
renderedSkill("metaproject-router", "core", ["minimal", "recommended", "full"], "Choose which Metaproject module, working skill, or project-skill should be used for a user request.", [
|
|
8661
8942
|
"Read `.metaproject/index.md` first.",
|
|
@@ -8699,7 +8980,7 @@ var init_catalog = __esm(() => {
|
|
|
8699
8980
|
"Write the reviewer against the orchestrated review contract; point at the canonical severity rubric instead of inventing one.",
|
|
8700
8981
|
"Drop the persona, keep the method, and state the reason beside every rule kept.",
|
|
8701
8982
|
"Confirm with `keryx review reviewers` \u2014 creating files is not registration, and registration is not discovery.",
|
|
8702
|
-
"For existing SKILL.md packages, run `keryx skills import --from <dir|file|https-url> --module <module>`; `keryx review import` is the review-shaped
|
|
8983
|
+
"For existing SKILL.md packages, run `keryx skills import --from <dir|file|https-url> --module <module>`; `keryx review import` is the review-shaped spelling (module implied). A tree of several packages needs `--only <glob>` to say which ones."
|
|
8703
8984
|
]),
|
|
8704
8985
|
renderedSkill("entity-skill-verifier", "core", ["minimal", "recommended", "full"], "Run `keryx skills verify` to check a project-skill's required files, SKILL.md metadata, manifest registration, target-path existence, and evidence artifacts (gdgraph, gdctx, validated gdwiki, Code Health, canonical accepted memory), then classify it as fresh, needs-review, stale, or blocked. The command does not read the skill's prose or compare it against current code \u2014 that comparison is a manual agent step.", [
|
|
8705
8986
|
"Resolve the target project-skill through gdgraph affected context or `keryx skills route <target>`.",
|
|
@@ -9105,10 +9386,10 @@ var init_catalog = __esm(() => {
|
|
|
9105
9386
|
"Report gaps and recommended follow-up pages."
|
|
9106
9387
|
]),
|
|
9107
9388
|
renderedSkill("agent-entrypoint-manager", "platform", ["minimal", "recommended", "full"], "Maintain AGENTS.md, CLAUDE.md, and local-first Metaproject references.", [
|
|
9108
|
-
"Find existing root agent entrypoints.",
|
|
9109
|
-
"Keep managed Metaproject blocks idempotent.",
|
|
9389
|
+
"Find existing root agent entrypoints and the block targets `agentEntrypoints.root` names: `CLAUDE.local.md` and `AGENTS.override.md` (scope `local`, the default; Codex mode `override` or `skip`), or `CLAUDE.md` / `AGENTS.md` (scope `shared`).",
|
|
9390
|
+
"Keep managed Metaproject blocks idempotent: refresh them with `keryx update` or `keryx rules sync`, which also moves a block out of a tracked entrypoint and regenerates `AGENTS.override.md` from `AGENTS.md` \u2014 never write a bare block there.",
|
|
9110
9391
|
"Ensure local `.metaproject/index.md` and skill catalog are first-class references."
|
|
9111
|
-
], ["agents.md", "claude.md", "entrypoint"], "Use when AGENTS.md or CLAUDE.md needs its managed Metaproject block added, refreshed, or kept idempotent. NOT for: splitting an oversized entrypoint into rules and project-skills (see agent-entrypoint-distiller)."),
|
|
9392
|
+
], ["agents.md", "claude.md", "entrypoint"], "Use when AGENTS.md or CLAUDE.md needs its managed Metaproject block added, refreshed, or kept idempotent \u2014 the block lives in CLAUDE.local.md / AGENTS.override.md unless scope is shared. NOT for: splitting an oversized entrypoint into rules and project-skills (see agent-entrypoint-distiller)."),
|
|
9112
9393
|
skill("agent-entrypoint-distiller", "platform", ["minimal", "recommended", "full"], "Split large AGENTS.md/CLAUDE.md files into high-priority Metaproject rules and project-specific skills.", [
|
|
9113
9394
|
"Run `keryx rules distill` when the user asks to decompose a large CLAUDE.md/AGENTS.md.",
|
|
9114
9395
|
"Keep root entrypoints compact: non-project/highest-priority instructions plus `.metaproject/index.md` routing.",
|
|
@@ -9150,7 +9431,7 @@ var init_catalog = __esm(() => {
|
|
|
9150
9431
|
"Sync only selected skills and report changed files."
|
|
9151
9432
|
], ["sync skills", "install runtime skills", "global skill sync"], "Use when already-exported runtime skills need pushing to configured local runtimes, and only when sync is explicitly enabled. NOT for: producing the runtime export itself (see skill-runtime-exporter).")
|
|
9152
9433
|
];
|
|
9153
|
-
BUNDLED_TREE_MARKER =
|
|
9434
|
+
BUNDLED_TREE_MARKER = path36.join("src", "gdskills", "bundled");
|
|
9154
9435
|
});
|
|
9155
9436
|
|
|
9156
9437
|
// src/gdskills/resolve.ts
|
|
@@ -9283,13 +9564,13 @@ var init_skill_length_ceilings = __esm(() => {
|
|
|
9283
9564
|
});
|
|
9284
9565
|
|
|
9285
9566
|
// src/gdskills/bundled-eval.ts
|
|
9286
|
-
import { existsSync as existsSync3, readFileSync as
|
|
9287
|
-
import
|
|
9567
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
9568
|
+
import path37 from "path";
|
|
9288
9569
|
function defaultBundledRoot() {
|
|
9289
|
-
const directPath =
|
|
9570
|
+
const directPath = path37.join(import.meta.dir, "bundled");
|
|
9290
9571
|
if (existsSync3(directPath))
|
|
9291
9572
|
return directPath;
|
|
9292
|
-
const packagedPath =
|
|
9573
|
+
const packagedPath = path37.join(import.meta.dir, "..", "src", "gdskills", "bundled");
|
|
9293
9574
|
if (existsSync3(packagedPath))
|
|
9294
9575
|
return packagedPath;
|
|
9295
9576
|
return directPath;
|
|
@@ -9300,6 +9581,7 @@ var init_bundled_eval = __esm(() => {
|
|
|
9300
9581
|
init_catalog();
|
|
9301
9582
|
init_export();
|
|
9302
9583
|
init_model_tier();
|
|
9584
|
+
init_skill_frontmatter();
|
|
9303
9585
|
init_skill_length_ceilings();
|
|
9304
9586
|
PLACEHOLDER_ACCOUNTS = new Set([
|
|
9305
9587
|
"dev",
|
|
@@ -9486,7 +9768,7 @@ var init_bundled_eval = __esm(() => {
|
|
|
9486
9768
|
KNOWN_SKILL_COMPANION_DOCUMENTS = new Map([
|
|
9487
9769
|
[
|
|
9488
9770
|
"SKILL.detail.md",
|
|
9489
|
-
"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"
|
|
9771
|
+
"overflow reference linked from its skill's SKILL.md (`core/reviewer-skill-creator`, `orchestration/feature-analyzer`, `review/review-orchestrator`); carries no frontmatter and is not addressed by any runtime"
|
|
9490
9772
|
],
|
|
9491
9773
|
[
|
|
9492
9774
|
"orchestrator-prompt.md",
|
|
@@ -9805,18 +10087,18 @@ var init_schema = __esm(() => {
|
|
|
9805
10087
|
});
|
|
9806
10088
|
|
|
9807
10089
|
// src/agents/catalog.ts
|
|
9808
|
-
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as
|
|
9809
|
-
import
|
|
10090
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
10091
|
+
import path38 from "path";
|
|
9810
10092
|
function agentDefinitionFiles(dir) {
|
|
9811
10093
|
if (!existsSync4(dir)) {
|
|
9812
10094
|
return [];
|
|
9813
10095
|
}
|
|
9814
|
-
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) =>
|
|
10096
|
+
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => path38.join(dir, entry.name)).sort();
|
|
9815
10097
|
}
|
|
9816
10098
|
function loadOne(filePath, kind) {
|
|
9817
10099
|
let raw;
|
|
9818
10100
|
try {
|
|
9819
|
-
raw =
|
|
10101
|
+
raw = readFileSync5(filePath, "utf8");
|
|
9820
10102
|
} catch (cause) {
|
|
9821
10103
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
9822
10104
|
return { error: { reason: "unreadable-file", path: filePath, message: `could not read ${filePath}: ${detail}` } };
|
|
@@ -9843,7 +10125,7 @@ function loadOne(filePath, kind) {
|
|
|
9843
10125
|
};
|
|
9844
10126
|
}
|
|
9845
10127
|
const definition = buildAgentDefinition(parsed.result.data, parsed.result.body);
|
|
9846
|
-
const stem =
|
|
10128
|
+
const stem = path38.basename(filePath, ".md");
|
|
9847
10129
|
if (stem !== definition.name) {
|
|
9848
10130
|
return {
|
|
9849
10131
|
error: {
|
|
@@ -9877,8 +10159,8 @@ function loadSource(dir, kind, byName, errors) {
|
|
|
9877
10159
|
}
|
|
9878
10160
|
}
|
|
9879
10161
|
function loadAgentCatalog(projectRoot, options = {}) {
|
|
9880
|
-
const bundledRoot = options.bundledRoot ??
|
|
9881
|
-
const projectDir =
|
|
10162
|
+
const bundledRoot = options.bundledRoot ?? path38.join(defaultBundledRoot(), "agents");
|
|
10163
|
+
const projectDir = path38.join(projectRoot, ".metaproject", "agents");
|
|
9882
10164
|
const byName = new Map;
|
|
9883
10165
|
const errors = [];
|
|
9884
10166
|
loadSource(bundledRoot, "bundled", byName, errors);
|
|
@@ -10671,7 +10953,7 @@ var init_compile = __esm(() => {
|
|
|
10671
10953
|
|
|
10672
10954
|
// src/agents/export.ts
|
|
10673
10955
|
import { readFile as readFile16, readdir as readdir8, lstat as lstat6, realpath as realpath7 } from "fs/promises";
|
|
10674
|
-
import
|
|
10956
|
+
import path39 from "path";
|
|
10675
10957
|
function matrixStateToExportLevel(state) {
|
|
10676
10958
|
return state === "native" || state === "adapter" ? state : "instruction-only";
|
|
10677
10959
|
}
|
|
@@ -10683,7 +10965,7 @@ function proseExportRelativePath(runtime, name) {
|
|
|
10683
10965
|
return `${PROSE_EXPORT_ROOT}/${runtime}/${name}.md`;
|
|
10684
10966
|
}
|
|
10685
10967
|
async function readExistingFileContent(projectRoot, relativePath) {
|
|
10686
|
-
const absolute =
|
|
10968
|
+
const absolute = path39.join(projectRoot, ...relativePath.split("/"));
|
|
10687
10969
|
try {
|
|
10688
10970
|
return await readFile16(absolute, "utf8");
|
|
10689
10971
|
} catch (error) {
|
|
@@ -10696,7 +10978,7 @@ async function checkExportPathSafety(projectRoot, relativePath) {
|
|
|
10696
10978
|
const segments = relativePath.split("/");
|
|
10697
10979
|
let current = projectRoot;
|
|
10698
10980
|
for (const segment of segments) {
|
|
10699
|
-
current =
|
|
10981
|
+
current = path39.join(current, segment);
|
|
10700
10982
|
let stats;
|
|
10701
10983
|
try {
|
|
10702
10984
|
stats = await lstat6(current);
|
|
@@ -10706,12 +10988,12 @@ async function checkExportPathSafety(projectRoot, relativePath) {
|
|
|
10706
10988
|
throw error;
|
|
10707
10989
|
}
|
|
10708
10990
|
if (stats.isSymbolicLink()) {
|
|
10709
|
-
return `refusing to write ${relativePath} \u2014 ${
|
|
10991
|
+
return `refusing to write ${relativePath} \u2014 ${path39.relative(projectRoot, current) || "."} is a symlink; this exporter never writes through a symlink`;
|
|
10710
10992
|
}
|
|
10711
10993
|
}
|
|
10712
|
-
let ancestor =
|
|
10994
|
+
let ancestor = path39.dirname(path39.join(projectRoot, ...segments));
|
|
10713
10995
|
for (let guard = 0;guard < segments.length + 1 && !await pathExists(ancestor); guard += 1) {
|
|
10714
|
-
const parent =
|
|
10996
|
+
const parent = path39.dirname(ancestor);
|
|
10715
10997
|
if (parent === ancestor)
|
|
10716
10998
|
break;
|
|
10717
10999
|
ancestor = parent;
|
|
@@ -10744,7 +11026,7 @@ function decideAction(existing, generated, format, expectedName) {
|
|
|
10744
11026
|
return { action: "update" };
|
|
10745
11027
|
}
|
|
10746
11028
|
async function readClaudeSubagentAliasesConfig(projectRoot) {
|
|
10747
|
-
const file =
|
|
11029
|
+
const file = path39.join(projectRoot, CLAUDE_SUBAGENT_ALIASES_CONFIG_PATH);
|
|
10748
11030
|
if (!await pathExists(file)) {
|
|
10749
11031
|
return { enabled: true };
|
|
10750
11032
|
}
|
|
@@ -10886,7 +11168,7 @@ async function writeAgentExport(projectRoot, plan, opts = {}) {
|
|
|
10886
11168
|
}
|
|
10887
11169
|
async function scanManagedAgentExports(projectRoot, runtime) {
|
|
10888
11170
|
const dirRelative = HOST_AGENTS_DIR[runtime];
|
|
10889
|
-
const dirAbsolute =
|
|
11171
|
+
const dirAbsolute = path39.join(projectRoot, ...dirRelative.split("/"));
|
|
10890
11172
|
let dirStats;
|
|
10891
11173
|
try {
|
|
10892
11174
|
dirStats = await lstat6(dirAbsolute);
|
|
@@ -10907,7 +11189,7 @@ async function scanManagedAgentExports(projectRoot, runtime) {
|
|
|
10907
11189
|
if (!entry.isFile())
|
|
10908
11190
|
continue;
|
|
10909
11191
|
const relativePath = `${dirRelative}/${entry.name}`;
|
|
10910
|
-
const absolute =
|
|
11192
|
+
const absolute = path39.join(projectRoot, ...relativePath.split("/"));
|
|
10911
11193
|
let entryStats;
|
|
10912
11194
|
try {
|
|
10913
11195
|
entryStats = await lstat6(absolute);
|
|
@@ -10922,7 +11204,7 @@ async function scanManagedAgentExports(projectRoot, runtime) {
|
|
|
10922
11204
|
} catch {
|
|
10923
11205
|
continue;
|
|
10924
11206
|
}
|
|
10925
|
-
const stem = entry.name.slice(0, entry.name.length -
|
|
11207
|
+
const stem = entry.name.slice(0, entry.name.length - path39.extname(entry.name).length);
|
|
10926
11208
|
const format = agentSentinelFormatOf(relativePath);
|
|
10927
11209
|
if (!isStructurallyManaged(content, format, stem))
|
|
10928
11210
|
continue;
|
|
@@ -11092,7 +11374,7 @@ var init_surfaces_agents = __esm(() => {
|
|
|
11092
11374
|
|
|
11093
11375
|
// src/rules/export-render.ts
|
|
11094
11376
|
import { readFile as readFile17, readdir as readdir9 } from "fs/promises";
|
|
11095
|
-
import
|
|
11377
|
+
import path40 from "path";
|
|
11096
11378
|
function isUnsafeRulePath(relativePath) {
|
|
11097
11379
|
return UNSAFE_RULE_PATH_PATTERN.test(relativePath) || relativePath.includes("<!--") || relativePath.includes("-->");
|
|
11098
11380
|
}
|
|
@@ -11135,7 +11417,7 @@ function firstHeading(body) {
|
|
|
11135
11417
|
return match ? match[1].trim() : undefined;
|
|
11136
11418
|
}
|
|
11137
11419
|
function titleFromFileName(relativePath) {
|
|
11138
|
-
const base =
|
|
11420
|
+
const base = path40.basename(relativePath).replace(/\.(mdc|md)$/i, "");
|
|
11139
11421
|
return base.split(/[-_]+/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11140
11422
|
}
|
|
11141
11423
|
function parseRuleFile(fileName, content) {
|
|
@@ -11148,7 +11430,7 @@ function parseRuleFile(fileName, content) {
|
|
|
11148
11430
|
return { title: neutralise(collapseSingleLine(title, 120)), description };
|
|
11149
11431
|
}
|
|
11150
11432
|
async function collectCanonicalRules(root) {
|
|
11151
|
-
const rulesDir =
|
|
11433
|
+
const rulesDir = path40.join(root, ".metaproject", "rules");
|
|
11152
11434
|
if (!await pathExists(rulesDir))
|
|
11153
11435
|
return { rules: [], skipped: [] };
|
|
11154
11436
|
const rules = [];
|
|
@@ -11162,14 +11444,14 @@ async function collectCanonicalRules(root) {
|
|
|
11162
11444
|
}
|
|
11163
11445
|
for (const dirent of dirents) {
|
|
11164
11446
|
const childRelative = dirRelative ? `${dirRelative}/${dirent.name}` : dirent.name;
|
|
11165
|
-
const childAbsolute =
|
|
11447
|
+
const childAbsolute = path40.join(dirAbsolute, dirent.name);
|
|
11166
11448
|
if (dirent.isDirectory()) {
|
|
11167
11449
|
await walk(childAbsolute, childRelative);
|
|
11168
11450
|
continue;
|
|
11169
11451
|
}
|
|
11170
11452
|
if (!dirent.isFile())
|
|
11171
11453
|
continue;
|
|
11172
|
-
const ext =
|
|
11454
|
+
const ext = path40.extname(dirent.name).toLowerCase();
|
|
11173
11455
|
if (!RULE_EXTENSIONS.has(ext))
|
|
11174
11456
|
continue;
|
|
11175
11457
|
if (dirent.name.toLowerCase() === README_BASENAME)
|
|
@@ -11220,7 +11502,7 @@ var init_export_render = __esm(() => {
|
|
|
11220
11502
|
});
|
|
11221
11503
|
|
|
11222
11504
|
// src/integrations/surfaces-rules.ts
|
|
11223
|
-
import
|
|
11505
|
+
import path41 from "path";
|
|
11224
11506
|
async function rulesSpec(root) {
|
|
11225
11507
|
const { rules, skipped } = await collectCanonicalRules(root);
|
|
11226
11508
|
const body = renderRulesBlockBody(rules);
|
|
@@ -11265,7 +11547,7 @@ function rulesExportSurface(params) {
|
|
|
11265
11547
|
...riskNotes !== undefined ? { riskNotes } : {},
|
|
11266
11548
|
sourceDocs,
|
|
11267
11549
|
optIn: true,
|
|
11268
|
-
settingsFile: (root) =>
|
|
11550
|
+
settingsFile: (root) => path41.join(root, ...relativePath.split("/")),
|
|
11269
11551
|
relativePath,
|
|
11270
11552
|
label: relativePath,
|
|
11271
11553
|
slots: [],
|
|
@@ -11348,7 +11630,6 @@ var init_surfaces_rules = __esm(() => {
|
|
|
11348
11630
|
});
|
|
11349
11631
|
|
|
11350
11632
|
// src/integrations/surfaces-learning.ts
|
|
11351
|
-
import path41 from "path";
|
|
11352
11633
|
function learningObserverCommand() {
|
|
11353
11634
|
return "keryx learn observe --hook claude";
|
|
11354
11635
|
}
|
|
@@ -11377,6 +11658,7 @@ function learningObserverValidate(settings) {
|
|
|
11377
11658
|
var LEARNING_OBSERVER_SENTINEL = "learning-observer-hooks", LEARNING_OBSERVER_EVENTS, TOOL_EVENTS, TOOL_MATCHER = "*", HOOK_TIMEOUT_SECONDS = 5, LEARNING_OBSERVER_CLAUDE;
|
|
11378
11659
|
var init_surfaces_learning = __esm(() => {
|
|
11379
11660
|
init_settings_json();
|
|
11661
|
+
init_claude_settings();
|
|
11380
11662
|
LEARNING_OBSERVER_EVENTS = [
|
|
11381
11663
|
"PreToolUse",
|
|
11382
11664
|
"PostToolUse",
|
|
@@ -11403,8 +11685,7 @@ var init_surfaces_learning = __esm(() => {
|
|
|
11403
11685
|
"docs/requirements/keryx-agent-platform-expansion/workstreams/W3-self-learning.md",
|
|
11404
11686
|
"src/learning/observe.ts"
|
|
11405
11687
|
],
|
|
11406
|
-
|
|
11407
|
-
relativePath: ".claude/settings.json",
|
|
11688
|
+
...claudeSettingsLocation(),
|
|
11408
11689
|
slots: [
|
|
11409
11690
|
{ key: "hooks", type: "object", access: "owns" },
|
|
11410
11691
|
{ key: "_keryxManaged", type: "array", access: "owns" },
|
|
@@ -11425,7 +11706,7 @@ var init_surfaces_learning = __esm(() => {
|
|
|
11425
11706
|
return next;
|
|
11426
11707
|
},
|
|
11427
11708
|
validate: learningObserverValidate,
|
|
11428
|
-
label:
|
|
11709
|
+
label: `${CLAUDE_LOCAL_SETTINGS_PATH} (learning observer)`,
|
|
11429
11710
|
groupShape: "nested",
|
|
11430
11711
|
groupKey: "PreToolUse",
|
|
11431
11712
|
groupContainer: "hooks"
|
|
@@ -11497,6 +11778,9 @@ function getHarnessAdapter(id) {
|
|
|
11497
11778
|
function surfacesOf(adapter, query = {}) {
|
|
11498
11779
|
return adapter.surfaces.filter((s) => (query.flag === undefined || s.flag === query.flag) && (query.subsystem === undefined || s.subsystem === query.subsystem));
|
|
11499
11780
|
}
|
|
11781
|
+
function surfacePaths(surface) {
|
|
11782
|
+
return surface.relativePathCandidates ?? (surface.relativePath ? [surface.relativePath] : []);
|
|
11783
|
+
}
|
|
11500
11784
|
function assertRegistryCoherent(adapters = HARNESS_ADAPTERS) {
|
|
11501
11785
|
for (const adapter of adapters) {
|
|
11502
11786
|
const seenIds = new Set;
|
|
@@ -11510,34 +11794,34 @@ function assertRegistryCoherent(adapters = HARNESS_ADAPTERS) {
|
|
|
11510
11794
|
const surfaceOwnerByFile = new Map;
|
|
11511
11795
|
for (const adapter of adapters) {
|
|
11512
11796
|
for (const surface of adapter.surfaces) {
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11797
|
+
for (const relativePath of surfacePaths(surface)) {
|
|
11798
|
+
let owningAdapterById = surfaceOwnerByFile.get(relativePath);
|
|
11799
|
+
if (!owningAdapterById) {
|
|
11800
|
+
owningAdapterById = new Map;
|
|
11801
|
+
surfaceOwnerByFile.set(relativePath, owningAdapterById);
|
|
11802
|
+
}
|
|
11803
|
+
const existingOwner = owningAdapterById.get(surface.id);
|
|
11804
|
+
if (existingOwner !== undefined && existingOwner !== adapter.id) {
|
|
11805
|
+
throw new Error(`integrations registry: "${relativePath}" has surface id "${surface.id}" registered by both ` + `"${existingOwner}" and "${adapter.id}" \u2014 a SettingsFileOwner keys its surfaces by id per file, so this ` + `would clobber one adapter's surface with the other's`);
|
|
11806
|
+
}
|
|
11807
|
+
owningAdapterById.set(surface.id, adapter.id);
|
|
11523
11808
|
}
|
|
11524
|
-
owningAdapterById.set(surface.id, adapter.id);
|
|
11525
11809
|
}
|
|
11526
11810
|
}
|
|
11527
11811
|
const slotsByFile = new Map;
|
|
11528
11812
|
for (const adapter of adapters) {
|
|
11529
11813
|
for (const surface of adapter.surfaces) {
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11814
|
+
for (const relativePath of surfacePaths(surface)) {
|
|
11815
|
+
let byKey = slotsByFile.get(relativePath);
|
|
11816
|
+
if (!byKey) {
|
|
11817
|
+
byKey = new Map;
|
|
11818
|
+
slotsByFile.set(relativePath, byKey);
|
|
11819
|
+
}
|
|
11820
|
+
for (const slot of surface.slots) {
|
|
11821
|
+
const records = byKey.get(slot.key) ?? [];
|
|
11822
|
+
records.push({ owner: `${adapter.id}/${surface.id}`, type: slot.type, access: slot.access });
|
|
11823
|
+
byKey.set(slot.key, records);
|
|
11824
|
+
}
|
|
11541
11825
|
}
|
|
11542
11826
|
}
|
|
11543
11827
|
}
|
|
@@ -11736,11 +12020,13 @@ var init_registry = __esm(() => {
|
|
|
11736
12020
|
const byPath = new Map;
|
|
11737
12021
|
for (const adapter of HARNESS_ADAPTERS) {
|
|
11738
12022
|
for (const surface of adapter.surfaces) {
|
|
11739
|
-
if (!surface.
|
|
12023
|
+
if (!surface.merge || !surface.strip)
|
|
11740
12024
|
continue;
|
|
11741
|
-
const
|
|
11742
|
-
|
|
11743
|
-
|
|
12025
|
+
for (const relativePath of surfacePaths(surface)) {
|
|
12026
|
+
const list = byPath.get(relativePath) ?? [];
|
|
12027
|
+
list.push(surface);
|
|
12028
|
+
byPath.set(relativePath, list);
|
|
12029
|
+
}
|
|
11744
12030
|
}
|
|
11745
12031
|
}
|
|
11746
12032
|
return [...byPath.entries()].map(([relativePath, surfaces]) => createSettingsFileOwner(relativePath, surfaces));
|
|
@@ -11786,7 +12072,68 @@ var init_rules_export = __esm(() => {
|
|
|
11786
12072
|
});
|
|
11787
12073
|
|
|
11788
12074
|
// src/integrations/jev-edit-guard-surface.ts
|
|
12075
|
+
function editGuardHookCommand() {
|
|
12076
|
+
return "keryx review jev-edit-guard --hook claude";
|
|
12077
|
+
}
|
|
12078
|
+
function editGuardPostToolUseGroup() {
|
|
12079
|
+
return {
|
|
12080
|
+
matcher: EDIT_GUARD_HOOK_MATCHER,
|
|
12081
|
+
hooks: [{ type: "command", command: editGuardHookCommand() }],
|
|
12082
|
+
[MANAGED_KEY]: EDIT_GUARD_HOOK_SENTINEL
|
|
12083
|
+
};
|
|
12084
|
+
}
|
|
12085
|
+
function validateEditGuardSurface(settings) {
|
|
12086
|
+
const present = managedGroups(settings, {
|
|
12087
|
+
sentinel: EDIT_GUARD_HOOK_SENTINEL,
|
|
12088
|
+
container: "hooks",
|
|
12089
|
+
key: "PostToolUse",
|
|
12090
|
+
shape: "nested",
|
|
12091
|
+
commandMatches: (c) => c === editGuardHookCommand()
|
|
12092
|
+
});
|
|
12093
|
+
if (present.length === 0)
|
|
12094
|
+
return [`jev-edit-guard: missing PostToolUse(${EDIT_GUARD_HOOK_MATCHER}) guard`];
|
|
12095
|
+
const stale = present.some((g) => typeof g.matcher !== "string" || g.matcher !== EDIT_GUARD_HOOK_MATCHER);
|
|
12096
|
+
if (stale) {
|
|
12097
|
+
return [
|
|
12098
|
+
`jev-edit-guard: PostToolUse guard does not match ${EDIT_GUARD_HOOK_MATCHER}; an edit tool it should cover bypasses it. ` + "Re-run `keryx review jev-edit-guard install`."
|
|
12099
|
+
];
|
|
12100
|
+
}
|
|
12101
|
+
return [];
|
|
12102
|
+
}
|
|
12103
|
+
var EDIT_GUARD_HOOK_SENTINEL = "jev-edit-guard-hooks", EDIT_GUARD_HOOK_MATCHER = "Edit|Write|MultiEdit", JEV_EDIT_GUARD_SURFACE;
|
|
11789
12104
|
var init_jev_edit_guard_surface = __esm(() => {
|
|
12105
|
+
init_claude_settings();
|
|
12106
|
+
init_settings_json();
|
|
12107
|
+
JEV_EDIT_GUARD_SURFACE = {
|
|
12108
|
+
id: "jev-edit-guard",
|
|
12109
|
+
flag: "post-tool",
|
|
12110
|
+
subsystem: "jev-edit-guard",
|
|
12111
|
+
sentinel: EDIT_GUARD_HOOK_SENTINEL,
|
|
12112
|
+
confidence: "verified",
|
|
12113
|
+
sourceDocs: ["src/commands/review-jev-edit-guard.ts"],
|
|
12114
|
+
...claudeSettingsLocation(),
|
|
12115
|
+
slots: [
|
|
12116
|
+
{ key: "hooks", type: "object", access: "owns" },
|
|
12117
|
+
{ key: "_keryxManaged", type: "array", access: "owns" },
|
|
12118
|
+
{ key: "unmigratedHooks", type: "array", access: "owns" }
|
|
12119
|
+
],
|
|
12120
|
+
merge: (s) => mergeIntoHookArray(s, "PostToolUse", editGuardPostToolUseGroup(), EDIT_GUARD_HOOK_SENTINEL),
|
|
12121
|
+
strip: (s) => stripFromHookArray(s, "PostToolUse", EDIT_GUARD_HOOK_SENTINEL),
|
|
12122
|
+
validate: validateEditGuardSurface
|
|
12123
|
+
};
|
|
12124
|
+
});
|
|
12125
|
+
|
|
12126
|
+
// src/integrations/claude-settings-migration.ts
|
|
12127
|
+
var init_claude_settings_migration = __esm(() => {
|
|
12128
|
+
init_contained_write();
|
|
12129
|
+
init_git_worktrees();
|
|
12130
|
+
init_symlink_safety();
|
|
12131
|
+
init_entrypoint_migration();
|
|
12132
|
+
init_entrypoint_targets();
|
|
12133
|
+
init_claude_settings();
|
|
12134
|
+
init_install_state();
|
|
12135
|
+
init_jev_edit_guard_surface();
|
|
12136
|
+
init_registry();
|
|
11790
12137
|
init_settings_json();
|
|
11791
12138
|
});
|
|
11792
12139
|
|
|
@@ -11799,6 +12146,8 @@ var init_service = __esm(() => {
|
|
|
11799
12146
|
init_surfaces();
|
|
11800
12147
|
init_rules_export();
|
|
11801
12148
|
init_jev_edit_guard_surface();
|
|
12149
|
+
init_claude_settings();
|
|
12150
|
+
init_claude_settings_migration();
|
|
11802
12151
|
init_surfaces_w5b();
|
|
11803
12152
|
init_surfaces_learning();
|
|
11804
12153
|
init_markdown_block();
|
|
@@ -11981,7 +12330,9 @@ var init_surfaces2 = __esm(() => {
|
|
|
11981
12330
|
init_integrations();
|
|
11982
12331
|
INSTRUCTION_FILENAMES = [
|
|
11983
12332
|
"AGENTS.md",
|
|
12333
|
+
"AGENTS.override.md",
|
|
11984
12334
|
"CLAUDE.md",
|
|
12335
|
+
"CLAUDE.local.md",
|
|
11985
12336
|
"GEMINI.md",
|
|
11986
12337
|
".github/copilot-instructions.md",
|
|
11987
12338
|
".cursor/rules/keryx-rules.mdc",
|
|
@@ -12657,7 +13008,7 @@ function isReportedSpeechContext(content, start) {
|
|
|
12657
13008
|
return REPORTED_SPEECH_RE.test(before);
|
|
12658
13009
|
}
|
|
12659
13010
|
function isDowngradedInjectionContext(content, origStart, origEnd) {
|
|
12660
|
-
if (isInsideFence(
|
|
13011
|
+
if (isInsideFence(computeFencedRanges3(content), origStart))
|
|
12661
13012
|
return true;
|
|
12662
13013
|
if (isQuotedExample(content, origStart, origEnd))
|
|
12663
13014
|
return true;
|
|
@@ -13032,7 +13383,7 @@ function checkHookRemoteExec(relativePath, hookCommand, pointer) {
|
|
|
13032
13383
|
}
|
|
13033
13384
|
];
|
|
13034
13385
|
}
|
|
13035
|
-
function
|
|
13386
|
+
function computeFencedRanges3(content) {
|
|
13036
13387
|
const ranges = [];
|
|
13037
13388
|
let offset = 0;
|
|
13038
13389
|
let fenceStart;
|
|
@@ -13063,7 +13414,7 @@ function isInsideFence(ranges, index) {
|
|
|
13063
13414
|
function checkRemoteExecInText(surface, relativePath, content) {
|
|
13064
13415
|
const matches = matchAllRemoteExecShapes(content);
|
|
13065
13416
|
if (matches.length > 0) {
|
|
13066
|
-
const fencedRanges =
|
|
13417
|
+
const fencedRanges = computeFencedRanges3(content);
|
|
13067
13418
|
const unfenced = matches.find((m) => !isInsideFence(fencedRanges, m.index));
|
|
13068
13419
|
const reported = unfenced ?? matches[0];
|
|
13069
13420
|
const fenced = unfenced === undefined;
|
|
@@ -17437,7 +17788,7 @@ function describeJsonValue(value) {
|
|
|
17437
17788
|
}
|
|
17438
17789
|
return `a ${typeof value}`;
|
|
17439
17790
|
}
|
|
17440
|
-
function
|
|
17791
|
+
function isRecord3(value) {
|
|
17441
17792
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17442
17793
|
}
|
|
17443
17794
|
function stringOr(value, fallback) {
|
|
@@ -17447,7 +17798,7 @@ function nullableString(value) {
|
|
|
17447
17798
|
return typeof value === "string" ? value : null;
|
|
17448
17799
|
}
|
|
17449
17800
|
function parseEntry2(value) {
|
|
17450
|
-
if (!
|
|
17801
|
+
if (!isRecord3(value)) {
|
|
17451
17802
|
return { ok: false };
|
|
17452
17803
|
}
|
|
17453
17804
|
const kind = value["kind"];
|
|
@@ -17469,7 +17820,7 @@ function parseEntry2(value) {
|
|
|
17469
17820
|
}
|
|
17470
17821
|
function parseTombstone(value) {
|
|
17471
17822
|
const base = parseEntry2(value);
|
|
17472
|
-
if (!base.ok || !
|
|
17823
|
+
if (!base.ok || !isRecord3(value)) {
|
|
17473
17824
|
return { ok: false };
|
|
17474
17825
|
}
|
|
17475
17826
|
const removedAt = value["removedAt"];
|
|
@@ -17483,7 +17834,7 @@ function parseTombstone(value) {
|
|
|
17483
17834
|
}
|
|
17484
17835
|
function parseLifted(value) {
|
|
17485
17836
|
const base = parseTombstone(value);
|
|
17486
|
-
if (!base.ok || !
|
|
17837
|
+
if (!base.ok || !isRecord3(value)) {
|
|
17487
17838
|
return { ok: false };
|
|
17488
17839
|
}
|
|
17489
17840
|
const restoredAt = value["restoredAt"];
|
|
@@ -21621,6 +21972,201 @@ var init_wiki_layer = __esm(() => {
|
|
|
21621
21972
|
init_section_index();
|
|
21622
21973
|
});
|
|
21623
21974
|
|
|
21975
|
+
// src/gdskills/contracts.ts
|
|
21976
|
+
import { readFile as readFile46 } from "fs/promises";
|
|
21977
|
+
import { existsSync as existsSync14 } from "fs";
|
|
21978
|
+
import path98 from "path";
|
|
21979
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21980
|
+
async function loadSchema(name) {
|
|
21981
|
+
const contract = CONTRACTS.find((entry) => entry.name === name);
|
|
21982
|
+
if (!contract) {
|
|
21983
|
+
throw new Error(`Unknown contract schema: ${name}`);
|
|
21984
|
+
}
|
|
21985
|
+
const raw = await readFile46(contractPath(contract), "utf8");
|
|
21986
|
+
return JSON.parse(raw);
|
|
21987
|
+
}
|
|
21988
|
+
function contractPath(contract) {
|
|
21989
|
+
const here = path98.dirname(fileURLToPath2(import.meta.url));
|
|
21990
|
+
if (contract.sourcePath) {
|
|
21991
|
+
for (const root of [path98.join(here, "..", ".."), path98.join(here, ".."), here]) {
|
|
21992
|
+
const candidate = path98.resolve(root, contract.sourcePath);
|
|
21993
|
+
if (existsSync14(candidate)) {
|
|
21994
|
+
return candidate;
|
|
21995
|
+
}
|
|
21996
|
+
}
|
|
21997
|
+
}
|
|
21998
|
+
const directPath = fileURLToPath2(new URL(`./contracts/${contract.fileName}`, import.meta.url));
|
|
21999
|
+
if (existsSync14(directPath)) {
|
|
22000
|
+
return directPath;
|
|
22001
|
+
}
|
|
22002
|
+
const packagedSourcePath = path98.join(here, "..", "src", "gdskills", "contracts", contract.fileName);
|
|
22003
|
+
if (existsSync14(packagedSourcePath)) {
|
|
22004
|
+
return packagedSourcePath;
|
|
22005
|
+
}
|
|
22006
|
+
return directPath;
|
|
22007
|
+
}
|
|
22008
|
+
var CONTRACTS, SCHEMA_ROOTS;
|
|
22009
|
+
var init_contracts = __esm(() => {
|
|
22010
|
+
CONTRACTS = [
|
|
22011
|
+
{
|
|
22012
|
+
name: "agent-event",
|
|
22013
|
+
fileName: "agent-event.schema.json",
|
|
22014
|
+
description: "Append-only lifecycle event emitted by orchestrators and subagents.",
|
|
22015
|
+
enforcement: {
|
|
22016
|
+
kind: "none",
|
|
22017
|
+
reason: "Events are appended by whichever agent is running, in that agent's own runtime. No keryx process sits between the emitter and the log, so there is no point at which a malformed event could be refused rather than written."
|
|
22018
|
+
}
|
|
22019
|
+
},
|
|
22020
|
+
{
|
|
22021
|
+
name: "flow-orchestrator-input",
|
|
22022
|
+
fileName: "flow-orchestrator-input-contract.schema.json",
|
|
22023
|
+
description: "Dispatch payload handed to flow-orchestrator (request + base branch + completion outcome + constraints).",
|
|
22024
|
+
sourcePath: "src/gdskills/bundled/skills/orchestration/flow-orchestrator/input-contract.schema.json",
|
|
22025
|
+
enforcement: {
|
|
22026
|
+
kind: "none",
|
|
22027
|
+
reason: "This payload is handed from one AGENT to another. In a session driven by a host agent's own dispatch tool, no keryx process is on that path, so nothing can refuse a malformed dispatch. Same structural position as reviewer-input, recorded for the same reason. Registering it made `keryx skills contracts validate --schema flow-orchestrator-input` possible, which is a validator an agent must remember to run, not an enforcement."
|
|
22028
|
+
}
|
|
22029
|
+
},
|
|
22030
|
+
{
|
|
22031
|
+
name: "review-pr-feedback-input",
|
|
22032
|
+
fileName: "review-pr-feedback-input-contract.schema.json",
|
|
22033
|
+
description: "Request handed to review-pr-feedback (PR reference, --fix, and the operator confirmation --fix requires).",
|
|
22034
|
+
sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/input-contract.schema.json",
|
|
22035
|
+
enforcement: {
|
|
22036
|
+
kind: "none",
|
|
22037
|
+
reason: "The request is what an operator or a host agent hands the skill before any keryx command runs, so keryx is not yet in the path when the payload would have to be refused. This is the one where that hurts most: `--fix` merges third-party review comments into somebody else's pull request, and `operator_confirmed` is the fence. The conditional is expressed in the schema and `keryx skills contracts validate --schema review-pr-feedback-input` will apply it \u2014 but only when something invokes it."
|
|
22038
|
+
}
|
|
22039
|
+
},
|
|
22040
|
+
{
|
|
22041
|
+
name: "review-pr-feedback-output",
|
|
22042
|
+
fileName: "review-pr-feedback-output-contract.schema.json",
|
|
22043
|
+
description: "Result review-pr-feedback returns: verdict counts, the injection-screen record, and what the fix run merged.",
|
|
22044
|
+
sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/output-contract.schema.json",
|
|
22045
|
+
enforcement: {
|
|
22046
|
+
kind: "opt-in",
|
|
22047
|
+
module: "src/commands/review.ts",
|
|
22048
|
+
switchedOnBy: "keryx review comments reply --result <file>",
|
|
22049
|
+
refuses: "A result that contradicts itself, before the pass is built and before any network call: an analyze-mode run reporting a branch and a merge, or a record saying the injection screen never ran while claiming it excluded comments."
|
|
22050
|
+
}
|
|
22051
|
+
},
|
|
22052
|
+
{
|
|
22053
|
+
name: "job-orchestrator-state",
|
|
22054
|
+
fileName: "job-orchestrator-state.schema.json",
|
|
22055
|
+
description: "Persisted job package state (.metaproject/jobs/<name>/state.json), written by `keryx job`.",
|
|
22056
|
+
sourcePath: "src/gdskills/bundled/skills/orchestration/job-orchestrator/state.schema.json",
|
|
22057
|
+
enforcement: {
|
|
22058
|
+
kind: "production",
|
|
22059
|
+
module: "src/job/store.ts",
|
|
22060
|
+
refuses: "`writeJob` refuses to write a state file that does not satisfy the schema, so an invalid job package cannot reach disk."
|
|
22061
|
+
}
|
|
22062
|
+
},
|
|
22063
|
+
{
|
|
22064
|
+
name: "orchestrator-state",
|
|
22065
|
+
fileName: "orchestrator-state.schema.json",
|
|
22066
|
+
description: "Persisted resumable orchestrator state.",
|
|
22067
|
+
enforcement: {
|
|
22068
|
+
kind: "none",
|
|
22069
|
+
reason: "No keryx command reads or writes this file. It describes state an orchestrator agent persists for itself, in its own runtime, so there is no keryx-owned write path to refuse at. Contrast job-orchestrator-state, which looks similar and IS enforced for exactly one reason: `keryx job` writes that one."
|
|
22070
|
+
}
|
|
22071
|
+
},
|
|
22072
|
+
{
|
|
22073
|
+
name: "review-finding",
|
|
22074
|
+
fileName: "review-finding.schema.json",
|
|
22075
|
+
description: "Normalized reviewer finding consumed by review-orchestrator and learning flows.",
|
|
22076
|
+
enforcement: {
|
|
22077
|
+
kind: "production",
|
|
22078
|
+
module: "src/review/managed.ts",
|
|
22079
|
+
refuses: "`createManagedReviewPackage` refuses to record findings that do not satisfy the schema, and the disposition sub-schema is applied the same way, so a review round cannot be written with a malformed finding or an unreadable outcome."
|
|
22080
|
+
}
|
|
22081
|
+
},
|
|
22082
|
+
{
|
|
22083
|
+
name: "subagent-dispatch",
|
|
22084
|
+
fileName: "subagent-dispatch.schema.json",
|
|
22085
|
+
description: "Orchestrator-to-subagent dispatch payload.",
|
|
22086
|
+
enforcement: {
|
|
22087
|
+
kind: "none",
|
|
22088
|
+
reason: "The dispatch is built and consumed inside the harness without the canonical schema being loaded: `subagent-dispatch` appears only as a label on the extension metadata. Its sibling `subagent-result` IS validated, because the harness parses a child's reply back and has to decide whether it is well-formed; nothing performs the equivalent act on the way out."
|
|
22089
|
+
}
|
|
22090
|
+
},
|
|
22091
|
+
{
|
|
22092
|
+
name: "subagent-result",
|
|
22093
|
+
fileName: "subagent-result.schema.json",
|
|
22094
|
+
description: "Subagent-to-orchestrator result payload.",
|
|
22095
|
+
enforcement: {
|
|
22096
|
+
kind: "production",
|
|
22097
|
+
module: "src/harness/external/runtime.ts",
|
|
22098
|
+
refuses: "A child's reply that does not parse as a well-formed result is rejected when the harness reads it back, so a malformed result cannot be persisted as if the child had succeeded."
|
|
22099
|
+
}
|
|
22100
|
+
},
|
|
22101
|
+
{
|
|
22102
|
+
name: "task-implementer-input",
|
|
22103
|
+
fileName: "task-implementer-input-contract.schema.json",
|
|
22104
|
+
description: "Task request handed to task-implementer (task + workspace + automation), validated before dispatch.",
|
|
22105
|
+
sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json",
|
|
22106
|
+
enforcement: {
|
|
22107
|
+
kind: "none",
|
|
22108
|
+
reason: "Dispatched agent-to-agent, like flow-orchestrator-input. The skill's Phase 1.4 lists five `ASSERT \u2026 \u2192 ABORT(\u2026)` refusals; registering the contract is what lets `keryx skills contracts validate` perform them, and nothing forces that call. The comment beside this registration has said so since it was written."
|
|
22109
|
+
}
|
|
22110
|
+
},
|
|
22111
|
+
{
|
|
22112
|
+
name: "task-implementer-output",
|
|
22113
|
+
fileName: "task-implementer-output-contract.schema.json",
|
|
22114
|
+
description: "JSON result task-implementer writes in Phase 6.1 before it emits its STATUS line.",
|
|
22115
|
+
sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json",
|
|
22116
|
+
enforcement: {
|
|
22117
|
+
kind: "none",
|
|
22118
|
+
reason: "The skill writes this file itself, in its own runtime, and no keryx command reads it back. Contrast subagent-result, which is the same shape of thing and IS enforced for one reason: the harness reads that one back and must decide whether it is well-formed."
|
|
22119
|
+
}
|
|
22120
|
+
}
|
|
22121
|
+
];
|
|
22122
|
+
SCHEMA_ROOTS = [
|
|
22123
|
+
fileURLToPath2(new URL("./contracts/", import.meta.url)),
|
|
22124
|
+
fileURLToPath2(new URL("./bundled/skills/review/review-orchestrator/", import.meta.url))
|
|
22125
|
+
];
|
|
22126
|
+
});
|
|
22127
|
+
|
|
22128
|
+
// src/lib/config-dir.ts
|
|
22129
|
+
import {
|
|
22130
|
+
appendFileSync,
|
|
22131
|
+
chmodSync,
|
|
22132
|
+
mkdirSync,
|
|
22133
|
+
readFileSync as readFileSync6,
|
|
22134
|
+
renameSync,
|
|
22135
|
+
statSync as statSync2,
|
|
22136
|
+
unlinkSync,
|
|
22137
|
+
writeFileSync
|
|
22138
|
+
} from "fs";
|
|
22139
|
+
function readBoundedFile(file, maxBytes) {
|
|
22140
|
+
let stats;
|
|
22141
|
+
try {
|
|
22142
|
+
stats = statSync2(file);
|
|
22143
|
+
} catch {
|
|
22144
|
+
return { ok: false, reason: "absent" };
|
|
22145
|
+
}
|
|
22146
|
+
if (!stats.isFile()) {
|
|
22147
|
+
return { ok: false, reason: "not-regular" };
|
|
22148
|
+
}
|
|
22149
|
+
if (stats.size > maxBytes) {
|
|
22150
|
+
return { ok: false, reason: "too-large" };
|
|
22151
|
+
}
|
|
22152
|
+
try {
|
|
22153
|
+
return { ok: true, text: readFileSync6(file, "utf8") };
|
|
22154
|
+
} catch {
|
|
22155
|
+
return { ok: false, reason: "unreadable" };
|
|
22156
|
+
}
|
|
22157
|
+
}
|
|
22158
|
+
function readConfigFile(file) {
|
|
22159
|
+
return readBoundedFile(file, MAX_CONFIG_FILE_BYTES);
|
|
22160
|
+
}
|
|
22161
|
+
function readTranscriptFile(file) {
|
|
22162
|
+
return readBoundedFile(file, MAX_TRANSCRIPT_FILE_BYTES);
|
|
22163
|
+
}
|
|
22164
|
+
var MAX_CONFIG_FILE_BYTES = 1e6, MAX_CONTENT_FILE_BYTES, MAX_TRANSCRIPT_FILE_BYTES;
|
|
22165
|
+
var init_config_dir = __esm(() => {
|
|
22166
|
+
MAX_CONTENT_FILE_BYTES = 64 * 1024 * 1024;
|
|
22167
|
+
MAX_TRANSCRIPT_FILE_BYTES = MAX_CONTENT_FILE_BYTES;
|
|
22168
|
+
});
|
|
22169
|
+
|
|
21624
22170
|
// src/flow/service.ts
|
|
21625
22171
|
init_fs();
|
|
21626
22172
|
init_validator();
|
|
@@ -26317,7 +26863,7 @@ async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
|
26317
26863
|
const options = parsed.compilerOptions ?? {};
|
|
26318
26864
|
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot, path71.resolve(configDir, options.baseUrl)) : null;
|
|
26319
26865
|
const baseUrl = ownBaseUrl ?? inherited?.baseUrl ?? null;
|
|
26320
|
-
const ownPaths =
|
|
26866
|
+
const ownPaths = isRecord4(options.paths) ? Object.entries(options.paths).filter((entry) => Array.isArray(entry[1])).map(([pattern, targets]) => ({
|
|
26321
26867
|
pattern,
|
|
26322
26868
|
targets: targets.filter((target) => typeof target === "string")
|
|
26323
26869
|
})) : null;
|
|
@@ -26413,7 +26959,7 @@ function stripJsonComments(source) {
|
|
|
26413
26959
|
}
|
|
26414
26960
|
return out;
|
|
26415
26961
|
}
|
|
26416
|
-
function
|
|
26962
|
+
function isRecord4(value) {
|
|
26417
26963
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26418
26964
|
}
|
|
26419
26965
|
async function writeGraph(projectRoot, graph) {
|
|
@@ -31725,161 +32271,10 @@ function defaultPlan(intent) {
|
|
|
31725
32271
|
|
|
31726
32272
|
// src/job/store.ts
|
|
31727
32273
|
init_validator();
|
|
32274
|
+
init_contracts();
|
|
32275
|
+
init_fs();
|
|
31728
32276
|
import { appendFile as appendFile5, mkdir as mkdir27, readFile as readFile47, readdir as readdir16 } from "fs/promises";
|
|
31729
32277
|
import path99 from "path";
|
|
31730
|
-
|
|
31731
|
-
// src/gdskills/contracts.ts
|
|
31732
|
-
import { readFile as readFile46 } from "fs/promises";
|
|
31733
|
-
import { existsSync as existsSync14 } from "fs";
|
|
31734
|
-
import path98 from "path";
|
|
31735
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
31736
|
-
var CONTRACTS = [
|
|
31737
|
-
{
|
|
31738
|
-
name: "agent-event",
|
|
31739
|
-
fileName: "agent-event.schema.json",
|
|
31740
|
-
description: "Append-only lifecycle event emitted by orchestrators and subagents.",
|
|
31741
|
-
enforcement: {
|
|
31742
|
-
kind: "none",
|
|
31743
|
-
reason: "Events are appended by whichever agent is running, in that agent's own runtime. No keryx process sits between the emitter and the log, so there is no point at which a malformed event could be refused rather than written."
|
|
31744
|
-
}
|
|
31745
|
-
},
|
|
31746
|
-
{
|
|
31747
|
-
name: "flow-orchestrator-input",
|
|
31748
|
-
fileName: "flow-orchestrator-input-contract.schema.json",
|
|
31749
|
-
description: "Dispatch payload handed to flow-orchestrator (request + base branch + completion outcome + constraints).",
|
|
31750
|
-
sourcePath: "src/gdskills/bundled/skills/orchestration/flow-orchestrator/input-contract.schema.json",
|
|
31751
|
-
enforcement: {
|
|
31752
|
-
kind: "none",
|
|
31753
|
-
reason: "This payload is handed from one AGENT to another. In a session driven by a host agent's own dispatch tool, no keryx process is on that path, so nothing can refuse a malformed dispatch. Same structural position as reviewer-input, recorded for the same reason. Registering it made `keryx skills contracts validate --schema flow-orchestrator-input` possible, which is a validator an agent must remember to run, not an enforcement."
|
|
31754
|
-
}
|
|
31755
|
-
},
|
|
31756
|
-
{
|
|
31757
|
-
name: "review-pr-feedback-input",
|
|
31758
|
-
fileName: "review-pr-feedback-input-contract.schema.json",
|
|
31759
|
-
description: "Request handed to review-pr-feedback (PR reference, --fix, and the operator confirmation --fix requires).",
|
|
31760
|
-
sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/input-contract.schema.json",
|
|
31761
|
-
enforcement: {
|
|
31762
|
-
kind: "none",
|
|
31763
|
-
reason: "The request is what an operator or a host agent hands the skill before any keryx command runs, so keryx is not yet in the path when the payload would have to be refused. This is the one where that hurts most: `--fix` merges third-party review comments into somebody else's pull request, and `operator_confirmed` is the fence. The conditional is expressed in the schema and `keryx skills contracts validate --schema review-pr-feedback-input` will apply it \u2014 but only when something invokes it."
|
|
31764
|
-
}
|
|
31765
|
-
},
|
|
31766
|
-
{
|
|
31767
|
-
name: "review-pr-feedback-output",
|
|
31768
|
-
fileName: "review-pr-feedback-output-contract.schema.json",
|
|
31769
|
-
description: "Result review-pr-feedback returns: verdict counts, the injection-screen record, and what the fix run merged.",
|
|
31770
|
-
sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/output-contract.schema.json",
|
|
31771
|
-
enforcement: {
|
|
31772
|
-
kind: "opt-in",
|
|
31773
|
-
module: "src/commands/review.ts",
|
|
31774
|
-
switchedOnBy: "keryx review comments reply --result <file>",
|
|
31775
|
-
refuses: "A result that contradicts itself, before the pass is built and before any network call: an analyze-mode run reporting a branch and a merge, or a record saying the injection screen never ran while claiming it excluded comments."
|
|
31776
|
-
}
|
|
31777
|
-
},
|
|
31778
|
-
{
|
|
31779
|
-
name: "job-orchestrator-state",
|
|
31780
|
-
fileName: "job-orchestrator-state.schema.json",
|
|
31781
|
-
description: "Persisted job package state (.metaproject/jobs/<name>/state.json), written by `keryx job`.",
|
|
31782
|
-
sourcePath: "src/gdskills/bundled/skills/orchestration/job-orchestrator/state.schema.json",
|
|
31783
|
-
enforcement: {
|
|
31784
|
-
kind: "production",
|
|
31785
|
-
module: "src/job/store.ts",
|
|
31786
|
-
refuses: "`writeJob` refuses to write a state file that does not satisfy the schema, so an invalid job package cannot reach disk."
|
|
31787
|
-
}
|
|
31788
|
-
},
|
|
31789
|
-
{
|
|
31790
|
-
name: "orchestrator-state",
|
|
31791
|
-
fileName: "orchestrator-state.schema.json",
|
|
31792
|
-
description: "Persisted resumable orchestrator state.",
|
|
31793
|
-
enforcement: {
|
|
31794
|
-
kind: "none",
|
|
31795
|
-
reason: "No keryx command reads or writes this file. It describes state an orchestrator agent persists for itself, in its own runtime, so there is no keryx-owned write path to refuse at. Contrast job-orchestrator-state, which looks similar and IS enforced for exactly one reason: `keryx job` writes that one."
|
|
31796
|
-
}
|
|
31797
|
-
},
|
|
31798
|
-
{
|
|
31799
|
-
name: "review-finding",
|
|
31800
|
-
fileName: "review-finding.schema.json",
|
|
31801
|
-
description: "Normalized reviewer finding consumed by review-orchestrator and learning flows.",
|
|
31802
|
-
enforcement: {
|
|
31803
|
-
kind: "production",
|
|
31804
|
-
module: "src/review/managed.ts",
|
|
31805
|
-
refuses: "`createManagedReviewPackage` refuses to record findings that do not satisfy the schema, and the disposition sub-schema is applied the same way, so a review round cannot be written with a malformed finding or an unreadable outcome."
|
|
31806
|
-
}
|
|
31807
|
-
},
|
|
31808
|
-
{
|
|
31809
|
-
name: "subagent-dispatch",
|
|
31810
|
-
fileName: "subagent-dispatch.schema.json",
|
|
31811
|
-
description: "Orchestrator-to-subagent dispatch payload.",
|
|
31812
|
-
enforcement: {
|
|
31813
|
-
kind: "none",
|
|
31814
|
-
reason: "The dispatch is built and consumed inside the harness without the canonical schema being loaded: `subagent-dispatch` appears only as a label on the extension metadata. Its sibling `subagent-result` IS validated, because the harness parses a child's reply back and has to decide whether it is well-formed; nothing performs the equivalent act on the way out."
|
|
31815
|
-
}
|
|
31816
|
-
},
|
|
31817
|
-
{
|
|
31818
|
-
name: "subagent-result",
|
|
31819
|
-
fileName: "subagent-result.schema.json",
|
|
31820
|
-
description: "Subagent-to-orchestrator result payload.",
|
|
31821
|
-
enforcement: {
|
|
31822
|
-
kind: "production",
|
|
31823
|
-
module: "src/harness/external/runtime.ts",
|
|
31824
|
-
refuses: "A child's reply that does not parse as a well-formed result is rejected when the harness reads it back, so a malformed result cannot be persisted as if the child had succeeded."
|
|
31825
|
-
}
|
|
31826
|
-
},
|
|
31827
|
-
{
|
|
31828
|
-
name: "task-implementer-input",
|
|
31829
|
-
fileName: "task-implementer-input-contract.schema.json",
|
|
31830
|
-
description: "Task request handed to task-implementer (task + workspace + automation), validated before dispatch.",
|
|
31831
|
-
sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json",
|
|
31832
|
-
enforcement: {
|
|
31833
|
-
kind: "none",
|
|
31834
|
-
reason: "Dispatched agent-to-agent, like flow-orchestrator-input. The skill's Phase 1.4 lists five `ASSERT \u2026 \u2192 ABORT(\u2026)` refusals; registering the contract is what lets `keryx skills contracts validate` perform them, and nothing forces that call. The comment beside this registration has said so since it was written."
|
|
31835
|
-
}
|
|
31836
|
-
},
|
|
31837
|
-
{
|
|
31838
|
-
name: "task-implementer-output",
|
|
31839
|
-
fileName: "task-implementer-output-contract.schema.json",
|
|
31840
|
-
description: "JSON result task-implementer writes in Phase 6.1 before it emits its STATUS line.",
|
|
31841
|
-
sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json",
|
|
31842
|
-
enforcement: {
|
|
31843
|
-
kind: "none",
|
|
31844
|
-
reason: "The skill writes this file itself, in its own runtime, and no keryx command reads it back. Contrast subagent-result, which is the same shape of thing and IS enforced for one reason: the harness reads that one back and must decide whether it is well-formed."
|
|
31845
|
-
}
|
|
31846
|
-
}
|
|
31847
|
-
];
|
|
31848
|
-
async function loadSchema(name) {
|
|
31849
|
-
const contract = CONTRACTS.find((entry) => entry.name === name);
|
|
31850
|
-
if (!contract) {
|
|
31851
|
-
throw new Error(`Unknown contract schema: ${name}`);
|
|
31852
|
-
}
|
|
31853
|
-
const raw = await readFile46(contractPath(contract), "utf8");
|
|
31854
|
-
return JSON.parse(raw);
|
|
31855
|
-
}
|
|
31856
|
-
function contractPath(contract) {
|
|
31857
|
-
const here = path98.dirname(fileURLToPath2(import.meta.url));
|
|
31858
|
-
if (contract.sourcePath) {
|
|
31859
|
-
for (const root of [path98.join(here, "..", ".."), path98.join(here, ".."), here]) {
|
|
31860
|
-
const candidate = path98.resolve(root, contract.sourcePath);
|
|
31861
|
-
if (existsSync14(candidate)) {
|
|
31862
|
-
return candidate;
|
|
31863
|
-
}
|
|
31864
|
-
}
|
|
31865
|
-
}
|
|
31866
|
-
const directPath = fileURLToPath2(new URL(`./contracts/${contract.fileName}`, import.meta.url));
|
|
31867
|
-
if (existsSync14(directPath)) {
|
|
31868
|
-
return directPath;
|
|
31869
|
-
}
|
|
31870
|
-
const packagedSourcePath = path98.join(here, "..", "src", "gdskills", "contracts", contract.fileName);
|
|
31871
|
-
if (existsSync14(packagedSourcePath)) {
|
|
31872
|
-
return packagedSourcePath;
|
|
31873
|
-
}
|
|
31874
|
-
return directPath;
|
|
31875
|
-
}
|
|
31876
|
-
var SCHEMA_ROOTS = [
|
|
31877
|
-
fileURLToPath2(new URL("./contracts/", import.meta.url)),
|
|
31878
|
-
fileURLToPath2(new URL("./bundled/skills/review/review-orchestrator/", import.meta.url))
|
|
31879
|
-
];
|
|
31880
|
-
|
|
31881
|
-
// src/job/store.ts
|
|
31882
|
-
init_fs();
|
|
31883
32278
|
function jobsRoot(cwd) {
|
|
31884
32279
|
return path99.join(cwd, ".metaproject", "jobs");
|
|
31885
32280
|
}
|
|
@@ -32322,7 +32717,7 @@ function compareStrictUtc(left, right) {
|
|
|
32322
32717
|
return 0;
|
|
32323
32718
|
return leftFraction < rightFraction ? -1 : 1;
|
|
32324
32719
|
}
|
|
32325
|
-
function
|
|
32720
|
+
function isRecord5(value) {
|
|
32326
32721
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32327
32722
|
}
|
|
32328
32723
|
async function loadNormativeSchema(schema) {
|
|
@@ -32338,18 +32733,18 @@ function resolveSchemaRef(root, ref) {
|
|
|
32338
32733
|
return;
|
|
32339
32734
|
let current = root;
|
|
32340
32735
|
for (const part of ref.slice(2).split("/")) {
|
|
32341
|
-
if (!
|
|
32736
|
+
if (!isRecord5(current))
|
|
32342
32737
|
return;
|
|
32343
32738
|
current = current[part.replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
32344
32739
|
}
|
|
32345
|
-
return
|
|
32740
|
+
return isRecord5(current) ? current : undefined;
|
|
32346
32741
|
}
|
|
32347
32742
|
function jsonEqual(left, right) {
|
|
32348
32743
|
if (Object.is(left, right))
|
|
32349
32744
|
return true;
|
|
32350
32745
|
if (Array.isArray(left) && Array.isArray(right))
|
|
32351
32746
|
return left.length === right.length && left.every((item, index) => jsonEqual(item, right[index]));
|
|
32352
|
-
if (
|
|
32747
|
+
if (isRecord5(left) && isRecord5(right)) {
|
|
32353
32748
|
const leftKeys = Object.keys(left).sort();
|
|
32354
32749
|
const rightKeys = Object.keys(right).sort();
|
|
32355
32750
|
return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && jsonEqual(left[key], right[key]));
|
|
@@ -32369,7 +32764,7 @@ function validateNormativeSchema(root, schema, value, errors, field) {
|
|
|
32369
32764
|
if (Array.isArray(schema.oneOf)) {
|
|
32370
32765
|
const candidates = schema.oneOf.map((candidate) => {
|
|
32371
32766
|
const candidateErrors = [];
|
|
32372
|
-
const valid =
|
|
32767
|
+
const valid = isRecord5(candidate) && validateNormativeSchema(root, candidate, value, candidateErrors, field);
|
|
32373
32768
|
return { valid, errors: candidateErrors };
|
|
32374
32769
|
});
|
|
32375
32770
|
const matched = candidates.filter((candidate) => candidate.valid).length;
|
|
@@ -32381,22 +32776,22 @@ function validateNormativeSchema(root, schema, value, errors, field) {
|
|
|
32381
32776
|
}
|
|
32382
32777
|
if (Array.isArray(schema.allOf)) {
|
|
32383
32778
|
for (const candidate of schema.allOf)
|
|
32384
|
-
if (
|
|
32779
|
+
if (isRecord5(candidate))
|
|
32385
32780
|
validateNormativeSchema(root, candidate, value, errors, field);
|
|
32386
32781
|
}
|
|
32387
|
-
if (
|
|
32782
|
+
if (isRecord5(schema.if)) {
|
|
32388
32783
|
const conditionErrors = [];
|
|
32389
32784
|
const branch = validateNormativeSchema(root, schema.if, value, conditionErrors, field) ? schema.then : schema.else;
|
|
32390
|
-
if (
|
|
32785
|
+
if (isRecord5(branch))
|
|
32391
32786
|
validateNormativeSchema(root, branch, value, errors, field);
|
|
32392
32787
|
}
|
|
32393
|
-
if (
|
|
32788
|
+
if (isRecord5(schema.not)) {
|
|
32394
32789
|
const prohibitedErrors = [];
|
|
32395
32790
|
if (validateNormativeSchema(root, schema.not, value, prohibitedErrors, field))
|
|
32396
32791
|
error(errors, "schema_not", field, "matches a prohibited normative schema branch");
|
|
32397
32792
|
}
|
|
32398
32793
|
if (Array.isArray(schema.anyOf)) {
|
|
32399
|
-
const matched = schema.anyOf.some((candidate) =>
|
|
32794
|
+
const matched = schema.anyOf.some((candidate) => isRecord5(candidate) && validateNormativeSchema(root, candidate, value, [], field));
|
|
32400
32795
|
if (!matched)
|
|
32401
32796
|
error(errors, "schema_any_of", field, "must match a normative schema branch");
|
|
32402
32797
|
}
|
|
@@ -32404,12 +32799,12 @@ function validateNormativeSchema(root, schema, value, errors, field) {
|
|
|
32404
32799
|
error(errors, "schema_const", field, "does not match normative const");
|
|
32405
32800
|
if (Array.isArray(schema.enum) && !schema.enum.some((candidate) => value === candidate))
|
|
32406
32801
|
error(errors, "schema_enum", field, "does not match normative enum");
|
|
32407
|
-
if (schema.type === "object" && !
|
|
32802
|
+
if (schema.type === "object" && !isRecord5(value)) {
|
|
32408
32803
|
error(errors, "schema_type", field, "must be an object");
|
|
32409
32804
|
return false;
|
|
32410
32805
|
}
|
|
32411
|
-
if (
|
|
32412
|
-
const properties =
|
|
32806
|
+
if (isRecord5(value) && (schema.type === "object" || isRecord5(schema.properties) || Array.isArray(schema.required) || schema.additionalProperties === false)) {
|
|
32807
|
+
const properties = isRecord5(schema.properties) ? schema.properties : {};
|
|
32413
32808
|
if (Array.isArray(schema.required)) {
|
|
32414
32809
|
for (const key of schema.required)
|
|
32415
32810
|
if (typeof key === "string" && !(key in value))
|
|
@@ -32421,7 +32816,7 @@ function validateNormativeSchema(root, schema, value, errors, field) {
|
|
|
32421
32816
|
error(errors, "additional_property", `${field}.${key}`, "is not allowed by normative schema");
|
|
32422
32817
|
}
|
|
32423
32818
|
for (const [key, propertySchema] of Object.entries(properties))
|
|
32424
|
-
if (key in value &&
|
|
32819
|
+
if (key in value && isRecord5(propertySchema))
|
|
32425
32820
|
validateNormativeSchema(root, propertySchema, value[key], errors, `${field}.${key}`);
|
|
32426
32821
|
}
|
|
32427
32822
|
if (schema.type === "array") {
|
|
@@ -32437,9 +32832,9 @@ function validateNormativeSchema(root, schema, value, errors, field) {
|
|
|
32437
32832
|
if (jsonEqual(value[left], value[right]))
|
|
32438
32833
|
error(errors, "schema_unique_items", `${field}[${right}]`, "duplicates an earlier item prohibited by the normative schema");
|
|
32439
32834
|
}
|
|
32440
|
-
if (
|
|
32835
|
+
if (isRecord5(schema.items))
|
|
32441
32836
|
value.forEach((item, index) => validateNormativeSchema(root, schema.items, item, errors, `${field}[${index}]`));
|
|
32442
|
-
if (
|
|
32837
|
+
if (isRecord5(schema.contains)) {
|
|
32443
32838
|
const matches = value.filter((item) => validateNormativeSchema(root, schema.contains, item, [], field)).length;
|
|
32444
32839
|
const minimum = typeof schema.minContains === "number" ? schema.minContains : 1;
|
|
32445
32840
|
if (matches < minimum)
|
|
@@ -32476,7 +32871,7 @@ function error(errors, code, field, message) {
|
|
|
32476
32871
|
errors.push({ code, path: field, message });
|
|
32477
32872
|
}
|
|
32478
32873
|
function requireObject(value, errors, field) {
|
|
32479
|
-
if (!
|
|
32874
|
+
if (!isRecord5(value)) {
|
|
32480
32875
|
error(errors, "schema_type", field, "must be an object");
|
|
32481
32876
|
return false;
|
|
32482
32877
|
}
|
|
@@ -32730,7 +33125,7 @@ function validateWriteIntent(document, errors) {
|
|
|
32730
33125
|
workspaceUri(document.approvalRef, errors, "$.approvalRef");
|
|
32731
33126
|
if (!requireObject(document.security, errors, "$.security") || document.security.gate !== "pass")
|
|
32732
33127
|
error(errors, "security_gate_failed", "$.security.gate", "write intent requires pass");
|
|
32733
|
-
if (
|
|
33128
|
+
if (isRecord5(document.security)) {
|
|
32734
33129
|
workspaceUri(document.security.policyRef, errors, "$.security.policyRef");
|
|
32735
33130
|
stringMatch(document.security.policyRevision, revisionPattern, errors, "$.security.policyRevision");
|
|
32736
33131
|
}
|
|
@@ -32771,13 +33166,13 @@ function validateTransition(document, errors) {
|
|
|
32771
33166
|
error(errors, "idempotency_mismatch", "$.acceptance.idempotencyKey", "must match the transition key");
|
|
32772
33167
|
if (!requireObject(acceptance.security, errors, "$.acceptance.security") || acceptance.security.gate !== "pass")
|
|
32773
33168
|
error(errors, "security_gate_failed", "$.acceptance.security.gate", "acceptance requires pass");
|
|
32774
|
-
if (
|
|
33169
|
+
if (isRecord5(acceptance.security)) {
|
|
32775
33170
|
workspaceUri(acceptance.security.policyRef, errors, "$.acceptance.security.policyRef");
|
|
32776
33171
|
stringMatch(acceptance.security.policyRevision, revisionPattern, errors, "$.acceptance.security.policyRevision");
|
|
32777
33172
|
}
|
|
32778
33173
|
if (!requireObject(acceptance.freshness, errors, "$.acceptance.freshness") || acceptance.freshness.state !== "fresh")
|
|
32779
33174
|
error(errors, "stale_evidence", "$.acceptance.freshness.state", "acceptance requires fresh evidence");
|
|
32780
|
-
if (
|
|
33175
|
+
if (isRecord5(acceptance.freshness))
|
|
32781
33176
|
utc(acceptance.freshness.verifiedAt, errors, "$.acceptance.freshness.verifiedAt");
|
|
32782
33177
|
if (!requireObject(acceptance.targetWrite, errors, "$.acceptance.targetWrite"))
|
|
32783
33178
|
error(errors, "missing_target_write", "$.acceptance.targetWrite", "acceptance requires a guarded write receipt");
|
|
@@ -32806,7 +33201,7 @@ function validateTransition(document, errors) {
|
|
|
32806
33201
|
stringMatch(binding.policyRevision, revisionPattern, errors, "$.acceptance.targetWrite.binding.policyRevision");
|
|
32807
33202
|
if (!["owner", "editor"].includes(binding.reviewerAuthority))
|
|
32808
33203
|
error(errors, "schema_enum", "$.acceptance.targetWrite.binding.reviewerAuthority", "must be owner or editor");
|
|
32809
|
-
if (binding.intentRef !== acceptance.writeIntentRef || binding.proposalId !== document.proposalId || binding.proposalRevision !== document.proposalRevision || binding.workspaceId !== document.workspaceId || binding.correlationId !== document.correlationId || binding.idempotencyKey !== document.idempotencyKey || binding.reviewerSubject !== (
|
|
33204
|
+
if (binding.intentRef !== acceptance.writeIntentRef || binding.proposalId !== document.proposalId || binding.proposalRevision !== document.proposalRevision || binding.workspaceId !== document.workspaceId || binding.correlationId !== document.correlationId || binding.idempotencyKey !== document.idempotencyKey || binding.reviewerSubject !== (isRecord5(acceptance.reviewer) ? acceptance.reviewer.subject : undefined) || binding.reviewerAuthority !== (isRecord5(acceptance.reviewer) ? acceptance.reviewer.authority : undefined) || binding.policyRevision !== (isRecord5(acceptance.security) ? acceptance.security.policyRevision : undefined))
|
|
32810
33205
|
error(errors, "receipt_binding_mismatch", "$.acceptance.targetWrite.binding", "must bind this exact accepted transition");
|
|
32811
33206
|
else if (binding.bindingHash !== hashSacRecord({ owner: binding.owner, intentRef: binding.intentRef, proposalId: binding.proposalId, proposalRevision: binding.proposalRevision, workspaceId: binding.workspaceId, correlationId: binding.correlationId, idempotencyKey: binding.idempotencyKey, reviewerSubject: binding.reviewerSubject, reviewerAuthority: binding.reviewerAuthority, policyRevision: binding.policyRevision }))
|
|
32812
33207
|
error(errors, "receipt_binding_hash_mismatch", "$.acceptance.targetWrite.binding.bindingHash", "must hash the retained owner binding");
|
|
@@ -32817,10 +33212,10 @@ function validateTransition(document, errors) {
|
|
|
32817
33212
|
else
|
|
32818
33213
|
acceptance.evidence.forEach((item, index) => {
|
|
32819
33214
|
validateEvidence(item, errors, `$.acceptance.evidence[${index}]`, false);
|
|
32820
|
-
if (
|
|
32821
|
-
ordered(
|
|
33215
|
+
if (isRecord5(acceptance.freshness))
|
|
33216
|
+
ordered(isRecord5(item) ? item.observedAt : undefined, acceptance.freshness.verifiedAt, errors, `$.acceptance.evidence[${index}].observedAt`);
|
|
32822
33217
|
});
|
|
32823
|
-
if (
|
|
33218
|
+
if (isRecord5(acceptance.freshness) && isRecord5(acceptance.targetWrite)) {
|
|
32824
33219
|
ordered(acceptance.freshness.verifiedAt, acceptance.targetWrite.completedAt, errors, "$.acceptance.freshness/targetWrite");
|
|
32825
33220
|
ordered(acceptance.targetWrite.completedAt, document.occurredAt, errors, "$.acceptance.targetWrite/occurredAt");
|
|
32826
33221
|
}
|
|
@@ -32838,11 +33233,11 @@ function validateReview(document, errors) {
|
|
|
32838
33233
|
if (!["accepted", "rejected", "dismissed"].includes(document.decision))
|
|
32839
33234
|
error(errors, "schema_enum", "$.decision", "is invalid");
|
|
32840
33235
|
if (document.decision === "accepted") {
|
|
32841
|
-
if (!
|
|
33236
|
+
if (!isRecord5(document.security) || document.security.gate !== "pass")
|
|
32842
33237
|
error(errors, "security_gate_failed", "$.security", "accepted requires a passing gate");
|
|
32843
|
-
if (!
|
|
33238
|
+
if (!isRecord5(document.freshness) || document.freshness.state !== "fresh")
|
|
32844
33239
|
error(errors, "stale_evidence", "$.freshness", "accepted requires fresh evidence");
|
|
32845
|
-
if (!
|
|
33240
|
+
if (!isRecord5(document.targetWrite))
|
|
32846
33241
|
error(errors, "missing_target_write", "$.targetWrite", "accepted requires target write");
|
|
32847
33242
|
} else if (typeof document.reason !== "string" || document.reason.length === 0 || document.targetWrite !== undefined || document.freshness !== undefined)
|
|
32848
33243
|
error(errors, "schema_conditional", "$.reason", "non-accepted decisions require only a reason");
|
|
@@ -32980,7 +33375,7 @@ function createSacAuthorizationServer(input) {
|
|
|
32980
33375
|
} });
|
|
32981
33376
|
}
|
|
32982
33377
|
function isTrustedActorContext(actorContext) {
|
|
32983
|
-
return
|
|
33378
|
+
return isRecord5(actorContext) && trustedActors.has(actorContext);
|
|
32984
33379
|
}
|
|
32985
33380
|
var roleRank = { viewer: 1, editor: 2, owner: 3 };
|
|
32986
33381
|
function authorizationResult(allowed, code, baseline, required, workspaceId) {
|
|
@@ -32998,7 +33393,7 @@ function authorizationResult(allowed, code, baseline, required, workspaceId) {
|
|
|
32998
33393
|
}
|
|
32999
33394
|
async function authorizeSacUse(input) {
|
|
33000
33395
|
const required = input.action === "read" ? 1 : 2;
|
|
33001
|
-
if (!
|
|
33396
|
+
if (!isRecord5(input.actorContext) || !trustedActors.has(input.actorContext))
|
|
33002
33397
|
return authorizationResult(false, "untrusted_actor", { role: "revoked", revision: "invalid", workspaceId: input.workspaceId }, required, input.workspaceId);
|
|
33003
33398
|
const current = await input.resolveCurrentRole(input.actorContext.subject, input.workspaceId);
|
|
33004
33399
|
if (current.workspaceId !== input.workspaceId)
|
|
@@ -34153,11 +34548,11 @@ async function resolveLedgerState(ledger, checkpointPath, verifier) {
|
|
|
34153
34548
|
return auditLedger(ledger, checkpointPath, verifier);
|
|
34154
34549
|
}
|
|
34155
34550
|
var policyRefPattern = /^\.\/.+/;
|
|
34156
|
-
var
|
|
34551
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34157
34552
|
var asString2 = (value) => typeof value === "string" && value.length > 0;
|
|
34158
34553
|
var isBaselineArtifact = (artifact) => artifact.schemaVersion === "1.0" && artifact.kind === "deterministic-baseline" && isImmutableVersion2(artifact.version) && artifact.selection === "eligible-ids-in-input-order";
|
|
34159
34554
|
var isCandidateArtifact = (artifact) => artifact.schemaVersion === "1.0" && artifact.kind === "offline-selection-advisor" && isImmutableVersion2(artifact.version) && asString2(artifact.output) && typeof artifact.mutations === "boolean";
|
|
34160
|
-
var isEvaluationReport = (report) => report.schemaVersion === "1.0" && asString2(report.status) && asString2(report.baselineVersion) && asString2(report.baselineDigest) && reportHashPattern.test(report.baselineDigest) && asString2(report.candidateVersion) && asString2(report.candidateDigest) && reportHashPattern.test(report.candidateDigest) && asString2(report.corpusVersion) && asString2(report.corpusDigest) && reportHashPattern.test(report.corpusDigest) && asString2(report.reportDigest) && reportHashPattern.test(report.reportDigest) && asString2(report.sandboxProfileDigest) && reportHashPattern.test(report.sandboxProfileDigest) &&
|
|
34555
|
+
var isEvaluationReport = (report) => report.schemaVersion === "1.0" && asString2(report.status) && asString2(report.baselineVersion) && asString2(report.baselineDigest) && reportHashPattern.test(report.baselineDigest) && asString2(report.candidateVersion) && asString2(report.candidateDigest) && reportHashPattern.test(report.candidateDigest) && asString2(report.corpusVersion) && asString2(report.corpusDigest) && reportHashPattern.test(report.corpusDigest) && asString2(report.reportDigest) && reportHashPattern.test(report.reportDigest) && asString2(report.sandboxProfileDigest) && reportHashPattern.test(report.sandboxProfileDigest) && isRecord6(report.train) && typeof report.train.status === "string" && typeof report.train.cases === "number" && isRecord6(report.holdout) && typeof report.holdout.status === "string" && typeof report.holdout.cases === "number" && isRecord6(report.adversarial) && typeof report.adversarial.status === "string" && typeof report.adversarial.cases === "number" && Array.isArray(report.candidateSelectedIds) && Array.isArray(report.reasons);
|
|
34161
34556
|
var isConfigRecord = (value) => typeof value.enabled === "boolean" && typeof value.killSwitch === "boolean";
|
|
34162
34557
|
async function readPinnedJson(input) {
|
|
34163
34558
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input.workspaceRoot, kind: "artifact", uri: input.uri });
|
|
@@ -34188,7 +34583,7 @@ async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
|
34188
34583
|
if (raw === undefined)
|
|
34189
34584
|
return canonicalFallback;
|
|
34190
34585
|
const parsed = JSON.parse(raw);
|
|
34191
|
-
if (!
|
|
34586
|
+
if (!isRecord6(parsed) || !isConfigRecord(parsed))
|
|
34192
34587
|
return canonicalFallback;
|
|
34193
34588
|
configJson = parsed;
|
|
34194
34589
|
} catch {
|
|
@@ -34267,7 +34662,7 @@ async function resolvePolicySelectionSafely(workspaceRoot, canonicalFallback) {
|
|
|
34267
34662
|
}
|
|
34268
34663
|
}
|
|
34269
34664
|
function isCompleteCorpus(value) {
|
|
34270
|
-
return
|
|
34665
|
+
return isRecord6(value) && isRecord6(value.manifest) && Array.isArray(value.rows) && Array.isArray(value.quarantine) && verifyPolicyCorpus(value);
|
|
34271
34666
|
}
|
|
34272
34667
|
var validHash = (value) => reportHashPattern.test(value);
|
|
34273
34668
|
|
|
@@ -34480,7 +34875,7 @@ function normalizeFwkResult(result) {
|
|
|
34480
34875
|
// src/sac/proposal-lifecycle.ts
|
|
34481
34876
|
init_fs();
|
|
34482
34877
|
import { createHash as createHash29, randomUUID as randomUUID7 } from "crypto";
|
|
34483
|
-
import { appendFile as appendFile7, mkdir as
|
|
34878
|
+
import { appendFile as appendFile7, mkdir as mkdir33, readdir as readdir19, readFile as readFile59 } from "fs/promises";
|
|
34484
34879
|
import path114 from "path";
|
|
34485
34880
|
|
|
34486
34881
|
// src/sac/trusted-wrap-up.ts
|
|
@@ -34556,6 +34951,7 @@ import { mkdir as mkdir32, writeFile as writeFile21 } from "fs/promises";
|
|
|
34556
34951
|
import path108 from "path";
|
|
34557
34952
|
|
|
34558
34953
|
// src/session/store.ts
|
|
34954
|
+
init_config_dir();
|
|
34559
34955
|
import {
|
|
34560
34956
|
chmodSync as chmodSync2,
|
|
34561
34957
|
existsSync as existsSync16,
|
|
@@ -34566,46 +34962,6 @@ import {
|
|
|
34566
34962
|
} from "fs";
|
|
34567
34963
|
import path107 from "path";
|
|
34568
34964
|
|
|
34569
|
-
// src/lib/config-dir.ts
|
|
34570
|
-
import {
|
|
34571
|
-
appendFileSync,
|
|
34572
|
-
chmodSync,
|
|
34573
|
-
mkdirSync,
|
|
34574
|
-
readFileSync as readFileSync5,
|
|
34575
|
-
renameSync,
|
|
34576
|
-
statSync as statSync2,
|
|
34577
|
-
unlinkSync,
|
|
34578
|
-
writeFileSync
|
|
34579
|
-
} from "fs";
|
|
34580
|
-
var MAX_CONFIG_FILE_BYTES = 1e6;
|
|
34581
|
-
var MAX_CONTENT_FILE_BYTES = 64 * 1024 * 1024;
|
|
34582
|
-
var MAX_TRANSCRIPT_FILE_BYTES = MAX_CONTENT_FILE_BYTES;
|
|
34583
|
-
function readBoundedFile(file, maxBytes) {
|
|
34584
|
-
let stats;
|
|
34585
|
-
try {
|
|
34586
|
-
stats = statSync2(file);
|
|
34587
|
-
} catch {
|
|
34588
|
-
return { ok: false, reason: "absent" };
|
|
34589
|
-
}
|
|
34590
|
-
if (!stats.isFile()) {
|
|
34591
|
-
return { ok: false, reason: "not-regular" };
|
|
34592
|
-
}
|
|
34593
|
-
if (stats.size > maxBytes) {
|
|
34594
|
-
return { ok: false, reason: "too-large" };
|
|
34595
|
-
}
|
|
34596
|
-
try {
|
|
34597
|
-
return { ok: true, text: readFileSync5(file, "utf8") };
|
|
34598
|
-
} catch {
|
|
34599
|
-
return { ok: false, reason: "unreadable" };
|
|
34600
|
-
}
|
|
34601
|
-
}
|
|
34602
|
-
function readConfigFile(file) {
|
|
34603
|
-
return readBoundedFile(file, MAX_CONFIG_FILE_BYTES);
|
|
34604
|
-
}
|
|
34605
|
-
function readTranscriptFile(file) {
|
|
34606
|
-
return readBoundedFile(file, MAX_TRANSCRIPT_FILE_BYTES);
|
|
34607
|
-
}
|
|
34608
|
-
|
|
34609
34965
|
// src/session/paths.ts
|
|
34610
34966
|
import { existsSync as existsSync15 } from "fs";
|
|
34611
34967
|
import { homedir } from "os";
|
|
@@ -35817,14 +36173,42 @@ function createRealWikiOwnerWriter(cwd, opts) {
|
|
|
35817
36173
|
import { readFile as readFile58 } from "fs/promises";
|
|
35818
36174
|
|
|
35819
36175
|
// src/gdskills/project-skills.ts
|
|
36176
|
+
init_contained_write();
|
|
35820
36177
|
init_fs();
|
|
35821
36178
|
init_json();
|
|
35822
36179
|
init_guard();
|
|
35823
36180
|
import { createHash as createHash28 } from "crypto";
|
|
35824
36181
|
import { homedir as homedir2 } from "os";
|
|
35825
|
-
import {
|
|
36182
|
+
import { readFile as readFile57, stat as stat14 } from "fs/promises";
|
|
35826
36183
|
import path113 from "path";
|
|
35827
36184
|
var VERSION = "0.1.0";
|
|
36185
|
+
var PROJECT_SKILLS_DIR = ".metaproject/project-skills";
|
|
36186
|
+
var PROJECT_SKILLS_MANIFEST_PATH = ".metaproject/metaproject.json";
|
|
36187
|
+
var PROJECT_SKILLS_CATALOG_PATH = ".metaproject/skills/catalog.md";
|
|
36188
|
+
var PROJECT_SKILLS_LOCK_PATH = ".metaproject/data/gdskills/project-skills.lock";
|
|
36189
|
+
var PROJECT_SKILLS_CATALOG_START = "<!-- gdskills:project-skills:start -->";
|
|
36190
|
+
var PROJECT_SKILLS_CATALOG_END = "<!-- gdskills:project-skills:end -->";
|
|
36191
|
+
var PROJECT_SKILLS_CATALOG_EMPTY_ROW = "| _none_ | _none_ | _none_ | - |";
|
|
36192
|
+
function projectSkillPackagePath(moduleName, skillName) {
|
|
36193
|
+
return `${PROJECT_SKILLS_DIR}/${moduleName}/${skillName}`;
|
|
36194
|
+
}
|
|
36195
|
+
function projectSkillKey(entry) {
|
|
36196
|
+
return `${entry.module}/${entry.name}`;
|
|
36197
|
+
}
|
|
36198
|
+
function projectSkillCatalogRow(entry) {
|
|
36199
|
+
return `| ${entry.module} | ${entry.name} | \`${entry.target}\` | ${entry.path}/SKILL.md |`;
|
|
36200
|
+
}
|
|
36201
|
+
function projectSkillWritePaths(moduleName, skillName, format) {
|
|
36202
|
+
const packagePath = projectSkillPackagePath(moduleName, skillName);
|
|
36203
|
+
const files = format === "single" ? ["SKILL.md", "skill-changelog.md"] : ["SKILL.md", "skill-changelog.md", "references/context.md", "templates/README.md", "verification.md"];
|
|
36204
|
+
return [...files.map((file) => `${packagePath}/${file}`), PROJECT_SKILLS_MANIFEST_PATH, PROJECT_SKILLS_CATALOG_PATH];
|
|
36205
|
+
}
|
|
36206
|
+
async function assertProjectWritesContained(projectRoot, paths) {
|
|
36207
|
+
await assertWritableContained(projectRoot, PROJECT_SKILLS_LOCK_PATH, "directory");
|
|
36208
|
+
for (const rel of [...new Set(paths)]) {
|
|
36209
|
+
await assertWritableContained(projectRoot, rel);
|
|
36210
|
+
}
|
|
36211
|
+
}
|
|
35828
36212
|
async function createProjectSkill(projectRoot, options) {
|
|
35829
36213
|
const metaprojectRoot = path113.join(projectRoot, ".metaproject");
|
|
35830
36214
|
if (!await pathExists(metaprojectRoot)) {
|
|
@@ -35833,11 +36217,12 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
35833
36217
|
if (!isRoutableSkillTarget(options.target)) {
|
|
35834
36218
|
throw new Error(`keryx skills create: target ${JSON.stringify(options.target)} reads as prose, not a routing key. ` + 'A target is what `keryx skills route` matches against \u2014 a concept ("auth flow"), a symbol (`IResultDqReport`), ' + "or a path (`src/dq/DqScoreCard.tsx`). A sentence matches nothing and verifies as permanently stale.");
|
|
35835
36219
|
}
|
|
35836
|
-
const moduleName =
|
|
35837
|
-
const skillName =
|
|
36220
|
+
const moduleName = projectSkillSlug(options.module ?? inferModule(options.target));
|
|
36221
|
+
const skillName = projectSkillSlug(options.name ?? inferSkillName(options.target));
|
|
35838
36222
|
const format = options.format ?? "auto";
|
|
35839
|
-
const
|
|
35840
|
-
const
|
|
36223
|
+
const relativeSkillPath = projectSkillPackagePath(moduleName, skillName);
|
|
36224
|
+
const packageRoot = path113.join(projectRoot, relativeSkillPath);
|
|
36225
|
+
await assertProjectWritesContained(projectRoot, projectSkillWritePaths(moduleName, skillName, format));
|
|
35841
36226
|
const origin = options.origin ? options.originContent !== undefined ? {
|
|
35842
36227
|
ref: options.origin.trim(),
|
|
35843
36228
|
hash: hashOriginContent(options.originContent),
|
|
@@ -35847,7 +36232,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
35847
36232
|
const warnings = collectWarnings(evidence, format);
|
|
35848
36233
|
const files = filesForPackage(packageRoot, format);
|
|
35849
36234
|
if (!options.dryRun) {
|
|
35850
|
-
await withFileLock(path113.join(
|
|
36235
|
+
await withFileLock(path113.join(projectRoot, PROJECT_SKILLS_LOCK_PATH), async () => {
|
|
35851
36236
|
await writeProjectSkillPackage({
|
|
35852
36237
|
projectRoot,
|
|
35853
36238
|
packageRoot,
|
|
@@ -35931,19 +36316,16 @@ async function writeProjectSkillPackage({
|
|
|
35931
36316
|
if (!output.allowed) {
|
|
35932
36317
|
throw new Error(`Project skill blocked by the security gate: ${output.reason}`);
|
|
35933
36318
|
}
|
|
35934
|
-
|
|
35935
|
-
const
|
|
35936
|
-
await
|
|
35937
|
-
|
|
35938
|
-
|
|
35939
|
-
await writeFileAtomic(changelogPath, renderSkillChangelog({ moduleName, skillName, target }));
|
|
36319
|
+
const packageRel = toPosix(path113.relative(projectRoot, packageRoot));
|
|
36320
|
+
const write = (file, content) => writeContained(projectRoot, `${packageRel}/${file}`, content);
|
|
36321
|
+
await write("SKILL.md", output.content);
|
|
36322
|
+
if (!await pathExists(path113.join(packageRoot, "skill-changelog.md"))) {
|
|
36323
|
+
await write("skill-changelog.md", renderSkillChangelog({ moduleName, skillName, target }));
|
|
35940
36324
|
}
|
|
35941
36325
|
if (packageFormat === "package") {
|
|
35942
|
-
await
|
|
35943
|
-
await
|
|
35944
|
-
await
|
|
35945
|
-
await writeFileAtomic(path113.join(packageRoot, "templates", "README.md"), renderTemplatesReadme({ moduleName, skillName }));
|
|
35946
|
-
await writeFileAtomic(path113.join(packageRoot, "verification.md"), renderVerification({ moduleName, skillName, evidence }));
|
|
36326
|
+
await write("references/context.md", renderReferenceContext({ moduleName, skillName, target, evidence }));
|
|
36327
|
+
await write("templates/README.md", renderTemplatesReadme({ moduleName, skillName }));
|
|
36328
|
+
await write("verification.md", renderVerification({ moduleName, skillName, evidence }));
|
|
35947
36329
|
}
|
|
35948
36330
|
}
|
|
35949
36331
|
async function collectEvidence(projectRoot, target) {
|
|
@@ -36245,7 +36627,7 @@ keryx skills verify ${moduleName}/${skillName}
|
|
|
36245
36627
|
`;
|
|
36246
36628
|
}
|
|
36247
36629
|
async function updateManifest(projectRoot, entry) {
|
|
36248
|
-
const manifestPath = path113.join(projectRoot,
|
|
36630
|
+
const manifestPath = path113.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
36249
36631
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
36250
36632
|
manifest.modules ??= {};
|
|
36251
36633
|
manifest.modules.gdskills ??= {};
|
|
@@ -36253,29 +36635,29 @@ async function updateManifest(projectRoot, entry) {
|
|
|
36253
36635
|
const nextRegistry = [
|
|
36254
36636
|
...registry.filter((existing) => existing.path !== entry.path),
|
|
36255
36637
|
entry
|
|
36256
|
-
].sort((a, b) =>
|
|
36638
|
+
].sort((a, b) => projectSkillKey(a).localeCompare(projectSkillKey(b)));
|
|
36257
36639
|
manifest.modules.gdskills.projectSkillRegistry = nextRegistry;
|
|
36258
|
-
await
|
|
36640
|
+
await writeContained(projectRoot, PROJECT_SKILLS_MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
|
|
36259
36641
|
`);
|
|
36260
36642
|
}
|
|
36261
36643
|
async function updateSkillsCatalog(projectRoot) {
|
|
36262
|
-
const manifestPath = path113.join(projectRoot,
|
|
36263
|
-
const catalogPath = path113.join(projectRoot,
|
|
36644
|
+
const manifestPath = path113.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
36645
|
+
const catalogPath = path113.join(projectRoot, PROJECT_SKILLS_CATALOG_PATH);
|
|
36264
36646
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
36265
36647
|
const registry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
36266
|
-
const rows = registry.length > 0 ? registry.map(
|
|
36267
|
-
`) :
|
|
36268
|
-
const section =
|
|
36648
|
+
const rows = registry.length > 0 ? registry.map(projectSkillCatalogRow).join(`
|
|
36649
|
+
`) : PROJECT_SKILLS_CATALOG_EMPTY_ROW;
|
|
36650
|
+
const section = `${PROJECT_SKILLS_CATALOG_START}
|
|
36269
36651
|
## Project Skills
|
|
36270
36652
|
|
|
36271
36653
|
| Module | Skill | Target | Entry |
|
|
36272
36654
|
|---|---|---|---|
|
|
36273
36655
|
${rows}
|
|
36274
|
-
|
|
36656
|
+
${PROJECT_SKILLS_CATALOG_END}`;
|
|
36275
36657
|
const current = await pathExists(catalogPath) ? await readFile57(catalogPath, "utf8") : `# Metaproject Skills Catalog
|
|
36276
36658
|
`;
|
|
36277
|
-
const start =
|
|
36278
|
-
const end =
|
|
36659
|
+
const start = PROJECT_SKILLS_CATALOG_START;
|
|
36660
|
+
const end = PROJECT_SKILLS_CATALOG_END;
|
|
36279
36661
|
const startIndex = current.indexOf(start);
|
|
36280
36662
|
const endIndex = current.indexOf(end);
|
|
36281
36663
|
const next = startIndex >= 0 && endIndex > startIndex ? `${current.slice(0, startIndex).trimEnd()}
|
|
@@ -36285,7 +36667,7 @@ ${current.slice(endIndex + end.length).trimStart()}` : `${current.trimEnd()}
|
|
|
36285
36667
|
|
|
36286
36668
|
${section}
|
|
36287
36669
|
`;
|
|
36288
|
-
await
|
|
36670
|
+
await writeContained(projectRoot, PROJECT_SKILLS_CATALOG_PATH, next);
|
|
36289
36671
|
}
|
|
36290
36672
|
function inferModule(target) {
|
|
36291
36673
|
const normalized = toPosix(target).replace(/^\.\//, "");
|
|
@@ -36303,9 +36685,11 @@ function inferSkillName(target) {
|
|
|
36303
36685
|
const base = path113.basename(normalized).replace(/\.[^.]+$/, "");
|
|
36304
36686
|
return base || "entity";
|
|
36305
36687
|
}
|
|
36306
|
-
function
|
|
36307
|
-
|
|
36308
|
-
|
|
36688
|
+
function projectSkillSlug(value) {
|
|
36689
|
+
return slugOf(value) || "entity";
|
|
36690
|
+
}
|
|
36691
|
+
function slugOf(value) {
|
|
36692
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
36309
36693
|
}
|
|
36310
36694
|
function titleize(value) {
|
|
36311
36695
|
return value.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
@@ -36422,7 +36806,7 @@ class ProposalLifecycleService {
|
|
|
36422
36806
|
if (manifest.status === "archived")
|
|
36423
36807
|
throw new ProposalLifecycleError("guard_denied", "workspace is archived");
|
|
36424
36808
|
const file = this.proposalPath(workspaceId, proposal.id);
|
|
36425
|
-
await
|
|
36809
|
+
await mkdir33(path114.dirname(file), { recursive: true, mode: 448 });
|
|
36426
36810
|
return withFileLock(`${file}.lock`, async () => {
|
|
36427
36811
|
const consume = this.options.wrapUpAuthority.consume(input.wrapUp, { actor, workspaceId });
|
|
36428
36812
|
if (consume !== "ok")
|
|
@@ -36450,7 +36834,7 @@ class ProposalLifecycleService {
|
|
|
36450
36834
|
return this.options.workspaces.withAuthorizedActor({ actorContext: actor, workspaceId: input.workspaceId, action: "review", execute: async (manifest) => {
|
|
36451
36835
|
const proposal = await this.loadProposal(input.workspaceId, input.proposalId);
|
|
36452
36836
|
const ledger = this.ledgerPath(input.workspaceId);
|
|
36453
|
-
await
|
|
36837
|
+
await mkdir33(path114.dirname(ledger), { recursive: true, mode: 448 });
|
|
36454
36838
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
36455
36839
|
const records = (await this.records(ledger)).filter((record) => record.proposalId === proposal.id);
|
|
36456
36840
|
const events = records.filter((record) => record.recordType === "proposal-transition");
|
|
@@ -36902,7 +37286,7 @@ function normalizeProposalLifecycleResult(value) {
|
|
|
36902
37286
|
return JSON.parse(JSON.stringify(value));
|
|
36903
37287
|
}
|
|
36904
37288
|
// src/sac/collaboration-service.ts
|
|
36905
|
-
import { appendFile as appendFile8, mkdir as
|
|
37289
|
+
import { appendFile as appendFile8, mkdir as mkdir34, readFile as readFile60 } from "fs/promises";
|
|
36906
37290
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
36907
37291
|
import path115 from "path";
|
|
36908
37292
|
class CollaborationServiceError extends Error {
|
|
@@ -36956,7 +37340,7 @@ class CollaborationService {
|
|
|
36956
37340
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
36957
37341
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
36958
37342
|
});
|
|
36959
|
-
await
|
|
37343
|
+
await mkdir34(path115.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
36960
37344
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
36961
37345
|
`, { mode: 384 });
|
|
36962
37346
|
return normalizeCollaborationResult(activity);
|
|
@@ -37005,13 +37389,13 @@ function normalizeCollaborationResult(value) {
|
|
|
37005
37389
|
}
|
|
37006
37390
|
// src/session/external-slate.ts
|
|
37007
37391
|
init_fs();
|
|
37008
|
-
import { mkdir as
|
|
37392
|
+
import { mkdir as mkdir36, readdir as readdir20, readFile as readFile61 } from "fs/promises";
|
|
37009
37393
|
import path117 from "path";
|
|
37010
37394
|
|
|
37011
37395
|
// src/sac/machine-wrap-up.ts
|
|
37012
37396
|
init_fs();
|
|
37013
37397
|
import { createHash as createHash30, randomUUID as randomUUID10 } from "crypto";
|
|
37014
|
-
import { mkdir as
|
|
37398
|
+
import { mkdir as mkdir35 } from "fs/promises";
|
|
37015
37399
|
import path116 from "path";
|
|
37016
37400
|
|
|
37017
37401
|
// src/sac/workspace-resolve.ts
|
|
@@ -37212,7 +37596,7 @@ ${safeSeeds.content}`;
|
|
|
37212
37596
|
summary = result.text.trim().length > 0 ? result.text.trim() : mechanicalSummary(summaryDiff, course);
|
|
37213
37597
|
}
|
|
37214
37598
|
const evidenceDir = path116.join(input.cwd, ".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
37215
|
-
await
|
|
37599
|
+
await mkdir35(evidenceDir, { recursive: true });
|
|
37216
37600
|
const diffFile = `${input.kind}.${shortHash}.diff.txt`;
|
|
37217
37601
|
const flowFile = `${input.kind}.${shortHash}.flow.json`;
|
|
37218
37602
|
const seedsFile = `${input.kind}.${shortHash}.seeds.json`;
|
|
@@ -37255,7 +37639,7 @@ async function writeFlooredArchiveArtifact(params) {
|
|
|
37255
37639
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
37256
37640
|
const body = floor.bodies[0];
|
|
37257
37641
|
const archiveDir = path116.join(params.dir, "slate-archive");
|
|
37258
|
-
await
|
|
37642
|
+
await mkdir35(archiveDir, { recursive: true });
|
|
37259
37643
|
await writeFileAtomic(path116.join(archiveDir, params.filename), body.content);
|
|
37260
37644
|
if (floor.notice !== undefined) {
|
|
37261
37645
|
await writeFileAtomic(path116.join(archiveDir, `${params.filename}${ARCHIVE_REDACTION_NOTICE_SUFFIX}`), floor.notice);
|
|
@@ -37295,7 +37679,7 @@ async function writeWrapUpOutcomeArtifact(cwd, dir, trigger, now, groups) {
|
|
|
37295
37679
|
const refusalIso = now().toISOString();
|
|
37296
37680
|
const refusalFilename = `${refusalIso.replace(/[:.]/g, "-")}-wrap-up-outcome.json`;
|
|
37297
37681
|
const archiveDir = path116.join(dir, "slate-archive");
|
|
37298
|
-
await
|
|
37682
|
+
await mkdir35(archiveDir, { recursive: true });
|
|
37299
37683
|
await writeFileAtomic(path116.join(archiveDir, refusalFilename), `${JSON.stringify({
|
|
37300
37684
|
recordType: "wrap-up-outcome",
|
|
37301
37685
|
trigger,
|
|
@@ -37462,7 +37846,7 @@ async function readExternalSlate(cwd, externalSessionId) {
|
|
|
37462
37846
|
}
|
|
37463
37847
|
}
|
|
37464
37848
|
async function writeExternalSlate(cwd, externalSessionId, update) {
|
|
37465
|
-
await
|
|
37849
|
+
await mkdir36(externalSlatesDir(cwd), { recursive: true });
|
|
37466
37850
|
return withFileLock(externalSlateLockPath(cwd, externalSessionId), async () => {
|
|
37467
37851
|
const prev = await readExternalSlate(cwd, externalSessionId);
|
|
37468
37852
|
const next = update(prev);
|
|
@@ -37488,7 +37872,7 @@ function isExternalSlateStale(slate, now = () => new Date) {
|
|
|
37488
37872
|
return now().getTime() - lastWriteMs > DEFAULT_LOCK_STALE_MS;
|
|
37489
37873
|
}
|
|
37490
37874
|
async function closeExternalSlate(cwd, externalSessionId, trigger, now = () => new Date) {
|
|
37491
|
-
await
|
|
37875
|
+
await mkdir36(externalSlatesDir(cwd), { recursive: true });
|
|
37492
37876
|
return withFileLock(externalSlateLockPath(cwd, externalSessionId), async () => {
|
|
37493
37877
|
const external = await readExternalSlate(cwd, externalSessionId);
|
|
37494
37878
|
if (!external || external.closedAt !== undefined)
|
|
@@ -37689,18 +38073,32 @@ var SCHEMA_REGISTRY2 = {
|
|
|
37689
38073
|
|
|
37690
38074
|
// src/standard/profiles.ts
|
|
37691
38075
|
init_fs();
|
|
38076
|
+
init_entrypoint_targets();
|
|
37692
38077
|
import path118 from "path";
|
|
37693
38078
|
import { readFile as readFile62 } from "fs/promises";
|
|
37694
38079
|
var AGENT_MODULES = ["gdgraph", "gdctx", "gdskills", "gdwiki", "memory"];
|
|
37695
38080
|
var CI_MODULES = ["health", "testing"];
|
|
37696
|
-
var ROOT_ENTRYPOINTS = ["AGENTS.md", "agents.md", "CLAUDE.md", "claude.md"];
|
|
37697
38081
|
var INDEX_LINK = ".metaproject/index.md";
|
|
38082
|
+
function namedEntrypoints(manifest) {
|
|
38083
|
+
const agentEntrypoints = manifest.agentEntrypoints;
|
|
38084
|
+
const named = [];
|
|
38085
|
+
const seen = new Set;
|
|
38086
|
+
for (const entry of normalizeEntrypointTargets(agentEntrypoints).targets.root) {
|
|
38087
|
+
if (entry.runtime === "codex" && entry.scope === "local" && entry.mode === "skip")
|
|
38088
|
+
continue;
|
|
38089
|
+
if (seen.has(entry.path.toLowerCase()))
|
|
38090
|
+
continue;
|
|
38091
|
+
seen.add(entry.path.toLowerCase());
|
|
38092
|
+
named.push({ path: entry.path, scope: entry.scope });
|
|
38093
|
+
}
|
|
38094
|
+
return named;
|
|
38095
|
+
}
|
|
37698
38096
|
function enabledModuleKeys(manifest) {
|
|
37699
38097
|
const modules = manifest.modules ?? {};
|
|
37700
38098
|
return Object.entries(modules).filter(([, entry]) => entry?.enabled === true).map(([key]) => key);
|
|
37701
38099
|
}
|
|
37702
|
-
async function rootEntrypointLinksIndex(cwd) {
|
|
37703
|
-
for (const name of
|
|
38100
|
+
async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
38101
|
+
for (const { path: name } of namedEntrypoints(manifest)) {
|
|
37704
38102
|
const filePath = path118.join(cwd, name);
|
|
37705
38103
|
if (!await pathExists(filePath)) {
|
|
37706
38104
|
continue;
|
|
@@ -37748,7 +38146,7 @@ async function evaluateProfiles(cwd, manifest) {
|
|
|
37748
38146
|
satisfied.push("minimal");
|
|
37749
38147
|
}
|
|
37750
38148
|
const hasAgentModule = AGENT_MODULES.some((key) => new Set(enabledModuleKeys(manifest)).has(key));
|
|
37751
|
-
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd) && await pathExists(path118.join(cwd, ".metaproject", "rules"));
|
|
38149
|
+
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(path118.join(cwd, ".metaproject", "rules"));
|
|
37752
38150
|
if (agent) {
|
|
37753
38151
|
satisfied.push("agent");
|
|
37754
38152
|
}
|
|
@@ -37895,7 +38293,6 @@ function validateBranch(value, schema, rootSchema) {
|
|
|
37895
38293
|
walk4(value, schema, "$", rootSchema, errors);
|
|
37896
38294
|
return errors;
|
|
37897
38295
|
}
|
|
37898
|
-
var ROOT_ENTRYPOINTS2 = ["AGENTS.md", "agents.md", "CLAUDE.md", "claude.md"];
|
|
37899
38296
|
var INDEX_LINK2 = ".metaproject/index.md";
|
|
37900
38297
|
var MODULE_PATH_FIELDS = [
|
|
37901
38298
|
"core",
|
|
@@ -37988,14 +38385,14 @@ async function validateWorkspace2(cwd) {
|
|
|
37988
38385
|
}
|
|
37989
38386
|
}
|
|
37990
38387
|
}
|
|
37991
|
-
for (const name of
|
|
38388
|
+
for (const { path: name, scope } of namedEntrypoints(manifest)) {
|
|
37992
38389
|
const filePath = path119.join(cwd, name);
|
|
37993
38390
|
if (!await pathExists(filePath)) {
|
|
37994
38391
|
continue;
|
|
37995
38392
|
}
|
|
37996
38393
|
const content = await readFile63(filePath, "utf8");
|
|
37997
38394
|
if (!content.includes(INDEX_LINK2)) {
|
|
37998
|
-
errors.push(issue("entrypoint-missing-index-link", `Root entrypoint ${name} does not link ${INDEX_LINK2}`, `Add a reference to ${INDEX_LINK2} in ${name} (\`keryx rules sync\` does this).`));
|
|
38395
|
+
errors.push(issue("entrypoint-missing-index-link", `Root entrypoint ${name} does not link ${INDEX_LINK2}`, scope === "local" ? `Run \`keryx update\` to put the managed block back into ${name}.` : `Add a reference to ${INDEX_LINK2} in ${name} (\`keryx rules sync\` does this).`));
|
|
37999
38396
|
}
|
|
38000
38397
|
}
|
|
38001
38398
|
const profileEval = await evaluateProfiles(cwd, manifest);
|
|
@@ -38148,6 +38545,12 @@ var HELP_GROUPS = [
|
|
|
38148
38545
|
group: "Look and feel",
|
|
38149
38546
|
summary: "Open the theme picker \u2014 /theme [name] applies immediately."
|
|
38150
38547
|
},
|
|
38548
|
+
{
|
|
38549
|
+
kind: "slash",
|
|
38550
|
+
name: "/settings",
|
|
38551
|
+
group: "Look and feel",
|
|
38552
|
+
summary: "Every setting in one place, each with its On/Off or value buttons."
|
|
38553
|
+
},
|
|
38151
38554
|
{ kind: "slash", name: "/model", group: "Look and feel", summary: "Switch the model." },
|
|
38152
38555
|
{
|
|
38153
38556
|
kind: "slash",
|