@mrciphersmith/keryx 0.3.51 → 0.3.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +10480 -9688
- package/dist/core.js +957 -711
- package/package.json +1 -1
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.detail.md +114 -10
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.md +0 -1
- package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +3 -4
package/dist/core.js
CHANGED
|
@@ -7414,6 +7414,7 @@ async function worktreeFileIsClean(projectRoot, relativePath) {
|
|
|
7414
7414
|
}
|
|
7415
7415
|
|
|
7416
7416
|
// src/lib/git-worktrees.ts
|
|
7417
|
+
import { readdir as readdir8, readFile as readFile15, realpath as realpath7, stat as stat7 } from "fs/promises";
|
|
7417
7418
|
import path32 from "path";
|
|
7418
7419
|
async function resolveGitCommonDir(cwd) {
|
|
7419
7420
|
try {
|
|
@@ -7422,7 +7423,12 @@ async function resolveGitCommonDir(cwd) {
|
|
|
7422
7423
|
if (await proc.exited !== 0 || out.length === 0) {
|
|
7423
7424
|
return;
|
|
7424
7425
|
}
|
|
7425
|
-
|
|
7426
|
+
const absolute = path32.isAbsolute(out) ? out : path32.resolve(cwd, out);
|
|
7427
|
+
try {
|
|
7428
|
+
return path32.join(await realpath7(path32.dirname(absolute)), path32.basename(absolute));
|
|
7429
|
+
} catch {
|
|
7430
|
+
return absolute;
|
|
7431
|
+
}
|
|
7426
7432
|
} catch {
|
|
7427
7433
|
return;
|
|
7428
7434
|
}
|
|
@@ -7605,7 +7611,7 @@ var init_entrypoint_targets = __esm(() => {
|
|
|
7605
7611
|
});
|
|
7606
7612
|
|
|
7607
7613
|
// src/rules/export-render.ts
|
|
7608
|
-
import { readFile as
|
|
7614
|
+
import { readFile as readFile16, readdir as readdir9 } from "fs/promises";
|
|
7609
7615
|
import path33 from "path";
|
|
7610
7616
|
function isUnsafeRulePath(relativePath) {
|
|
7611
7617
|
return UNSAFE_RULE_PATH_PATTERN.test(relativePath) || relativePath.includes("<!--") || relativePath.includes("-->");
|
|
@@ -7670,7 +7676,7 @@ async function collectCanonicalRules(root) {
|
|
|
7670
7676
|
async function walk(dirAbsolute, dirRelative) {
|
|
7671
7677
|
let dirents;
|
|
7672
7678
|
try {
|
|
7673
|
-
dirents = await
|
|
7679
|
+
dirents = await readdir9(dirAbsolute, { withFileTypes: true });
|
|
7674
7680
|
} catch {
|
|
7675
7681
|
return;
|
|
7676
7682
|
}
|
|
@@ -7698,7 +7704,7 @@ async function collectCanonicalRules(root) {
|
|
|
7698
7704
|
}
|
|
7699
7705
|
let content;
|
|
7700
7706
|
try {
|
|
7701
|
-
content = await
|
|
7707
|
+
content = await readFile16(childAbsolute, "utf8");
|
|
7702
7708
|
} catch {
|
|
7703
7709
|
continue;
|
|
7704
7710
|
}
|
|
@@ -8384,7 +8390,7 @@ var init_surfaces = __esm(() => {
|
|
|
8384
8390
|
});
|
|
8385
8391
|
|
|
8386
8392
|
// src/integrations/markdown-block.ts
|
|
8387
|
-
import { readFile as
|
|
8393
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
8388
8394
|
import path36 from "path";
|
|
8389
8395
|
function dominantEol(content) {
|
|
8390
8396
|
const crlf = (content.match(/\r\n/g) ?? []).length;
|
|
@@ -8538,7 +8544,7 @@ async function installMarkdownBlock(root, relativePath, frontMatter, spec = INST
|
|
|
8538
8544
|
await writeContained(root, relativePath, `${frontMatter ?? ""}${spec.render()}`);
|
|
8539
8545
|
return [];
|
|
8540
8546
|
}
|
|
8541
|
-
const raw = await
|
|
8547
|
+
const raw = await readFile17(file, "utf8");
|
|
8542
8548
|
const eol = dominantEol(raw);
|
|
8543
8549
|
let blocks;
|
|
8544
8550
|
try {
|
|
@@ -8563,7 +8569,7 @@ async function uninstallMarkdownBlock(root, relativePath, frontMatter, spec = IN
|
|
|
8563
8569
|
const file = fileFor(root, relativePath);
|
|
8564
8570
|
if (!await pathExists(file))
|
|
8565
8571
|
return false;
|
|
8566
|
-
const raw = await
|
|
8572
|
+
const raw = await readFile17(file, "utf8");
|
|
8567
8573
|
const blocks = parseBlocks(raw, relativePath, spec);
|
|
8568
8574
|
if (blocks.length === 0)
|
|
8569
8575
|
return false;
|
|
@@ -8585,7 +8591,7 @@ async function inspectMarkdownBlock(root, relativePath, spec = INSTRUCTIONS_BLOC
|
|
|
8585
8591
|
const file = fileFor(root, relativePath);
|
|
8586
8592
|
if (!await pathExists(file))
|
|
8587
8593
|
return { state: "absent-file" };
|
|
8588
|
-
const raw = await
|
|
8594
|
+
const raw = await readFile17(file, "utf8");
|
|
8589
8595
|
let blocks;
|
|
8590
8596
|
try {
|
|
8591
8597
|
blocks = parseBlocks(raw, relativePath, spec);
|
|
@@ -9780,6 +9786,7 @@ function isModelTier(value) {
|
|
|
9780
9786
|
var MODEL_TIERS, TIER_ALIASES, SKILL_TIER_KEY = "model_tier", TIER_DECLARATION;
|
|
9781
9787
|
var init_model_tier = __esm(() => {
|
|
9782
9788
|
init_model_version();
|
|
9789
|
+
init_skill_frontmatter();
|
|
9783
9790
|
MODEL_TIERS = ["light", "standard", "deep"];
|
|
9784
9791
|
TIER_ALIASES = new Map([["cheap", "light"]]);
|
|
9785
9792
|
TIER_DECLARATION = new RegExp(`^\\s*(?:[-*]\\s*)?"?(?:${SKILL_TIER_KEY}|model-tier|modelTier)"?\\s*[:=]\\s*(.+?)\\s*,?\\s*$`, "i");
|
|
@@ -9789,7 +9796,7 @@ var init_model_tier = __esm(() => {
|
|
|
9789
9796
|
var CEILINGS_BY_KEY, SKILL_LENGTH_CEILINGS;
|
|
9790
9797
|
var init_skill_length_ceilings = __esm(() => {
|
|
9791
9798
|
CEILINGS_BY_KEY = {
|
|
9792
|
-
"core/reviewer-skill-creator":
|
|
9799
|
+
"core/reviewer-skill-creator": 279,
|
|
9793
9800
|
"orchestration/code-verifier": 339,
|
|
9794
9801
|
"orchestration/context-collector": 671,
|
|
9795
9802
|
"orchestration/feature-analyzer": 447,
|
|
@@ -11260,7 +11267,7 @@ var init_compile = __esm(() => {
|
|
|
11260
11267
|
});
|
|
11261
11268
|
|
|
11262
11269
|
// src/agents/export.ts
|
|
11263
|
-
import { readFile as
|
|
11270
|
+
import { readFile as readFile18, readdir as readdir10, lstat as lstat6, realpath as realpath8 } from "fs/promises";
|
|
11264
11271
|
import path41 from "path";
|
|
11265
11272
|
function matrixStateToExportLevel(state) {
|
|
11266
11273
|
return state === "native" || state === "adapter" ? state : "instruction-only";
|
|
@@ -11275,7 +11282,7 @@ function proseExportRelativePath(runtime, name) {
|
|
|
11275
11282
|
async function readExistingFileContent(projectRoot, relativePath) {
|
|
11276
11283
|
const absolute = path41.join(projectRoot, ...relativePath.split("/"));
|
|
11277
11284
|
try {
|
|
11278
|
-
return await
|
|
11285
|
+
return await readFile18(absolute, "utf8");
|
|
11279
11286
|
} catch (error) {
|
|
11280
11287
|
if (isNotFound(error))
|
|
11281
11288
|
return;
|
|
@@ -11307,7 +11314,7 @@ async function checkExportPathSafety(projectRoot, relativePath) {
|
|
|
11307
11314
|
ancestor = parent;
|
|
11308
11315
|
}
|
|
11309
11316
|
try {
|
|
11310
|
-
const [resolvedRoot, resolvedAncestor] = await Promise.all([
|
|
11317
|
+
const [resolvedRoot, resolvedAncestor] = await Promise.all([realpath8(projectRoot), realpath8(ancestor)]);
|
|
11311
11318
|
if (!isPathInside(resolvedRoot, resolvedAncestor)) {
|
|
11312
11319
|
return `refusing to write ${relativePath} \u2014 its resolved directory is outside the project root`;
|
|
11313
11320
|
}
|
|
@@ -11487,7 +11494,7 @@ async function scanManagedAgentExports(projectRoot, runtime) {
|
|
|
11487
11494
|
return { verified: [], handEdited: [] };
|
|
11488
11495
|
let entries;
|
|
11489
11496
|
try {
|
|
11490
|
-
entries = await
|
|
11497
|
+
entries = await readdir10(dirAbsolute, { withFileTypes: true });
|
|
11491
11498
|
} catch {
|
|
11492
11499
|
return { verified: [], handEdited: [] };
|
|
11493
11500
|
}
|
|
@@ -11508,7 +11515,7 @@ async function scanManagedAgentExports(projectRoot, runtime) {
|
|
|
11508
11515
|
continue;
|
|
11509
11516
|
let content;
|
|
11510
11517
|
try {
|
|
11511
|
-
content = await
|
|
11518
|
+
content = await readFile18(absolute, "utf8");
|
|
11512
11519
|
} catch {
|
|
11513
11520
|
continue;
|
|
11514
11521
|
}
|
|
@@ -11681,7 +11688,7 @@ var init_surfaces_agents = __esm(() => {
|
|
|
11681
11688
|
});
|
|
11682
11689
|
|
|
11683
11690
|
// src/lib/git-local-ignore.ts
|
|
11684
|
-
import { lstat as lstat7, mkdir as mkdir9, readFile as
|
|
11691
|
+
import { lstat as lstat7, mkdir as mkdir9, readFile as readFile19, realpath as realpath9, writeFile as writeFile6 } from "fs/promises";
|
|
11685
11692
|
import path42 from "path";
|
|
11686
11693
|
async function isPathIgnored(projectRoot, relativePath) {
|
|
11687
11694
|
if (await resolveGitCommonDir(projectRoot) === undefined)
|
|
@@ -11768,7 +11775,7 @@ async function explainIgnoredPaths(projectRoot, relativePaths) {
|
|
|
11768
11775
|
return [];
|
|
11769
11776
|
const excludePath = path42.join(commonDir, "info", "exclude");
|
|
11770
11777
|
const block = findManagedBlock(await readExcludeFile(excludePath).catch(() => ""), location.prefix);
|
|
11771
|
-
const excludeReal = await
|
|
11778
|
+
const excludeReal = await realpath9(excludePath).catch(() => excludePath);
|
|
11772
11779
|
let fields;
|
|
11773
11780
|
try {
|
|
11774
11781
|
const proc = Bun.spawn(["git", "check-ignore", "--verbose", "--non-matching", "--no-index", "-z", "--stdin"], {
|
|
@@ -11799,7 +11806,7 @@ async function explainIgnoredPaths(projectRoot, relativePaths) {
|
|
|
11799
11806
|
let sourcePath = sourcePaths.get(source);
|
|
11800
11807
|
if (sourcePath === undefined) {
|
|
11801
11808
|
const resolved = path42.resolve(location.toplevel, source);
|
|
11802
|
-
sourcePath = await
|
|
11809
|
+
sourcePath = await realpath9(resolved).catch(() => resolved);
|
|
11803
11810
|
sourcePaths.set(source, sourcePath);
|
|
11804
11811
|
}
|
|
11805
11812
|
const ignored = !pattern.startsWith("!");
|
|
@@ -11848,7 +11855,7 @@ async function resolveWorktreeLocation(projectRoot) {
|
|
|
11848
11855
|
}
|
|
11849
11856
|
}
|
|
11850
11857
|
async function readExcludeFile(excludePath) {
|
|
11851
|
-
return
|
|
11858
|
+
return readFile19(excludePath, "utf8").catch((error) => {
|
|
11852
11859
|
if (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
11853
11860
|
return "";
|
|
11854
11861
|
throw error;
|
|
@@ -11868,7 +11875,7 @@ function refuseUnsafeLines(lines) {
|
|
|
11868
11875
|
return;
|
|
11869
11876
|
}
|
|
11870
11877
|
async function refuseUnsafeExcludePath(commonDir, infoDir, excludePath) {
|
|
11871
|
-
const commonDirReal = await
|
|
11878
|
+
const commonDirReal = await realpath9(commonDir).catch(() => commonDir);
|
|
11872
11879
|
for (const [target, label] of [
|
|
11873
11880
|
[infoDir, "the git info directory"],
|
|
11874
11881
|
[excludePath, "the git exclude file"]
|
|
@@ -11877,7 +11884,7 @@ async function refuseUnsafeExcludePath(commonDir, infoDir, excludePath) {
|
|
|
11877
11884
|
if (stats === null)
|
|
11878
11885
|
continue;
|
|
11879
11886
|
if (stats.isSymbolicLink()) {
|
|
11880
|
-
const real = await
|
|
11887
|
+
const real = await realpath9(target).catch(() => {
|
|
11881
11888
|
return;
|
|
11882
11889
|
});
|
|
11883
11890
|
if (real === undefined)
|
|
@@ -11904,7 +11911,7 @@ var init_git_local_ignore = __esm(() => {
|
|
|
11904
11911
|
});
|
|
11905
11912
|
|
|
11906
11913
|
// src/lib/metaproject-gitignore.ts
|
|
11907
|
-
import { readFile as
|
|
11914
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
11908
11915
|
import path43 from "path";
|
|
11909
11916
|
async function syncMetaprojectIgnoreRules(projectRoot, options = {}) {
|
|
11910
11917
|
const notice = options.onNotice ?? (() => {});
|
|
@@ -11958,7 +11965,7 @@ async function moveBlockOutOfGitignore(projectRoot, notice) {
|
|
|
11958
11965
|
const gitignorePath = path43.join(projectRoot, GITIGNORE);
|
|
11959
11966
|
if (!await pathExists(gitignorePath))
|
|
11960
11967
|
return false;
|
|
11961
|
-
const content = await
|
|
11968
|
+
const content = await readFile20(gitignorePath, "utf8");
|
|
11962
11969
|
if (!managedBlockPattern().test(content))
|
|
11963
11970
|
return false;
|
|
11964
11971
|
const head = await readHeadBlob(projectRoot, GITIGNORE);
|
|
@@ -12261,7 +12268,7 @@ var init_rules_export_target = __esm(() => {
|
|
|
12261
12268
|
|
|
12262
12269
|
// src/integrations/surfaces-rules.ts
|
|
12263
12270
|
import { readFileSync as readFileSync7 } from "fs";
|
|
12264
|
-
import { readFile as
|
|
12271
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
12265
12272
|
import path45 from "path";
|
|
12266
12273
|
async function rulesSpec(root) {
|
|
12267
12274
|
const { rules, skipped } = await collectCanonicalRules(root);
|
|
@@ -12389,7 +12396,7 @@ async function installIntoOverride(root, relativePath) {
|
|
|
12389
12396
|
const refusal = await refuseEscapingSymlink(root, relativePath);
|
|
12390
12397
|
if (refusal !== undefined)
|
|
12391
12398
|
return { errors: [refusal], warnings };
|
|
12392
|
-
const content = await
|
|
12399
|
+
const content = await readFile21(path45.join(root, ...relativePath.split("/")), "utf8");
|
|
12393
12400
|
let next;
|
|
12394
12401
|
try {
|
|
12395
12402
|
next = withOverrideRulesSlot(content, spec.render());
|
|
@@ -12447,7 +12454,7 @@ async function inspectOverrideSlot(root, relativePath) {
|
|
|
12447
12454
|
}
|
|
12448
12455
|
async function readTextIfPresent(root, relativePath) {
|
|
12449
12456
|
try {
|
|
12450
|
-
return await
|
|
12457
|
+
return await readFile21(path45.join(root, ...relativePath.split("/")), "utf8");
|
|
12451
12458
|
} catch (error) {
|
|
12452
12459
|
if (error.code === "ENOENT")
|
|
12453
12460
|
return;
|
|
@@ -13118,11 +13125,11 @@ var init_integrations = __esm(() => {
|
|
|
13118
13125
|
|
|
13119
13126
|
// src/security/audit-harness/surfaces.ts
|
|
13120
13127
|
import path47 from "path";
|
|
13121
|
-
import { lstat as lstat8, readdir as
|
|
13128
|
+
import { lstat as lstat8, readdir as readdir11, realpath as realpath10, stat as stat8 } from "fs/promises";
|
|
13122
13129
|
async function resolvesInsideRoot(root, absolute) {
|
|
13123
13130
|
try {
|
|
13124
|
-
const rootReal = await
|
|
13125
|
-
const real = await
|
|
13131
|
+
const rootReal = await realpath10(root).catch(() => path47.resolve(root));
|
|
13132
|
+
const real = await realpath10(absolute);
|
|
13126
13133
|
return isPathInside(rootReal, real);
|
|
13127
13134
|
} catch {
|
|
13128
13135
|
return false;
|
|
@@ -13160,7 +13167,7 @@ async function listFilesByExtensions(root, dirRelative, extensions) {
|
|
|
13160
13167
|
}
|
|
13161
13168
|
let entries;
|
|
13162
13169
|
try {
|
|
13163
|
-
entries = await
|
|
13170
|
+
entries = await readdir11(dirAbsolute, { withFileTypes: true });
|
|
13164
13171
|
} catch {
|
|
13165
13172
|
return { found: [], unreadable: [toPosix(dirRelative)] };
|
|
13166
13173
|
}
|
|
@@ -13222,7 +13229,7 @@ async function walkScripts(root, dirRelative, depth, visited, acc) {
|
|
|
13222
13229
|
acc.unreadable.push(toPosix(dirRelative));
|
|
13223
13230
|
return;
|
|
13224
13231
|
}
|
|
13225
|
-
const dirReal = await
|
|
13232
|
+
const dirReal = await realpath10(dirAbsolute).catch(() => {
|
|
13226
13233
|
return;
|
|
13227
13234
|
});
|
|
13228
13235
|
if (dirReal !== undefined) {
|
|
@@ -13232,7 +13239,7 @@ async function walkScripts(root, dirRelative, depth, visited, acc) {
|
|
|
13232
13239
|
}
|
|
13233
13240
|
let entries;
|
|
13234
13241
|
try {
|
|
13235
|
-
entries = await
|
|
13242
|
+
entries = await readdir11(dirAbsolute, { withFileTypes: true });
|
|
13236
13243
|
} catch {
|
|
13237
13244
|
acc.unreadable.push(toPosix(dirRelative));
|
|
13238
13245
|
return;
|
|
@@ -13253,7 +13260,7 @@ async function walkScripts(root, dirRelative, depth, visited, acc) {
|
|
|
13253
13260
|
acc.unreadable.push(toPosix(childRelative));
|
|
13254
13261
|
continue;
|
|
13255
13262
|
}
|
|
13256
|
-
const stats = await
|
|
13263
|
+
const stats = await stat8(absolute).catch(() => {
|
|
13257
13264
|
return;
|
|
13258
13265
|
});
|
|
13259
13266
|
if (stats?.isDirectory()) {
|
|
@@ -14787,7 +14794,7 @@ var init_checks = __esm(() => {
|
|
|
14787
14794
|
|
|
14788
14795
|
// src/security/audit-harness/baseline.ts
|
|
14789
14796
|
import path48 from "path";
|
|
14790
|
-
import { readFile as
|
|
14797
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
14791
14798
|
function defaultBaselinePath(root) {
|
|
14792
14799
|
return path48.join(root, ".metaproject", "security-audit-baseline.json");
|
|
14793
14800
|
}
|
|
@@ -14862,7 +14869,7 @@ function indefiniteSuppressionFindings(applicableEntries) {
|
|
|
14862
14869
|
}
|
|
14863
14870
|
async function readRawBaselineFindingIds(filePath) {
|
|
14864
14871
|
try {
|
|
14865
|
-
const text = await
|
|
14872
|
+
const text = await readFile22(filePath, "utf8");
|
|
14866
14873
|
const parsed = JSON.parse(text);
|
|
14867
14874
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.entries)) {
|
|
14868
14875
|
return [];
|
|
@@ -14887,7 +14894,7 @@ async function addBaselineEntry(root, entry, options = {}) {
|
|
|
14887
14894
|
}
|
|
14888
14895
|
const now = options.now ? options.now() : new Date;
|
|
14889
14896
|
backupPath = `${filePath}.bak-${now.toISOString().replace(/[:.]/g, "-")}`;
|
|
14890
|
-
const preResealBytes = await
|
|
14897
|
+
const preResealBytes = await readFile22(filePath);
|
|
14891
14898
|
await writeContained(root, path48.relative(root, backupPath), preResealBytes);
|
|
14892
14899
|
entries = loaded.state.entries;
|
|
14893
14900
|
const rawIds = await readRawBaselineFindingIds(filePath);
|
|
@@ -14994,12 +15001,12 @@ var init_types2 = __esm(() => {
|
|
|
14994
15001
|
|
|
14995
15002
|
// src/security/audit-harness/proposals.ts
|
|
14996
15003
|
import path49 from "path";
|
|
14997
|
-
import { readFile as
|
|
15004
|
+
import { readFile as readFile23, realpath as realpath11 } from "fs/promises";
|
|
14998
15005
|
async function assertContained2(rootReal, absolute, label) {
|
|
14999
15006
|
if (!isPathInside(rootReal, absolute)) {
|
|
15000
15007
|
throw new Error(`Refusing to write ${label}: path escapes project root ${rootReal}.`);
|
|
15001
15008
|
}
|
|
15002
|
-
const real = await
|
|
15009
|
+
const real = await realpath11(absolute).catch(() => {
|
|
15003
15010
|
return;
|
|
15004
15011
|
});
|
|
15005
15012
|
if (real !== undefined) {
|
|
@@ -15010,7 +15017,7 @@ async function assertContained2(rootReal, absolute, label) {
|
|
|
15010
15017
|
}
|
|
15011
15018
|
let dir = path49.dirname(absolute);
|
|
15012
15019
|
for (;; ) {
|
|
15013
|
-
const dirReal = await
|
|
15020
|
+
const dirReal = await realpath11(dir).catch(() => {
|
|
15014
15021
|
return;
|
|
15015
15022
|
});
|
|
15016
15023
|
if (dirReal !== undefined) {
|
|
@@ -15108,7 +15115,7 @@ function countOccurrences(haystack, needle) {
|
|
|
15108
15115
|
async function applyTextEdit(rootReal, edit) {
|
|
15109
15116
|
const absolute = path49.resolve(rootReal, edit.path);
|
|
15110
15117
|
await assertContained2(rootReal, absolute, `target file "${edit.path}"`);
|
|
15111
|
-
const content = await
|
|
15118
|
+
const content = await readFile23(absolute, "utf8");
|
|
15112
15119
|
const occurrences = countOccurrences(content, edit.from);
|
|
15113
15120
|
if (occurrences === 0) {
|
|
15114
15121
|
throw new Error(`Cannot apply proposal: expected text not found in ${edit.path}.`);
|
|
@@ -15133,7 +15140,7 @@ async function applyAuditProposal(root, proposalId, options = {}) {
|
|
|
15133
15140
|
if (!PROPOSAL_ID_RE.test(proposalId)) {
|
|
15134
15141
|
throw new Error(`Invalid proposal id: ${proposalId}`);
|
|
15135
15142
|
}
|
|
15136
|
-
const rootReal = await
|
|
15143
|
+
const rootReal = await realpath11(root).catch(() => {
|
|
15137
15144
|
return;
|
|
15138
15145
|
}) ?? path49.resolve(root);
|
|
15139
15146
|
return withFileLock(lockPath2(rootReal), async () => {
|
|
@@ -15189,7 +15196,7 @@ var init_proposals = __esm(() => {
|
|
|
15189
15196
|
// src/security/audit-harness/index.ts
|
|
15190
15197
|
import { createHash as createHash7 } from "crypto";
|
|
15191
15198
|
import path50 from "path";
|
|
15192
|
-
import { readFile as
|
|
15199
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
15193
15200
|
function findingId(f) {
|
|
15194
15201
|
const pointerOrLine = f.location?.pointer ?? (f.location?.line !== undefined ? `line:${f.location.line}` : "");
|
|
15195
15202
|
const policyId = f.evidence.policyId ?? "";
|
|
@@ -15214,14 +15221,14 @@ function compareFindings(a, b) {
|
|
|
15214
15221
|
}
|
|
15215
15222
|
async function safeReadText(absolute) {
|
|
15216
15223
|
try {
|
|
15217
|
-
return await
|
|
15224
|
+
return await readFile24(absolute, "utf8");
|
|
15218
15225
|
} catch {
|
|
15219
15226
|
return;
|
|
15220
15227
|
}
|
|
15221
15228
|
}
|
|
15222
15229
|
async function safeReadBuffer(absolute) {
|
|
15223
15230
|
try {
|
|
15224
|
-
return await
|
|
15231
|
+
return await readFile24(absolute);
|
|
15225
15232
|
} catch {
|
|
15226
15233
|
return;
|
|
15227
15234
|
}
|
|
@@ -15819,7 +15826,7 @@ __export(exports_service, {
|
|
|
15819
15826
|
verifyConfigChecksum: () => verifyConfigChecksum
|
|
15820
15827
|
});
|
|
15821
15828
|
import path51 from "path";
|
|
15822
|
-
import { readFile as
|
|
15829
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
15823
15830
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
15824
15831
|
function validateSerializedOutput(content, exemptExfil) {
|
|
15825
15832
|
return validateSerializedContentForTransport(content, exemptExfil);
|
|
@@ -15952,7 +15959,7 @@ async function readLatestReport(cwd) {
|
|
|
15952
15959
|
}
|
|
15953
15960
|
let parsed;
|
|
15954
15961
|
try {
|
|
15955
|
-
parsed = JSON.parse(await
|
|
15962
|
+
parsed = JSON.parse(await readFile25(file, "utf8"));
|
|
15956
15963
|
} catch {
|
|
15957
15964
|
return { kind: "unusable" };
|
|
15958
15965
|
}
|
|
@@ -16072,9 +16079,9 @@ var init_service2 = __esm(() => {
|
|
|
16072
16079
|
});
|
|
16073
16080
|
|
|
16074
16081
|
// src/gdgraph/config.ts
|
|
16075
|
-
import
|
|
16082
|
+
import path57 from "path";
|
|
16076
16083
|
function gdgraphConfigPath(cwd) {
|
|
16077
|
-
return
|
|
16084
|
+
return path57.join(cwd, ".metaproject", "gdgraph.config.json");
|
|
16078
16085
|
}
|
|
16079
16086
|
function mergeGdgraphConfig(parsed) {
|
|
16080
16087
|
const base = DEFAULT_GDGRAPH_CONFIG;
|
|
@@ -16679,7 +16686,7 @@ var init_extract = __esm(() => {
|
|
|
16679
16686
|
});
|
|
16680
16687
|
|
|
16681
16688
|
// src/gdgraph/treesitter/grammars.ts
|
|
16682
|
-
import
|
|
16689
|
+
import path58 from "path";
|
|
16683
16690
|
function grammarAssetId(language) {
|
|
16684
16691
|
return `tree-sitter-${language}`;
|
|
16685
16692
|
}
|
|
@@ -16695,7 +16702,7 @@ function symbolLanguage(language) {
|
|
|
16695
16702
|
async function resolveGrammar(cwd, language, grammarsPath) {
|
|
16696
16703
|
const id = grammarAssetId(language);
|
|
16697
16704
|
const lock = await loadAssetsLock(cwd);
|
|
16698
|
-
const overrides = grammarsPath ? { [id]: { path:
|
|
16705
|
+
const overrides = grammarsPath ? { [id]: { path: path58.join(grammarsPath, `${id}.wasm`) } } : undefined;
|
|
16699
16706
|
const registry = registryFromLock(lock, overrides);
|
|
16700
16707
|
const resolved = await resolveAsset(registry, id);
|
|
16701
16708
|
if (!resolved) {
|
|
@@ -16725,7 +16732,7 @@ function toGrammarLanguages(languages) {
|
|
|
16725
16732
|
return out;
|
|
16726
16733
|
}
|
|
16727
16734
|
function grammarForFile(file, available) {
|
|
16728
|
-
const ext =
|
|
16735
|
+
const ext = path58.extname(file);
|
|
16729
16736
|
const preference = ext === ".tsx" ? ["tsx", "typescript"] : ext === ".ts" ? ["typescript"] : ext === ".jsx" || ext === ".js" || ext === ".mjs" || ext === ".cjs" ? ["javascript"] : ext === ".java" ? ["java"] : ext === ".py" ? ["python"] : [];
|
|
16730
16737
|
for (const language of preference) {
|
|
16731
16738
|
if (available.includes(language)) {
|
|
@@ -16987,7 +16994,7 @@ var init_adapter = __esm(() => {
|
|
|
16987
16994
|
|
|
16988
16995
|
// src/gdgraph/enrich.ts
|
|
16989
16996
|
import { mkdir as mkdir13, writeFile as writeFile7 } from "fs/promises";
|
|
16990
|
-
import
|
|
16997
|
+
import path59 from "path";
|
|
16991
16998
|
async function enrichBuildWithSymbols(cwd, files, resolve) {
|
|
16992
16999
|
const config = await loadGdgraphConfig(cwd);
|
|
16993
17000
|
const treesitterConfig = {
|
|
@@ -17003,12 +17010,12 @@ async function enrichBuildWithSymbols(cwd, files, resolve) {
|
|
|
17003
17010
|
return { enriched: true, symbols: layer.symbols.length, calls: layer.calls.length };
|
|
17004
17011
|
}
|
|
17005
17012
|
async function writeSymbolLayer(cwd, layer) {
|
|
17006
|
-
const storageDir =
|
|
17013
|
+
const storageDir = path59.join(cwd, ".metaproject", "data", "gdgraph", "storage");
|
|
17007
17014
|
await mkdir13(storageDir, { recursive: true });
|
|
17008
|
-
await writeFile7(
|
|
17015
|
+
await writeFile7(path59.join(storageDir, "symbols.jsonl"), layer.symbols.map((symbol) => JSON.stringify(symbol)).join(`
|
|
17009
17016
|
`) + `
|
|
17010
17017
|
`, "utf8");
|
|
17011
|
-
await writeFile7(
|
|
17018
|
+
await writeFile7(path59.join(storageDir, "calls.jsonl"), layer.calls.map((call) => JSON.stringify(call)).join(`
|
|
17012
17019
|
`) + `
|
|
17013
17020
|
`, "utf8");
|
|
17014
17021
|
}
|
|
@@ -17200,11 +17207,11 @@ var NOT_CODE_SCOPED = "none";
|
|
|
17200
17207
|
var init_describes = () => {};
|
|
17201
17208
|
|
|
17202
17209
|
// src/sync/provenance.ts
|
|
17203
|
-
import { mkdir as mkdir14, readFile as
|
|
17204
|
-
import
|
|
17210
|
+
import { mkdir as mkdir14, readFile as readFile29, writeFile as writeFile8 } from "fs/promises";
|
|
17211
|
+
import path60 from "path";
|
|
17205
17212
|
import { spawn } from "child_process";
|
|
17206
17213
|
function provenancePath(cwd, module) {
|
|
17207
|
-
return
|
|
17214
|
+
return path60.join(cwd, ".metaproject", "data", module, ".provenance.json");
|
|
17208
17215
|
}
|
|
17209
17216
|
function gitCmdResult(cwd, args) {
|
|
17210
17217
|
return new Promise((resolve) => {
|
|
@@ -17249,7 +17256,7 @@ async function recordProvenance(cwd, module, at) {
|
|
|
17249
17256
|
if (!head)
|
|
17250
17257
|
return;
|
|
17251
17258
|
const file = provenancePath(cwd, module);
|
|
17252
|
-
await mkdir14(
|
|
17259
|
+
await mkdir14(path60.dirname(file), { recursive: true });
|
|
17253
17260
|
const provenance = { commit: head.commit, branch: head.branch, builtAt: at };
|
|
17254
17261
|
await writeFile8(file, `${JSON.stringify(provenance, null, 2)}
|
|
17255
17262
|
`, "utf8");
|
|
@@ -17260,7 +17267,7 @@ async function readProvenance(cwd, module) {
|
|
|
17260
17267
|
return null;
|
|
17261
17268
|
}
|
|
17262
17269
|
try {
|
|
17263
|
-
const parsed = JSON.parse(await
|
|
17270
|
+
const parsed = JSON.parse(await readFile29(file, "utf8"));
|
|
17264
17271
|
if (parsed && typeof parsed === "object" && typeof parsed.commit === "string") {
|
|
17265
17272
|
return parsed;
|
|
17266
17273
|
}
|
|
@@ -17273,8 +17280,8 @@ var init_provenance = __esm(() => {
|
|
|
17273
17280
|
|
|
17274
17281
|
// src/gdgraph/staleness.ts
|
|
17275
17282
|
import { spawn as spawn2 } from "child_process";
|
|
17276
|
-
import { stat as
|
|
17277
|
-
import
|
|
17283
|
+
import { stat as stat9 } from "fs/promises";
|
|
17284
|
+
import path61 from "path";
|
|
17278
17285
|
function gitStatusPorcelain(cwd) {
|
|
17279
17286
|
return new Promise((resolve) => {
|
|
17280
17287
|
try {
|
|
@@ -17297,10 +17304,10 @@ function gitStatusPorcelain(cwd) {
|
|
|
17297
17304
|
});
|
|
17298
17305
|
}
|
|
17299
17306
|
function nodesJsonlPath(cwd) {
|
|
17300
|
-
return
|
|
17307
|
+
return path61.join(cwd, ".metaproject", "data", "gdgraph", "storage", "nodes.jsonl");
|
|
17301
17308
|
}
|
|
17302
17309
|
function gdgraphConfigPath2(cwd) {
|
|
17303
|
-
return
|
|
17310
|
+
return path61.join(cwd, ".metaproject", "gdgraph.config.json");
|
|
17304
17311
|
}
|
|
17305
17312
|
function categorizeStatusEntries(entries, rootPrefix) {
|
|
17306
17313
|
const metaprojectPrefix = `${rootPrefix}.metaproject/`;
|
|
@@ -17360,7 +17367,7 @@ async function committedChangedFiles(cwd, fromCommit, toCommit, rootPrefix) {
|
|
|
17360
17367
|
return diff.split("\x00").filter((entry) => entry.length > 0 && !entry.startsWith(metaprojectPrefix));
|
|
17361
17368
|
}
|
|
17362
17369
|
async function checkGraphStaleness(cwd) {
|
|
17363
|
-
const nodesStat = await
|
|
17370
|
+
const nodesStat = await stat9(nodesJsonlPath(cwd)).catch(() => null);
|
|
17364
17371
|
if (!nodesStat) {
|
|
17365
17372
|
return { status: "stale", reasons: ["graph has not been built yet (no nodes.jsonl)"] };
|
|
17366
17373
|
}
|
|
@@ -17381,7 +17388,7 @@ async function checkGraphStaleness(cwd) {
|
|
|
17381
17388
|
}
|
|
17382
17389
|
} else {
|
|
17383
17390
|
provenanceMissing = true;
|
|
17384
|
-
const logsHeadStat = await
|
|
17391
|
+
const logsHeadStat = await stat9(path61.join(cwd, ".git", "logs", "HEAD")).catch(() => null);
|
|
17385
17392
|
if (logsHeadStat && logsHeadStat.mtimeMs > nodesStat.mtimeMs) {
|
|
17386
17393
|
reasons.push("repo HEAD moved since the graph was built (no build provenance recorded; inferred from .git/logs/HEAD)");
|
|
17387
17394
|
}
|
|
@@ -17410,7 +17417,7 @@ async function checkGraphStaleness(cwd) {
|
|
|
17410
17417
|
if (projectRelative === null) {
|
|
17411
17418
|
continue;
|
|
17412
17419
|
}
|
|
17413
|
-
const fileStat = await
|
|
17420
|
+
const fileStat = await stat9(path61.join(cwd, projectRelative)).catch(() => null);
|
|
17414
17421
|
if (fileStat && fileStat.mtimeMs > nodesStat.mtimeMs) {
|
|
17415
17422
|
postBuildEdits.push(displayPath);
|
|
17416
17423
|
}
|
|
@@ -17420,7 +17427,7 @@ async function checkGraphStaleness(cwd) {
|
|
|
17420
17427
|
}
|
|
17421
17428
|
}
|
|
17422
17429
|
}
|
|
17423
|
-
const configStat = await
|
|
17430
|
+
const configStat = await stat9(gdgraphConfigPath2(cwd)).catch(() => null);
|
|
17424
17431
|
if (configStat && configStat.mtimeMs > nodesStat.mtimeMs) {
|
|
17425
17432
|
reasons.push("gdgraph.config.json changed since the graph was built");
|
|
17426
17433
|
}
|
|
@@ -17545,19 +17552,19 @@ var init_types3 = __esm(() => {
|
|
|
17545
17552
|
});
|
|
17546
17553
|
|
|
17547
17554
|
// src/wiki/collect.ts
|
|
17548
|
-
import { readFile as
|
|
17549
|
-
import
|
|
17555
|
+
import { readFile as readFile30, readdir as readdir12 } from "fs/promises";
|
|
17556
|
+
import path62 from "path";
|
|
17550
17557
|
function wikiRootPath(cwd) {
|
|
17551
|
-
return
|
|
17558
|
+
return path62.join(cwd, ".metaproject", "wiki");
|
|
17552
17559
|
}
|
|
17553
17560
|
async function collectPages(cwd) {
|
|
17554
17561
|
const root = wikiRootPath(cwd);
|
|
17555
17562
|
const pages = [];
|
|
17556
17563
|
for (const { type, folder } of WIKI_PAGE_TYPES) {
|
|
17557
|
-
const dir =
|
|
17564
|
+
const dir = path62.join(root, folder);
|
|
17558
17565
|
let names;
|
|
17559
17566
|
try {
|
|
17560
|
-
names = await
|
|
17567
|
+
names = await readdir12(dir);
|
|
17561
17568
|
} catch (error) {
|
|
17562
17569
|
if (isNotFound(error)) {
|
|
17563
17570
|
continue;
|
|
@@ -17569,10 +17576,10 @@ async function collectPages(cwd) {
|
|
|
17569
17576
|
if (!entry.endsWith(".md")) {
|
|
17570
17577
|
continue;
|
|
17571
17578
|
}
|
|
17572
|
-
const absolutePath =
|
|
17579
|
+
const absolutePath = path62.join(dir, entry);
|
|
17573
17580
|
let content;
|
|
17574
17581
|
try {
|
|
17575
|
-
content = await
|
|
17582
|
+
content = await readFile30(absolutePath, "utf8");
|
|
17576
17583
|
} catch (error) {
|
|
17577
17584
|
const message = error instanceof Error ? error.message : String(error);
|
|
17578
17585
|
throw new WikiCollectionError(absolutePath, `the wiki store could not be read (${message}). This is not the same as "no pages": ` + `${absolutePath} exists but could not be read.`);
|
|
@@ -18379,7 +18386,7 @@ var init_section_index = __esm(() => {
|
|
|
18379
18386
|
});
|
|
18380
18387
|
|
|
18381
18388
|
// src/security/guard.ts
|
|
18382
|
-
import
|
|
18389
|
+
import path63 from "path";
|
|
18383
18390
|
function prepareOutputForPersistence(guard, original) {
|
|
18384
18391
|
if (!guard.allowed) {
|
|
18385
18392
|
return { allowed: false, reason: guard.reason ?? "security gate blocked" };
|
|
@@ -18397,7 +18404,7 @@ function prepareOutputForPersistence(guard, original) {
|
|
|
18397
18404
|
};
|
|
18398
18405
|
}
|
|
18399
18406
|
async function resolveManifestSecurityState(cwd) {
|
|
18400
|
-
const manifestPath =
|
|
18407
|
+
const manifestPath = path63.join(securityProjectRoot(cwd), ".metaproject", "metaproject.json");
|
|
18401
18408
|
if (!await pathExists(manifestPath)) {
|
|
18402
18409
|
return { enabled: false, manifestUnreadable: false };
|
|
18403
18410
|
}
|
|
@@ -18603,10 +18610,10 @@ var MEMORY_EMBEDDING_ID = "memory.embedding", DEFAULT_EMBED_DIMS = 96;
|
|
|
18603
18610
|
|
|
18604
18611
|
// src/memory/embedding/index.ts
|
|
18605
18612
|
import { createHash as createHash10 } from "crypto";
|
|
18606
|
-
import { mkdir as mkdir15, readFile as
|
|
18607
|
-
import
|
|
18613
|
+
import { mkdir as mkdir15, readFile as readFile31, writeFile as writeFile9 } from "fs/promises";
|
|
18614
|
+
import path64 from "path";
|
|
18608
18615
|
function embeddingsDir(cwd) {
|
|
18609
|
-
return
|
|
18616
|
+
return path64.join(cwd, ".metaproject", "data", "memory", "embeddings");
|
|
18610
18617
|
}
|
|
18611
18618
|
function entryText(entry) {
|
|
18612
18619
|
return `${entry.title}
|
|
@@ -18635,24 +18642,24 @@ async function buildEmbeddingIndex(cwd, entries, embedder, model, now) {
|
|
|
18635
18642
|
};
|
|
18636
18643
|
const dir = embeddingsDir(cwd);
|
|
18637
18644
|
await mkdir15(dir, { recursive: true });
|
|
18638
|
-
await writeFile9(
|
|
18645
|
+
await writeFile9(path64.join(dir, "index.meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
18639
18646
|
`, "utf8");
|
|
18640
|
-
await writeFile9(
|
|
18647
|
+
await writeFile9(path64.join(dir, "vectors.jsonl"), records.map((record) => JSON.stringify(record)).join(`
|
|
18641
18648
|
`) + (records.length > 0 ? `
|
|
18642
18649
|
` : ""), "utf8");
|
|
18643
18650
|
return toIndex(meta, records);
|
|
18644
18651
|
}
|
|
18645
18652
|
async function loadEmbeddingIndex(cwd) {
|
|
18646
18653
|
const dir = embeddingsDir(cwd);
|
|
18647
|
-
const metaPath =
|
|
18648
|
-
const vectorsPath =
|
|
18654
|
+
const metaPath = path64.join(dir, "index.meta.json");
|
|
18655
|
+
const vectorsPath = path64.join(dir, "vectors.jsonl");
|
|
18649
18656
|
if (!await pathExists(metaPath) || !await pathExists(vectorsPath)) {
|
|
18650
18657
|
return null;
|
|
18651
18658
|
}
|
|
18652
18659
|
try {
|
|
18653
|
-
const meta = JSON.parse(await
|
|
18660
|
+
const meta = JSON.parse(await readFile31(metaPath, "utf8"));
|
|
18654
18661
|
const records = [];
|
|
18655
|
-
for (const line of (await
|
|
18662
|
+
for (const line of (await readFile31(vectorsPath, "utf8")).split(`
|
|
18656
18663
|
`)) {
|
|
18657
18664
|
if (line.trim().length === 0) {
|
|
18658
18665
|
continue;
|
|
@@ -18726,10 +18733,10 @@ var init_embedding = __esm(() => {
|
|
|
18726
18733
|
});
|
|
18727
18734
|
|
|
18728
18735
|
// src/wiki/section-tombstone.ts
|
|
18729
|
-
import
|
|
18730
|
-
import { stat as
|
|
18736
|
+
import path65 from "path";
|
|
18737
|
+
import { stat as stat10 } from "fs/promises";
|
|
18731
18738
|
function sectionRegistryPath(cwd) {
|
|
18732
|
-
return
|
|
18739
|
+
return path65.join(cwd, ".metaproject", "wiki", ".sections.json");
|
|
18733
18740
|
}
|
|
18734
18741
|
function emptySectionRegistry() {
|
|
18735
18742
|
return { version: SECTION_REGISTRY_VERSION, entries: [], tombstones: [], lifted: [] };
|
|
@@ -18810,7 +18817,7 @@ function parseLifted(value) {
|
|
|
18810
18817
|
async function readSectionRegistryState(cwd) {
|
|
18811
18818
|
const file = sectionRegistryPath(cwd);
|
|
18812
18819
|
try {
|
|
18813
|
-
await
|
|
18820
|
+
await stat10(file);
|
|
18814
18821
|
} catch (error) {
|
|
18815
18822
|
if (isNotFound(error)) {
|
|
18816
18823
|
return { state: "absent", path: file };
|
|
@@ -19030,11 +19037,11 @@ var init_section_tombstone = __esm(() => {
|
|
|
19030
19037
|
});
|
|
19031
19038
|
|
|
19032
19039
|
// src/memory/cross-layer.ts
|
|
19033
|
-
import { readFile as
|
|
19034
|
-
import
|
|
19040
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
19041
|
+
import path66 from "path";
|
|
19035
19042
|
function extractWikiReferences(entryRelativePath, entryAbsolutePath, content, wikiRoot) {
|
|
19036
19043
|
const references = [];
|
|
19037
|
-
const entryDir =
|
|
19044
|
+
const entryDir = path66.dirname(entryAbsolutePath);
|
|
19038
19045
|
const lines = content.split(`
|
|
19039
19046
|
`);
|
|
19040
19047
|
for (let index = 0;index < lines.length; index += 1) {
|
|
@@ -19049,16 +19056,16 @@ function extractWikiReferences(entryRelativePath, entryAbsolutePath, content, wi
|
|
|
19049
19056
|
if (withoutAnchor.length === 0) {
|
|
19050
19057
|
continue;
|
|
19051
19058
|
}
|
|
19052
|
-
const absolute =
|
|
19053
|
-
const relativeToWiki =
|
|
19054
|
-
if (relativeToWiki.startsWith("..") ||
|
|
19059
|
+
const absolute = path66.resolve(entryDir, withoutAnchor);
|
|
19060
|
+
const relativeToWiki = path66.relative(wikiRoot, absolute);
|
|
19061
|
+
if (relativeToWiki.startsWith("..") || path66.isAbsolute(relativeToWiki)) {
|
|
19055
19062
|
continue;
|
|
19056
19063
|
}
|
|
19057
19064
|
references.push({
|
|
19058
19065
|
entry: entryRelativePath,
|
|
19059
19066
|
kind: "page-path",
|
|
19060
19067
|
raw: href,
|
|
19061
|
-
target: relativeToWiki.split(
|
|
19068
|
+
target: relativeToWiki.split(path66.sep).join("/"),
|
|
19062
19069
|
line
|
|
19063
19070
|
});
|
|
19064
19071
|
}
|
|
@@ -19078,11 +19085,11 @@ function isDangling(finding) {
|
|
|
19078
19085
|
return finding.state !== "intact";
|
|
19079
19086
|
}
|
|
19080
19087
|
async function loadWikiKnowledgeView(cwd) {
|
|
19081
|
-
const wikiRoot =
|
|
19088
|
+
const wikiRoot = path66.join(cwd, ".metaproject", "wiki");
|
|
19082
19089
|
const pages = await collectPages(cwd);
|
|
19083
19090
|
const index = buildSectionIndex(await Promise.all(pages.map(async (page) => ({
|
|
19084
19091
|
page,
|
|
19085
|
-
content: await
|
|
19092
|
+
content: await readFile32(page.absolutePath, "utf8").catch(() => "")
|
|
19086
19093
|
}))));
|
|
19087
19094
|
return {
|
|
19088
19095
|
index,
|
|
@@ -19195,7 +19202,7 @@ async function checkMemoryCrossLayer(cwd, entries, view) {
|
|
|
19195
19202
|
const resolved = view ?? await loadWikiKnowledgeView(cwd);
|
|
19196
19203
|
const findings = [];
|
|
19197
19204
|
for (const entry of entries) {
|
|
19198
|
-
const content = await
|
|
19205
|
+
const content = await readFile32(entry.absolutePath, "utf8").catch(() => null);
|
|
19199
19206
|
if (content === null) {
|
|
19200
19207
|
findings.push({
|
|
19201
19208
|
reference: {
|
|
@@ -19296,7 +19303,7 @@ function round2(value) {
|
|
|
19296
19303
|
var init_dedup = () => {};
|
|
19297
19304
|
|
|
19298
19305
|
// src/memory/check.ts
|
|
19299
|
-
import
|
|
19306
|
+
import path67 from "path";
|
|
19300
19307
|
async function checkMemory(cwd, config) {
|
|
19301
19308
|
const entries = await collectEntries(cwd);
|
|
19302
19309
|
const issues = [];
|
|
@@ -19312,7 +19319,7 @@ async function checkMemory(cwd, config) {
|
|
|
19312
19319
|
}
|
|
19313
19320
|
for (const file of entry.scopes.files) {
|
|
19314
19321
|
const clean = file.replace(/^`|`$/g, "");
|
|
19315
|
-
if (!await pathExists(
|
|
19322
|
+
if (!await pathExists(path67.resolve(cwd, clean))) {
|
|
19316
19323
|
issues.push({ path: entry.relativePath, kind: "link", message: `related file not found: ${clean}` });
|
|
19317
19324
|
}
|
|
19318
19325
|
}
|
|
@@ -19366,13 +19373,13 @@ var init_check = __esm(() => {
|
|
|
19366
19373
|
});
|
|
19367
19374
|
|
|
19368
19375
|
// src/memory/write.ts
|
|
19369
|
-
import { mkdir as mkdir16, open as open2, readFile as
|
|
19370
|
-
import
|
|
19376
|
+
import { mkdir as mkdir16, open as open2, readFile as readFile33, rename as rename5, rm as rm4, writeFile as writeFile10 } from "fs/promises";
|
|
19377
|
+
import path68 from "path";
|
|
19371
19378
|
async function resolveCanonicalEntryPath(cwd, relativePath) {
|
|
19372
|
-
const root =
|
|
19373
|
-
const absolutePath =
|
|
19374
|
-
const normalized = toPosix2(
|
|
19375
|
-
if (!normalized || normalized.startsWith("../") ||
|
|
19379
|
+
const root = path68.resolve(memoryRoot(cwd));
|
|
19380
|
+
const absolutePath = path68.resolve(root, relativePath);
|
|
19381
|
+
const normalized = toPosix2(path68.relative(root, absolutePath));
|
|
19382
|
+
if (!normalized || normalized.startsWith("../") || path68.isAbsolute(normalized) || !normalized.endsWith(".md")) {
|
|
19376
19383
|
return null;
|
|
19377
19384
|
}
|
|
19378
19385
|
return { absolutePath, relativePath: normalized };
|
|
@@ -19407,7 +19414,7 @@ async function writeCanonicalPair(input) {
|
|
|
19407
19414
|
return asPair(secondGuard.result, [first.value.relativePath]);
|
|
19408
19415
|
const prepared = [firstGuard.value, secondGuard.value];
|
|
19409
19416
|
const warnings = prepared.flatMap((entry) => entry.warnings);
|
|
19410
|
-
const [originalFirst, originalSecond] = await Promise.all(prepared.map((entry) =>
|
|
19417
|
+
const [originalFirst, originalSecond] = await Promise.all(prepared.map((entry) => readFile33(entry.absolutePath, "utf8")));
|
|
19411
19418
|
try {
|
|
19412
19419
|
await replaceAtomically(prepared[0].absolutePath, prepared[0].content);
|
|
19413
19420
|
if (input.failReplaceAt === 2)
|
|
@@ -19468,9 +19475,9 @@ function validateNextEntry(relativePath, content) {
|
|
|
19468
19475
|
return null;
|
|
19469
19476
|
}
|
|
19470
19477
|
async function replaceAtomically(target, content) {
|
|
19471
|
-
const dir =
|
|
19478
|
+
const dir = path68.dirname(target);
|
|
19472
19479
|
await mkdir16(dir, { recursive: true });
|
|
19473
|
-
const tmp =
|
|
19480
|
+
const tmp = path68.join(dir, `.${path68.basename(target)}.keryx-tmp-${process.pid}-${Math.random().toString(16).slice(2)}`);
|
|
19474
19481
|
try {
|
|
19475
19482
|
await writeFile10(tmp, content, "utf8");
|
|
19476
19483
|
const file = await open2(tmp, "r");
|
|
@@ -19506,7 +19513,7 @@ function header(content, name) {
|
|
|
19506
19513
|
return content.match(new RegExp(`^${name}:\\s*(.+)$`, "mi"))?.[1]?.trim() ?? null;
|
|
19507
19514
|
}
|
|
19508
19515
|
function toPosix2(value) {
|
|
19509
|
-
return value.split(
|
|
19516
|
+
return value.split(path68.sep).join("/");
|
|
19510
19517
|
}
|
|
19511
19518
|
function message(cause) {
|
|
19512
19519
|
return cause instanceof Error ? cause.message : String(cause);
|
|
@@ -19519,14 +19526,14 @@ var init_write = __esm(() => {
|
|
|
19519
19526
|
|
|
19520
19527
|
// src/memory/ingest.ts
|
|
19521
19528
|
import { existsSync as existsSync6 } from "fs";
|
|
19522
|
-
import { readFile as
|
|
19523
|
-
import
|
|
19529
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
19530
|
+
import path69 from "path";
|
|
19524
19531
|
async function ingestMemory(cwd, source, filePath, config, now) {
|
|
19525
|
-
const absolute =
|
|
19532
|
+
const absolute = path69.resolve(cwd, filePath);
|
|
19526
19533
|
if (!await pathExists(absolute)) {
|
|
19527
19534
|
throw new Error(`Ingest source not found: ${filePath}`);
|
|
19528
19535
|
}
|
|
19529
|
-
const content = await
|
|
19536
|
+
const content = await readFile34(absolute, "utf8");
|
|
19530
19537
|
const candidates = extractCandidates2(content);
|
|
19531
19538
|
const type = SOURCE_TYPE[source] ?? "lesson";
|
|
19532
19539
|
const confidence = SOURCE_CONFIDENCE[source] ?? "low";
|
|
@@ -19539,8 +19546,8 @@ async function ingestMemory(cwd, source, filePath, config, now) {
|
|
|
19539
19546
|
const securityWarnings = [];
|
|
19540
19547
|
const securitySkipped = [];
|
|
19541
19548
|
let skippedDuplicates = 0;
|
|
19542
|
-
const dir =
|
|
19543
|
-
const link =
|
|
19549
|
+
const dir = path69.join(memoryRoot(cwd), folder);
|
|
19550
|
+
const link = path69.relative(cwd, absolute);
|
|
19544
19551
|
const date = dateString(now);
|
|
19545
19552
|
for (const text of candidates) {
|
|
19546
19553
|
const title = toTitle(text);
|
|
@@ -19606,7 +19613,7 @@ async function ingestMemory(cwd, source, filePath, config, now) {
|
|
|
19606
19613
|
return result;
|
|
19607
19614
|
}
|
|
19608
19615
|
async function reconcileEntry(cwd, relativePath, absolutePath, source, link, date) {
|
|
19609
|
-
const content = await
|
|
19616
|
+
const content = await readFile34(absolutePath, "utf8");
|
|
19610
19617
|
const note = `- Reconciled: ${source} ${date}${link ? ` (${link})` : ""}`;
|
|
19611
19618
|
if (content.includes(note)) {
|
|
19612
19619
|
return { changed: false };
|
|
@@ -19721,7 +19728,7 @@ function uniqueSlug(dir, title) {
|
|
|
19721
19728
|
const base = slugify2(title) || "entry";
|
|
19722
19729
|
let slug = base;
|
|
19723
19730
|
let counter = 2;
|
|
19724
|
-
while (existsSync6(
|
|
19731
|
+
while (existsSync6(path69.join(dir, `${slug}.md`))) {
|
|
19725
19732
|
slug = `${base}-${counter}`;
|
|
19726
19733
|
counter += 1;
|
|
19727
19734
|
}
|
|
@@ -19755,8 +19762,8 @@ var init_ingest = __esm(() => {
|
|
|
19755
19762
|
});
|
|
19756
19763
|
|
|
19757
19764
|
// src/memory/supersede.ts
|
|
19758
|
-
import { readFile as
|
|
19759
|
-
import
|
|
19765
|
+
import { readFile as readFile35 } from "fs/promises";
|
|
19766
|
+
import path70 from "path";
|
|
19760
19767
|
async function supersedeEntry(input, now) {
|
|
19761
19768
|
const date = input.date ?? now.toISOString().slice(0, 10);
|
|
19762
19769
|
const today = now.toISOString().slice(0, 10);
|
|
@@ -19768,8 +19775,8 @@ async function supersedeEntry(input, now) {
|
|
|
19768
19775
|
if (!newResolved) {
|
|
19769
19776
|
throw new Error(`Superseding entry not found: ${input.newPath}`);
|
|
19770
19777
|
}
|
|
19771
|
-
const oldContent = await
|
|
19772
|
-
const newContent = await
|
|
19778
|
+
const oldContent = await readFile35(oldResolved.absolute, "utf8");
|
|
19779
|
+
const newContent = await readFile35(newResolved.absolute, "utf8");
|
|
19773
19780
|
if (headerValue(oldContent, "Superseded-By") === newResolved.relative && headerValue(newContent, "Supersedes") === oldResolved.relative) {
|
|
19774
19781
|
return {
|
|
19775
19782
|
superseded: oldResolved.relative,
|
|
@@ -19816,12 +19823,12 @@ async function supersedeEntry(input, now) {
|
|
|
19816
19823
|
async function resolveEntryPath(cwd, raw) {
|
|
19817
19824
|
const root = memoryRoot(cwd);
|
|
19818
19825
|
const candidates = [
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19826
|
+
path70.resolve(cwd, raw),
|
|
19827
|
+
path70.resolve(root, raw),
|
|
19828
|
+
path70.isAbsolute(raw) ? raw : path70.join(cwd, raw)
|
|
19822
19829
|
];
|
|
19823
19830
|
for (const absolute of candidates) {
|
|
19824
|
-
const confined = await resolveCanonicalEntryPath(cwd,
|
|
19831
|
+
const confined = await resolveCanonicalEntryPath(cwd, path70.relative(root, absolute));
|
|
19825
19832
|
if (confined && await pathExists(confined.absolutePath)) {
|
|
19826
19833
|
return { absolute: confined.absolutePath, relative: confined.relativePath };
|
|
19827
19834
|
}
|
|
@@ -20016,10 +20023,10 @@ __export(exports_service3, {
|
|
|
20016
20023
|
selectHandoffEntries: () => selectHandoffEntries
|
|
20017
20024
|
});
|
|
20018
20025
|
import { createHash as createHash11 } from "crypto";
|
|
20019
|
-
import { mkdir as mkdir17, readFile as
|
|
20020
|
-
import
|
|
20026
|
+
import { mkdir as mkdir17, readFile as readFile36, writeFile as writeFile11 } from "fs/promises";
|
|
20027
|
+
import path71 from "path";
|
|
20021
20028
|
function dataRoot2(cwd) {
|
|
20022
|
-
return
|
|
20029
|
+
return path71.join(cwd, ".metaproject", "data", "memory");
|
|
20023
20030
|
}
|
|
20024
20031
|
function createMemoryService() {
|
|
20025
20032
|
const reportStore = createMemoryReportStore();
|
|
@@ -20034,9 +20041,9 @@ function createMemoryService() {
|
|
|
20034
20041
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
20035
20042
|
throw new Error(`Invalid slug: ${slug}. Use lowercase letters, digits, and hyphens.`);
|
|
20036
20043
|
}
|
|
20037
|
-
const dir =
|
|
20038
|
-
const filePath =
|
|
20039
|
-
const relativePath =
|
|
20044
|
+
const dir = path71.join(memoryRoot(input.cwd), typeConfig.folder);
|
|
20045
|
+
const filePath = path71.join(dir, `${slug}.md`);
|
|
20046
|
+
const relativePath = path71.relative(input.cwd, filePath);
|
|
20040
20047
|
if (await pathExists(filePath) && !input.force) {
|
|
20041
20048
|
throw new Error(`Entry already exists: ${relativePath}. Use --force to overwrite.`);
|
|
20042
20049
|
}
|
|
@@ -20067,9 +20074,9 @@ function createMemoryService() {
|
|
|
20067
20074
|
async index(input) {
|
|
20068
20075
|
const entries = await collectEntries(input.cwd);
|
|
20069
20076
|
const generatedAt = new Date().toISOString();
|
|
20070
|
-
const indexDir =
|
|
20077
|
+
const indexDir = path71.join(dataRoot2(input.cwd), "index");
|
|
20071
20078
|
await mkdir17(indexDir, { recursive: true });
|
|
20072
|
-
const indexPath =
|
|
20079
|
+
const indexPath = path71.join(indexDir, "index.json");
|
|
20073
20080
|
await writeFile11(indexPath, `${JSON.stringify({
|
|
20074
20081
|
catalogVersion: 1,
|
|
20075
20082
|
sourceFingerprint: createHash11("sha256").update(JSON.stringify(entries.map((e) => ({
|
|
@@ -20090,7 +20097,7 @@ function createMemoryService() {
|
|
|
20090
20097
|
}, null, 2)}
|
|
20091
20098
|
`, "utf8");
|
|
20092
20099
|
const result = {
|
|
20093
|
-
path:
|
|
20100
|
+
path: path71.relative(input.cwd, indexPath),
|
|
20094
20101
|
entryCount: entries.length,
|
|
20095
20102
|
generatedAt
|
|
20096
20103
|
};
|
|
@@ -20103,7 +20110,7 @@ function createMemoryService() {
|
|
|
20103
20110
|
const built = await buildEmbeddingIndex(input.cwd, entries, embedder.embed, embedder.model, new Date);
|
|
20104
20111
|
result.embeddings = {
|
|
20105
20112
|
built: true,
|
|
20106
|
-
path:
|
|
20113
|
+
path: path71.relative(input.cwd, embeddingsDir(input.cwd)),
|
|
20107
20114
|
vectorCount: built.meta.entryCount,
|
|
20108
20115
|
model: built.meta.model
|
|
20109
20116
|
};
|
|
@@ -20143,7 +20150,7 @@ function createMemoryService() {
|
|
|
20143
20150
|
}
|
|
20144
20151
|
let content;
|
|
20145
20152
|
try {
|
|
20146
|
-
content = await
|
|
20153
|
+
content = await readFile36(resolved.absolutePath, "utf8");
|
|
20147
20154
|
} catch {
|
|
20148
20155
|
return { path: resolved.relativePath, from: "draft", to: input.to, changed: false, error: { code: "not-found", message: `Memory entry not found: ${resolved.relativePath}` } };
|
|
20149
20156
|
}
|
|
@@ -20334,7 +20341,7 @@ var init_service3 = __esm(() => {
|
|
|
20334
20341
|
});
|
|
20335
20342
|
|
|
20336
20343
|
// src/wiki/ask.ts
|
|
20337
|
-
import { readFile as
|
|
20344
|
+
import { readFile as readFile37 } from "fs/promises";
|
|
20338
20345
|
async function wikiAsk(input) {
|
|
20339
20346
|
const k = input.k && input.k > 0 ? input.k : DEFAULT_K;
|
|
20340
20347
|
const asOf = input.asOf ? validateAsOf(input.asOf, new Date) : undefined;
|
|
@@ -20501,7 +20508,7 @@ async function wikiCandidates(cwd, observedAt, historicalMode, asOf) {
|
|
|
20501
20508
|
}
|
|
20502
20509
|
const index = buildSectionIndex(await Promise.all(admitted.map(async (page) => ({
|
|
20503
20510
|
page,
|
|
20504
|
-
content: await
|
|
20511
|
+
content: await readFile37(page.absolutePath, "utf8").catch(() => "")
|
|
20505
20512
|
}))));
|
|
20506
20513
|
const result = [];
|
|
20507
20514
|
for (const section of index.sections) {
|
|
@@ -20705,7 +20712,7 @@ var init_ask = __esm(() => {
|
|
|
20705
20712
|
});
|
|
20706
20713
|
|
|
20707
20714
|
// src/wiki/backlinks.ts
|
|
20708
|
-
import
|
|
20715
|
+
import path72 from "path";
|
|
20709
20716
|
function extractCodePaths(content) {
|
|
20710
20717
|
const out = [];
|
|
20711
20718
|
let match;
|
|
@@ -20735,8 +20742,8 @@ function resolveLink(fromRepoPath, raw) {
|
|
|
20735
20742
|
const clean = (raw.split("#")[0] ?? "").trim();
|
|
20736
20743
|
if (!clean)
|
|
20737
20744
|
return "";
|
|
20738
|
-
const fromDir =
|
|
20739
|
-
return
|
|
20745
|
+
const fromDir = path72.posix.dirname(fromRepoPath.split(path72.sep).join("/"));
|
|
20746
|
+
return path72.posix.normalize(path72.posix.join(fromDir, clean));
|
|
20740
20747
|
}
|
|
20741
20748
|
function buildBacklinkIndex(pages) {
|
|
20742
20749
|
const index = new Map;
|
|
@@ -20750,18 +20757,18 @@ function buildBacklinkIndex(pages) {
|
|
|
20750
20757
|
}
|
|
20751
20758
|
};
|
|
20752
20759
|
for (const page of pages) {
|
|
20753
|
-
const from = page.repoPath.split(
|
|
20760
|
+
const from = page.repoPath.split(path72.sep).join("/");
|
|
20754
20761
|
for (const raw of extractLinks(page.content))
|
|
20755
20762
|
add(resolveLink(from, raw), from);
|
|
20756
20763
|
for (const raw of extractCodePaths(page.content))
|
|
20757
|
-
add(
|
|
20764
|
+
add(path72.posix.normalize(raw), from);
|
|
20758
20765
|
}
|
|
20759
20766
|
for (const list of index.values())
|
|
20760
20767
|
list.sort();
|
|
20761
20768
|
return index;
|
|
20762
20769
|
}
|
|
20763
20770
|
function backlinksFor(index, targetRepoPath) {
|
|
20764
|
-
const key =
|
|
20771
|
+
const key = path72.posix.normalize(targetRepoPath.split(path72.sep).join("/"));
|
|
20765
20772
|
return index.get(key) ?? [];
|
|
20766
20773
|
}
|
|
20767
20774
|
var LINK, CODE_SPAN, FILE_LIKE;
|
|
@@ -20774,7 +20781,7 @@ var init_backlinks = __esm(() => {
|
|
|
20774
20781
|
// src/ctx/assembly.ts
|
|
20775
20782
|
import { createHash as createHash12 } from "crypto";
|
|
20776
20783
|
import { mkdir as mkdir18, rename as rename6, writeFile as writeFile12 } from "fs/promises";
|
|
20777
|
-
import
|
|
20784
|
+
import path73 from "path";
|
|
20778
20785
|
function assembleContext(input) {
|
|
20779
20786
|
let items = 0;
|
|
20780
20787
|
let tokens = 0;
|
|
@@ -20818,9 +20825,9 @@ async function recordNoContentContext(input) {
|
|
|
20818
20825
|
return assembly;
|
|
20819
20826
|
}
|
|
20820
20827
|
async function recordContextAssembly(input) {
|
|
20821
|
-
const directory =
|
|
20828
|
+
const directory = path73.join(input.workspaceRoot, ".metaproject", "context-operations", "traces");
|
|
20822
20829
|
await mkdir18(directory, { recursive: true });
|
|
20823
|
-
const target =
|
|
20830
|
+
const target = path73.join(directory, path73.basename(input.assembly.traceRef));
|
|
20824
20831
|
const temporary = `${target}.${process.pid}.tmp`;
|
|
20825
20832
|
await writeFile12(temporary, `${JSON.stringify({ schemaVersion: "1.0", correlationId: input.correlationId, outcome: input.outcome, configurationRevision: input.assembly.configurationRevision, policy: { ref: input.assembly.policyRef, revision: input.assembly.policyRevision }, selected: input.assembly.selected, omittedOptional: input.assembly.omittedOptional, partial: input.assembly.partial })}
|
|
20826
20833
|
`, { mode: 384 });
|
|
@@ -21670,16 +21677,16 @@ __export(exports_service4, {
|
|
|
21670
21677
|
});
|
|
21671
21678
|
import { spawn as spawn3 } from "child_process";
|
|
21672
21679
|
import { createHash as createHash14 } from "crypto";
|
|
21673
|
-
import { mkdir as mkdir19, readFile as
|
|
21674
|
-
import
|
|
21680
|
+
import { mkdir as mkdir19, readFile as readFile38, readdir as readdir13, rm as rm5, writeFile as writeFile13 } from "fs/promises";
|
|
21681
|
+
import path74 from "path";
|
|
21675
21682
|
function wikiRootPath2(cwd) {
|
|
21676
|
-
return
|
|
21683
|
+
return path74.join(cwd, ".metaproject", "wiki");
|
|
21677
21684
|
}
|
|
21678
21685
|
function dataRootPath(cwd) {
|
|
21679
|
-
return
|
|
21686
|
+
return path74.join(cwd, ".metaproject", "data", "gdwiki");
|
|
21680
21687
|
}
|
|
21681
21688
|
function linkCheckReportPath(cwd) {
|
|
21682
|
-
return
|
|
21689
|
+
return path74.join(dataRootPath(cwd), "link-check", "latest.md");
|
|
21683
21690
|
}
|
|
21684
21691
|
async function wikiStatus(cwd) {
|
|
21685
21692
|
const root = wikiRootPath2(cwd);
|
|
@@ -21687,7 +21694,7 @@ async function wikiStatus(cwd) {
|
|
|
21687
21694
|
const pages = enabled ? await collectPages(cwd) : [];
|
|
21688
21695
|
return {
|
|
21689
21696
|
enabled,
|
|
21690
|
-
wikiRoot:
|
|
21697
|
+
wikiRoot: path74.relative(cwd, root),
|
|
21691
21698
|
totalPages: pages.length,
|
|
21692
21699
|
countsByType: WIKI_PAGE_TYPES.map(({ type }) => ({
|
|
21693
21700
|
type,
|
|
@@ -21702,7 +21709,7 @@ async function findPagesWithThinkTags(pages) {
|
|
|
21702
21709
|
const flagged = [];
|
|
21703
21710
|
for (const page of pages) {
|
|
21704
21711
|
try {
|
|
21705
|
-
const content = await
|
|
21712
|
+
const content = await readFile38(page.absolutePath, "utf8");
|
|
21706
21713
|
if (containsThinkTags(content)) {
|
|
21707
21714
|
flagged.push(page.relativePath);
|
|
21708
21715
|
}
|
|
@@ -21718,9 +21725,9 @@ async function wikiCreatePage(input) {
|
|
|
21718
21725
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(input.slug)) {
|
|
21719
21726
|
throw new Error(`Invalid slug: ${input.slug}. Use lowercase letters, digits, and hyphens.`);
|
|
21720
21727
|
}
|
|
21721
|
-
const dir =
|
|
21722
|
-
const filePath =
|
|
21723
|
-
const relativePath =
|
|
21728
|
+
const dir = path74.join(wikiRootPath2(input.cwd), typeConfig.folder);
|
|
21729
|
+
const filePath = path74.join(dir, `${input.slug}.md`);
|
|
21730
|
+
const relativePath = path74.relative(input.cwd, filePath);
|
|
21724
21731
|
if (await pathExists(filePath) && !input.force) {
|
|
21725
21732
|
throw new Error(`Page already exists: ${relativePath}. Use --force to overwrite.`);
|
|
21726
21733
|
}
|
|
@@ -21731,13 +21738,13 @@ async function wikiCreatePage(input) {
|
|
|
21731
21738
|
}
|
|
21732
21739
|
async function wikiGenerateIndex(cwd) {
|
|
21733
21740
|
const root = wikiRootPath2(cwd);
|
|
21734
|
-
const indexPath =
|
|
21741
|
+
const indexPath = path74.join(root, "index.md");
|
|
21735
21742
|
const pages = await collectPages(cwd);
|
|
21736
21743
|
const generatedAt = new Date().toISOString();
|
|
21737
21744
|
const managedBlock = `${WIKI_INDEX_BEGIN}
|
|
21738
21745
|
${renderIndexBody(pages, generatedAt)}
|
|
21739
21746
|
${WIKI_INDEX_END}`;
|
|
21740
|
-
const existing = await pathExists(indexPath) ? await
|
|
21747
|
+
const existing = await pathExists(indexPath) ? await readFile38(indexPath, "utf8") : renderWikiIndexScaffold();
|
|
21741
21748
|
const pattern = new RegExp(`${escapeRegExp3(WIKI_INDEX_BEGIN)}[\\s\\S]*?${escapeRegExp3(WIKI_INDEX_END)}`);
|
|
21742
21749
|
const replaced = pattern.test(existing) ? existing.replace(pattern, managedBlock) : `${existing.trimEnd()}
|
|
21743
21750
|
|
|
@@ -21749,7 +21756,7 @@ ${managedBlock}
|
|
|
21749
21756
|
await mkdir19(root, { recursive: true });
|
|
21750
21757
|
await writeFile13(indexPath, next, "utf8");
|
|
21751
21758
|
return {
|
|
21752
|
-
path:
|
|
21759
|
+
path: path74.relative(cwd, indexPath),
|
|
21753
21760
|
pageCount: pages.length,
|
|
21754
21761
|
generatedAt
|
|
21755
21762
|
};
|
|
@@ -21757,13 +21764,13 @@ ${managedBlock}
|
|
|
21757
21764
|
async function wikiCheckLinks(cwd) {
|
|
21758
21765
|
const root = wikiRootPath2(cwd);
|
|
21759
21766
|
const allFiles = await pathExists(root) ? await walkMarkdown(root) : [];
|
|
21760
|
-
const files = allFiles.filter((absolutePath) => !
|
|
21767
|
+
const files = allFiles.filter((absolutePath) => !path74.relative(root, absolutePath).startsWith(`templates${path74.sep}`));
|
|
21761
21768
|
const broken = [];
|
|
21762
21769
|
let checkedLinks = 0;
|
|
21763
21770
|
let skippedExternal = 0;
|
|
21764
21771
|
for (const absolutePath of files) {
|
|
21765
|
-
const content = await
|
|
21766
|
-
const pageRelative =
|
|
21772
|
+
const content = await readFile38(absolutePath, "utf8");
|
|
21773
|
+
const pageRelative = path74.relative(cwd, absolutePath);
|
|
21767
21774
|
for (const target of extractLinkTargets(content)) {
|
|
21768
21775
|
if (EXTERNAL_LINK.test(target)) {
|
|
21769
21776
|
skippedExternal += 1;
|
|
@@ -21774,7 +21781,7 @@ async function wikiCheckLinks(cwd) {
|
|
|
21774
21781
|
continue;
|
|
21775
21782
|
}
|
|
21776
21783
|
checkedLinks += 1;
|
|
21777
|
-
const resolved =
|
|
21784
|
+
const resolved = path74.resolve(path74.dirname(absolutePath), filePart);
|
|
21778
21785
|
if (!await pathExists(resolved)) {
|
|
21779
21786
|
broken.push({
|
|
21780
21787
|
page: pageRelative,
|
|
@@ -21786,7 +21793,7 @@ async function wikiCheckLinks(cwd) {
|
|
|
21786
21793
|
}
|
|
21787
21794
|
const generatedAt = new Date().toISOString();
|
|
21788
21795
|
const reportPath = linkCheckReportPath(cwd);
|
|
21789
|
-
await mkdir19(
|
|
21796
|
+
await mkdir19(path74.dirname(reportPath), { recursive: true });
|
|
21790
21797
|
await writeFile13(reportPath, renderLinkCheckReport({
|
|
21791
21798
|
generatedAt,
|
|
21792
21799
|
checkedPages: files.length,
|
|
@@ -21795,7 +21802,7 @@ async function wikiCheckLinks(cwd) {
|
|
|
21795
21802
|
broken
|
|
21796
21803
|
}), "utf8");
|
|
21797
21804
|
return {
|
|
21798
|
-
reportPath:
|
|
21805
|
+
reportPath: path74.relative(cwd, reportPath),
|
|
21799
21806
|
checkedPages: files.length,
|
|
21800
21807
|
checkedLinks,
|
|
21801
21808
|
skippedExternal,
|
|
@@ -21845,7 +21852,7 @@ async function wikiValidate(cwd) {
|
|
|
21845
21852
|
}
|
|
21846
21853
|
if (await isIndexStale(cwd, pages)) {
|
|
21847
21854
|
issues.push({
|
|
21848
|
-
page:
|
|
21855
|
+
page: path74.join(path74.relative(cwd, wikiRootPath2(cwd)), "index.md"),
|
|
21849
21856
|
kind: "index",
|
|
21850
21857
|
message: "index out of date, run `keryx wiki index`"
|
|
21851
21858
|
});
|
|
@@ -21882,7 +21889,7 @@ async function wikiCollect(input) {
|
|
|
21882
21889
|
};
|
|
21883
21890
|
}
|
|
21884
21891
|
async function wikiPruneOrphans(cwd) {
|
|
21885
|
-
const componentsDir =
|
|
21892
|
+
const componentsDir = path74.join(wikiRootPath2(cwd), "components");
|
|
21886
21893
|
if (!await pathExists(componentsDir)) {
|
|
21887
21894
|
return { pruned: [], orphanedAccepted: [] };
|
|
21888
21895
|
}
|
|
@@ -21891,7 +21898,7 @@ async function wikiPruneOrphans(cwd) {
|
|
|
21891
21898
|
const validSlugs = new Set(candidates.filter((candidate) => candidate.type === "component").map((candidate) => candidate.slug));
|
|
21892
21899
|
const pruned = [];
|
|
21893
21900
|
const orphanedAccepted = [];
|
|
21894
|
-
for (const entry of await
|
|
21901
|
+
for (const entry of await readdir13(componentsDir)) {
|
|
21895
21902
|
if (!entry.endsWith(".md")) {
|
|
21896
21903
|
continue;
|
|
21897
21904
|
}
|
|
@@ -21899,25 +21906,25 @@ async function wikiPruneOrphans(cwd) {
|
|
|
21899
21906
|
if (validSlugs.has(slug)) {
|
|
21900
21907
|
continue;
|
|
21901
21908
|
}
|
|
21902
|
-
const filePath =
|
|
21903
|
-
const current = await
|
|
21909
|
+
const filePath = path74.join(componentsDir, entry);
|
|
21910
|
+
const current = await readFile38(filePath, "utf8");
|
|
21904
21911
|
const isUnmodifiedDraft = /\nStatus:\s*draft\s*\n/.test(current) && current.includes("Generated by `keryx wiki collect`");
|
|
21905
21912
|
if (isUnmodifiedDraft) {
|
|
21906
21913
|
await rm5(filePath);
|
|
21907
|
-
pruned.push(
|
|
21914
|
+
pruned.push(path74.relative(cwd, filePath));
|
|
21908
21915
|
} else {
|
|
21909
|
-
orphanedAccepted.push(
|
|
21916
|
+
orphanedAccepted.push(path74.relative(cwd, filePath));
|
|
21910
21917
|
}
|
|
21911
21918
|
}
|
|
21912
21919
|
return { pruned, orphanedAccepted };
|
|
21913
21920
|
}
|
|
21914
21921
|
async function validModuleNames(cwd) {
|
|
21915
|
-
const nodesPath =
|
|
21922
|
+
const nodesPath = path74.join(cwd, ".metaproject", "data", "gdgraph", "storage", "nodes.jsonl");
|
|
21916
21923
|
if (!await pathExists(nodesPath)) {
|
|
21917
21924
|
return;
|
|
21918
21925
|
}
|
|
21919
21926
|
const modules = new Set;
|
|
21920
|
-
for (const node of parseJsonl(await
|
|
21927
|
+
for (const node of parseJsonl(await readFile38(nodesPath, "utf8"))) {
|
|
21921
21928
|
if (node.kind === "asset")
|
|
21922
21929
|
continue;
|
|
21923
21930
|
const nodePath = String(node.path ?? node.id ?? "unknown");
|
|
@@ -21948,7 +21955,7 @@ async function wikiEvidence(input) {
|
|
|
21948
21955
|
const pages = await collectPages(input.cwd);
|
|
21949
21956
|
const index = buildSectionIndex(await Promise.all(pages.map(async (page) => ({
|
|
21950
21957
|
page,
|
|
21951
|
-
content: await
|
|
21958
|
+
content: await readFile38(page.absolutePath, "utf8").catch(() => "")
|
|
21952
21959
|
}))));
|
|
21953
21960
|
const registry = await readSectionRegistryState(input.cwd);
|
|
21954
21961
|
const pageStatus = {};
|
|
@@ -21982,9 +21989,9 @@ async function wikiEvidence(input) {
|
|
|
21982
21989
|
});
|
|
21983
21990
|
}
|
|
21984
21991
|
function resolveEvidenceScope(cwd, override) {
|
|
21985
|
-
const absolute =
|
|
21992
|
+
const absolute = path74.resolve(cwd);
|
|
21986
21993
|
return {
|
|
21987
|
-
projectId: override?.projectId ?? (
|
|
21994
|
+
projectId: override?.projectId ?? (path74.basename(absolute) || "project"),
|
|
21988
21995
|
checkoutId: override?.checkoutId ?? `checkout:${createHash14("sha256").update(absolute).digest("hex").slice(0, 16)}`,
|
|
21989
21996
|
...override?.workspaceId ? { workspaceId: override.workspaceId } : {},
|
|
21990
21997
|
...override?.taskId ? { taskId: override.taskId } : {}
|
|
@@ -21999,7 +22006,7 @@ function isMachineAuthoredPage(pageType, relativePath, content) {
|
|
|
21999
22006
|
if (source === null) {
|
|
22000
22007
|
return false;
|
|
22001
22008
|
}
|
|
22002
|
-
const normalised = relativePath.split(
|
|
22009
|
+
const normalised = relativePath.split(path74.sep).join("/").replace(/^wiki\//, "");
|
|
22003
22010
|
return WIKI_PAGE_PRODUCERS.some((producer) => producer.source === source && producer.pageType === pageType && producer.path.test(normalised) && /^-[ \t]+Link:.*\bsha256\b/m.test(content));
|
|
22004
22011
|
}
|
|
22005
22012
|
function createGdWikiService() {
|
|
@@ -22015,8 +22022,8 @@ function createGdWikiService() {
|
|
|
22015
22022
|
};
|
|
22016
22023
|
}
|
|
22017
22024
|
async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules) {
|
|
22018
|
-
const nodesPath =
|
|
22019
|
-
const edgesPath =
|
|
22025
|
+
const nodesPath = path74.join(cwd, ".metaproject", "data", "gdgraph", "storage", "nodes.jsonl");
|
|
22026
|
+
const edgesPath = path74.join(cwd, ".metaproject", "data", "gdgraph", "storage", "edges.jsonl");
|
|
22020
22027
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
22021
22028
|
return [];
|
|
22022
22029
|
}
|
|
@@ -22025,7 +22032,7 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
22025
22032
|
let nodes = 0;
|
|
22026
22033
|
let files = 0;
|
|
22027
22034
|
let assets = 0;
|
|
22028
|
-
for (const node of parseJsonl(await
|
|
22035
|
+
for (const node of parseJsonl(await readFile38(nodesPath, "utf8"))) {
|
|
22029
22036
|
nodes += 1;
|
|
22030
22037
|
const nodePath = String(node.path ?? node.id ?? "unknown");
|
|
22031
22038
|
if (node.kind === "asset") {
|
|
@@ -22047,7 +22054,7 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules)
|
|
|
22047
22054
|
let edges = 0;
|
|
22048
22055
|
let imports = 0;
|
|
22049
22056
|
let unresolved = 0;
|
|
22050
|
-
for (const edge of parseJsonl(await
|
|
22057
|
+
for (const edge of parseJsonl(await readFile38(edgesPath, "utf8"))) {
|
|
22051
22058
|
edges += 1;
|
|
22052
22059
|
const kind = String(edge.kind ?? "");
|
|
22053
22060
|
if (kind === "unresolved") {
|
|
@@ -22200,12 +22207,12 @@ function topCounts(bucket, limit) {
|
|
|
22200
22207
|
return [...bucket.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit);
|
|
22201
22208
|
}
|
|
22202
22209
|
async function readModuleReadme(cwd, moduleName) {
|
|
22203
|
-
const readmePath =
|
|
22210
|
+
const readmePath = path74.join(cwd, ...moduleName.split("/"), "README.md");
|
|
22204
22211
|
if (!await pathExists(readmePath)) {
|
|
22205
22212
|
return null;
|
|
22206
22213
|
}
|
|
22207
22214
|
try {
|
|
22208
|
-
const content = await
|
|
22215
|
+
const content = await readFile38(readmePath, "utf8");
|
|
22209
22216
|
const paragraph = content.split(/\n\s*\n/).map((block) => block.trim()).find((block) => block.length > 0 && !block.startsWith("#"));
|
|
22210
22217
|
if (!paragraph) {
|
|
22211
22218
|
return null;
|
|
@@ -22221,12 +22228,12 @@ async function extractModuleExports(cwd, files) {
|
|
|
22221
22228
|
const declRe = /export\s+(?:async\s+)?(?:default\s+)?(?:const|let|var|function|class|type|interface|enum)\s+([A-Za-z_$][\w$]*)/g;
|
|
22222
22229
|
const braceRe = /export\s*\{([^}]+)\}/g;
|
|
22223
22230
|
for (const file of files.slice(0, 4)) {
|
|
22224
|
-
const filePath =
|
|
22231
|
+
const filePath = path74.join(cwd, ...file.split("/"));
|
|
22225
22232
|
if (!await pathExists(filePath)) {
|
|
22226
22233
|
continue;
|
|
22227
22234
|
}
|
|
22228
22235
|
try {
|
|
22229
|
-
const content = (await
|
|
22236
|
+
const content = (await readFile38(filePath, "utf8")).slice(0, 40000);
|
|
22230
22237
|
let match;
|
|
22231
22238
|
while ((match = declRe.exec(content)) !== null) {
|
|
22232
22239
|
if (match[1]) {
|
|
@@ -22252,14 +22259,14 @@ async function extractModuleExports(cwd, files) {
|
|
|
22252
22259
|
}
|
|
22253
22260
|
async function extractModuleApi(cwd, files) {
|
|
22254
22261
|
const names = await extractModuleExports(cwd, files);
|
|
22255
|
-
const symbolsPath =
|
|
22262
|
+
const symbolsPath = path74.join(cwd, ".metaproject", "data", "gdgraph", "storage", "symbols.jsonl");
|
|
22256
22263
|
if (names.length === 0 || !await pathExists(symbolsPath)) {
|
|
22257
22264
|
return names.map((name) => "`" + name + "`");
|
|
22258
22265
|
}
|
|
22259
22266
|
const fileSet = new Set(files);
|
|
22260
22267
|
const kindByName = new Map;
|
|
22261
22268
|
try {
|
|
22262
|
-
for (const raw of parseJsonl(await
|
|
22269
|
+
for (const raw of parseJsonl(await readFile38(symbolsPath, "utf8"))) {
|
|
22263
22270
|
const symbol = raw;
|
|
22264
22271
|
if (!symbol.name || !symbol.path || symbol.container != null)
|
|
22265
22272
|
continue;
|
|
@@ -22278,11 +22285,11 @@ async function extractModuleApi(cwd, files) {
|
|
|
22278
22285
|
});
|
|
22279
22286
|
}
|
|
22280
22287
|
async function collectHealthWikiCandidates(cwd, generatedAt) {
|
|
22281
|
-
const healthPath =
|
|
22288
|
+
const healthPath = path74.join(cwd, ".metaproject", "data", "health", "artifacts", "latest.json");
|
|
22282
22289
|
if (!await pathExists(healthPath)) {
|
|
22283
22290
|
return [];
|
|
22284
22291
|
}
|
|
22285
|
-
const report = JSON.parse(await
|
|
22292
|
+
const report = JSON.parse(await readFile38(healthPath, "utf8"));
|
|
22286
22293
|
const metrics = Array.isArray(report.metrics) ? report.metrics : [];
|
|
22287
22294
|
const project = metrics.find((metric) => metric.key === "project") ?? {};
|
|
22288
22295
|
const counts = project.findingCounts ?? {};
|
|
@@ -22320,11 +22327,11 @@ async function collectHealthWikiCandidates(cwd, generatedAt) {
|
|
|
22320
22327
|
}];
|
|
22321
22328
|
}
|
|
22322
22329
|
async function collectTestingWikiCandidates(cwd, generatedAt) {
|
|
22323
|
-
const contextPath =
|
|
22330
|
+
const contextPath = path74.join(cwd, ".metaproject", "data", "testing", "context.md");
|
|
22324
22331
|
if (!await pathExists(contextPath)) {
|
|
22325
22332
|
return [];
|
|
22326
22333
|
}
|
|
22327
|
-
const context = await
|
|
22334
|
+
const context = await readFile38(contextPath, "utf8");
|
|
22328
22335
|
const summary = firstMeaningfulLine(context) ?? "Generated from testing context.";
|
|
22329
22336
|
return [{
|
|
22330
22337
|
type: "architecture",
|
|
@@ -22351,14 +22358,14 @@ async function writeCollectedPage(cwd, candidate, force) {
|
|
|
22351
22358
|
if (!folder) {
|
|
22352
22359
|
throw new Error(`Unsupported collected wiki type: ${candidate.type}`);
|
|
22353
22360
|
}
|
|
22354
|
-
const filePath =
|
|
22355
|
-
const relativePath =
|
|
22361
|
+
const filePath = path74.join(wikiRootPath2(cwd), folder, `${candidate.slug}.md`);
|
|
22362
|
+
const relativePath = path74.relative(cwd, filePath);
|
|
22356
22363
|
const exists = await pathExists(filePath);
|
|
22357
22364
|
if (exists && !force) {
|
|
22358
22365
|
return { path: relativePath, type: candidate.type, source: candidate.source, action: "skipped" };
|
|
22359
22366
|
}
|
|
22360
22367
|
if (exists && force) {
|
|
22361
|
-
const current = await
|
|
22368
|
+
const current = await readFile38(filePath, "utf8");
|
|
22362
22369
|
const isUnmodifiedDraft = /\nStatus:\s*draft\s*\n/.test(current) && current.includes("Generated by `keryx wiki collect`");
|
|
22363
22370
|
if (!isUnmodifiedDraft) {
|
|
22364
22371
|
return { path: relativePath, type: candidate.type, source: candidate.source, action: "skipped" };
|
|
@@ -22380,7 +22387,7 @@ async function writeCollectedPage(cwd, candidate, force) {
|
|
|
22380
22387
|
securityReason: output.reason
|
|
22381
22388
|
};
|
|
22382
22389
|
}
|
|
22383
|
-
await mkdir19(
|
|
22390
|
+
await mkdir19(path74.dirname(filePath), { recursive: true });
|
|
22384
22391
|
await writeFile13(filePath, output.content, "utf8");
|
|
22385
22392
|
return {
|
|
22386
22393
|
path: relativePath,
|
|
@@ -22392,8 +22399,8 @@ async function writeCollectedPage(cwd, candidate, force) {
|
|
|
22392
22399
|
async function wikiPagesForFile(cwd, targetRepoPath) {
|
|
22393
22400
|
const pages = await collectPages(cwd);
|
|
22394
22401
|
const refs = await Promise.all(pages.map(async (page) => ({
|
|
22395
|
-
repoPath:
|
|
22396
|
-
content: await
|
|
22402
|
+
repoPath: path74.relative(cwd, page.absolutePath).split(path74.sep).join("/"),
|
|
22403
|
+
content: await readFile38(page.absolutePath, "utf8")
|
|
22397
22404
|
})));
|
|
22398
22405
|
return backlinksFor(buildBacklinkIndex(refs), targetRepoPath);
|
|
22399
22406
|
}
|
|
@@ -22431,11 +22438,11 @@ function renderIndexBody(pages, generatedAt) {
|
|
|
22431
22438
|
`).trimEnd();
|
|
22432
22439
|
}
|
|
22433
22440
|
async function isIndexStale(cwd, pages) {
|
|
22434
|
-
const indexPath =
|
|
22441
|
+
const indexPath = path74.join(wikiRootPath2(cwd), "index.md");
|
|
22435
22442
|
if (!await pathExists(indexPath)) {
|
|
22436
22443
|
return true;
|
|
22437
22444
|
}
|
|
22438
|
-
const content = await
|
|
22445
|
+
const content = await readFile38(indexPath, "utf8");
|
|
22439
22446
|
const managed = content.match(new RegExp(`${escapeRegExp3(WIKI_INDEX_BEGIN)}([\\s\\S]*?)${escapeRegExp3(WIKI_INDEX_END)}`));
|
|
22440
22447
|
if (!managed?.[1]) {
|
|
22441
22448
|
return true;
|
|
@@ -22447,9 +22454,9 @@ function stripStamp(body) {
|
|
|
22447
22454
|
}
|
|
22448
22455
|
async function walkMarkdown(root) {
|
|
22449
22456
|
const results = [];
|
|
22450
|
-
const entries = await
|
|
22457
|
+
const entries = await readdir13(root, { withFileTypes: true });
|
|
22451
22458
|
for (const entry of entries) {
|
|
22452
|
-
const absolutePath =
|
|
22459
|
+
const absolutePath = path74.join(root, entry.name);
|
|
22453
22460
|
if (entry.isDirectory()) {
|
|
22454
22461
|
results.push(...await walkMarkdown(absolutePath));
|
|
22455
22462
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -22506,11 +22513,11 @@ ${JSON.stringify(state, null, 2)}
|
|
|
22506
22513
|
`;
|
|
22507
22514
|
}
|
|
22508
22515
|
async function readIndexGeneratedAt(cwd) {
|
|
22509
|
-
const indexPath =
|
|
22516
|
+
const indexPath = path74.join(wikiRootPath2(cwd), "index.md");
|
|
22510
22517
|
if (!await pathExists(indexPath)) {
|
|
22511
22518
|
return null;
|
|
22512
22519
|
}
|
|
22513
|
-
const content = await
|
|
22520
|
+
const content = await readFile38(indexPath, "utf8");
|
|
22514
22521
|
const match = content.match(/<!--\s*generated:\s*([^|]+?)\s*\|/);
|
|
22515
22522
|
const value = match?.[1]?.trim();
|
|
22516
22523
|
return value && value !== "never" ? value : null;
|
|
@@ -22520,7 +22527,7 @@ async function readLinkCheckState(cwd) {
|
|
|
22520
22527
|
if (!await pathExists(reportPath)) {
|
|
22521
22528
|
return null;
|
|
22522
22529
|
}
|
|
22523
|
-
const content = await
|
|
22530
|
+
const content = await readFile38(reportPath, "utf8");
|
|
22524
22531
|
const matches = [...content.matchAll(/```json\n([\s\S]*?)```/g)];
|
|
22525
22532
|
const last = matches.at(-1);
|
|
22526
22533
|
if (!last?.[1]) {
|
|
@@ -22718,7 +22725,7 @@ async function validateStructure(cwd, pages, issues) {
|
|
|
22718
22725
|
for (const page of pages) {
|
|
22719
22726
|
let content;
|
|
22720
22727
|
try {
|
|
22721
|
-
content = await
|
|
22728
|
+
content = await readFile38(page.absolutePath, "utf8");
|
|
22722
22729
|
} catch {
|
|
22723
22730
|
continue;
|
|
22724
22731
|
}
|
|
@@ -22743,7 +22750,7 @@ async function validateStructure(cwd, pages, issues) {
|
|
|
22743
22750
|
if (pattern.includes("*")) {
|
|
22744
22751
|
continue;
|
|
22745
22752
|
}
|
|
22746
|
-
if (!await pathExists(
|
|
22753
|
+
if (!await pathExists(path74.join(cwd, pattern))) {
|
|
22747
22754
|
issues.push({
|
|
22748
22755
|
page: page.relativePath,
|
|
22749
22756
|
kind: "describes",
|
|
@@ -22813,10 +22820,10 @@ var init_service4 = __esm(() => {
|
|
|
22813
22820
|
|
|
22814
22821
|
// src/gdgraph/wiki-layer.ts
|
|
22815
22822
|
import { createHash as createHash15 } from "crypto";
|
|
22816
|
-
import { mkdir as mkdir20, stat as
|
|
22817
|
-
import
|
|
22823
|
+
import { mkdir as mkdir20, stat as stat11, writeFile as writeFile14 } from "fs/promises";
|
|
22824
|
+
import path75 from "path";
|
|
22818
22825
|
function storageDir(projectRoot) {
|
|
22819
|
-
return
|
|
22826
|
+
return path75.join(projectRoot, ".metaproject", "data", "gdgraph", "storage");
|
|
22820
22827
|
}
|
|
22821
22828
|
function wikiPageId(relativePath) {
|
|
22822
22829
|
return `wiki:${relativePath}`;
|
|
@@ -22882,11 +22889,11 @@ async function enrichBuildWithWikiLayer(input) {
|
|
|
22882
22889
|
const mtimes = new Map;
|
|
22883
22890
|
for (const record of input.fileRecords) {
|
|
22884
22891
|
try {
|
|
22885
|
-
const info = await
|
|
22892
|
+
const info = await stat11(path75.join(input.projectRoot, record.path));
|
|
22886
22893
|
mtimes.set(record.path, info.mtimeMs);
|
|
22887
22894
|
} catch {}
|
|
22888
22895
|
}
|
|
22889
|
-
await writeFile14(
|
|
22896
|
+
await writeFile14(path75.join(dir, BUILD_MANIFEST_FILE), `${JSON.stringify({ version: 1, files: computeFingerprints(input.fileRecords, mtimes) }, null, 2)}
|
|
22890
22897
|
`, "utf8");
|
|
22891
22898
|
const validModules = input.validModules !== undefined ? input.validModules : await (input.loadValidModules ?? defaultLoadValidModules)(input.projectRoot);
|
|
22892
22899
|
const wikiPages = await collectPages(input.projectRoot);
|
|
@@ -22911,8 +22918,8 @@ async function enrichBuildWithWikiLayer(input) {
|
|
|
22911
22918
|
version: page.version
|
|
22912
22919
|
}))
|
|
22913
22920
|
});
|
|
22914
|
-
await writeJsonl(
|
|
22915
|
-
await writeJsonl(
|
|
22921
|
+
await writeJsonl(path75.join(dir, WIKI_PAGES_FILE), layer.pages);
|
|
22922
|
+
await writeJsonl(path75.join(dir, DESCRIBES_FILE), layer.describes);
|
|
22916
22923
|
return layer;
|
|
22917
22924
|
}
|
|
22918
22925
|
async function defaultLoadValidModules(projectRoot) {
|
|
@@ -22928,23 +22935,23 @@ var init_wiki_layer = __esm(() => {
|
|
|
22928
22935
|
});
|
|
22929
22936
|
|
|
22930
22937
|
// src/gdskills/contracts.ts
|
|
22931
|
-
import { readFile as
|
|
22938
|
+
import { readFile as readFile50 } from "fs/promises";
|
|
22932
22939
|
import { existsSync as existsSync15 } from "fs";
|
|
22933
|
-
import
|
|
22940
|
+
import path103 from "path";
|
|
22934
22941
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
22935
22942
|
async function loadSchema(name) {
|
|
22936
22943
|
const contract = CONTRACTS.find((entry) => entry.name === name);
|
|
22937
22944
|
if (!contract) {
|
|
22938
22945
|
throw new Error(`Unknown contract schema: ${name}`);
|
|
22939
22946
|
}
|
|
22940
|
-
const raw = await
|
|
22947
|
+
const raw = await readFile50(contractPath(contract), "utf8");
|
|
22941
22948
|
return JSON.parse(raw);
|
|
22942
22949
|
}
|
|
22943
22950
|
function contractPath(contract) {
|
|
22944
|
-
const here =
|
|
22951
|
+
const here = path103.dirname(fileURLToPath2(import.meta.url));
|
|
22945
22952
|
if (contract.sourcePath) {
|
|
22946
|
-
for (const root of [
|
|
22947
|
-
const candidate =
|
|
22953
|
+
for (const root of [path103.join(here, "..", ".."), path103.join(here, ".."), here]) {
|
|
22954
|
+
const candidate = path103.resolve(root, contract.sourcePath);
|
|
22948
22955
|
if (existsSync15(candidate)) {
|
|
22949
22956
|
return candidate;
|
|
22950
22957
|
}
|
|
@@ -22954,7 +22961,7 @@ function contractPath(contract) {
|
|
|
22954
22961
|
if (existsSync15(directPath)) {
|
|
22955
22962
|
return directPath;
|
|
22956
22963
|
}
|
|
22957
|
-
const packagedSourcePath =
|
|
22964
|
+
const packagedSourcePath = path103.join(here, "..", "src", "gdskills", "contracts", contract.fileName);
|
|
22958
22965
|
if (existsSync15(packagedSourcePath)) {
|
|
22959
22966
|
return packagedSourcePath;
|
|
22960
22967
|
}
|
|
@@ -23185,8 +23192,8 @@ __export(exports_service2, {
|
|
|
23185
23192
|
validateCriterionName: () => validateCriterionName,
|
|
23186
23193
|
writeAcCheckCache: () => writeAcCheckCache
|
|
23187
23194
|
});
|
|
23188
|
-
import { mkdir as mkdir12, readFile as
|
|
23189
|
-
import
|
|
23195
|
+
import { mkdir as mkdir12, readFile as readFile28 } from "fs/promises";
|
|
23196
|
+
import path56 from "path";
|
|
23190
23197
|
|
|
23191
23198
|
// src/flow/machine.ts
|
|
23192
23199
|
var TRANSITIONS = {
|
|
@@ -24920,6 +24927,10 @@ function flowStateSchema() {
|
|
|
24920
24927
|
type: "boolean",
|
|
24921
24928
|
description: "Written by `flow init`. When true, `flow complete` fails the owner gate with a named reason while no owner is set, and passes it once one is. Absent on packages created before the gate existed, which report the gate `skipped` rather than `fail`."
|
|
24922
24929
|
},
|
|
24930
|
+
folderCommitted: {
|
|
24931
|
+
type: "boolean",
|
|
24932
|
+
description: "flow 384: written by `flow init`. When true, `flow complete` fails the folder-committed gate while the flow folder's flow.json is not in HEAD, and passes it once it is. Absent on packages created before the gate existed, which report the gate `skipped` rather than `fail`."
|
|
24933
|
+
},
|
|
24923
24934
|
confirmation: {
|
|
24924
24935
|
type: "boolean",
|
|
24925
24936
|
description: "flow 299: written by `flow init --require-confirmation`, or by `flow init` when `.metaproject/tasks.config.json` has `completion.require_confirmation: true`. When true, `flow complete` fails the confirmation gate unless `--confirm-token` carries a valid, unexpired, unspent token minted by `keryx flow confirm` for this flow and its current criteria checksum. Absent on every other flow, which reports the gate `skipped`."
|
|
@@ -25162,6 +25173,7 @@ function flowStateSchema() {
|
|
|
25162
25173
|
"review",
|
|
25163
25174
|
"base-branch",
|
|
25164
25175
|
"owner",
|
|
25176
|
+
"folder-committed",
|
|
25165
25177
|
"confirmation"
|
|
25166
25178
|
]
|
|
25167
25179
|
},
|
|
@@ -25895,7 +25907,7 @@ async function intentNoteForNewFlow(cwd, dir) {
|
|
|
25895
25907
|
}
|
|
25896
25908
|
// src/flow/check-ac.ts
|
|
25897
25909
|
import { createHash as createHash8 } from "crypto";
|
|
25898
|
-
import { chmod as chmod4, mkdir as mkdir10, readFile as
|
|
25910
|
+
import { chmod as chmod4, mkdir as mkdir10, readFile as readFile26 } from "fs/promises";
|
|
25899
25911
|
import path52 from "path";
|
|
25900
25912
|
|
|
25901
25913
|
// src/review/cost.ts
|
|
@@ -26084,7 +26096,7 @@ function factsOnlyVerdict(item) {
|
|
|
26084
26096
|
}
|
|
26085
26097
|
async function readAcCheckEnabled(cwd) {
|
|
26086
26098
|
try {
|
|
26087
|
-
const parsed = JSON.parse(await
|
|
26099
|
+
const parsed = JSON.parse(await readFile26(path52.join(cwd, REVIEW_GATE_CONFIG_PATH), "utf8"));
|
|
26088
26100
|
if (typeof parsed !== "object" || parsed === null)
|
|
26089
26101
|
return false;
|
|
26090
26102
|
const review = parsed["review"];
|
|
@@ -26111,7 +26123,7 @@ async function readAcCheckCache(cachePath) {
|
|
|
26111
26123
|
if (!await pathExists(cachePath))
|
|
26112
26124
|
return;
|
|
26113
26125
|
try {
|
|
26114
|
-
return JSON.parse(await
|
|
26126
|
+
return JSON.parse(await readFile26(cachePath, "utf8"));
|
|
26115
26127
|
} catch {
|
|
26116
26128
|
return;
|
|
26117
26129
|
}
|
|
@@ -26174,7 +26186,7 @@ function renderAcCheckAdvisoryNotice(verdicts) {
|
|
|
26174
26186
|
return `acceptance-criteria check (advisory): ${counts.likelyMet} likely met, ${counts.notEvident} not evident, ${counts.notCheckable} not checkable` + (inDoubt.length > 0 ? ` \u2014 in doubt: ${inDoubt.join(", ")}` : "") + ` (\`keryx flow check-ac\` for detail)`;
|
|
26175
26187
|
}
|
|
26176
26188
|
// src/flow/allocation.ts
|
|
26177
|
-
import { appendFile as appendFile3, mkdir as mkdir11, readFile as
|
|
26189
|
+
import { appendFile as appendFile3, mkdir as mkdir11, readFile as readFile27 } from "fs/promises";
|
|
26178
26190
|
import path54 from "path";
|
|
26179
26191
|
|
|
26180
26192
|
// src/lib/clone-scope.ts
|
|
@@ -26239,7 +26251,7 @@ async function readAllocationLedger(scope) {
|
|
|
26239
26251
|
if (!scope.ledgerPath || !await pathExists(scope.ledgerPath)) {
|
|
26240
26252
|
return [];
|
|
26241
26253
|
}
|
|
26242
|
-
const content = await
|
|
26254
|
+
const content = await readFile27(scope.ledgerPath, "utf8");
|
|
26243
26255
|
const records = [];
|
|
26244
26256
|
for (const line of content.split(`
|
|
26245
26257
|
`)) {
|
|
@@ -26269,6 +26281,173 @@ async function recordAllocation(scope, record) {
|
|
|
26269
26281
|
`, "utf8");
|
|
26270
26282
|
}
|
|
26271
26283
|
|
|
26284
|
+
// src/flow/remote-flows.ts
|
|
26285
|
+
import path55 from "path";
|
|
26286
|
+
var MAX_REFS = 500;
|
|
26287
|
+
var CONCURRENCY = 8;
|
|
26288
|
+
async function runGit2(cwd, args) {
|
|
26289
|
+
try {
|
|
26290
|
+
const proc = Bun.spawn(["git", ...args], { cwd, stdout: "pipe", stderr: "ignore" });
|
|
26291
|
+
const stdout = await new Response(proc.stdout).text();
|
|
26292
|
+
return { code: await proc.exited, stdout };
|
|
26293
|
+
} catch {
|
|
26294
|
+
return;
|
|
26295
|
+
}
|
|
26296
|
+
}
|
|
26297
|
+
function lines(text) {
|
|
26298
|
+
return text.split(`
|
|
26299
|
+
`).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
26300
|
+
}
|
|
26301
|
+
function nulSplit(text) {
|
|
26302
|
+
return text.split("\x00").filter((entry) => entry.length > 0);
|
|
26303
|
+
}
|
|
26304
|
+
function safeDirName(dir) {
|
|
26305
|
+
let out = "";
|
|
26306
|
+
for (const char of dir) {
|
|
26307
|
+
const code = char.codePointAt(0) ?? 0;
|
|
26308
|
+
if (code > 31 && (code < 127 || code > 159)) {
|
|
26309
|
+
out += char;
|
|
26310
|
+
}
|
|
26311
|
+
}
|
|
26312
|
+
return out;
|
|
26313
|
+
}
|
|
26314
|
+
async function showPrefix(cwd) {
|
|
26315
|
+
const result = await runGit2(cwd, ["rev-parse", "--show-prefix"]);
|
|
26316
|
+
if (result === undefined || result.code !== 0) {
|
|
26317
|
+
return null;
|
|
26318
|
+
}
|
|
26319
|
+
return result.stdout.trim();
|
|
26320
|
+
}
|
|
26321
|
+
function orderRemoteRefs(entries, max = MAX_REFS) {
|
|
26322
|
+
const headTargets = new Set(entries.map((entry) => entry.symref).filter((symref) => symref.length > 0));
|
|
26323
|
+
const rank = (ref) => {
|
|
26324
|
+
if (/^refs\/remotes\/[^/]+\/(main|master)$/.test(ref)) {
|
|
26325
|
+
return 0;
|
|
26326
|
+
}
|
|
26327
|
+
return headTargets.has(ref) ? 1 : 2;
|
|
26328
|
+
};
|
|
26329
|
+
const candidates = entries.filter((entry) => entry.ref.startsWith("refs/remotes/") && !entry.ref.endsWith("/HEAD") && entry.symref === "").sort((a, b) => rank(a.ref) - rank(b.ref) || (a.ref < b.ref ? -1 : a.ref > b.ref ? 1 : 0));
|
|
26330
|
+
const seenTips = new Set;
|
|
26331
|
+
const refs = [];
|
|
26332
|
+
for (const entry of candidates) {
|
|
26333
|
+
if (seenTips.has(entry.tip)) {
|
|
26334
|
+
continue;
|
|
26335
|
+
}
|
|
26336
|
+
seenTips.add(entry.tip);
|
|
26337
|
+
refs.push(entry.ref);
|
|
26338
|
+
if (refs.length >= max) {
|
|
26339
|
+
break;
|
|
26340
|
+
}
|
|
26341
|
+
}
|
|
26342
|
+
return refs;
|
|
26343
|
+
}
|
|
26344
|
+
async function remoteRefs(cwd) {
|
|
26345
|
+
const result = await runGit2(cwd, ["for-each-ref", "--format=%(objectname) %(refname) %(symref)", "refs/remotes/"]);
|
|
26346
|
+
if (result === undefined || result.code !== 0) {
|
|
26347
|
+
return [];
|
|
26348
|
+
}
|
|
26349
|
+
const entries = [];
|
|
26350
|
+
for (const line of lines(result.stdout)) {
|
|
26351
|
+
const [tip, ref, symref] = line.split(" ");
|
|
26352
|
+
if (tip === undefined || ref === undefined) {
|
|
26353
|
+
continue;
|
|
26354
|
+
}
|
|
26355
|
+
entries.push({ tip, ref, symref: symref ?? "" });
|
|
26356
|
+
}
|
|
26357
|
+
return orderRemoteRefs(entries);
|
|
26358
|
+
}
|
|
26359
|
+
async function flowDirsOnRef(cwd, ref, flowsPath) {
|
|
26360
|
+
const result = await runGit2(cwd, ["ls-tree", "-z", "-d", "--name-only", "--full-tree", ref, "--", `${flowsPath}/`]);
|
|
26361
|
+
if (result === undefined || result.code !== 0) {
|
|
26362
|
+
return [];
|
|
26363
|
+
}
|
|
26364
|
+
return nulSplit(result.stdout).map((entry) => path55.posix.basename(entry)).filter((name) => /^\d{3}-/.test(name));
|
|
26365
|
+
}
|
|
26366
|
+
async function knownRemoteFlowDirs(cwd) {
|
|
26367
|
+
try {
|
|
26368
|
+
const prefix = await showPrefix(cwd);
|
|
26369
|
+
if (prefix === null) {
|
|
26370
|
+
return [];
|
|
26371
|
+
}
|
|
26372
|
+
const refs = await remoteRefs(cwd);
|
|
26373
|
+
if (refs.length === 0) {
|
|
26374
|
+
return [];
|
|
26375
|
+
}
|
|
26376
|
+
const flowsPath = `${prefix}.metaproject/flows`;
|
|
26377
|
+
const found = new Array(refs.length).fill([]);
|
|
26378
|
+
let next = 0;
|
|
26379
|
+
const worker = async () => {
|
|
26380
|
+
while (next < refs.length) {
|
|
26381
|
+
const index = next;
|
|
26382
|
+
next += 1;
|
|
26383
|
+
const ref = refs[index];
|
|
26384
|
+
if (ref === undefined) {
|
|
26385
|
+
continue;
|
|
26386
|
+
}
|
|
26387
|
+
const short = ref.slice("refs/remotes/".length);
|
|
26388
|
+
found[index] = (await flowDirsOnRef(cwd, ref, flowsPath)).map((dir) => ({ ref: short, dir }));
|
|
26389
|
+
}
|
|
26390
|
+
};
|
|
26391
|
+
await Promise.all(Array.from({ length: Math.min(CONCURRENCY, refs.length) }, () => worker()));
|
|
26392
|
+
return found.flat();
|
|
26393
|
+
} catch {
|
|
26394
|
+
return [];
|
|
26395
|
+
}
|
|
26396
|
+
}
|
|
26397
|
+
function flowNumberOfDir(dir) {
|
|
26398
|
+
const dash = dir.indexOf("-");
|
|
26399
|
+
return dash > 0 ? Number(dir.slice(0, dash)) : Number.NaN;
|
|
26400
|
+
}
|
|
26401
|
+
async function remoteFlowNumbers(cwd) {
|
|
26402
|
+
const dirs = await knownRemoteFlowDirs(cwd);
|
|
26403
|
+
const numbers = new Set;
|
|
26404
|
+
for (const { dir } of dirs) {
|
|
26405
|
+
const value = flowNumberOfDir(dir);
|
|
26406
|
+
if (!Number.isNaN(value)) {
|
|
26407
|
+
numbers.add(value);
|
|
26408
|
+
}
|
|
26409
|
+
}
|
|
26410
|
+
return [...numbers].sort((a, b) => a - b);
|
|
26411
|
+
}
|
|
26412
|
+
|
|
26413
|
+
// src/flow/folder-committed.ts
|
|
26414
|
+
var BATCH = 100;
|
|
26415
|
+
async function flowFoldersInHead(cwd, dirs) {
|
|
26416
|
+
const prefix = await showPrefix(cwd);
|
|
26417
|
+
if (prefix === null) {
|
|
26418
|
+
return null;
|
|
26419
|
+
}
|
|
26420
|
+
const committed = new Set;
|
|
26421
|
+
if (dirs.length === 0) {
|
|
26422
|
+
return committed;
|
|
26423
|
+
}
|
|
26424
|
+
const head = await runGit2(cwd, ["rev-parse", "--verify", "-q", "HEAD"]);
|
|
26425
|
+
if (head === undefined) {
|
|
26426
|
+
return null;
|
|
26427
|
+
}
|
|
26428
|
+
if (head.code !== 0) {
|
|
26429
|
+
return committed;
|
|
26430
|
+
}
|
|
26431
|
+
for (let start = 0;start < dirs.length; start += BATCH) {
|
|
26432
|
+
const batch = dirs.slice(start, start + BATCH);
|
|
26433
|
+
const paths = batch.map((dir) => `${prefix}.metaproject/flows/${dir}/flow.json`);
|
|
26434
|
+
const result = await runGit2(cwd, ["ls-tree", "-z", "--name-only", "--full-tree", "HEAD", "--", ...paths]);
|
|
26435
|
+
if (result === undefined) {
|
|
26436
|
+
return null;
|
|
26437
|
+
}
|
|
26438
|
+
if (result.code !== 0) {
|
|
26439
|
+
throw new Error(`could not read HEAD tree (git ls-tree exited ${result.code}); not treating the flow folders as uncommitted`);
|
|
26440
|
+
}
|
|
26441
|
+
const found = new Set(nulSplit(result.stdout));
|
|
26442
|
+
for (const [index, dir] of batch.entries()) {
|
|
26443
|
+
if (found.has(paths[index] ?? "")) {
|
|
26444
|
+
committed.add(dir);
|
|
26445
|
+
}
|
|
26446
|
+
}
|
|
26447
|
+
}
|
|
26448
|
+
return committed;
|
|
26449
|
+
}
|
|
26450
|
+
|
|
26272
26451
|
// src/flow/templates.ts
|
|
26273
26452
|
function renderDescription(title, source) {
|
|
26274
26453
|
return `# ${title}
|
|
@@ -26353,7 +26532,7 @@ function renderJournal(createdAt) {
|
|
|
26353
26532
|
|
|
26354
26533
|
// src/flow/service.ts
|
|
26355
26534
|
async function deriveAcKinds(cwd, dir, flowId) {
|
|
26356
|
-
const { report } = buildAcKindReport(flowId, await
|
|
26535
|
+
const { report } = buildAcKindReport(flowId, await readFile28(acPath(cwd, dir), "utf8"));
|
|
26357
26536
|
return { ...report.criteria };
|
|
26358
26537
|
}
|
|
26359
26538
|
function prMergeState(pr, gates) {
|
|
@@ -26384,6 +26563,7 @@ function prMergeState(pr, gates) {
|
|
|
26384
26563
|
}
|
|
26385
26564
|
var UNCONFIRMED_PREFIX = "unconfirmed: ";
|
|
26386
26565
|
var NO_PR_DETAIL = "no PR recorded";
|
|
26566
|
+
var FOLDER_UNCOMMITTED_PREFIX = "flow folder ";
|
|
26387
26567
|
function completionAdmitted(status, merged) {
|
|
26388
26568
|
return canTransition(status, "completing") || merged && status === "in-progress";
|
|
26389
26569
|
}
|
|
@@ -26409,6 +26589,10 @@ function completionFixHint(gate, flowId) {
|
|
|
26409
26589
|
return "keryx health run";
|
|
26410
26590
|
case "confirmation":
|
|
26411
26591
|
return `keryx flow confirm ${flowId} (in a terminal)`;
|
|
26592
|
+
case "folder-committed": {
|
|
26593
|
+
const dir = gate.detail.startsWith(FOLDER_UNCOMMITTED_PREFIX) ? gate.detail.slice(FOLDER_UNCOMMITTED_PREFIX.length).split(" ")[0] : undefined;
|
|
26594
|
+
return dir ? `git add .metaproject/flows/${dir} && git commit` : undefined;
|
|
26595
|
+
}
|
|
26412
26596
|
default:
|
|
26413
26597
|
return;
|
|
26414
26598
|
}
|
|
@@ -26541,6 +26725,11 @@ function createFlowService(deps) {
|
|
|
26541
26725
|
}
|
|
26542
26726
|
gates.push(taskGate(flow));
|
|
26543
26727
|
gates.push(ownerGate(flow));
|
|
26728
|
+
try {
|
|
26729
|
+
gates.push(await folderCommittedGate(cwd, dir, flow));
|
|
26730
|
+
} catch {
|
|
26731
|
+
gates.push(unevaluableGate("folder-committed"));
|
|
26732
|
+
}
|
|
26544
26733
|
try {
|
|
26545
26734
|
gates.push(await reviewGate({
|
|
26546
26735
|
cwd,
|
|
@@ -26612,11 +26801,14 @@ function createFlowService(deps) {
|
|
|
26612
26801
|
const title = input.title ?? context.issueTitle ?? provisionalTitle;
|
|
26613
26802
|
const scope = await resolveAllocationScope(input.cwd);
|
|
26614
26803
|
return withFileLock(scope.lockPath, async () => {
|
|
26615
|
-
const id = await nextFlowId(input.cwd,
|
|
26804
|
+
const id = await nextFlowId(input.cwd, [
|
|
26805
|
+
...await reservedIds(scope),
|
|
26806
|
+
...await remoteFlowNumbers(input.cwd)
|
|
26807
|
+
]);
|
|
26616
26808
|
const date = now().slice(0, 10);
|
|
26617
26809
|
const slug = slugify(input.slug ?? title);
|
|
26618
26810
|
const dir = `${id}-${date}-${slug}`;
|
|
26619
|
-
const absolute =
|
|
26811
|
+
const absolute = path56.join(flowsRoot(input.cwd), dir);
|
|
26620
26812
|
if (await pathExists(absolute)) {
|
|
26621
26813
|
throw new Error(`Flow directory already exists: ${dir}`);
|
|
26622
26814
|
}
|
|
@@ -26628,6 +26820,7 @@ function createFlowService(deps) {
|
|
|
26628
26820
|
tasks: true,
|
|
26629
26821
|
review: true,
|
|
26630
26822
|
owner: true,
|
|
26823
|
+
folderCommitted: true,
|
|
26631
26824
|
...input.requireConfirmation === true || await readRequireConfirmationDefault(input.cwd) ? { confirmation: true } : {}
|
|
26632
26825
|
},
|
|
26633
26826
|
id,
|
|
@@ -26650,15 +26843,15 @@ function createFlowService(deps) {
|
|
|
26650
26843
|
history: [{ at: createdAt, event: "created" }]
|
|
26651
26844
|
};
|
|
26652
26845
|
const sourceLabel = input.issue ?? "user description";
|
|
26653
|
-
await writeFileAtomic(
|
|
26654
|
-
await writeFileAtomic(
|
|
26655
|
-
await writeFileAtomic(
|
|
26656
|
-
await writeFileAtomic(
|
|
26657
|
-
await writeFileAtomic(
|
|
26658
|
-
await writeFileAtomic(
|
|
26846
|
+
await writeFileAtomic(path56.join(absolute, "description.md"), renderDescription(title, sourceLabel));
|
|
26847
|
+
await writeFileAtomic(path56.join(absolute, "context.md"), context.markdown);
|
|
26848
|
+
await writeFileAtomic(path56.join(absolute, "plan.md"), renderPlan());
|
|
26849
|
+
await writeFileAtomic(path56.join(absolute, "tasks.md"), renderTasksDoc());
|
|
26850
|
+
await writeFileAtomic(path56.join(absolute, "acceptance-criteria.md"), renderAcceptanceCriteria());
|
|
26851
|
+
await writeFileAtomic(path56.join(absolute, "journal.md"), renderJournal(createdAt));
|
|
26659
26852
|
await writeFlow(input.cwd, dir, flow);
|
|
26660
26853
|
await recordAllocation(scope, { id, dir, at: createdAt, project: scope.project });
|
|
26661
|
-
return { flow, dir:
|
|
26854
|
+
return { flow, dir: path56.relative(input.cwd, absolute), contextNotes: context.notes };
|
|
26662
26855
|
});
|
|
26663
26856
|
},
|
|
26664
26857
|
async list({ cwd }) {
|
|
@@ -26883,7 +27076,7 @@ function createFlowService(deps) {
|
|
|
26883
27076
|
},
|
|
26884
27077
|
async acKinds({ cwd, id }) {
|
|
26885
27078
|
const { dir, flow } = await load(cwd, id);
|
|
26886
|
-
return buildAcKindReport(flow.id, await
|
|
27079
|
+
return buildAcKindReport(flow.id, await readFile28(acPath(cwd, dir), "utf8"));
|
|
26887
27080
|
},
|
|
26888
27081
|
async acReseal({ cwd, id, reason }) {
|
|
26889
27082
|
if (!reason?.trim()) {
|
|
@@ -27141,6 +27334,10 @@ function createFlowService(deps) {
|
|
|
27141
27334
|
if ((await reservedIds(scope)).includes(Number(to))) {
|
|
27142
27335
|
throw new Error(`Flow id ${to} was already used in this repository and cannot be reused. ` + "Pick a fresh number (see .metaproject/flows/id-map.json).");
|
|
27143
27336
|
}
|
|
27337
|
+
const remoteUse = (await knownRemoteFlowDirs(cwd)).find((entry) => flowNumberOfDir(entry.dir) === Number(to));
|
|
27338
|
+
if (remoteUse) {
|
|
27339
|
+
throw new Error(`Flow id ${to} is already used on ${remoteUse.ref} (${safeDirName(remoteUse.dir)}) and cannot be reused. ` + "Pick a fresh number.");
|
|
27340
|
+
}
|
|
27144
27341
|
const toDir = `${to}${fromDir.slice(3)}`;
|
|
27145
27342
|
const { flow, reviewRecords } = await withFileLock(flowLockPath(cwd, fromDir), async () => {
|
|
27146
27343
|
const reviewRecords = await moveFlowDirWithReviewRecords({ cwd, from, to, fromDir, toDir });
|
|
@@ -27176,6 +27373,34 @@ function createFlowService(deps) {
|
|
|
27176
27373
|
});
|
|
27177
27374
|
}
|
|
27178
27375
|
}
|
|
27376
|
+
const remoteDirs = await knownRemoteFlowDirs(cwd);
|
|
27377
|
+
if (remoteDirs.length > 0) {
|
|
27378
|
+
for (const dir of allDirs) {
|
|
27379
|
+
const clash = remoteDirs.find((entry) => entry.dir !== dir && flowNumberOfDir(entry.dir) === Number(flowIdOf(dir)));
|
|
27380
|
+
if (clash) {
|
|
27381
|
+
issues.push({
|
|
27382
|
+
flow: dir,
|
|
27383
|
+
kind: "duplicate-id",
|
|
27384
|
+
message: `flow id ${flowIdOf(dir)} is also used on ${clash.ref} by a different flow (${safeDirName(clash.dir)})` + ` \u2014 repair with: keryx flow renumber ${dir} --to <free id> --reason "<why>"`
|
|
27385
|
+
});
|
|
27386
|
+
}
|
|
27387
|
+
}
|
|
27388
|
+
}
|
|
27389
|
+
const warnings = [];
|
|
27390
|
+
const inHead = await flowFoldersInHead(cwd, allDirs).catch(() => {
|
|
27391
|
+
return null;
|
|
27392
|
+
});
|
|
27393
|
+
if (inHead !== null) {
|
|
27394
|
+
for (const dir of allDirs) {
|
|
27395
|
+
if (!inHead.has(dir)) {
|
|
27396
|
+
warnings.push({
|
|
27397
|
+
flow: dir,
|
|
27398
|
+
kind: "untracked",
|
|
27399
|
+
message: `flow folder ${dir} is not committed: commit it in the same PR as the code`
|
|
27400
|
+
});
|
|
27401
|
+
}
|
|
27402
|
+
}
|
|
27403
|
+
}
|
|
27179
27404
|
for (const dir of allDirs) {
|
|
27180
27405
|
let flow;
|
|
27181
27406
|
try {
|
|
@@ -27192,7 +27417,7 @@ function createFlowService(deps) {
|
|
|
27192
27417
|
issues.push({ flow: dir, kind: "schema", message: `unknown schemaVersion ${flow.schemaVersion}` });
|
|
27193
27418
|
}
|
|
27194
27419
|
try {
|
|
27195
|
-
const raw = JSON.parse(await Bun.file(
|
|
27420
|
+
const raw = JSON.parse(await Bun.file(path56.join(flowsRoot(cwd), dir, "flow.json")).text());
|
|
27196
27421
|
const validation = validateAgainstSchemaObject(flowStateSchema(), raw);
|
|
27197
27422
|
if (!validation.valid) {
|
|
27198
27423
|
issues.push({
|
|
@@ -27203,7 +27428,7 @@ function createFlowService(deps) {
|
|
|
27203
27428
|
}
|
|
27204
27429
|
} catch {}
|
|
27205
27430
|
for (const file of ["description.md", "context.md", "plan.md", "tasks.md", "acceptance-criteria.md", "journal.md"]) {
|
|
27206
|
-
if (!await pathExists(
|
|
27431
|
+
if (!await pathExists(path56.join(flowsRoot(cwd), dir, file))) {
|
|
27207
27432
|
issues.push({ flow: dir, kind: "structure", message: `missing ${file}` });
|
|
27208
27433
|
}
|
|
27209
27434
|
}
|
|
@@ -27237,15 +27462,15 @@ function createFlowService(deps) {
|
|
|
27237
27462
|
});
|
|
27238
27463
|
}
|
|
27239
27464
|
}
|
|
27240
|
-
return { ok: issues.length === 0, issues };
|
|
27465
|
+
return { ok: issues.length === 0, issues, warnings };
|
|
27241
27466
|
}
|
|
27242
27467
|
};
|
|
27243
27468
|
}
|
|
27244
27469
|
async function appendIdMap(cwd, entry) {
|
|
27245
|
-
const file =
|
|
27470
|
+
const file = path56.join(flowsRoot(cwd), "id-map.json");
|
|
27246
27471
|
let entries = [];
|
|
27247
27472
|
if (await pathExists(file)) {
|
|
27248
|
-
const raw = await
|
|
27473
|
+
const raw = await readFile28(file, "utf8");
|
|
27249
27474
|
try {
|
|
27250
27475
|
const parsed = JSON.parse(raw);
|
|
27251
27476
|
if (!Array.isArray(parsed)) {
|
|
@@ -27253,7 +27478,7 @@ async function appendIdMap(cwd, entry) {
|
|
|
27253
27478
|
}
|
|
27254
27479
|
entries = parsed;
|
|
27255
27480
|
} catch (error) {
|
|
27256
|
-
throw new Error(`Cannot read ${
|
|
27481
|
+
throw new Error(`Cannot read ${path56.relative(cwd, file)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
27257
27482
|
}
|
|
27258
27483
|
}
|
|
27259
27484
|
entries.push(entry);
|
|
@@ -27347,6 +27572,27 @@ function ownerGate(flow) {
|
|
|
27347
27572
|
}
|
|
27348
27573
|
return { name: "owner", status: "pass", detail: `owner: ${flow.owner.value}` };
|
|
27349
27574
|
}
|
|
27575
|
+
async function folderCommittedGate(cwd, dir, flow) {
|
|
27576
|
+
if (!flow.gates?.folderCommitted) {
|
|
27577
|
+
return {
|
|
27578
|
+
name: "folder-committed",
|
|
27579
|
+
status: "skipped",
|
|
27580
|
+
detail: "folder-committed gate not enabled for this package (created before the gate); " + "flows created by this keryx version opt in automatically"
|
|
27581
|
+
};
|
|
27582
|
+
}
|
|
27583
|
+
const committed = await flowFoldersInHead(cwd, [dir]);
|
|
27584
|
+
if (committed === null) {
|
|
27585
|
+
return { name: "folder-committed", status: "skipped", detail: "not a git repository; nothing to compare the flow folder with" };
|
|
27586
|
+
}
|
|
27587
|
+
if (!committed.has(dir)) {
|
|
27588
|
+
return {
|
|
27589
|
+
name: "folder-committed",
|
|
27590
|
+
status: "fail",
|
|
27591
|
+
detail: `${FOLDER_UNCOMMITTED_PREFIX}${dir} is not committed. Commit it (git add .metaproject/flows/${dir} && git commit) ` + "in the PR that carries the code, then run flow complete again"
|
|
27592
|
+
};
|
|
27593
|
+
}
|
|
27594
|
+
return { name: "folder-committed", status: "pass", detail: `${dir} is committed in HEAD` };
|
|
27595
|
+
}
|
|
27350
27596
|
function confirmPreconditionError(flow, merged) {
|
|
27351
27597
|
const confirmable = flow.status === "implemented" || flow.status === "in-progress" && merged;
|
|
27352
27598
|
if (!confirmable) {
|
|
@@ -27504,9 +27750,9 @@ __export(exports_service5, {
|
|
|
27504
27750
|
});
|
|
27505
27751
|
|
|
27506
27752
|
// src/gdgraph/build.ts
|
|
27507
|
-
import { mkdir as mkdir21, readdir as
|
|
27753
|
+
import { mkdir as mkdir21, readdir as readdir14, readFile as readFile39, writeFile as writeFile15 } from "fs/promises";
|
|
27508
27754
|
import { existsSync as existsSync7 } from "fs";
|
|
27509
|
-
import
|
|
27755
|
+
import path76 from "path";
|
|
27510
27756
|
|
|
27511
27757
|
// src/gdgraph/types.ts
|
|
27512
27758
|
var UNKNOWN_IMPORT_KIND = "unknown-static";
|
|
@@ -27571,8 +27817,8 @@ async function buildGraph(projectRoot) {
|
|
|
27571
27817
|
const assetNodes = new Map;
|
|
27572
27818
|
const fileRecords = [];
|
|
27573
27819
|
for (const file of files) {
|
|
27574
|
-
const absolutePath =
|
|
27575
|
-
const content = await
|
|
27820
|
+
const absolutePath = path76.join(projectRoot, file);
|
|
27821
|
+
const content = await readFile39(absolutePath, "utf8");
|
|
27576
27822
|
fileRecords.push({ path: file, content });
|
|
27577
27823
|
const language = getLanguage(file);
|
|
27578
27824
|
const resolver = pickResolver(resolvers, language);
|
|
@@ -27624,24 +27870,24 @@ async function collectSourceFiles(projectRoot) {
|
|
|
27624
27870
|
const result = [];
|
|
27625
27871
|
const skippedDirectories = [];
|
|
27626
27872
|
async function walk(relativeDir) {
|
|
27627
|
-
const absoluteDir =
|
|
27628
|
-
const entries = await
|
|
27873
|
+
const absoluteDir = path76.join(projectRoot, relativeDir);
|
|
27874
|
+
const entries = await readdir14(absoluteDir, { withFileTypes: true });
|
|
27629
27875
|
for (const entry of entries) {
|
|
27630
27876
|
if (entry.isDirectory()) {
|
|
27631
27877
|
if (IGNORE_DIRS.has(entry.name)) {
|
|
27632
|
-
skippedDirectories.push(normalizePath2(
|
|
27878
|
+
skippedDirectories.push(normalizePath2(path76.posix.join(relativeDir, entry.name)).replace(/^\.\//, ""));
|
|
27633
27879
|
continue;
|
|
27634
27880
|
}
|
|
27635
|
-
await walk(normalizePath2(
|
|
27881
|
+
await walk(normalizePath2(path76.posix.join(relativeDir, entry.name)));
|
|
27636
27882
|
continue;
|
|
27637
27883
|
}
|
|
27638
27884
|
if (!entry.isFile()) {
|
|
27639
27885
|
continue;
|
|
27640
27886
|
}
|
|
27641
|
-
if (!SOURCE_EXTENSIONS.includes(
|
|
27887
|
+
if (!SOURCE_EXTENSIONS.includes(path76.extname(entry.name))) {
|
|
27642
27888
|
continue;
|
|
27643
27889
|
}
|
|
27644
|
-
result.push(normalizePath2(
|
|
27890
|
+
result.push(normalizePath2(path76.posix.join(relativeDir, entry.name)).replace(/^\.\//, ""));
|
|
27645
27891
|
}
|
|
27646
27892
|
}
|
|
27647
27893
|
await walk(".");
|
|
@@ -27723,8 +27969,8 @@ function resolveImport(projectRoot, fromFile, specifier, fileSet, resolver) {
|
|
|
27723
27969
|
}
|
|
27724
27970
|
function resolveAssetImport(projectRoot, fromFile, specifier, resolver) {
|
|
27725
27971
|
for (const candidate of importCandidateBases(projectRoot, fromFile, specifier, resolver)) {
|
|
27726
|
-
const extension =
|
|
27727
|
-
if (ASSET_EXTENSIONS.includes(extension) && existsSync7(
|
|
27972
|
+
const extension = path76.posix.extname(candidate);
|
|
27973
|
+
if (ASSET_EXTENSIONS.includes(extension) && existsSync7(path76.join(projectRoot, candidate))) {
|
|
27728
27974
|
return candidate;
|
|
27729
27975
|
}
|
|
27730
27976
|
}
|
|
@@ -27733,17 +27979,17 @@ function resolveAssetImport(projectRoot, fromFile, specifier, resolver) {
|
|
|
27733
27979
|
function importCandidateBases(projectRoot, fromFile, specifier, resolver) {
|
|
27734
27980
|
const normalizedSpecifier = stripImportSuffix(specifier);
|
|
27735
27981
|
if (normalizedSpecifier.startsWith(".") && getLanguage(fromFile) !== "python") {
|
|
27736
|
-
const fromDir =
|
|
27737
|
-
return [normalizePath2(
|
|
27982
|
+
const fromDir = path76.posix.dirname(fromFile);
|
|
27983
|
+
return [normalizePath2(path76.posix.normalize(path76.posix.join(fromDir, normalizedSpecifier)))];
|
|
27738
27984
|
}
|
|
27739
|
-
return resolver.candidateBases(normalizedSpecifier, fromFile).map((candidate) => normalizePath2(
|
|
27985
|
+
return resolver.candidateBases(normalizedSpecifier, fromFile).map((candidate) => normalizePath2(path76.relative(projectRoot, path76.join(projectRoot, candidate))));
|
|
27740
27986
|
}
|
|
27741
27987
|
function resolveSourceCandidate(base, fileSet) {
|
|
27742
27988
|
const candidates = [
|
|
27743
27989
|
base,
|
|
27744
27990
|
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) => `${base}${extension}`),
|
|
27745
|
-
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) =>
|
|
27746
|
-
|
|
27991
|
+
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) => path76.posix.join(base, `index${extension}`)),
|
|
27992
|
+
path76.posix.join(base, "__init__.py")
|
|
27747
27993
|
];
|
|
27748
27994
|
return candidates.find((candidate) => fileSet.has(candidate)) ?? null;
|
|
27749
27995
|
}
|
|
@@ -27773,20 +28019,20 @@ async function loadJavaResolver(projectRoot) {
|
|
|
27773
28019
|
return createJavaResolver(roots);
|
|
27774
28020
|
}
|
|
27775
28021
|
async function discoverJavaSourceRoots(projectRoot) {
|
|
27776
|
-
const pomPath =
|
|
28022
|
+
const pomPath = path76.join(projectRoot, "pom.xml");
|
|
27777
28023
|
if (existsSync7(pomPath)) {
|
|
27778
28024
|
try {
|
|
27779
|
-
const roots = parseMavenSourceRoots(await
|
|
28025
|
+
const roots = parseMavenSourceRoots(await readFile39(pomPath, "utf8"));
|
|
27780
28026
|
if (roots.length > 0) {
|
|
27781
28027
|
return roots;
|
|
27782
28028
|
}
|
|
27783
28029
|
} catch {}
|
|
27784
28030
|
}
|
|
27785
28031
|
for (const gradleName of ["build.gradle", "build.gradle.kts"]) {
|
|
27786
|
-
const gradlePath =
|
|
28032
|
+
const gradlePath = path76.join(projectRoot, gradleName);
|
|
27787
28033
|
if (existsSync7(gradlePath)) {
|
|
27788
28034
|
try {
|
|
27789
|
-
const roots = parseGradleSourceRoots(await
|
|
28035
|
+
const roots = parseGradleSourceRoots(await readFile39(gradlePath, "utf8"));
|
|
27790
28036
|
if (roots.length > 0) {
|
|
27791
28037
|
return roots;
|
|
27792
28038
|
}
|
|
@@ -27805,12 +28051,12 @@ function createJavaResolver(sourceRoots) {
|
|
|
27805
28051
|
return [];
|
|
27806
28052
|
}
|
|
27807
28053
|
const relative = specifier.split(".").join("/");
|
|
27808
|
-
return sourceRoots.map((root) => normalizePath2(
|
|
28054
|
+
return sourceRoots.map((root) => normalizePath2(path76.posix.join(root, relative)));
|
|
27809
28055
|
}
|
|
27810
28056
|
};
|
|
27811
28057
|
}
|
|
27812
28058
|
function loadPythonResolver(projectRoot) {
|
|
27813
|
-
const hasSrc = existsSync7(
|
|
28059
|
+
const hasSrc = existsSync7(path76.join(projectRoot, "src"));
|
|
27814
28060
|
return createPythonResolver(hasSrc);
|
|
27815
28061
|
}
|
|
27816
28062
|
function createPythonResolver(hasSrc) {
|
|
@@ -27823,16 +28069,16 @@ function createPythonResolver(hasSrc) {
|
|
|
27823
28069
|
if (specifier.startsWith(".")) {
|
|
27824
28070
|
const dots = /^\.+/.exec(specifier)?.[0].length ?? 0;
|
|
27825
28071
|
const rest = specifier.slice(dots);
|
|
27826
|
-
let baseDir =
|
|
28072
|
+
let baseDir = path76.posix.dirname(fromFile);
|
|
27827
28073
|
for (let level = 1;level < dots; level += 1) {
|
|
27828
|
-
baseDir =
|
|
28074
|
+
baseDir = path76.posix.dirname(baseDir);
|
|
27829
28075
|
}
|
|
27830
28076
|
const suffix = rest ? rest.split(".").join("/") : "";
|
|
27831
|
-
const base = suffix ?
|
|
28077
|
+
const base = suffix ? path76.posix.join(baseDir, suffix) : baseDir;
|
|
27832
28078
|
return [normalizePath2(base)];
|
|
27833
28079
|
}
|
|
27834
28080
|
const relative = specifier.split(".").join("/");
|
|
27835
|
-
return roots.map((root) => normalizePath2(root ?
|
|
28081
|
+
return roots.map((root) => normalizePath2(root ? path76.posix.join(root, relative) : relative));
|
|
27836
28082
|
}
|
|
27837
28083
|
};
|
|
27838
28084
|
}
|
|
@@ -27841,8 +28087,8 @@ function parseMavenSourceRoots(pomXml) {
|
|
|
27841
28087
|
for (const match of pomXml.matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
|
|
27842
28088
|
const moduleDir = match[1]?.trim();
|
|
27843
28089
|
if (moduleDir && !moduleDir.includes("${")) {
|
|
27844
|
-
roots.add(normalizePath2(
|
|
27845
|
-
roots.add(normalizePath2(
|
|
28090
|
+
roots.add(normalizePath2(path76.posix.join(moduleDir, "src/main/java")));
|
|
28091
|
+
roots.add(normalizePath2(path76.posix.join(moduleDir, "src/test/java")));
|
|
27846
28092
|
}
|
|
27847
28093
|
}
|
|
27848
28094
|
for (const tag of ["sourceDirectory", "testSourceDirectory"]) {
|
|
@@ -27875,7 +28121,7 @@ function parseGradleSourceRoots(buildGradle) {
|
|
|
27875
28121
|
var TSCONFIG_EXTENDS_MAX_DEPTH = 10;
|
|
27876
28122
|
async function loadTsconfigResolver(projectRoot) {
|
|
27877
28123
|
const empty = createTsconfigResolver(null, []);
|
|
27878
|
-
const tsconfigPath =
|
|
28124
|
+
const tsconfigPath = path76.join(projectRoot, "tsconfig.json");
|
|
27879
28125
|
if (!existsSync7(tsconfigPath)) {
|
|
27880
28126
|
return empty;
|
|
27881
28127
|
}
|
|
@@ -27888,12 +28134,12 @@ async function loadTsconfigResolver(projectRoot) {
|
|
|
27888
28134
|
return createTsconfigResolver(resolved.baseUrl, paths);
|
|
27889
28135
|
}
|
|
27890
28136
|
function toProjectRelativeDir(projectRoot, absoluteDir) {
|
|
27891
|
-
const relative = normalizePath2(
|
|
28137
|
+
const relative = normalizePath2(path76.relative(projectRoot, absoluteDir)).replace(/^\.\//, "");
|
|
27892
28138
|
return relative === "." ? "" : relative;
|
|
27893
28139
|
}
|
|
27894
28140
|
async function readTsconfigJson(configPath) {
|
|
27895
28141
|
try {
|
|
27896
|
-
const raw = await
|
|
28142
|
+
const raw = await readFile39(configPath, "utf8");
|
|
27897
28143
|
return JSON.parse(stripJsonComments(raw));
|
|
27898
28144
|
} catch {
|
|
27899
28145
|
return null;
|
|
@@ -27903,11 +28149,11 @@ function resolveTsconfigExtendsPath(configDir, extendsSpecifier) {
|
|
|
27903
28149
|
if (!extendsSpecifier.startsWith(".") && !extendsSpecifier.startsWith("/")) {
|
|
27904
28150
|
return null;
|
|
27905
28151
|
}
|
|
27906
|
-
const resolved =
|
|
28152
|
+
const resolved = path76.resolve(configDir, extendsSpecifier);
|
|
27907
28153
|
return resolved.endsWith(".json") ? resolved : `${resolved}.json`;
|
|
27908
28154
|
}
|
|
27909
28155
|
async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
27910
|
-
const resolvedPath =
|
|
28156
|
+
const resolvedPath = path76.resolve(configPath);
|
|
27911
28157
|
if (depth > TSCONFIG_EXTENDS_MAX_DEPTH || visited.has(resolvedPath)) {
|
|
27912
28158
|
return null;
|
|
27913
28159
|
}
|
|
@@ -27918,15 +28164,15 @@ async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
|
27918
28164
|
}
|
|
27919
28165
|
let inherited = null;
|
|
27920
28166
|
if (typeof parsed.extends === "string") {
|
|
27921
|
-
const basePath = resolveTsconfigExtendsPath(
|
|
28167
|
+
const basePath = resolveTsconfigExtendsPath(path76.dirname(resolvedPath), parsed.extends);
|
|
27922
28168
|
if (basePath) {
|
|
27923
28169
|
inherited = await resolveTsconfigOptions(basePath, visited, depth + 1, projectRoot);
|
|
27924
28170
|
}
|
|
27925
28171
|
}
|
|
27926
|
-
const configDir =
|
|
28172
|
+
const configDir = path76.dirname(resolvedPath);
|
|
27927
28173
|
const declaringDir = toProjectRelativeDir(projectRoot, configDir);
|
|
27928
28174
|
const options = parsed.compilerOptions ?? {};
|
|
27929
|
-
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot,
|
|
28175
|
+
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot, path76.resolve(configDir, options.baseUrl)) : null;
|
|
27930
28176
|
const baseUrl = ownBaseUrl ?? inherited?.baseUrl ?? null;
|
|
27931
28177
|
const ownPaths = isRecord4(options.paths) ? Object.entries(options.paths).filter((entry) => Array.isArray(entry[1])).map(([pattern, targets]) => ({
|
|
27932
28178
|
pattern,
|
|
@@ -27955,7 +28201,7 @@ function createTsconfigResolver(baseUrl, mappings) {
|
|
|
27955
28201
|
}
|
|
27956
28202
|
}
|
|
27957
28203
|
if (baseUrl !== null) {
|
|
27958
|
-
candidates.push(normalizePath2(
|
|
28204
|
+
candidates.push(normalizePath2(path76.posix.join(baseUrl, normalizedSpecifier)));
|
|
27959
28205
|
}
|
|
27960
28206
|
return [...new Set(candidates.map((candidate) => candidate.replace(/^\.\//, "")))];
|
|
27961
28207
|
}
|
|
@@ -27975,7 +28221,7 @@ function matchPathPattern(pattern, specifier) {
|
|
|
27975
28221
|
}
|
|
27976
28222
|
function applyPathTarget(base, target, wildcard) {
|
|
27977
28223
|
const replaced = target.includes("*") ? target.replace("*", wildcard) : target;
|
|
27978
|
-
return normalizePath2(
|
|
28224
|
+
return normalizePath2(path76.posix.normalize(path76.posix.join(base, replaced)));
|
|
27979
28225
|
}
|
|
27980
28226
|
function stripJsonComments(source) {
|
|
27981
28227
|
let out = "";
|
|
@@ -28028,21 +28274,21 @@ function isRecord4(value) {
|
|
|
28028
28274
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28029
28275
|
}
|
|
28030
28276
|
async function writeGraph(projectRoot, graph) {
|
|
28031
|
-
const storageDir =
|
|
28032
|
-
const artifactsDir =
|
|
28277
|
+
const storageDir = path76.join(projectRoot, ".metaproject", "data", "gdgraph", "storage");
|
|
28278
|
+
const artifactsDir = path76.join(projectRoot, ".metaproject", "data", "gdgraph", "artifacts");
|
|
28033
28279
|
await mkdir21(storageDir, { recursive: true });
|
|
28034
28280
|
await mkdir21(artifactsDir, { recursive: true });
|
|
28035
|
-
await writeFile15(
|
|
28281
|
+
await writeFile15(path76.join(storageDir, "nodes.jsonl"), graph.nodes.map((node) => JSON.stringify(node)).join(`
|
|
28036
28282
|
`) + `
|
|
28037
28283
|
`, "utf8");
|
|
28038
|
-
await writeFile15(
|
|
28284
|
+
await writeFile15(path76.join(storageDir, "edges.jsonl"), graph.edges.map((edge) => JSON.stringify(edge)).join(`
|
|
28039
28285
|
`) + `
|
|
28040
28286
|
`, "utf8");
|
|
28041
|
-
await writeFile15(
|
|
28287
|
+
await writeFile15(path76.join(artifactsDir, "module-map.json"), JSON.stringify(buildModuleMap(graph), null, 2) + `
|
|
28042
28288
|
`, "utf8");
|
|
28043
28289
|
}
|
|
28044
28290
|
async function writeSummary(projectRoot, graph, collection) {
|
|
28045
|
-
const artifactsDir =
|
|
28291
|
+
const artifactsDir = path76.join(projectRoot, ".metaproject", "data", "gdgraph", "artifacts");
|
|
28046
28292
|
await mkdir21(artifactsDir, { recursive: true });
|
|
28047
28293
|
const unresolved = graph.edges.filter((edge) => edge.kind === "unresolved");
|
|
28048
28294
|
const imports = graph.edges.filter((edge) => edge.kind === "imports");
|
|
@@ -28057,7 +28303,7 @@ async function writeSummary(projectRoot, graph, collection) {
|
|
|
28057
28303
|
`);
|
|
28058
28304
|
const skippedList = collection.skippedDirectories.length > 0 ? collection.skippedDirectories.slice(0, 30).map((dir) => `- \`${dir}\``).join(`
|
|
28059
28305
|
`) : "- none";
|
|
28060
|
-
const summaryPath =
|
|
28306
|
+
const summaryPath = path76.join(artifactsDir, "summary.md");
|
|
28061
28307
|
const content = `# gdgraph Summary
|
|
28062
28308
|
|
|
28063
28309
|
## Stats
|
|
@@ -28138,7 +28384,7 @@ function groupBy(items, getKey) {
|
|
|
28138
28384
|
}
|
|
28139
28385
|
function unresolvedType(specifier) {
|
|
28140
28386
|
const stripped = stripImportSuffix(specifier);
|
|
28141
|
-
const extension =
|
|
28387
|
+
const extension = path76.posix.extname(stripped);
|
|
28142
28388
|
if (extension) {
|
|
28143
28389
|
return extension;
|
|
28144
28390
|
}
|
|
@@ -28148,13 +28394,13 @@ function unresolvedType(specifier) {
|
|
|
28148
28394
|
return "package";
|
|
28149
28395
|
}
|
|
28150
28396
|
function normalizePath2(value) {
|
|
28151
|
-
return value.split(
|
|
28397
|
+
return value.split(path76.sep).join("/");
|
|
28152
28398
|
}
|
|
28153
28399
|
|
|
28154
28400
|
// src/gdgraph/target.ts
|
|
28155
|
-
import
|
|
28401
|
+
import path77 from "path";
|
|
28156
28402
|
function normalizeTarget(target) {
|
|
28157
|
-
return target.replace(/^\.\//, "").split(
|
|
28403
|
+
return target.replace(/^\.\//, "").split(path77.sep).join("/");
|
|
28158
28404
|
}
|
|
28159
28405
|
function resolveGraphTarget(graph, target) {
|
|
28160
28406
|
const normalized = normalizeTarget(target);
|
|
@@ -28246,16 +28492,16 @@ function normalizeDepth(depth) {
|
|
|
28246
28492
|
init_config3();
|
|
28247
28493
|
|
|
28248
28494
|
// src/gdgraph/query.ts
|
|
28249
|
-
import { readFile as
|
|
28250
|
-
import
|
|
28495
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
28496
|
+
import path78 from "path";
|
|
28251
28497
|
async function loadGraph(projectRoot) {
|
|
28252
|
-
const storageDir =
|
|
28253
|
-
const nodes = await readJsonl(
|
|
28254
|
-
const edges = await readJsonl(
|
|
28255
|
-
const symbols = await readJsonl(
|
|
28256
|
-
const calls = await readJsonl(
|
|
28257
|
-
const wikiPages = await readJsonl(
|
|
28258
|
-
const describes = await readJsonl(
|
|
28498
|
+
const storageDir = path78.join(projectRoot, ".metaproject", "data", "gdgraph", "storage");
|
|
28499
|
+
const nodes = await readJsonl(path78.join(storageDir, "nodes.jsonl"));
|
|
28500
|
+
const edges = await readJsonl(path78.join(storageDir, "edges.jsonl"));
|
|
28501
|
+
const symbols = await readJsonl(path78.join(storageDir, "symbols.jsonl"));
|
|
28502
|
+
const calls = await readJsonl(path78.join(storageDir, "calls.jsonl"));
|
|
28503
|
+
const wikiPages = await readJsonl(path78.join(storageDir, "wiki-pages.jsonl"));
|
|
28504
|
+
const describes = await readJsonl(path78.join(storageDir, "describes.jsonl"));
|
|
28259
28505
|
const graph = { nodes, edges };
|
|
28260
28506
|
if (symbols.length > 0) {
|
|
28261
28507
|
graph.symbols = symbols;
|
|
@@ -28315,7 +28561,7 @@ async function bunfigPreloadRoots(projectRoot) {
|
|
|
28315
28561
|
const roots = new Set;
|
|
28316
28562
|
let text;
|
|
28317
28563
|
try {
|
|
28318
|
-
text = await
|
|
28564
|
+
text = await readFile40(path78.join(projectRoot, "bunfig.toml"), "utf8");
|
|
28319
28565
|
} catch {
|
|
28320
28566
|
return roots;
|
|
28321
28567
|
}
|
|
@@ -28329,8 +28575,8 @@ async function bunfigPreloadRoots(projectRoot) {
|
|
|
28329
28575
|
if (raw.length === 0) {
|
|
28330
28576
|
continue;
|
|
28331
28577
|
}
|
|
28332
|
-
const absolute =
|
|
28333
|
-
const relative =
|
|
28578
|
+
const absolute = path78.resolve(projectRoot, raw);
|
|
28579
|
+
const relative = path78.relative(projectRoot, absolute).split(path78.sep).join("/");
|
|
28334
28580
|
roots.add(relative);
|
|
28335
28581
|
}
|
|
28336
28582
|
}
|
|
@@ -28379,7 +28625,7 @@ function getCycles(graph) {
|
|
|
28379
28625
|
async function readJsonl(filePath) {
|
|
28380
28626
|
let content;
|
|
28381
28627
|
try {
|
|
28382
|
-
content = await
|
|
28628
|
+
content = await readFile40(filePath, "utf8");
|
|
28383
28629
|
} catch {
|
|
28384
28630
|
return [];
|
|
28385
28631
|
}
|
|
@@ -28403,7 +28649,7 @@ function canonicalCycle(cycle) {
|
|
|
28403
28649
|
|
|
28404
28650
|
// src/gdgraph/repomap.ts
|
|
28405
28651
|
import { mkdir as mkdir22, writeFile as writeFile16 } from "fs/promises";
|
|
28406
|
-
import
|
|
28652
|
+
import path79 from "path";
|
|
28407
28653
|
|
|
28408
28654
|
// src/gdgraph/pagerank.ts
|
|
28409
28655
|
function personalizedPageRank(nodes, edges, options) {
|
|
@@ -28514,7 +28760,7 @@ function overflowContent(required, missing, budget) {
|
|
|
28514
28760
|
`);
|
|
28515
28761
|
}
|
|
28516
28762
|
function repomapArtifactPath(cwd) {
|
|
28517
|
-
return
|
|
28763
|
+
return path79.join(cwd, ".metaproject", "data", "gdgraph", "artifacts", "repomap.md");
|
|
28518
28764
|
}
|
|
28519
28765
|
function buildRankEdges(graph, config) {
|
|
28520
28766
|
const edges = [];
|
|
@@ -28659,7 +28905,7 @@ function computeRepomap(graph, config, options = {}) {
|
|
|
28659
28905
|
async function writeRepomap(cwd, graph, config, options = {}) {
|
|
28660
28906
|
const result = computeRepomap(graph, config, options);
|
|
28661
28907
|
const artifactPath = repomapArtifactPath(cwd);
|
|
28662
|
-
await mkdir22(
|
|
28908
|
+
await mkdir22(path79.dirname(artifactPath), { recursive: true });
|
|
28663
28909
|
await writeFile16(artifactPath, result.content, "utf8");
|
|
28664
28910
|
return { ...result, path: artifactPath };
|
|
28665
28911
|
}
|
|
@@ -28741,18 +28987,18 @@ init_section_tombstone();
|
|
|
28741
28987
|
|
|
28742
28988
|
// src/forgetting/journal.ts
|
|
28743
28989
|
init_fs();
|
|
28744
|
-
import { appendFile as appendFile4, mkdir as mkdir23, readFile as
|
|
28745
|
-
import
|
|
28990
|
+
import { appendFile as appendFile4, mkdir as mkdir23, readFile as readFile41, stat as stat12 } from "fs/promises";
|
|
28991
|
+
import path80 from "path";
|
|
28746
28992
|
function forgettingDataDir(cwd) {
|
|
28747
|
-
return
|
|
28993
|
+
return path80.join(cwd, ".metaproject", "data", "forgetting");
|
|
28748
28994
|
}
|
|
28749
28995
|
function deletionJournalPath(cwd) {
|
|
28750
|
-
return
|
|
28996
|
+
return path80.join(forgettingDataDir(cwd), "journal.jsonl");
|
|
28751
28997
|
}
|
|
28752
28998
|
async function readDeletionJournal(cwd) {
|
|
28753
28999
|
const file = deletionJournalPath(cwd);
|
|
28754
29000
|
try {
|
|
28755
|
-
await
|
|
29001
|
+
await stat12(file);
|
|
28756
29002
|
} catch (error) {
|
|
28757
29003
|
if (isNotFound(error)) {
|
|
28758
29004
|
return { state: "absent", path: file };
|
|
@@ -28766,7 +29012,7 @@ async function readDeletionJournal(cwd) {
|
|
|
28766
29012
|
}
|
|
28767
29013
|
let content;
|
|
28768
29014
|
try {
|
|
28769
|
-
content = await
|
|
29015
|
+
content = await readFile41(file, "utf8");
|
|
28770
29016
|
} catch (error) {
|
|
28771
29017
|
const message = error instanceof Error ? error.message : String(error);
|
|
28772
29018
|
return {
|
|
@@ -29166,12 +29412,12 @@ __export(exports_service7, {
|
|
|
29166
29412
|
gateExitCode: () => gateExitCode,
|
|
29167
29413
|
readWikiFreshnessMetric: () => readWikiFreshnessMetric
|
|
29168
29414
|
});
|
|
29169
|
-
import
|
|
29415
|
+
import path100 from "path";
|
|
29170
29416
|
|
|
29171
29417
|
// src/health/config.ts
|
|
29172
29418
|
init_fs();
|
|
29173
29419
|
init_json();
|
|
29174
|
-
import
|
|
29420
|
+
import path81 from "path";
|
|
29175
29421
|
var DEFAULT_HEALTH_CONFIG = {
|
|
29176
29422
|
schemaVersion: 2,
|
|
29177
29423
|
ignore: {
|
|
@@ -29227,7 +29473,7 @@ var DEFAULT_HEALTH_CONFIG = {
|
|
|
29227
29473
|
}
|
|
29228
29474
|
};
|
|
29229
29475
|
function configPath2(cwd) {
|
|
29230
|
-
return
|
|
29476
|
+
return path81.join(cwd, ".metaproject", "health.config.json");
|
|
29231
29477
|
}
|
|
29232
29478
|
var STRICTEST_GATE = {
|
|
29233
29479
|
failOnPriorities: ["P0", "P1", "P2", "P3"],
|
|
@@ -29278,7 +29524,7 @@ async function loadHealthConfig(cwd) {
|
|
|
29278
29524
|
// src/health/run.ts
|
|
29279
29525
|
init_fs();
|
|
29280
29526
|
import { mkdir as mkdir27, writeFile as writeFile20 } from "fs/promises";
|
|
29281
|
-
import
|
|
29527
|
+
import path99 from "path";
|
|
29282
29528
|
|
|
29283
29529
|
// src/metrics/provenance.ts
|
|
29284
29530
|
async function collectGitProvenance(cwd) {
|
|
@@ -29378,12 +29624,12 @@ function computeGate2(input) {
|
|
|
29378
29624
|
}
|
|
29379
29625
|
|
|
29380
29626
|
// src/health/scopes.ts
|
|
29381
|
-
import
|
|
29627
|
+
import path84 from "path";
|
|
29382
29628
|
|
|
29383
29629
|
// src/health/util.ts
|
|
29384
29630
|
init_fs();
|
|
29385
|
-
import { mkdir as mkdir24, readFile as
|
|
29386
|
-
import
|
|
29631
|
+
import { mkdir as mkdir24, readFile as readFile42, readdir as readdir15, writeFile as writeFile17 } from "fs/promises";
|
|
29632
|
+
import path82 from "path";
|
|
29387
29633
|
var IGNORED_DIRS = new Set([
|
|
29388
29634
|
"node_modules",
|
|
29389
29635
|
".git",
|
|
@@ -29427,14 +29673,14 @@ async function toolVersion(command, cwd) {
|
|
|
29427
29673
|
}
|
|
29428
29674
|
}
|
|
29429
29675
|
function dataRoot3(cwd) {
|
|
29430
|
-
return
|
|
29676
|
+
return path82.join(cwd, ".metaproject", "data", "health");
|
|
29431
29677
|
}
|
|
29432
29678
|
async function writeRaw(cwd, source, content, stamp) {
|
|
29433
|
-
const dir =
|
|
29679
|
+
const dir = path82.join(dataRoot3(cwd), "raw", source);
|
|
29434
29680
|
await mkdir24(dir, { recursive: true });
|
|
29435
|
-
const file =
|
|
29681
|
+
const file = path82.join(dir, `${stamp}.log`);
|
|
29436
29682
|
await writeFile17(file, content, "utf8");
|
|
29437
|
-
return
|
|
29683
|
+
return path82.relative(cwd, file);
|
|
29438
29684
|
}
|
|
29439
29685
|
async function listSourceFiles(cwd, ignorePaths = []) {
|
|
29440
29686
|
const { files } = await listSourceFilesWithReasons(cwd, ignorePaths);
|
|
@@ -29452,9 +29698,9 @@ async function listSourceFilesWithReasons(cwd, ignorePaths = []) {
|
|
|
29452
29698
|
async function walk2(root, dir, out, incompleteReasons) {
|
|
29453
29699
|
let entries;
|
|
29454
29700
|
try {
|
|
29455
|
-
entries = await
|
|
29701
|
+
entries = await readdir15(dir, { withFileTypes: true });
|
|
29456
29702
|
} catch (error) {
|
|
29457
|
-
incompleteReasons.push(`${
|
|
29703
|
+
incompleteReasons.push(`${path82.relative(root, dir) || "."}: ${error instanceof Error ? error.message : String(error)}`);
|
|
29458
29704
|
return;
|
|
29459
29705
|
}
|
|
29460
29706
|
for (const entry of entries) {
|
|
@@ -29462,14 +29708,14 @@ async function walk2(root, dir, out, incompleteReasons) {
|
|
|
29462
29708
|
if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {}
|
|
29463
29709
|
continue;
|
|
29464
29710
|
}
|
|
29465
|
-
const abs =
|
|
29711
|
+
const abs = path82.join(dir, entry.name);
|
|
29466
29712
|
if (entry.isDirectory()) {
|
|
29467
29713
|
if (IGNORED_DIRS.has(entry.name)) {
|
|
29468
29714
|
continue;
|
|
29469
29715
|
}
|
|
29470
29716
|
await walk2(root, abs, out, incompleteReasons);
|
|
29471
|
-
} else if (entry.isFile() && SOURCE_EXT.has(
|
|
29472
|
-
out.push(
|
|
29717
|
+
} else if (entry.isFile() && SOURCE_EXT.has(path82.extname(entry.name))) {
|
|
29718
|
+
out.push(path82.relative(root, abs));
|
|
29473
29719
|
}
|
|
29474
29720
|
}
|
|
29475
29721
|
}
|
|
@@ -29484,7 +29730,7 @@ function moduleOfFile(file) {
|
|
|
29484
29730
|
return null;
|
|
29485
29731
|
}
|
|
29486
29732
|
function isSourceFile(file) {
|
|
29487
|
-
return SOURCE_EXT.has(
|
|
29733
|
+
return SOURCE_EXT.has(path82.extname(file));
|
|
29488
29734
|
}
|
|
29489
29735
|
function matchesAnyPattern(file, patterns) {
|
|
29490
29736
|
const normalized = file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -29583,8 +29829,8 @@ function rankHotspots(files, churn, sourceAnalysis) {
|
|
|
29583
29829
|
|
|
29584
29830
|
// src/health/source-analysis.ts
|
|
29585
29831
|
init_fs();
|
|
29586
|
-
import { readFile as
|
|
29587
|
-
import
|
|
29832
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
29833
|
+
import path83 from "path";
|
|
29588
29834
|
|
|
29589
29835
|
// src/health/metrics/complexity.ts
|
|
29590
29836
|
var CONTROL_KEYWORDS = new Set([
|
|
@@ -29818,11 +30064,11 @@ async function analyzeSourceFiles(cwd, sourceFiles) {
|
|
|
29818
30064
|
return new Map(entries.filter((entry) => entry !== null));
|
|
29819
30065
|
}
|
|
29820
30066
|
async function analyzeSourceFile(cwd, file) {
|
|
29821
|
-
const abs =
|
|
30067
|
+
const abs = path83.join(cwd, file);
|
|
29822
30068
|
if (!await pathExists(abs)) {
|
|
29823
30069
|
return null;
|
|
29824
30070
|
}
|
|
29825
|
-
const content = await
|
|
30071
|
+
const content = await readFile43(abs, "utf8");
|
|
29826
30072
|
return [
|
|
29827
30073
|
file,
|
|
29828
30074
|
{
|
|
@@ -29891,14 +30137,14 @@ async function computeMetrics(input) {
|
|
|
29891
30137
|
}
|
|
29892
30138
|
const components = new Map;
|
|
29893
30139
|
for (const file of sourceFiles) {
|
|
29894
|
-
const dir =
|
|
30140
|
+
const dir = path84.posix.dirname(file);
|
|
29895
30141
|
if (dir === "." || dir === moduleOfFile(file)) {
|
|
29896
30142
|
continue;
|
|
29897
30143
|
}
|
|
29898
30144
|
components.set(dir, [...components.get(dir) ?? [], file]);
|
|
29899
30145
|
}
|
|
29900
30146
|
for (const [name, files] of [...components.entries()].sort()) {
|
|
29901
|
-
const compFindings = findings.filter((f) => f.file &&
|
|
30147
|
+
const compFindings = findings.filter((f) => f.file && path84.posix.dirname(f.file) === name);
|
|
29902
30148
|
scopes.push(build("component", `component:${name}`, name, files, compFindings, averageCoverage(files, coverage)));
|
|
29903
30149
|
}
|
|
29904
30150
|
const findingFiles = findings.map((f) => f.file).filter((f) => Boolean(f));
|
|
@@ -29942,10 +30188,10 @@ function tally2(values) {
|
|
|
29942
30188
|
|
|
29943
30189
|
// src/health/baseline.ts
|
|
29944
30190
|
init_fs();
|
|
29945
|
-
import { mkdir as mkdir25, readFile as
|
|
29946
|
-
import
|
|
30191
|
+
import { mkdir as mkdir25, readFile as readFile44, writeFile as writeFile18 } from "fs/promises";
|
|
30192
|
+
import path85 from "path";
|
|
29947
30193
|
function baselinePath(cwd) {
|
|
29948
|
-
return
|
|
30194
|
+
return path85.join(cwd, ".metaproject", "health", "baselines", "scores.json");
|
|
29949
30195
|
}
|
|
29950
30196
|
async function loadBaseline(cwd) {
|
|
29951
30197
|
const file = baselinePath(cwd);
|
|
@@ -29953,7 +30199,7 @@ async function loadBaseline(cwd) {
|
|
|
29953
30199
|
return new Map;
|
|
29954
30200
|
}
|
|
29955
30201
|
try {
|
|
29956
|
-
const data = JSON.parse(await
|
|
30202
|
+
const data = JSON.parse(await readFile44(file, "utf8"));
|
|
29957
30203
|
return new Map(Object.entries(data.scopes ?? {}));
|
|
29958
30204
|
} catch {
|
|
29959
30205
|
return new Map;
|
|
@@ -29977,7 +30223,7 @@ async function writeBaseline(cwd, metrics, generatedAt, selector) {
|
|
|
29977
30223
|
scopes[key] = value;
|
|
29978
30224
|
}
|
|
29979
30225
|
const file = baselinePath(cwd);
|
|
29980
|
-
await mkdir25(
|
|
30226
|
+
await mkdir25(path85.dirname(file), { recursive: true });
|
|
29981
30227
|
await writeFile18(file, `${JSON.stringify({ generatedAt, scopes }, null, 2)}
|
|
29982
30228
|
`, "utf8");
|
|
29983
30229
|
return updated;
|
|
@@ -30030,11 +30276,11 @@ async function getChurn(cwd, windowDays) {
|
|
|
30030
30276
|
}
|
|
30031
30277
|
|
|
30032
30278
|
// src/health/metrics/wiki-freshness.ts
|
|
30033
|
-
import { readFile as
|
|
30034
|
-
import
|
|
30279
|
+
import { readFile as readFile45, stat as stat13 } from "fs/promises";
|
|
30280
|
+
import path86 from "path";
|
|
30035
30281
|
var DEFAULT_STALE_EVIDENCE_DAYS = 7;
|
|
30036
30282
|
function freshnessReportPath(cwd) {
|
|
30037
|
-
return
|
|
30283
|
+
return path86.join(cwd, ".metaproject", "data", "wiki", "freshness", "latest.json");
|
|
30038
30284
|
}
|
|
30039
30285
|
async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
30040
30286
|
const file = freshnessReportPath(cwd);
|
|
@@ -30042,7 +30288,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
30042
30288
|
const now = (options.now ?? (() => new Date))();
|
|
30043
30289
|
let raw;
|
|
30044
30290
|
try {
|
|
30045
|
-
raw = await
|
|
30291
|
+
raw = await readFile45(file, "utf8");
|
|
30046
30292
|
} catch {
|
|
30047
30293
|
return {
|
|
30048
30294
|
status: "no-report",
|
|
@@ -30055,7 +30301,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
30055
30301
|
} catch {
|
|
30056
30302
|
return {
|
|
30057
30303
|
status: "unreadable-report",
|
|
30058
|
-
reason: `${
|
|
30304
|
+
reason: `${path86.relative(cwd, file)} is not valid JSON; re-run \`keryx wiki freshness\`.`
|
|
30059
30305
|
};
|
|
30060
30306
|
}
|
|
30061
30307
|
const report = parsed;
|
|
@@ -30065,7 +30311,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
30065
30311
|
if (pagesTotal === null || pagesFresh === null) {
|
|
30066
30312
|
return {
|
|
30067
30313
|
status: "unreadable-report",
|
|
30068
|
-
reason: `${
|
|
30314
|
+
reason: `${path86.relative(cwd, file)} is missing its totals; re-run \`keryx wiki freshness\`.`
|
|
30069
30315
|
};
|
|
30070
30316
|
}
|
|
30071
30317
|
const pagesUndecidable = numberOr2(totals.pagesUndecidable) ?? 0;
|
|
@@ -30102,7 +30348,7 @@ async function reportAgeDays(file, generatedAt, now) {
|
|
|
30102
30348
|
return Math.floor((now.getTime() - stamped) / 86400000);
|
|
30103
30349
|
}
|
|
30104
30350
|
try {
|
|
30105
|
-
const info = await
|
|
30351
|
+
const info = await stat13(file);
|
|
30106
30352
|
return Math.floor((now.getTime() - info.mtimeMs) / 86400000);
|
|
30107
30353
|
} catch {
|
|
30108
30354
|
return null;
|
|
@@ -30119,12 +30365,12 @@ function renderWikiFreshnessLine(metric) {
|
|
|
30119
30365
|
}
|
|
30120
30366
|
|
|
30121
30367
|
// src/health/sources/helpers.ts
|
|
30122
|
-
import
|
|
30368
|
+
import path87 from "path";
|
|
30123
30369
|
import { existsSync as existsSync8 } from "fs";
|
|
30124
30370
|
class NoImportError extends Error {
|
|
30125
30371
|
}
|
|
30126
30372
|
function resolveBin(cwd, name) {
|
|
30127
|
-
const local =
|
|
30373
|
+
const local = path87.join(cwd, "node_modules", ".bin", name);
|
|
30128
30374
|
if (existsSync8(local)) {
|
|
30129
30375
|
return local;
|
|
30130
30376
|
}
|
|
@@ -30205,13 +30451,13 @@ async function getComplexityFindings(cwd, sourceFiles, config, sourceAnalysis) {
|
|
|
30205
30451
|
|
|
30206
30452
|
// src/health/metrics/coverage.ts
|
|
30207
30453
|
import { existsSync as existsSync9 } from "fs";
|
|
30208
|
-
import
|
|
30454
|
+
import path88 from "path";
|
|
30209
30455
|
var SUMMARY_PATHS = [
|
|
30210
30456
|
"coverage/coverage-summary.json",
|
|
30211
30457
|
"coverage/coverage-final.json"
|
|
30212
30458
|
];
|
|
30213
30459
|
async function getCoverage(cwd) {
|
|
30214
|
-
const summary = SUMMARY_PATHS.map((rel) =>
|
|
30460
|
+
const summary = SUMMARY_PATHS.map((rel) => path88.join(cwd, rel)).find((abs) => existsSync9(abs));
|
|
30215
30461
|
if (!summary) {
|
|
30216
30462
|
return { status: "missing", total: null, byFile: new Map };
|
|
30217
30463
|
}
|
|
@@ -30232,7 +30478,7 @@ async function getCoverage(cwd) {
|
|
|
30232
30478
|
total = pct;
|
|
30233
30479
|
continue;
|
|
30234
30480
|
}
|
|
30235
|
-
const relative =
|
|
30481
|
+
const relative = path88.isAbsolute(key) ? path88.relative(cwd, key) : key;
|
|
30236
30482
|
byFile.set(relative.replace(/\\/g, "/"), pct);
|
|
30237
30483
|
}
|
|
30238
30484
|
return { status: "available", total, byFile };
|
|
@@ -30364,17 +30610,17 @@ function renderWikiFreshness(metric) {
|
|
|
30364
30610
|
|
|
30365
30611
|
// src/health/skills.ts
|
|
30366
30612
|
init_fs();
|
|
30367
|
-
import { readFile as
|
|
30368
|
-
import
|
|
30613
|
+
import { readFile as readFile46 } from "fs/promises";
|
|
30614
|
+
import path89 from "path";
|
|
30369
30615
|
var EMPTY = { skills: [], skillForFile: () => null };
|
|
30370
30616
|
async function loadSkillOwnership(cwd) {
|
|
30371
|
-
const manifestPath =
|
|
30617
|
+
const manifestPath = path89.join(cwd, ".metaproject", "metaproject.json");
|
|
30372
30618
|
if (!await pathExists(manifestPath)) {
|
|
30373
30619
|
return EMPTY;
|
|
30374
30620
|
}
|
|
30375
30621
|
let registry;
|
|
30376
30622
|
try {
|
|
30377
|
-
const manifest = JSON.parse(await
|
|
30623
|
+
const manifest = JSON.parse(await readFile46(manifestPath, "utf8"));
|
|
30378
30624
|
registry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
30379
30625
|
} catch {
|
|
30380
30626
|
return EMPTY;
|
|
@@ -30406,7 +30652,7 @@ function normalize(value) {
|
|
|
30406
30652
|
|
|
30407
30653
|
// src/health/sources/eslint.ts
|
|
30408
30654
|
import { existsSync as existsSync10 } from "fs";
|
|
30409
|
-
import
|
|
30655
|
+
import path90 from "path";
|
|
30410
30656
|
var CONFIG_FILES = [
|
|
30411
30657
|
".eslintrc",
|
|
30412
30658
|
".eslintrc.js",
|
|
@@ -30420,7 +30666,7 @@ var CONFIG_FILES = [
|
|
|
30420
30666
|
"eslint.config.ts"
|
|
30421
30667
|
];
|
|
30422
30668
|
function hasConfig(cwd) {
|
|
30423
|
-
return CONFIG_FILES.some((file) => existsSync10(
|
|
30669
|
+
return CONFIG_FILES.some((file) => existsSync10(path90.join(cwd, file)));
|
|
30424
30670
|
}
|
|
30425
30671
|
var eslintAdapter = {
|
|
30426
30672
|
id: "eslint",
|
|
@@ -30445,7 +30691,7 @@ var eslintAdapter = {
|
|
|
30445
30691
|
};
|
|
30446
30692
|
},
|
|
30447
30693
|
async import(ctx) {
|
|
30448
|
-
const report =
|
|
30694
|
+
const report = path90.join(ctx.cwd, "eslint-report.json");
|
|
30449
30695
|
if (!existsSync10(report)) {
|
|
30450
30696
|
throw new NoImportError("no eslint-report.json");
|
|
30451
30697
|
}
|
|
@@ -30472,7 +30718,7 @@ var eslintAdapter = {
|
|
|
30472
30718
|
}
|
|
30473
30719
|
const findings = [];
|
|
30474
30720
|
for (const file of data) {
|
|
30475
|
-
const relative =
|
|
30721
|
+
const relative = path90.isAbsolute(file.filePath) ? path90.relative(ctx.cwd, file.filePath) : file.filePath;
|
|
30476
30722
|
for (const message of file.messages ?? []) {
|
|
30477
30723
|
const isError = message.severity === 2;
|
|
30478
30724
|
findings.push(makeFinding({
|
|
@@ -30504,9 +30750,9 @@ var eslintAdapter = {
|
|
|
30504
30750
|
|
|
30505
30751
|
// src/health/sources/typescript.ts
|
|
30506
30752
|
import { existsSync as existsSync11 } from "fs";
|
|
30507
|
-
import
|
|
30753
|
+
import path91 from "path";
|
|
30508
30754
|
function hasTsconfig(cwd) {
|
|
30509
|
-
return existsSync11(
|
|
30755
|
+
return existsSync11(path91.join(cwd, "tsconfig.json"));
|
|
30510
30756
|
}
|
|
30511
30757
|
var typescriptAdapter = {
|
|
30512
30758
|
id: "typescript",
|
|
@@ -30581,8 +30827,8 @@ __export(exports_service6, {
|
|
|
30581
30827
|
testingDataRoot: () => testingDataRoot
|
|
30582
30828
|
});
|
|
30583
30829
|
import { existsSync as existsSync12 } from "fs";
|
|
30584
|
-
import { readFile as
|
|
30585
|
-
import
|
|
30830
|
+
import { readFile as readFile49, readdir as readdir16 } from "fs/promises";
|
|
30831
|
+
import path96 from "path";
|
|
30586
30832
|
|
|
30587
30833
|
// src/lib/git-env.ts
|
|
30588
30834
|
var GIT_DISCOVERY_OVERRIDE_VARS = [
|
|
@@ -30606,8 +30852,8 @@ function withoutGitDiscoveryOverrides(env) {
|
|
|
30606
30852
|
// src/metrics/lifecycle.ts
|
|
30607
30853
|
init_fs();
|
|
30608
30854
|
init_guard();
|
|
30609
|
-
import { readFile as
|
|
30610
|
-
import
|
|
30855
|
+
import { readFile as readFile47 } from "fs/promises";
|
|
30856
|
+
import path92 from "path";
|
|
30611
30857
|
|
|
30612
30858
|
// src/metrics/record.ts
|
|
30613
30859
|
var RELIABILITIES = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -30622,16 +30868,16 @@ var RETRY_TYPES = new Set([
|
|
|
30622
30868
|
|
|
30623
30869
|
// src/metrics/lifecycle.ts
|
|
30624
30870
|
async function readArtifactPointer(root, expected) {
|
|
30625
|
-
const pointerFile =
|
|
30871
|
+
const pointerFile = path92.join(root, "latest.json");
|
|
30626
30872
|
if (!await pathExists(pointerFile))
|
|
30627
30873
|
return { status: "missing", pointer: null };
|
|
30628
30874
|
let pointer;
|
|
30629
30875
|
try {
|
|
30630
|
-
pointer = JSON.parse(await
|
|
30876
|
+
pointer = JSON.parse(await readFile47(pointerFile, "utf8"));
|
|
30631
30877
|
} catch {
|
|
30632
30878
|
return { status: "stale", pointer: null, reason: "latest pointer is invalid JSON" };
|
|
30633
30879
|
}
|
|
30634
|
-
const recordFile = pointer.record ?
|
|
30880
|
+
const recordFile = pointer.record ? path92.join(root, pointer.record) : null;
|
|
30635
30881
|
if (!recordFile || !isPathInside(root, recordFile) || !await pathExists(recordFile)) {
|
|
30636
30882
|
return { status: "stale", pointer, reason: "pointer record is missing" };
|
|
30637
30883
|
}
|
|
@@ -30647,9 +30893,9 @@ init_guard();
|
|
|
30647
30893
|
// src/testing/capability.ts
|
|
30648
30894
|
init_fs();
|
|
30649
30895
|
init_json();
|
|
30650
|
-
import
|
|
30896
|
+
import path93 from "path";
|
|
30651
30897
|
async function isTestingCapabilityEnabled(cwd, capability) {
|
|
30652
|
-
const manifestPath =
|
|
30898
|
+
const manifestPath = path93.join(cwd, ".metaproject", "metaproject.json");
|
|
30653
30899
|
if (!await pathExists(manifestPath)) {
|
|
30654
30900
|
return false;
|
|
30655
30901
|
}
|
|
@@ -30674,11 +30920,11 @@ async function isTestingCapabilityEnabled(cwd, capability) {
|
|
|
30674
30920
|
// src/testing/coverage-map.ts
|
|
30675
30921
|
init_fs();
|
|
30676
30922
|
init_guard();
|
|
30677
|
-
import { readFile as
|
|
30678
|
-
import
|
|
30923
|
+
import { readFile as readFile48, mkdir as mkdir26, writeFile as writeFile19 } from "fs/promises";
|
|
30924
|
+
import path94 from "path";
|
|
30679
30925
|
var COVERAGE_MAP_SCHEMA_VERSION = 1;
|
|
30680
30926
|
function coverageMapPath(cwd, cfg) {
|
|
30681
|
-
return
|
|
30927
|
+
return path94.isAbsolute(cfg.coverageMap.artifact) ? cfg.coverageMap.artifact : path94.join(cwd, cfg.coverageMap.artifact);
|
|
30682
30928
|
}
|
|
30683
30929
|
async function loadCoverageMap(cwd, cfg) {
|
|
30684
30930
|
const file = coverageMapPath(cwd, cfg);
|
|
@@ -30686,7 +30932,7 @@ async function loadCoverageMap(cwd, cfg) {
|
|
|
30686
30932
|
return null;
|
|
30687
30933
|
}
|
|
30688
30934
|
try {
|
|
30689
|
-
const parsed = JSON.parse(await
|
|
30935
|
+
const parsed = JSON.parse(await readFile48(file, "utf8"));
|
|
30690
30936
|
if (!parsed || typeof parsed !== "object" || typeof parsed.map !== "object") {
|
|
30691
30937
|
return null;
|
|
30692
30938
|
}
|
|
@@ -30752,20 +30998,20 @@ function normalizePath4(file) {
|
|
|
30752
30998
|
}
|
|
30753
30999
|
|
|
30754
31000
|
// src/testing/selection.ts
|
|
30755
|
-
import
|
|
31001
|
+
import path95 from "path";
|
|
30756
31002
|
var TEST_FILE_RE = /(^|\/)(__tests__\/.*|.*\.(test|spec)\.[cm]?[tj]sx?$|e2e\/.*\.[cm]?[tj]sx?$|tests\/.*\.[cm]?[tj]sx?$)/;
|
|
30757
31003
|
function normalizePath5(file) {
|
|
30758
31004
|
return file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
30759
31005
|
}
|
|
30760
31006
|
function relatedByNamingAndDirectory(target, testFiles) {
|
|
30761
31007
|
const normalized = normalizePath5(target);
|
|
30762
|
-
const ext =
|
|
31008
|
+
const ext = path95.extname(normalized);
|
|
30763
31009
|
const withoutExt = ext ? normalized.slice(0, -ext.length) : normalized;
|
|
30764
|
-
const base =
|
|
30765
|
-
const dir =
|
|
31010
|
+
const base = path95.basename(withoutExt);
|
|
31011
|
+
const dir = path95.dirname(normalized);
|
|
30766
31012
|
return testFiles.filter((file) => {
|
|
30767
|
-
const fileDir =
|
|
30768
|
-
const fileBase =
|
|
31013
|
+
const fileDir = path95.dirname(file);
|
|
31014
|
+
const fileBase = path95.basename(file);
|
|
30769
31015
|
return file.startsWith(`${withoutExt}.`) || file.includes(`${withoutExt}.`) || fileDir === dir && fileBase.startsWith(`${base}.`) || fileDir.startsWith(dir) && fileBase.includes(base);
|
|
30770
31016
|
}).sort();
|
|
30771
31017
|
}
|
|
@@ -30895,7 +31141,7 @@ var DEFAULT_TESTING_CONFIG = {
|
|
|
30895
31141
|
}
|
|
30896
31142
|
};
|
|
30897
31143
|
function testingDataRoot(cwd) {
|
|
30898
|
-
return
|
|
31144
|
+
return path96.join(cwd, ".metaproject", "data", "testing");
|
|
30899
31145
|
}
|
|
30900
31146
|
async function computeTestingContext(cwd) {
|
|
30901
31147
|
const { files, incompleteReasons } = await listProjectFiles(cwd);
|
|
@@ -31070,33 +31316,33 @@ async function runTesting(input) {
|
|
|
31070
31316
|
return runResult;
|
|
31071
31317
|
}
|
|
31072
31318
|
async function loadTestingContext(cwd) {
|
|
31073
|
-
const file =
|
|
31319
|
+
const file = path96.join(testingDataRoot(cwd), "context.json");
|
|
31074
31320
|
if (!await pathExists(file)) {
|
|
31075
31321
|
return null;
|
|
31076
31322
|
}
|
|
31077
31323
|
try {
|
|
31078
|
-
return JSON.parse(await
|
|
31324
|
+
return JSON.parse(await readFile49(file, "utf8"));
|
|
31079
31325
|
} catch {
|
|
31080
31326
|
return null;
|
|
31081
31327
|
}
|
|
31082
31328
|
}
|
|
31083
31329
|
async function loadTestingReport(cwd) {
|
|
31084
|
-
const file =
|
|
31330
|
+
const file = path96.join(testingDataRoot(cwd), "artifacts", "latest.json");
|
|
31085
31331
|
if (!await pathExists(file)) {
|
|
31086
31332
|
return null;
|
|
31087
31333
|
}
|
|
31088
31334
|
try {
|
|
31089
|
-
const latest = JSON.parse(await
|
|
31335
|
+
const latest = JSON.parse(await readFile49(file, "utf8"));
|
|
31090
31336
|
if (typeof latest.record === "string") {
|
|
31091
|
-
const pointer = await readArtifactPointer(
|
|
31337
|
+
const pointer = await readArtifactPointer(path96.join(testingDataRoot(cwd), "artifacts"), await collectGitProvenance(cwd));
|
|
31092
31338
|
if (pointer.status !== "fresh")
|
|
31093
31339
|
return null;
|
|
31094
|
-
const recordFile =
|
|
31095
|
-
if (!isPathInside(
|
|
31340
|
+
const recordFile = path96.join(testingDataRoot(cwd), "artifacts", latest.record);
|
|
31341
|
+
if (!isPathInside(path96.join(testingDataRoot(cwd), "artifacts"), recordFile))
|
|
31096
31342
|
return null;
|
|
31097
31343
|
if (!await pathExists(recordFile))
|
|
31098
31344
|
return null;
|
|
31099
|
-
return JSON.parse(await
|
|
31345
|
+
return JSON.parse(await readFile49(recordFile, "utf8"));
|
|
31100
31346
|
}
|
|
31101
31347
|
return latest;
|
|
31102
31348
|
} catch {
|
|
@@ -31139,28 +31385,28 @@ async function ensureContext(cwd) {
|
|
|
31139
31385
|
}
|
|
31140
31386
|
async function writeContext(cwd, context) {
|
|
31141
31387
|
const root = testingDataRoot(cwd);
|
|
31142
|
-
const rel = (p) =>
|
|
31388
|
+
const rel = (p) => path96.relative(cwd, p);
|
|
31143
31389
|
await mkdirContained(cwd, rel(root));
|
|
31144
|
-
await writeContained(cwd, rel(
|
|
31390
|
+
await writeContained(cwd, rel(path96.join(root, "context.json")), `${JSON.stringify(context, null, 2)}
|
|
31145
31391
|
`);
|
|
31146
|
-
await writeContained(cwd, rel(
|
|
31147
|
-
await writeContained(cwd, rel(
|
|
31392
|
+
await writeContained(cwd, rel(path96.join(root, "context.md")), renderContextMarkdown(context));
|
|
31393
|
+
await writeContained(cwd, rel(path96.join(root, "recommendations.md")), renderRecommendationsMarkdown(context));
|
|
31148
31394
|
}
|
|
31149
31395
|
async function writeReport(cwd, report) {
|
|
31150
31396
|
const root = testingDataRoot(cwd);
|
|
31151
|
-
const artifacts =
|
|
31152
|
-
const history =
|
|
31153
|
-
const rel = (p) =>
|
|
31397
|
+
const artifacts = path96.join(root, "artifacts");
|
|
31398
|
+
const history = path96.join(root, "history");
|
|
31399
|
+
const rel = (p) => path96.relative(cwd, p);
|
|
31154
31400
|
await mkdirContained(cwd, rel(artifacts));
|
|
31155
31401
|
await mkdirContained(cwd, rel(history));
|
|
31156
31402
|
const stamp = report.generatedAt.replace(/[:.]/g, "-");
|
|
31157
|
-
const json =
|
|
31158
|
-
const markdown =
|
|
31403
|
+
const json = path96.join(artifacts, "latest.json");
|
|
31404
|
+
const markdown = path96.join(artifacts, "latest.md");
|
|
31159
31405
|
const serialized = `${JSON.stringify(report, null, 2)}
|
|
31160
31406
|
`;
|
|
31161
31407
|
if (report.runId) {
|
|
31162
|
-
const runJson =
|
|
31163
|
-
const runMarkdown =
|
|
31408
|
+
const runJson = path96.join(artifacts, "runs", `${report.runId}.json`);
|
|
31409
|
+
const runMarkdown = path96.join(artifacts, "runs", `${report.runId}.md`);
|
|
31164
31410
|
if (await pathExists(runJson))
|
|
31165
31411
|
throw new Error(`immutable testing run already exists: ${report.runId}`);
|
|
31166
31412
|
await writeContained(cwd, rel(runJson), serialized);
|
|
@@ -31171,26 +31417,26 @@ async function writeReport(cwd, report) {
|
|
|
31171
31417
|
branch: report.provenance?.branch ?? null,
|
|
31172
31418
|
worktree: report.provenance?.worktree ?? null,
|
|
31173
31419
|
generated_at: report.generatedAt,
|
|
31174
|
-
record:
|
|
31420
|
+
record: path96.posix.join("runs", `${report.runId}.json`)
|
|
31175
31421
|
}, null, 2)}
|
|
31176
31422
|
`);
|
|
31177
31423
|
} else {
|
|
31178
31424
|
await writeContained(cwd, rel(json), serialized);
|
|
31179
31425
|
}
|
|
31180
31426
|
await writeContained(cwd, rel(markdown), renderReportMarkdown3(report));
|
|
31181
|
-
await writeContained(cwd, rel(
|
|
31427
|
+
await writeContained(cwd, rel(path96.join(history, `${stamp}.json`)), serialized);
|
|
31182
31428
|
return {
|
|
31183
|
-
markdownPath:
|
|
31184
|
-
jsonPath:
|
|
31429
|
+
markdownPath: path96.relative(cwd, markdown),
|
|
31430
|
+
jsonPath: path96.relative(cwd, json)
|
|
31185
31431
|
};
|
|
31186
31432
|
}
|
|
31187
31433
|
async function writeRawLog(cwd, raw) {
|
|
31188
|
-
const logs =
|
|
31189
|
-
const rel = (p) =>
|
|
31434
|
+
const logs = path96.join(testingDataRoot(cwd), "logs");
|
|
31435
|
+
const rel = (p) => path96.relative(cwd, p);
|
|
31190
31436
|
await mkdirContained(cwd, rel(logs));
|
|
31191
|
-
const latest =
|
|
31437
|
+
const latest = path96.join(logs, "latest.raw.log");
|
|
31192
31438
|
await writeContained(cwd, rel(latest), raw);
|
|
31193
|
-
return
|
|
31439
|
+
return path96.relative(cwd, latest);
|
|
31194
31440
|
}
|
|
31195
31441
|
async function listProjectFiles(cwd) {
|
|
31196
31442
|
const out = [];
|
|
@@ -31201,13 +31447,13 @@ async function listProjectFiles(cwd) {
|
|
|
31201
31447
|
async function walk3(root, dir, out, incompleteReasons) {
|
|
31202
31448
|
let entries;
|
|
31203
31449
|
try {
|
|
31204
|
-
entries = await
|
|
31450
|
+
entries = await readdir16(dir, { withFileTypes: true });
|
|
31205
31451
|
} catch (error) {
|
|
31206
|
-
incompleteReasons.push(`${normalizePath6(
|
|
31452
|
+
incompleteReasons.push(`${normalizePath6(path96.relative(root, dir)) || "."}: ${error instanceof Error ? error.message : String(error)}`);
|
|
31207
31453
|
return;
|
|
31208
31454
|
}
|
|
31209
31455
|
for (const entry of entries) {
|
|
31210
|
-
const abs =
|
|
31456
|
+
const abs = path96.join(dir, entry.name);
|
|
31211
31457
|
if (entry.isDirectory()) {
|
|
31212
31458
|
if (IGNORED_DIRS2.has(entry.name) || entry.name.startsWith(".tmp-")) {
|
|
31213
31459
|
continue;
|
|
@@ -31216,28 +31462,28 @@ async function walk3(root, dir, out, incompleteReasons) {
|
|
|
31216
31462
|
continue;
|
|
31217
31463
|
}
|
|
31218
31464
|
if (entry.isFile()) {
|
|
31219
|
-
out.push(normalizePath6(
|
|
31465
|
+
out.push(normalizePath6(path96.relative(root, abs)));
|
|
31220
31466
|
}
|
|
31221
31467
|
}
|
|
31222
31468
|
}
|
|
31223
31469
|
async function readPackageJson(cwd) {
|
|
31224
|
-
const file =
|
|
31470
|
+
const file = path96.join(cwd, "package.json");
|
|
31225
31471
|
if (!await pathExists(file)) {
|
|
31226
31472
|
return null;
|
|
31227
31473
|
}
|
|
31228
31474
|
try {
|
|
31229
|
-
return JSON.parse(await
|
|
31475
|
+
return JSON.parse(await readFile49(file, "utf8"));
|
|
31230
31476
|
} catch {
|
|
31231
31477
|
return null;
|
|
31232
31478
|
}
|
|
31233
31479
|
}
|
|
31234
31480
|
async function loadTestingConfig(cwd) {
|
|
31235
|
-
const file =
|
|
31481
|
+
const file = path96.join(cwd, ".metaproject", "testing.config.json");
|
|
31236
31482
|
if (!await pathExists(file)) {
|
|
31237
31483
|
return DEFAULT_TESTING_CONFIG;
|
|
31238
31484
|
}
|
|
31239
31485
|
try {
|
|
31240
|
-
const parsed = JSON.parse(await
|
|
31486
|
+
const parsed = JSON.parse(await readFile49(file, "utf8"));
|
|
31241
31487
|
return {
|
|
31242
31488
|
schemaVersion: parsed.schemaVersion ?? DEFAULT_TESTING_CONFIG.schemaVersion,
|
|
31243
31489
|
enabled: parsed.enabled ?? DEFAULT_TESTING_CONFIG.enabled,
|
|
@@ -31297,11 +31543,11 @@ function detectFrameworks(input) {
|
|
|
31297
31543
|
async function extractConventions(cwd, files) {
|
|
31298
31544
|
const conventions = new Set;
|
|
31299
31545
|
for (const file of files.slice(0, 40)) {
|
|
31300
|
-
const abs =
|
|
31546
|
+
const abs = path96.join(cwd, file);
|
|
31301
31547
|
if (!await pathExists(abs)) {
|
|
31302
31548
|
continue;
|
|
31303
31549
|
}
|
|
31304
|
-
const text = await
|
|
31550
|
+
const text = await readFile49(abs, "utf8");
|
|
31305
31551
|
for (const line of text.split(`
|
|
31306
31552
|
`)) {
|
|
31307
31553
|
if (/test|testing|spec|coverage|playwright|vitest|jest|bun:test/i.test(line)) {
|
|
@@ -31427,11 +31673,11 @@ async function findTestsByImportedTargets(cwd, targets, testFiles) {
|
|
|
31427
31673
|
const targetKeys = new Set(targets.map((target) => modulePathKey(normalizePath6(target))));
|
|
31428
31674
|
const selected = new Set;
|
|
31429
31675
|
for (const testFile of testFiles) {
|
|
31430
|
-
const absoluteTest =
|
|
31676
|
+
const absoluteTest = path96.join(cwd, testFile);
|
|
31431
31677
|
if (!await pathExists(absoluteTest)) {
|
|
31432
31678
|
continue;
|
|
31433
31679
|
}
|
|
31434
|
-
const content = await
|
|
31680
|
+
const content = await readFile49(absoluteTest, "utf8");
|
|
31435
31681
|
const imports = extractImportPaths(content);
|
|
31436
31682
|
if (imports.length === 0) {
|
|
31437
31683
|
continue;
|
|
@@ -31461,10 +31707,10 @@ function resolveImportPath(cwd, testFile, importPath) {
|
|
|
31461
31707
|
if (!importPath.startsWith(".")) {
|
|
31462
31708
|
return null;
|
|
31463
31709
|
}
|
|
31464
|
-
const importerDir =
|
|
31465
|
-
const absoluteResolved =
|
|
31466
|
-
const relative =
|
|
31467
|
-
if (relative.startsWith(`..${
|
|
31710
|
+
const importerDir = path96.dirname(testFile);
|
|
31711
|
+
const absoluteResolved = path96.resolve(path96.join(cwd, importerDir), importPath);
|
|
31712
|
+
const relative = path96.relative(cwd, absoluteResolved);
|
|
31713
|
+
if (relative.startsWith(`..${path96.sep}`) || relative === "..") {
|
|
31468
31714
|
return null;
|
|
31469
31715
|
}
|
|
31470
31716
|
const relativeNormalized = normalizePath6(relative);
|
|
@@ -31516,13 +31762,13 @@ function resolveTestCommand(cwd, context, input) {
|
|
|
31516
31762
|
}
|
|
31517
31763
|
var BUN_LOCKFILES = ["bun.lock", "bun.lockb"];
|
|
31518
31764
|
function detectPackageManager(cwd) {
|
|
31519
|
-
if (resolveBunBin() && BUN_LOCKFILES.some((name) => pathExistsSync(
|
|
31765
|
+
if (resolveBunBin() && BUN_LOCKFILES.some((name) => pathExistsSync(path96.join(cwd, name)))) {
|
|
31520
31766
|
return "bun";
|
|
31521
31767
|
}
|
|
31522
|
-
if (Bun.which("pnpm") && pathExistsSync(
|
|
31768
|
+
if (Bun.which("pnpm") && pathExistsSync(path96.join(cwd, "pnpm-lock.yaml"))) {
|
|
31523
31769
|
return "pnpm";
|
|
31524
31770
|
}
|
|
31525
|
-
if (Bun.which("yarn") && pathExistsSync(
|
|
31771
|
+
if (Bun.which("yarn") && pathExistsSync(path96.join(cwd, "yarn.lock"))) {
|
|
31526
31772
|
return "yarn";
|
|
31527
31773
|
}
|
|
31528
31774
|
return "npm";
|
|
@@ -31862,7 +32108,7 @@ async function compatibleReportForHealth(ctx) {
|
|
|
31862
32108
|
|
|
31863
32109
|
// src/health/sources/dependency-audit.ts
|
|
31864
32110
|
import { existsSync as existsSync13 } from "fs";
|
|
31865
|
-
import
|
|
32111
|
+
import path97 from "path";
|
|
31866
32112
|
function auditSeverityToPriority(severity) {
|
|
31867
32113
|
if (severity === "critical" || severity === "high")
|
|
31868
32114
|
return "P0";
|
|
@@ -31981,7 +32227,7 @@ var LOCKFILE_COMMANDS = [
|
|
|
31981
32227
|
];
|
|
31982
32228
|
function auditCommand(cwd) {
|
|
31983
32229
|
for (const entry of LOCKFILE_COMMANDS) {
|
|
31984
|
-
if (!entry.lockfiles.some((name) => existsSync13(
|
|
32230
|
+
if (!entry.lockfiles.some((name) => existsSync13(path97.join(cwd, name))))
|
|
31985
32231
|
continue;
|
|
31986
32232
|
const bin = resolveBin(cwd, entry.bin);
|
|
31987
32233
|
if (bin)
|
|
@@ -32040,13 +32286,13 @@ var dependencyAuditAdapter = {
|
|
|
32040
32286
|
|
|
32041
32287
|
// src/health/sources/sonarqube.ts
|
|
32042
32288
|
import { existsSync as existsSync14 } from "fs";
|
|
32043
|
-
import
|
|
32289
|
+
import path98 from "path";
|
|
32044
32290
|
var REPORT_FILES = ["sonar-issues.json", ".metaproject/data/health/sonar-issues.json"];
|
|
32045
32291
|
function reportPath(cwd) {
|
|
32046
32292
|
const fromEnv = process.env.SONAR_ISSUES_FILE;
|
|
32047
32293
|
const candidates = fromEnv ? [fromEnv, ...REPORT_FILES] : REPORT_FILES;
|
|
32048
32294
|
for (const rel of candidates) {
|
|
32049
|
-
const abs =
|
|
32295
|
+
const abs = path98.isAbsolute(rel) ? rel : path98.join(cwd, rel);
|
|
32050
32296
|
if (existsSync14(abs)) {
|
|
32051
32297
|
return abs;
|
|
32052
32298
|
}
|
|
@@ -32130,7 +32376,7 @@ var sonarqubeAdapter = {
|
|
|
32130
32376
|
function relativizeComponent(component, cwd) {
|
|
32131
32377
|
const colon = component.indexOf(":");
|
|
32132
32378
|
const value = colon >= 0 ? component.slice(colon + 1) : component;
|
|
32133
|
-
return
|
|
32379
|
+
return path98.isAbsolute(value) ? path98.relative(cwd, value) : value;
|
|
32134
32380
|
}
|
|
32135
32381
|
|
|
32136
32382
|
// src/health/sources/index.ts
|
|
@@ -32467,19 +32713,19 @@ function filteredInfo(source, cfg) {
|
|
|
32467
32713
|
};
|
|
32468
32714
|
}
|
|
32469
32715
|
async function writeOutputs(cwd, report, config, stamp) {
|
|
32470
|
-
const artifacts =
|
|
32471
|
-
const history =
|
|
32716
|
+
const artifacts = path99.join(dataRoot3(cwd), "artifacts");
|
|
32717
|
+
const history = path99.join(dataRoot3(cwd), "history");
|
|
32472
32718
|
await mkdir27(artifacts, { recursive: true });
|
|
32473
32719
|
await mkdir27(history, { recursive: true });
|
|
32474
|
-
const markdown =
|
|
32475
|
-
const json =
|
|
32476
|
-
const historyJson =
|
|
32720
|
+
const markdown = path99.join(artifacts, "latest.md");
|
|
32721
|
+
const json = path99.join(artifacts, "latest.json");
|
|
32722
|
+
const historyJson = path99.join(history, `${stamp}.json`);
|
|
32477
32723
|
const serialized = `${JSON.stringify(report, null, 2)}
|
|
32478
32724
|
`;
|
|
32479
32725
|
await writeFile20(markdown, renderReportMarkdown2(report, config), "utf8");
|
|
32480
32726
|
if (report.runId) {
|
|
32481
|
-
const runJson =
|
|
32482
|
-
const runMarkdown =
|
|
32727
|
+
const runJson = path99.join(artifacts, "runs", `${report.runId}.json`);
|
|
32728
|
+
const runMarkdown = path99.join(artifacts, "runs", `${report.runId}.md`);
|
|
32483
32729
|
if (await pathExists(runJson))
|
|
32484
32730
|
throw new Error(`immutable health run already exists: ${report.runId}`);
|
|
32485
32731
|
await writeFileAtomic(runJson, serialized);
|
|
@@ -32490,7 +32736,7 @@ async function writeOutputs(cwd, report, config, stamp) {
|
|
|
32490
32736
|
branch: report.provenance?.branch ?? null,
|
|
32491
32737
|
worktree: report.provenance?.worktree ?? null,
|
|
32492
32738
|
generated_at: report.generatedAt,
|
|
32493
|
-
record:
|
|
32739
|
+
record: path99.posix.join("runs", `${report.runId}.json`)
|
|
32494
32740
|
}, null, 2)}
|
|
32495
32741
|
`);
|
|
32496
32742
|
} else {
|
|
@@ -32498,8 +32744,8 @@ async function writeOutputs(cwd, report, config, stamp) {
|
|
|
32498
32744
|
}
|
|
32499
32745
|
await writeFile20(historyJson, serialized, "utf8");
|
|
32500
32746
|
return {
|
|
32501
|
-
markdownPath:
|
|
32502
|
-
jsonPath:
|
|
32747
|
+
markdownPath: path99.relative(cwd, markdown),
|
|
32748
|
+
jsonPath: path99.relative(cwd, json)
|
|
32503
32749
|
};
|
|
32504
32750
|
}
|
|
32505
32751
|
async function resolveChanged(cwd, selector) {
|
|
@@ -32552,7 +32798,7 @@ function hasGateShape(value) {
|
|
|
32552
32798
|
return Array.isArray(report.metrics) && Array.isArray(report.sources) && Array.isArray(report.findings);
|
|
32553
32799
|
}
|
|
32554
32800
|
async function readLatest(cwd) {
|
|
32555
|
-
const file =
|
|
32801
|
+
const file = path100.join(dataRoot3(cwd), "artifacts", "latest.json");
|
|
32556
32802
|
if (!await pathExists(file)) {
|
|
32557
32803
|
return null;
|
|
32558
32804
|
}
|
|
@@ -32563,11 +32809,11 @@ async function readLatest(cwd) {
|
|
|
32563
32809
|
const latest = read.value;
|
|
32564
32810
|
const record = latest.record;
|
|
32565
32811
|
if (typeof record === "string") {
|
|
32566
|
-
const pointer = await readArtifactPointer(
|
|
32812
|
+
const pointer = await readArtifactPointer(path100.join(dataRoot3(cwd), "artifacts"), await collectGitProvenance(cwd));
|
|
32567
32813
|
if (pointer.status !== "fresh")
|
|
32568
32814
|
return null;
|
|
32569
|
-
const recordFile =
|
|
32570
|
-
if (!isPathInside(
|
|
32815
|
+
const recordFile = path100.join(dataRoot3(cwd), "artifacts", record);
|
|
32816
|
+
if (!isPathInside(path100.join(dataRoot3(cwd), "artifacts"), recordFile))
|
|
32571
32817
|
return null;
|
|
32572
32818
|
if (!await pathExists(recordFile))
|
|
32573
32819
|
return null;
|
|
@@ -32640,7 +32886,7 @@ function createCodeHealthService() {
|
|
|
32640
32886
|
return {
|
|
32641
32887
|
run: (input) => runHealth(input),
|
|
32642
32888
|
async status(input) {
|
|
32643
|
-
const enabled = await pathExists(
|
|
32889
|
+
const enabled = await pathExists(path100.join(input.cwd, ".metaproject", "health.config.json"));
|
|
32644
32890
|
const latest = await readLatest(input.cwd);
|
|
32645
32891
|
const project = latest?.metrics.find((m) => m.key === "project") ?? null;
|
|
32646
32892
|
const decliningScopes = latest ? latest.metrics.filter((m) => m.regression_score > 0).length : 0;
|
|
@@ -32798,10 +33044,10 @@ function renderEvidenceBlock(evidences, siblings) {
|
|
|
32798
33044
|
init_fs();
|
|
32799
33045
|
init_contained_write();
|
|
32800
33046
|
init_json();
|
|
32801
|
-
import
|
|
33047
|
+
import path101 from "path";
|
|
32802
33048
|
import { createHash as createHash16 } from "crypto";
|
|
32803
33049
|
function impactEvidenceDataRoot(root) {
|
|
32804
|
-
return
|
|
33050
|
+
return path101.join(root, ".metaproject", "data", "security", "impact-evidence");
|
|
32805
33051
|
}
|
|
32806
33052
|
function sanitizeSessionId(sessionId) {
|
|
32807
33053
|
const cleaned = sessionId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -32813,7 +33059,7 @@ function sessionStateFilename(sessionId) {
|
|
|
32813
33059
|
return `${sanitized}-${suffix}.json`;
|
|
32814
33060
|
}
|
|
32815
33061
|
function sessionStatePath(root, sessionId) {
|
|
32816
|
-
return
|
|
33062
|
+
return path101.join(impactEvidenceDataRoot(root), "sessions", sessionStateFilename(sessionId));
|
|
32817
33063
|
}
|
|
32818
33064
|
async function loadSessionState(root, sessionId) {
|
|
32819
33065
|
const file = sessionStatePath(root, sessionId);
|
|
@@ -32832,11 +33078,11 @@ async function loadSessionState(root, sessionId) {
|
|
|
32832
33078
|
}
|
|
32833
33079
|
async function saveSessionState(root, sessionId, state) {
|
|
32834
33080
|
const file = sessionStatePath(root, sessionId);
|
|
32835
|
-
await writeContained(root,
|
|
33081
|
+
await writeContained(root, path101.relative(root, file), `${JSON.stringify(state, null, 2)}
|
|
32836
33082
|
`);
|
|
32837
33083
|
}
|
|
32838
33084
|
function logPath(root) {
|
|
32839
|
-
return
|
|
33085
|
+
return path101.join(impactEvidenceDataRoot(root), "log.jsonl");
|
|
32840
33086
|
}
|
|
32841
33087
|
async function appendLogRecord(root, record) {
|
|
32842
33088
|
const full = {
|
|
@@ -32847,7 +33093,7 @@ async function appendLogRecord(root, record) {
|
|
|
32847
33093
|
...record.detail !== undefined ? { detail: record.detail } : {}
|
|
32848
33094
|
};
|
|
32849
33095
|
const file = logPath(root);
|
|
32850
|
-
await appendContained(root,
|
|
33096
|
+
await appendContained(root, path101.relative(root, file), `${JSON.stringify(full)}
|
|
32851
33097
|
`);
|
|
32852
33098
|
return full;
|
|
32853
33099
|
}
|
|
@@ -32874,7 +33120,7 @@ init_command_risk();
|
|
|
32874
33120
|
init_shell_syntax();
|
|
32875
33121
|
init_fs();
|
|
32876
33122
|
init_service2();
|
|
32877
|
-
import
|
|
33123
|
+
import path102 from "path";
|
|
32878
33124
|
import { createHash as createHash17 } from "crypto";
|
|
32879
33125
|
import { realpathSync as realpathSync2 } from "fs";
|
|
32880
33126
|
function impactEvidenceHookClass(strict) {
|
|
@@ -32890,29 +33136,29 @@ function realpathNearestExisting(p) {
|
|
|
32890
33136
|
for (;; ) {
|
|
32891
33137
|
try {
|
|
32892
33138
|
const real = realpathSync2(current);
|
|
32893
|
-
return tail.length > 0 ?
|
|
33139
|
+
return tail.length > 0 ? path102.join(real, ...tail) : real;
|
|
32894
33140
|
} catch {
|
|
32895
|
-
const parent =
|
|
33141
|
+
const parent = path102.dirname(current);
|
|
32896
33142
|
if (parent === current) {
|
|
32897
|
-
return tail.length > 0 ?
|
|
33143
|
+
return tail.length > 0 ? path102.join(current, ...tail) : current;
|
|
32898
33144
|
}
|
|
32899
|
-
tail.unshift(
|
|
33145
|
+
tail.unshift(path102.basename(current));
|
|
32900
33146
|
current = parent;
|
|
32901
33147
|
}
|
|
32902
33148
|
}
|
|
32903
33149
|
}
|
|
32904
33150
|
function normalizeRequestFiles(root, files) {
|
|
32905
|
-
const rootReal = realpathNearestExisting(
|
|
33151
|
+
const rootReal = realpathNearestExisting(path102.resolve(root));
|
|
32906
33152
|
const normalized = [];
|
|
32907
33153
|
const rejected = [];
|
|
32908
33154
|
for (const file of files) {
|
|
32909
|
-
const lexicalAbsolute =
|
|
33155
|
+
const lexicalAbsolute = path102.isAbsolute(file) ? path102.resolve(file) : path102.resolve(rootReal, toPosix(file).replace(/^\.\//, ""));
|
|
32910
33156
|
const effective = realpathNearestExisting(lexicalAbsolute);
|
|
32911
33157
|
if (!isPathInside(rootReal, effective)) {
|
|
32912
33158
|
rejected.push(file);
|
|
32913
33159
|
continue;
|
|
32914
33160
|
}
|
|
32915
|
-
normalized.push(toPosix(
|
|
33161
|
+
normalized.push(toPosix(path102.relative(rootReal, effective)));
|
|
32916
33162
|
}
|
|
32917
33163
|
return { files: [...new Set(normalized)], rejected };
|
|
32918
33164
|
}
|
|
@@ -33198,7 +33444,7 @@ __export(exports_service8, {
|
|
|
33198
33444
|
createJobService: () => createJobService
|
|
33199
33445
|
});
|
|
33200
33446
|
import { copyFile, mkdir as mkdir29 } from "fs/promises";
|
|
33201
|
-
import
|
|
33447
|
+
import path105 from "path";
|
|
33202
33448
|
|
|
33203
33449
|
// src/job/types.ts
|
|
33204
33450
|
var JOB_INTENTS = ["implement", "analyze", "review", "custom"];
|
|
@@ -33338,16 +33584,16 @@ function defaultPlan(intent) {
|
|
|
33338
33584
|
init_validator();
|
|
33339
33585
|
init_contracts();
|
|
33340
33586
|
init_fs();
|
|
33341
|
-
import { appendFile as appendFile5, mkdir as mkdir28, readFile as
|
|
33342
|
-
import
|
|
33587
|
+
import { appendFile as appendFile5, mkdir as mkdir28, readFile as readFile51, readdir as readdir17 } from "fs/promises";
|
|
33588
|
+
import path104 from "path";
|
|
33343
33589
|
function jobsRoot(cwd) {
|
|
33344
|
-
return
|
|
33590
|
+
return path104.join(cwd, ".metaproject", "jobs");
|
|
33345
33591
|
}
|
|
33346
33592
|
function jobDir(cwd, name) {
|
|
33347
|
-
return
|
|
33593
|
+
return path104.join(jobsRoot(cwd), name);
|
|
33348
33594
|
}
|
|
33349
33595
|
function jobStatePath(cwd, name) {
|
|
33350
|
-
return
|
|
33596
|
+
return path104.join(jobDir(cwd, name), "state.json");
|
|
33351
33597
|
}
|
|
33352
33598
|
var JOB_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
33353
33599
|
function assertJobName(name) {
|
|
@@ -33364,7 +33610,7 @@ async function listJobNames(cwd) {
|
|
|
33364
33610
|
if (!await pathExists(root)) {
|
|
33365
33611
|
return [];
|
|
33366
33612
|
}
|
|
33367
|
-
const entries = await
|
|
33613
|
+
const entries = await readdir17(root, { withFileTypes: true });
|
|
33368
33614
|
return entries.filter((entry) => entry.isDirectory() && JOB_NAME_PATTERN.test(entry.name)).map((entry) => entry.name).sort();
|
|
33369
33615
|
}
|
|
33370
33616
|
async function readJob(cwd, name) {
|
|
@@ -33373,7 +33619,7 @@ async function readJob(cwd, name) {
|
|
|
33373
33619
|
const known = await listJobNames(cwd);
|
|
33374
33620
|
throw new Error(`Job not found: ${name}.` + (known.length > 0 ? ` Known: ${known.join(", ")}` : " Run: keryx job list"));
|
|
33375
33621
|
}
|
|
33376
|
-
return JSON.parse(await
|
|
33622
|
+
return JSON.parse(await readFile51(file, "utf8"));
|
|
33377
33623
|
}
|
|
33378
33624
|
async function writeJob(cwd, name, state) {
|
|
33379
33625
|
const schema = await loadSchema("job-orchestrator-state");
|
|
@@ -33384,12 +33630,12 @@ async function writeJob(cwd, name, state) {
|
|
|
33384
33630
|
`));
|
|
33385
33631
|
}
|
|
33386
33632
|
const file = jobStatePath(cwd, name);
|
|
33387
|
-
await mkdir28(
|
|
33633
|
+
await mkdir28(path104.dirname(file), { recursive: true });
|
|
33388
33634
|
await writeFileAtomic(file, `${JSON.stringify(state, null, 2)}
|
|
33389
33635
|
`);
|
|
33390
33636
|
}
|
|
33391
33637
|
async function appendJournal2(cwd, name, at, line) {
|
|
33392
|
-
await appendFile5(
|
|
33638
|
+
await appendFile5(path104.join(jobDir(cwd, name), "journal.md"), `- ${at} - ${line}
|
|
33393
33639
|
`, "utf8");
|
|
33394
33640
|
}
|
|
33395
33641
|
function renderJournal2(name, at) {
|
|
@@ -33410,7 +33656,7 @@ function assertDocumentType(raw) {
|
|
|
33410
33656
|
return raw;
|
|
33411
33657
|
}
|
|
33412
33658
|
function documentFileName(type, sourcePath) {
|
|
33413
|
-
const extension =
|
|
33659
|
+
const extension = path104.extname(sourcePath) || ".md";
|
|
33414
33660
|
return `${type}${extension}`;
|
|
33415
33661
|
}
|
|
33416
33662
|
|
|
@@ -33418,7 +33664,7 @@ function documentFileName(type, sourcePath) {
|
|
|
33418
33664
|
function createJobService(deps) {
|
|
33419
33665
|
const now = () => deps.now().toISOString();
|
|
33420
33666
|
function lockPath(cwd, name) {
|
|
33421
|
-
return
|
|
33667
|
+
return path105.join(jobsRoot(cwd), `.job-lock-${name}`);
|
|
33422
33668
|
}
|
|
33423
33669
|
async function mutate(cwd, name, run) {
|
|
33424
33670
|
assertJobName(name);
|
|
@@ -33467,20 +33713,20 @@ function createJobService(deps) {
|
|
|
33467
33713
|
},
|
|
33468
33714
|
plan: { steps: defaultPlan(intent) },
|
|
33469
33715
|
documentation: {
|
|
33470
|
-
job_path:
|
|
33716
|
+
job_path: path105.join(".metaproject", "jobs", name),
|
|
33471
33717
|
documents_created: []
|
|
33472
33718
|
},
|
|
33473
33719
|
metrics: { steps: [] },
|
|
33474
|
-
jobs_root:
|
|
33720
|
+
jobs_root: path105.join(".metaproject", "jobs"),
|
|
33475
33721
|
updated_at: createdAt
|
|
33476
33722
|
};
|
|
33477
33723
|
const first = firstOpenStep(state.plan.steps);
|
|
33478
33724
|
if (first) {
|
|
33479
33725
|
state.plan.current_step = first.id;
|
|
33480
33726
|
}
|
|
33481
|
-
await writeFileAtomic(
|
|
33727
|
+
await writeFileAtomic(path105.join(absolute, "journal.md"), renderJournal2(name, createdAt));
|
|
33482
33728
|
await writeJob(input.cwd, name, state);
|
|
33483
|
-
return { state, dir:
|
|
33729
|
+
return { state, dir: path105.relative(input.cwd, absolute) };
|
|
33484
33730
|
},
|
|
33485
33731
|
async status({ cwd, name }) {
|
|
33486
33732
|
assertJobName(name);
|
|
@@ -33492,7 +33738,7 @@ function createJobService(deps) {
|
|
|
33492
33738
|
}
|
|
33493
33739
|
return {
|
|
33494
33740
|
state,
|
|
33495
|
-
dir:
|
|
33741
|
+
dir: path105.join(".metaproject", "jobs", name),
|
|
33496
33742
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
33497
33743
|
stepsTotal: state.plan.steps.length,
|
|
33498
33744
|
nextStep: firstOpenStep(state.plan.steps),
|
|
@@ -33549,23 +33795,23 @@ function createJobService(deps) {
|
|
|
33549
33795
|
if (!input.file) {
|
|
33550
33796
|
throw new Error("Missing --file. Usage: keryx job document <name> --type <type> --file <path>");
|
|
33551
33797
|
}
|
|
33552
|
-
const source =
|
|
33798
|
+
const source = path105.isAbsolute(input.file) ? input.file : path105.join(input.cwd, input.file);
|
|
33553
33799
|
if (!await pathExists(source)) {
|
|
33554
33800
|
throw new Error(`--file not found: ${input.file}. Write the document first, then record it.`);
|
|
33555
33801
|
}
|
|
33556
33802
|
let recorded;
|
|
33557
33803
|
await mutate(input.cwd, input.name, async (state) => {
|
|
33558
33804
|
const fileName = documentFileName(type, source);
|
|
33559
|
-
const target =
|
|
33805
|
+
const target = path105.join(jobDir(input.cwd, state.job_name), fileName);
|
|
33560
33806
|
await copyFile(source, target);
|
|
33561
33807
|
state.documentation ??= {};
|
|
33562
|
-
state.documentation.job_path ??=
|
|
33808
|
+
state.documentation.job_path ??= path105.join(".metaproject", "jobs", state.job_name);
|
|
33563
33809
|
const created = state.documentation.documents_created ?? [];
|
|
33564
33810
|
state.documentation.documents_created = created.includes(fileName) ? created : [...created, fileName];
|
|
33565
33811
|
recorded = {
|
|
33566
33812
|
state,
|
|
33567
33813
|
document: fileName,
|
|
33568
|
-
path:
|
|
33814
|
+
path: path105.join(".metaproject", "jobs", state.job_name, fileName)
|
|
33569
33815
|
};
|
|
33570
33816
|
return { state, event: "document", detail: `${type} -> ${fileName}` };
|
|
33571
33817
|
});
|
|
@@ -33600,7 +33846,7 @@ function createJobService(deps) {
|
|
|
33600
33846
|
name: state.job_name,
|
|
33601
33847
|
phase: state.phase,
|
|
33602
33848
|
intent: state.intent,
|
|
33603
|
-
dir:
|
|
33849
|
+
dir: path105.join(".metaproject", "jobs", name),
|
|
33604
33850
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
33605
33851
|
stepsTotal: state.plan.steps.length,
|
|
33606
33852
|
nextStep: firstOpenStep(state.plan.steps)?.id ?? null
|
|
@@ -33704,13 +33950,13 @@ function normalizeWorkspaceReference(raw) {
|
|
|
33704
33950
|
|
|
33705
33951
|
// src/sac/fwk-service.ts
|
|
33706
33952
|
init_assembly();
|
|
33707
|
-
import { appendFile as appendFile6, mkdir as mkdir31, open as open3, readFile as
|
|
33708
|
-
import
|
|
33953
|
+
import { appendFile as appendFile6, mkdir as mkdir31, open as open3, readFile as readFile54, rename as rename7, rm as rm6, stat as stat14, writeFile as writeFile21 } from "fs/promises";
|
|
33954
|
+
import path109 from "path";
|
|
33709
33955
|
|
|
33710
33956
|
// src/sac/index.ts
|
|
33711
|
-
import { access as access3, readFile as
|
|
33957
|
+
import { access as access3, readFile as readFile52, realpath as realpath12 } from "fs/promises";
|
|
33712
33958
|
import { createHash as createHash18 } from "crypto";
|
|
33713
|
-
import
|
|
33959
|
+
import path106 from "path";
|
|
33714
33960
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
33715
33961
|
var idPattern = /^[a-z][a-z0-9-]{2,63}$/;
|
|
33716
33962
|
var subjectPattern = /^(?:user|team|service|agent):[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
@@ -33728,21 +33974,21 @@ var normativeSchemaFiles = {
|
|
|
33728
33974
|
"review-decision": "review-decision.schema.json"
|
|
33729
33975
|
};
|
|
33730
33976
|
var normativeSchemas = new Map;
|
|
33731
|
-
var NORMATIVE_SCHEMA_DIR =
|
|
33977
|
+
var NORMATIVE_SCHEMA_DIR = path106.join("docs", "requirements", "shared-agent-context", "schemas");
|
|
33732
33978
|
async function resolveSacNormativeSchemaPath(fileName, searchFrom = [fileURLToPath3(new URL(".", import.meta.url)), process.cwd()]) {
|
|
33733
33979
|
const seen = new Set;
|
|
33734
33980
|
for (const start of searchFrom) {
|
|
33735
|
-
let dir =
|
|
33981
|
+
let dir = path106.resolve(start);
|
|
33736
33982
|
for (let i = 0;i < 10; i++) {
|
|
33737
33983
|
if (seen.has(dir))
|
|
33738
33984
|
break;
|
|
33739
33985
|
seen.add(dir);
|
|
33740
|
-
const candidate =
|
|
33986
|
+
const candidate = path106.join(dir, NORMATIVE_SCHEMA_DIR, fileName);
|
|
33741
33987
|
try {
|
|
33742
33988
|
await access3(candidate);
|
|
33743
33989
|
return candidate;
|
|
33744
33990
|
} catch {}
|
|
33745
|
-
const parent =
|
|
33991
|
+
const parent = path106.dirname(dir);
|
|
33746
33992
|
if (parent === dir)
|
|
33747
33993
|
break;
|
|
33748
33994
|
dir = parent;
|
|
@@ -33788,7 +34034,7 @@ function isRecord5(value) {
|
|
|
33788
34034
|
async function loadNormativeSchema(schema) {
|
|
33789
34035
|
let pending = normativeSchemas.get(schema);
|
|
33790
34036
|
if (!pending) {
|
|
33791
|
-
pending = resolveSacNormativeSchemaPath(normativeSchemaFiles[schema]).then((file) =>
|
|
34037
|
+
pending = resolveSacNormativeSchemaPath(normativeSchemaFiles[schema]).then((file) => readFile52(file, "utf8")).then((source) => JSON.parse(source));
|
|
33792
34038
|
normativeSchemas.set(schema, pending);
|
|
33793
34039
|
}
|
|
33794
34040
|
return pending;
|
|
@@ -34411,20 +34657,20 @@ class SacReferenceError extends Error {
|
|
|
34411
34657
|
code = "unsafe_workspace_reference";
|
|
34412
34658
|
}
|
|
34413
34659
|
async function resolveWorkspaceReference(input) {
|
|
34414
|
-
if (!workspacePathPattern.test(input.uri) ||
|
|
34660
|
+
if (!workspacePathPattern.test(input.uri) || path106.isAbsolute(input.uri) || /^[a-z][a-z0-9+.-]*:/i.test(input.uri) || input.uri.includes("\\"))
|
|
34415
34661
|
throw new SacReferenceError("unsafe workspace reference");
|
|
34416
|
-
const lexicalRoot =
|
|
34417
|
-
const root = await
|
|
34418
|
-
const candidate =
|
|
34419
|
-
if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${
|
|
34662
|
+
const lexicalRoot = path106.resolve(input.workspaceRoot);
|
|
34663
|
+
const root = await realpath12(lexicalRoot);
|
|
34664
|
+
const candidate = path106.resolve(lexicalRoot, input.uri.slice(2));
|
|
34665
|
+
if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${path106.sep}`))
|
|
34420
34666
|
throw new SacReferenceError("workspace reference escapes root");
|
|
34421
34667
|
let resolved;
|
|
34422
34668
|
try {
|
|
34423
|
-
resolved = await
|
|
34669
|
+
resolved = await realpath12(candidate);
|
|
34424
34670
|
} catch {
|
|
34425
34671
|
throw new SacReferenceError("workspace reference is not resolvable");
|
|
34426
34672
|
}
|
|
34427
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
34673
|
+
if (resolved !== root && !resolved.startsWith(`${root}${path106.sep}`))
|
|
34428
34674
|
throw new SacReferenceError("workspace reference escapes root after realpath");
|
|
34429
34675
|
return candidate;
|
|
34430
34676
|
}
|
|
@@ -34478,18 +34724,18 @@ async function evaluateStrictSacGuard(input) {
|
|
|
34478
34724
|
|
|
34479
34725
|
// src/sac/workspace-service.ts
|
|
34480
34726
|
init_fs();
|
|
34481
|
-
import { mkdir as mkdir30, readdir as
|
|
34727
|
+
import { mkdir as mkdir30, readdir as readdir18, readFile as readFile53 } from "fs/promises";
|
|
34482
34728
|
import { createHash as createHash19, randomUUID as randomUUID3 } from "crypto";
|
|
34483
|
-
import
|
|
34729
|
+
import path108 from "path";
|
|
34484
34730
|
|
|
34485
34731
|
// src/sac/secure-resource-read.ts
|
|
34486
34732
|
init_contained_read();
|
|
34487
34733
|
init_descriptor_read();
|
|
34488
|
-
import
|
|
34734
|
+
import path107 from "path";
|
|
34489
34735
|
function readWorkspaceFileNoFollow(workspaceRoot, absolutePath) {
|
|
34490
|
-
const relativePath =
|
|
34491
|
-
const components = relativePath.split(
|
|
34492
|
-
if (!relativePath ||
|
|
34736
|
+
const relativePath = path107.relative(workspaceRoot, absolutePath);
|
|
34737
|
+
const components = relativePath.split(path107.sep);
|
|
34738
|
+
if (!relativePath || path107.isAbsolute(relativePath) || components.some((component) => !component || component === "." || component === "..")) {
|
|
34493
34739
|
throw new Error("safe source path is not workspace-relative");
|
|
34494
34740
|
}
|
|
34495
34741
|
return readDescriptorChain(workspaceRoot, absolutePath, {
|
|
@@ -34516,8 +34762,8 @@ class WorkspaceService {
|
|
|
34516
34762
|
now;
|
|
34517
34763
|
constructor(options) {
|
|
34518
34764
|
this.options = options;
|
|
34519
|
-
this.root =
|
|
34520
|
-
this.storageRoot =
|
|
34765
|
+
this.root = path108.resolve(options.workspaceRoot);
|
|
34766
|
+
this.storageRoot = path108.resolve(options.storageRoot ?? path108.join(this.root, ".metaproject", "workspaces"));
|
|
34521
34767
|
this.now = options.now ?? (() => new Date);
|
|
34522
34768
|
}
|
|
34523
34769
|
async create(input) {
|
|
@@ -34540,7 +34786,7 @@ class WorkspaceService {
|
|
|
34540
34786
|
await mkdir30(this.storageRoot, { recursive: true, mode: 448 });
|
|
34541
34787
|
await withFileLock(this.lockPath(manifest.id), async () => {
|
|
34542
34788
|
try {
|
|
34543
|
-
await
|
|
34789
|
+
await readFile53(this.manifestPath(manifest.id), "utf8");
|
|
34544
34790
|
throw new WorkspaceServiceError("conflict", "workspace already exists");
|
|
34545
34791
|
} catch (error) {
|
|
34546
34792
|
if (error instanceof WorkspaceServiceError || !isNotFound(error))
|
|
@@ -34569,7 +34815,7 @@ class WorkspaceService {
|
|
|
34569
34815
|
} catch {
|
|
34570
34816
|
return [];
|
|
34571
34817
|
}
|
|
34572
|
-
const entries = await
|
|
34818
|
+
const entries = await readdir18(this.storageRoot, { withFileTypes: true });
|
|
34573
34819
|
const visible = [];
|
|
34574
34820
|
for (const entry of entries) {
|
|
34575
34821
|
if (!entry.isDirectory())
|
|
@@ -34830,7 +35076,7 @@ class WorkspaceService {
|
|
|
34830
35076
|
throw new WorkspaceServiceError("not_found", "workspace not found");
|
|
34831
35077
|
let parsed;
|
|
34832
35078
|
try {
|
|
34833
|
-
parsed = JSON.parse(await
|
|
35079
|
+
parsed = JSON.parse(await readFile53(this.manifestPath(id), "utf8"));
|
|
34834
35080
|
} catch (error) {
|
|
34835
35081
|
if (isNotFound(error))
|
|
34836
35082
|
throw new WorkspaceServiceError("not_found", "workspace not found");
|
|
@@ -34842,13 +35088,13 @@ class WorkspaceService {
|
|
|
34842
35088
|
return parsed;
|
|
34843
35089
|
}
|
|
34844
35090
|
workspaceDir(id) {
|
|
34845
|
-
return
|
|
35091
|
+
return path108.join(this.storageRoot, id);
|
|
34846
35092
|
}
|
|
34847
35093
|
manifestPath(id) {
|
|
34848
|
-
return
|
|
35094
|
+
return path108.join(this.workspaceDir(id), "workspace.json");
|
|
34849
35095
|
}
|
|
34850
35096
|
lockPath(id) {
|
|
34851
|
-
return
|
|
35097
|
+
return path108.join(this.storageRoot, `.${id}.lock`);
|
|
34852
35098
|
}
|
|
34853
35099
|
timestamp() {
|
|
34854
35100
|
return this.now().toISOString();
|
|
@@ -35457,7 +35703,7 @@ var ledgerDigestChunkBytes = 64 * 1024;
|
|
|
35457
35703
|
var isMissing = (error) => error instanceof Error && error.code === "ENOENT";
|
|
35458
35704
|
var checkpointHash = (body) => createHash22("sha256").update(JSON.stringify(body), "utf8").digest("hex");
|
|
35459
35705
|
async function ledgerByteLength(ledger) {
|
|
35460
|
-
const value = await
|
|
35706
|
+
const value = await stat14(ledger, { bigint: true });
|
|
35461
35707
|
if (value.size > BigInt(Number.MAX_SAFE_INTEGER))
|
|
35462
35708
|
throw new Error("invalid access receipt ledger: ledger-too-large");
|
|
35463
35709
|
return Number(value.size);
|
|
@@ -35503,7 +35749,7 @@ function parseCheckpoint(value) {
|
|
|
35503
35749
|
}
|
|
35504
35750
|
async function readCheckpoint(checkpointPath) {
|
|
35505
35751
|
try {
|
|
35506
|
-
const raw = await
|
|
35752
|
+
const raw = await readFile54(checkpointPath, "utf8");
|
|
35507
35753
|
try {
|
|
35508
35754
|
const checkpoint = parseCheckpoint(JSON.parse(raw));
|
|
35509
35755
|
return checkpoint ? { present: true, checkpoint } : { present: true };
|
|
@@ -35571,7 +35817,7 @@ async function fastCheckpointState(ledger, checkpoint, ledgerBytes) {
|
|
|
35571
35817
|
}
|
|
35572
35818
|
}
|
|
35573
35819
|
async function auditLedger(ledger, checkpointPath, verifier) {
|
|
35574
|
-
const bytes = await
|
|
35820
|
+
const bytes = await readFile54(ledger);
|
|
35575
35821
|
const raw = bytes.toString("utf8");
|
|
35576
35822
|
if (raw.length > 0 && !raw.endsWith(`
|
|
35577
35823
|
`))
|
|
@@ -35621,7 +35867,7 @@ var isEvaluationReport = (report) => report.schemaVersion === "1.0" && asString2
|
|
|
35621
35867
|
var isConfigRecord = (value) => typeof value.enabled === "boolean" && typeof value.killSwitch === "boolean";
|
|
35622
35868
|
async function readPinnedJson(input) {
|
|
35623
35869
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input.workspaceRoot, kind: "artifact", uri: input.uri });
|
|
35624
|
-
const raw = await
|
|
35870
|
+
const raw = await readFile54(ref, "utf8");
|
|
35625
35871
|
const digest = createHash22("sha256").update(raw).digest("hex");
|
|
35626
35872
|
if (digest !== input.digest)
|
|
35627
35873
|
throw new Error("policy experiment pinned artifact digest mismatch");
|
|
@@ -35629,7 +35875,7 @@ async function readPinnedJson(input) {
|
|
|
35629
35875
|
}
|
|
35630
35876
|
async function readWorkspaceJson(input) {
|
|
35631
35877
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input.workspaceRoot, kind: "artifact", uri: input.uri });
|
|
35632
|
-
const raw = await
|
|
35878
|
+
const raw = await readFile54(ref, "utf8");
|
|
35633
35879
|
return { artifact: JSON.parse(raw), raw };
|
|
35634
35880
|
}
|
|
35635
35881
|
function hasReportIntegrity(report) {
|
|
@@ -35637,10 +35883,10 @@ function hasReportIntegrity(report) {
|
|
|
35637
35883
|
return reportHashPattern.test(reportDigest) && sha2564(body) === reportDigest;
|
|
35638
35884
|
}
|
|
35639
35885
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
35640
|
-
const configPath =
|
|
35886
|
+
const configPath = path109.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
35641
35887
|
let configJson;
|
|
35642
35888
|
try {
|
|
35643
|
-
const raw = await
|
|
35889
|
+
const raw = await readFile54(configPath, "utf8").catch((error) => {
|
|
35644
35890
|
if (isMissing(error))
|
|
35645
35891
|
return;
|
|
35646
35892
|
throw error;
|
|
@@ -35835,9 +36081,9 @@ class FwkReadService {
|
|
|
35835
36081
|
const recordedAt = nowIso(now);
|
|
35836
36082
|
const id = `receipt-${randomUUID4().replace(/-/g, "").slice(0, 16)}`;
|
|
35837
36083
|
const base = { schemaVersion: "1.0", id, workspaceId, actor, action, decision, recordedAt, cost: { tokens: 0, toolCalls: 1, elapsedMs: 0 }, contextAssembly: { traceRef: assembly.traceRef, configurationRevision: assembly.configurationRevision, selected: assembly.selected.map((id) => `./ids/${id}`), omittedOptional: assembly.omittedOptional.map((id) => `./ids/${id}`) }, policy: { ref: assembly.policyRef, revision: assembly.policyRevision }, ...action === "resource" ? { resourceRef: `./ids/${resourceId ?? "unknown"}` } : {} };
|
|
35838
|
-
const ledger =
|
|
35839
|
-
const checkpointPath =
|
|
35840
|
-
await mkdir31(
|
|
36084
|
+
const ledger = path109.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
|
|
36085
|
+
const checkpointPath = path109.join(path109.dirname(ledger), "access-receipts.checkpoint.json");
|
|
36086
|
+
await mkdir31(path109.dirname(ledger), { recursive: true, mode: 448 });
|
|
35841
36087
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
35842
36088
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
35843
36089
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -35940,8 +36186,8 @@ function normalizeFwkResult(result) {
|
|
|
35940
36186
|
// src/sac/proposal-lifecycle.ts
|
|
35941
36187
|
init_fs();
|
|
35942
36188
|
import { createHash as createHash29, randomUUID as randomUUID7 } from "crypto";
|
|
35943
|
-
import { appendFile as appendFile7, mkdir as mkdir34, readdir as
|
|
35944
|
-
import
|
|
36189
|
+
import { appendFile as appendFile7, mkdir as mkdir34, readdir as readdir20, readFile as readFile63 } from "fs/promises";
|
|
36190
|
+
import path119 from "path";
|
|
35945
36191
|
|
|
35946
36192
|
// src/sac/trusted-wrap-up.ts
|
|
35947
36193
|
import { createHash as createHash23, randomUUID as randomUUID5 } from "crypto";
|
|
@@ -36013,7 +36259,7 @@ function bindingHash(owner, intent) {
|
|
|
36013
36259
|
// src/sac/session-wrap-up.ts
|
|
36014
36260
|
import { createHash as createHash25 } from "crypto";
|
|
36015
36261
|
import { mkdir as mkdir33, writeFile as writeFile22 } from "fs/promises";
|
|
36016
|
-
import
|
|
36262
|
+
import path113 from "path";
|
|
36017
36263
|
|
|
36018
36264
|
// src/session/store.ts
|
|
36019
36265
|
init_config_dir();
|
|
@@ -36025,12 +36271,12 @@ import {
|
|
|
36025
36271
|
renameSync as renameSync2,
|
|
36026
36272
|
writeFileSync as writeFileSync2
|
|
36027
36273
|
} from "fs";
|
|
36028
|
-
import
|
|
36274
|
+
import path112 from "path";
|
|
36029
36275
|
|
|
36030
36276
|
// src/session/paths.ts
|
|
36031
36277
|
import { existsSync as existsSync16 } from "fs";
|
|
36032
36278
|
import { homedir } from "os";
|
|
36033
|
-
import
|
|
36279
|
+
import path110 from "path";
|
|
36034
36280
|
function keryxDataDir(override) {
|
|
36035
36281
|
if (override !== undefined && override.length > 0) {
|
|
36036
36282
|
return override;
|
|
@@ -36042,22 +36288,22 @@ function keryxDataDir(override) {
|
|
|
36042
36288
|
const home = homedir();
|
|
36043
36289
|
if (process.platform === "win32") {
|
|
36044
36290
|
const appData = process.env.APPDATA;
|
|
36045
|
-
const base = appData !== undefined && appData.length > 0 ? appData :
|
|
36046
|
-
return
|
|
36291
|
+
const base = appData !== undefined && appData.length > 0 ? appData : path110.join(home, "AppData", "Roaming");
|
|
36292
|
+
return path110.join(base, "keryx");
|
|
36047
36293
|
}
|
|
36048
36294
|
const xdg = process.env.XDG_DATA_HOME;
|
|
36049
|
-
const base = xdg !== undefined && xdg.length > 0 ? xdg :
|
|
36050
|
-
return
|
|
36295
|
+
const base = xdg !== undefined && xdg.length > 0 ? xdg : path110.join(home, ".local", "share");
|
|
36296
|
+
return path110.join(base, "keryx");
|
|
36051
36297
|
}
|
|
36052
36298
|
function resolveProjectRoot2(cwd) {
|
|
36053
|
-
const abs =
|
|
36299
|
+
const abs = path110.resolve(cwd);
|
|
36054
36300
|
let dir = abs;
|
|
36055
36301
|
for (;; ) {
|
|
36056
|
-
const gitPath =
|
|
36302
|
+
const gitPath = path110.join(dir, ".git");
|
|
36057
36303
|
if (existsSync16(gitPath)) {
|
|
36058
36304
|
return dir;
|
|
36059
36305
|
}
|
|
36060
|
-
const parent =
|
|
36306
|
+
const parent = path110.dirname(dir);
|
|
36061
36307
|
if (parent === dir) {
|
|
36062
36308
|
break;
|
|
36063
36309
|
}
|
|
@@ -36066,12 +36312,12 @@ function resolveProjectRoot2(cwd) {
|
|
|
36066
36312
|
return abs;
|
|
36067
36313
|
}
|
|
36068
36314
|
function projectKeyFromPath(projectPath) {
|
|
36069
|
-
const abs =
|
|
36315
|
+
const abs = path110.resolve(projectPath);
|
|
36070
36316
|
const encoded = encodeURIComponent(abs);
|
|
36071
36317
|
if (encoded.length <= 200) {
|
|
36072
36318
|
return encoded;
|
|
36073
36319
|
}
|
|
36074
|
-
const base =
|
|
36320
|
+
const base = path110.basename(abs).replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40);
|
|
36075
36321
|
let h = 0;
|
|
36076
36322
|
for (let i = 0;i < abs.length; i++) {
|
|
36077
36323
|
h = Math.imul(31, h) + abs.charCodeAt(i) | 0;
|
|
@@ -36081,17 +36327,17 @@ function projectKeyFromPath(projectPath) {
|
|
|
36081
36327
|
}
|
|
36082
36328
|
function projectSessionsDir(projectPath, dataDir) {
|
|
36083
36329
|
const key = projectKeyFromPath(resolveProjectRoot2(projectPath));
|
|
36084
|
-
return
|
|
36330
|
+
return path110.join(keryxDataDir(dataDir), "sessions", key);
|
|
36085
36331
|
}
|
|
36086
36332
|
function sessionDir(projectPath, sessionId, dataDir) {
|
|
36087
|
-
return
|
|
36333
|
+
return path110.join(projectSessionsDir(projectPath, dataDir), sessionId);
|
|
36088
36334
|
}
|
|
36089
36335
|
|
|
36090
36336
|
// src/session/slate.ts
|
|
36091
36337
|
init_fs();
|
|
36092
36338
|
init_assembly();
|
|
36093
|
-
import { mkdir as mkdir32, readFile as
|
|
36094
|
-
import
|
|
36339
|
+
import { mkdir as mkdir32, readFile as readFile55, rm as rm7 } from "fs/promises";
|
|
36340
|
+
import path111 from "path";
|
|
36095
36341
|
init_redact();
|
|
36096
36342
|
var SLATE_SEED_KINDS = [
|
|
36097
36343
|
"decision",
|
|
@@ -36106,14 +36352,14 @@ function isSlateSeedKind(value) {
|
|
|
36106
36352
|
}
|
|
36107
36353
|
var SEED_TEXT_MAX_LENGTH = 4000;
|
|
36108
36354
|
function slatePath(dir) {
|
|
36109
|
-
return
|
|
36355
|
+
return path111.join(dir, "slate.json");
|
|
36110
36356
|
}
|
|
36111
36357
|
function slateLockPath(dir) {
|
|
36112
36358
|
return `${slatePath(dir)}.lock`;
|
|
36113
36359
|
}
|
|
36114
36360
|
async function readSlate(dir) {
|
|
36115
36361
|
try {
|
|
36116
|
-
const raw = await
|
|
36362
|
+
const raw = await readFile55(slatePath(dir), "utf8");
|
|
36117
36363
|
return JSON.parse(raw);
|
|
36118
36364
|
} catch (error) {
|
|
36119
36365
|
if (isNotFound(error))
|
|
@@ -36329,11 +36575,11 @@ function listSessions(cwd, dataDir) {
|
|
|
36329
36575
|
if (name.startsWith(".")) {
|
|
36330
36576
|
continue;
|
|
36331
36577
|
}
|
|
36332
|
-
const summary = readSummaryFile(
|
|
36578
|
+
const summary = readSummaryFile(path112.join(root, name, "summary.json"));
|
|
36333
36579
|
if (summary === undefined) {
|
|
36334
36580
|
continue;
|
|
36335
36581
|
}
|
|
36336
|
-
if (
|
|
36582
|
+
if (path112.resolve(summary.projectPath) !== path112.resolve(projectPath)) {
|
|
36337
36583
|
continue;
|
|
36338
36584
|
}
|
|
36339
36585
|
out.push(summary);
|
|
@@ -36366,15 +36612,15 @@ async function readSessionSlate(cwd, sessionId, dataDir) {
|
|
|
36366
36612
|
}
|
|
36367
36613
|
function loadContext(cwd, sessionId, dataDir) {
|
|
36368
36614
|
const dir = sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir);
|
|
36369
|
-
const contextPath =
|
|
36615
|
+
const contextPath = path112.join(dir, "context.jsonl");
|
|
36370
36616
|
if (existsSync17(contextPath)) {
|
|
36371
36617
|
return readJsonl2(contextPath);
|
|
36372
36618
|
}
|
|
36373
|
-
return readJsonl2(
|
|
36619
|
+
return readJsonl2(path112.join(dir, "transcript.jsonl"));
|
|
36374
36620
|
}
|
|
36375
36621
|
function loadArchive(cwd, sessionId, dataDir, onDegraded) {
|
|
36376
36622
|
const dir = sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir);
|
|
36377
|
-
const archivePath =
|
|
36623
|
+
const archivePath = path112.join(dir, "archive.jsonl");
|
|
36378
36624
|
if (existsSync17(archivePath)) {
|
|
36379
36625
|
try {
|
|
36380
36626
|
const archive = readJsonl2(archivePath);
|
|
@@ -36558,7 +36804,7 @@ function sessionEvidenceRef(workspaceId, sessionId) {
|
|
|
36558
36804
|
}
|
|
36559
36805
|
async function resolveSessionWrapUp(input) {
|
|
36560
36806
|
const now = input.now ?? (() => new Date);
|
|
36561
|
-
const sessionId =
|
|
36807
|
+
const sessionId = path113.posix.basename(input.sourceRef, ".md");
|
|
36562
36808
|
const summary = findSession(input.cwd, sessionId);
|
|
36563
36809
|
if (summary === undefined || input.sourceRef !== sessionEvidenceRef(input.workspaceId, summary.id)) {
|
|
36564
36810
|
throw new SessionWrapUpError("session_not_found", `no session matching "${input.sourceRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
@@ -36575,7 +36821,7 @@ async function resolveSessionWrapUp(input) {
|
|
|
36575
36821
|
throw new SessionWrapUpError("session_unreadable", `session "${summary.id}" could not be read: ${cause.message}`);
|
|
36576
36822
|
}
|
|
36577
36823
|
const relPath = sessionEvidenceRef(input.workspaceId, summary.id).slice(2);
|
|
36578
|
-
const evidenceDir =
|
|
36824
|
+
const evidenceDir = path113.dirname(relPath);
|
|
36579
36825
|
const slate = await readSessionSlate(input.cwd, summary.id);
|
|
36580
36826
|
const diff = await gitDiff(input.cwd);
|
|
36581
36827
|
const diffBody = diff.kind === "measured" ? diff.text : unmeasurableDiffBody(diff.detail);
|
|
@@ -36597,23 +36843,23 @@ async function resolveSessionWrapUp(input) {
|
|
|
36597
36843
|
""
|
|
36598
36844
|
].join(`
|
|
36599
36845
|
`);
|
|
36600
|
-
const wrapUpRelPath =
|
|
36601
|
-
const diffRelPath =
|
|
36602
|
-
const noticeRelPath =
|
|
36846
|
+
const wrapUpRelPath = path113.join(evidenceDir, `${summary.id}.wrap-up.md`);
|
|
36847
|
+
const diffRelPath = path113.join(evidenceDir, `${summary.id}.diff.txt`);
|
|
36848
|
+
const noticeRelPath = path113.join(evidenceDir, `${summary.id}.redaction.md`);
|
|
36603
36849
|
const floor = await applyEvidenceRedactionFloor(input.cwd, [
|
|
36604
|
-
{ name:
|
|
36605
|
-
{ name:
|
|
36606
|
-
{ name:
|
|
36850
|
+
{ name: path113.basename(relPath), content: markdown, path: relPath, source: "generated" },
|
|
36851
|
+
{ name: path113.basename(wrapUpRelPath), content: wrapUpMarkdown, path: wrapUpRelPath, source: "generated" },
|
|
36852
|
+
{ name: path113.basename(diffRelPath), content: diffBody, path: diffRelPath, source: "tool-output" }
|
|
36607
36853
|
]);
|
|
36608
36854
|
if (!floor.ok)
|
|
36609
36855
|
throw new SessionWrapUpError("security_denied", floor.reason);
|
|
36610
36856
|
const [safeSession, safeWrapUp, safeDiff] = floor.bodies;
|
|
36611
|
-
await mkdir33(
|
|
36612
|
-
await writeFile22(
|
|
36613
|
-
await writeFile22(
|
|
36614
|
-
await writeFile22(
|
|
36857
|
+
await mkdir33(path113.join(input.cwd, evidenceDir), { recursive: true });
|
|
36858
|
+
await writeFile22(path113.join(input.cwd, relPath), safeSession.content, "utf8");
|
|
36859
|
+
await writeFile22(path113.join(input.cwd, wrapUpRelPath), safeWrapUp.content, "utf8");
|
|
36860
|
+
await writeFile22(path113.join(input.cwd, diffRelPath), safeDiff.content, "utf8");
|
|
36615
36861
|
if (floor.notice !== undefined)
|
|
36616
|
-
await writeFile22(
|
|
36862
|
+
await writeFile22(path113.join(input.cwd, noticeRelPath), floor.notice, "utf8");
|
|
36617
36863
|
const observedAt = now().toISOString();
|
|
36618
36864
|
const evidence = [
|
|
36619
36865
|
{ kind: "wrap-up", uri: `./${wrapUpRelPath}`, revision: createHash25("sha256").update(safeWrapUp.content).digest("hex"), observedAt },
|
|
@@ -36635,14 +36881,14 @@ async function resolveSessionWrapUp(input) {
|
|
|
36635
36881
|
// src/sac/review-confirm-token.ts
|
|
36636
36882
|
init_fs();
|
|
36637
36883
|
import { createHash as createHash26, randomBytes as randomBytes5 } from "crypto";
|
|
36638
|
-
import { readFile as
|
|
36639
|
-
import
|
|
36884
|
+
import { readFile as readFile56 } from "fs/promises";
|
|
36885
|
+
import path114 from "path";
|
|
36640
36886
|
var CONFIRM_TOKEN_TTL_MS = 2 * 60000;
|
|
36641
36887
|
function confirmTokenPath2(cwd, workspaceId, proposalId) {
|
|
36642
|
-
return
|
|
36888
|
+
return path114.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.confirm-token.json`);
|
|
36643
36889
|
}
|
|
36644
36890
|
function confirmReceiptPath(cwd, workspaceId, proposalId, idempotencyKey) {
|
|
36645
|
-
return
|
|
36891
|
+
return path114.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${sha2565(idempotencyKey)}.confirm-receipt.json`);
|
|
36646
36892
|
}
|
|
36647
36893
|
function sha2565(text) {
|
|
36648
36894
|
return createHash26("sha256").update(text).digest("hex");
|
|
@@ -36658,7 +36904,7 @@ async function consumeConfirmToken(cwd, workspaceId, proposalId, idempotencyKey,
|
|
|
36658
36904
|
return { ok: false, reason: "token_required" };
|
|
36659
36905
|
let stored;
|
|
36660
36906
|
try {
|
|
36661
|
-
stored = JSON.parse(await
|
|
36907
|
+
stored = JSON.parse(await readFile56(file, "utf8"));
|
|
36662
36908
|
} catch (error) {
|
|
36663
36909
|
return { ok: false, reason: isNotFound(error) ? "token_required" : "token_invalid" };
|
|
36664
36910
|
}
|
|
@@ -36681,28 +36927,28 @@ async function consumeConfirmToken(cwd, workspaceId, proposalId, idempotencyKey,
|
|
|
36681
36927
|
init_dedup();
|
|
36682
36928
|
init_store();
|
|
36683
36929
|
init_config();
|
|
36684
|
-
import { readFile as
|
|
36930
|
+
import { readFile as readFile58, readdir as readdir19 } from "fs/promises";
|
|
36685
36931
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
36686
|
-
import
|
|
36932
|
+
import path116 from "path";
|
|
36687
36933
|
|
|
36688
36934
|
// src/sac/proposal-evidence.ts
|
|
36689
36935
|
init_fs();
|
|
36690
36936
|
import { createHash as createHash27 } from "crypto";
|
|
36691
|
-
import { readFile as
|
|
36692
|
-
import
|
|
36937
|
+
import { readFile as readFile57, rm as rm8 } from "fs/promises";
|
|
36938
|
+
import path115 from "path";
|
|
36693
36939
|
function proposalPath(cwd, workspaceId, proposalId) {
|
|
36694
|
-
return
|
|
36940
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
|
|
36695
36941
|
}
|
|
36696
36942
|
function ownerReceiptPath(cwd, owner, workspaceId, idempotencyKey) {
|
|
36697
|
-
return
|
|
36943
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-receipts`, `${idempotencyKey}.json`);
|
|
36698
36944
|
}
|
|
36699
36945
|
function proposalNotePath(cwd, workspaceId, proposalId) {
|
|
36700
|
-
return
|
|
36946
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.note.txt`);
|
|
36701
36947
|
}
|
|
36702
36948
|
async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
36703
36949
|
let proposal;
|
|
36704
36950
|
try {
|
|
36705
|
-
proposal = JSON.parse(await
|
|
36951
|
+
proposal = JSON.parse(await readFile57(proposalPath(cwd, workspaceId, proposalId), "utf8"));
|
|
36706
36952
|
} catch {
|
|
36707
36953
|
return { ok: false, code: "proposal_record_unreadable" };
|
|
36708
36954
|
}
|
|
@@ -36711,7 +36957,7 @@ async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
|
36711
36957
|
return { ok: false, code: "no_evidence_to_write" };
|
|
36712
36958
|
let content;
|
|
36713
36959
|
try {
|
|
36714
|
-
content = await
|
|
36960
|
+
content = await readFile57(path115.join(cwd, evidence.uri), "utf8");
|
|
36715
36961
|
} catch {
|
|
36716
36962
|
return { ok: false, code: "evidence_file_unreadable" };
|
|
36717
36963
|
}
|
|
@@ -36721,7 +36967,7 @@ async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
|
36721
36967
|
return { proposal, evidence, content };
|
|
36722
36968
|
}
|
|
36723
36969
|
async function readSidecarNote(cwd, workspaceId, proposalId) {
|
|
36724
|
-
return
|
|
36970
|
+
return readFile57(proposalNotePath(cwd, workspaceId, proposalId), "utf8").catch(() => {
|
|
36725
36971
|
return;
|
|
36726
36972
|
});
|
|
36727
36973
|
}
|
|
@@ -36730,7 +36976,7 @@ function digestOf2(content) {
|
|
|
36730
36976
|
}
|
|
36731
36977
|
async function readTargetVersion(absolutePath) {
|
|
36732
36978
|
try {
|
|
36733
|
-
return digestOf2(await
|
|
36979
|
+
return digestOf2(await readFile57(absolutePath, "utf8"));
|
|
36734
36980
|
} catch (error) {
|
|
36735
36981
|
if (isNotFound(error))
|
|
36736
36982
|
return null;
|
|
@@ -36741,26 +36987,26 @@ function targetRelPath(targetRef) {
|
|
|
36741
36987
|
return targetRef.replace(/^\.\//, "");
|
|
36742
36988
|
}
|
|
36743
36989
|
function targetAbsolutePath(cwd, targetRef) {
|
|
36744
|
-
return
|
|
36990
|
+
return path115.join(cwd, ".metaproject", targetRelPath(targetRef));
|
|
36745
36991
|
}
|
|
36746
36992
|
function targetKey(targetRef) {
|
|
36747
36993
|
return digestOf2(targetRelPath(targetRef)).slice(0, 32);
|
|
36748
36994
|
}
|
|
36749
36995
|
function ownerTargetVersionPath(cwd, owner, workspaceId, targetRef) {
|
|
36750
|
-
return
|
|
36996
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-target-versions`, `${targetKey(targetRef)}.json`);
|
|
36751
36997
|
}
|
|
36752
36998
|
function ownerTargetLockPath(cwd, owner, targetRef) {
|
|
36753
|
-
return
|
|
36999
|
+
return path115.join(cwd, ".metaproject", "data", "sac", "target-locks", owner, `${targetKey(targetRef)}.lock`);
|
|
36754
37000
|
}
|
|
36755
37001
|
function ownerStagingDir(cwd, owner, workspaceId, idempotencyKey) {
|
|
36756
|
-
return
|
|
37002
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-staging`, digestOf2(idempotencyKey).slice(0, 32));
|
|
36757
37003
|
}
|
|
36758
37004
|
function ownerConflictDir(cwd, owner, workspaceId, idempotencyKey) {
|
|
36759
|
-
return
|
|
37005
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-conflicts`, digestOf2(idempotencyKey).slice(0, 32));
|
|
36760
37006
|
}
|
|
36761
37007
|
async function readJson2(file) {
|
|
36762
37008
|
try {
|
|
36763
|
-
return JSON.parse(await
|
|
37009
|
+
return JSON.parse(await readFile57(file, "utf8"));
|
|
36764
37010
|
} catch {
|
|
36765
37011
|
return;
|
|
36766
37012
|
}
|
|
@@ -36827,7 +37073,7 @@ async function applyGuardedTargetWrite(input) {
|
|
|
36827
37073
|
staged: input.staged,
|
|
36828
37074
|
stagedAt: receipt.completedAt
|
|
36829
37075
|
};
|
|
36830
|
-
await writeFileAtomic(
|
|
37076
|
+
await writeFileAtomic(path115.join(ownerStagingDir(cwd, owner, intent.workspaceId, intent.idempotencyKey), "plan.json"), `${JSON.stringify(staged, null, 2)}
|
|
36831
37077
|
`);
|
|
36832
37078
|
const applied = await input.apply(input.staged);
|
|
36833
37079
|
if (!applied.ok) {
|
|
@@ -36853,15 +37099,15 @@ async function recoverStagedOwnerWrite(input) {
|
|
|
36853
37099
|
});
|
|
36854
37100
|
}
|
|
36855
37101
|
async function readStagedOwnerWrite(cwd, owner, workspaceId, idempotencyKey) {
|
|
36856
|
-
return readJson2(
|
|
37102
|
+
return readJson2(path115.join(ownerStagingDir(cwd, owner, workspaceId, idempotencyKey), "plan.json"));
|
|
36857
37103
|
}
|
|
36858
37104
|
async function recordConflict(input) {
|
|
36859
37105
|
const base = { targetRef: input.targetRef, expectedVersion: input.expectedVersion, currentVersion: input.currentVersion };
|
|
36860
37106
|
if (input.proposedContent === undefined)
|
|
36861
37107
|
return base;
|
|
36862
37108
|
const dir = ownerConflictDir(input.cwd, input.owner, input.intent.workspaceId, input.intent.idempotencyKey);
|
|
36863
|
-
await writeFileAtomic(
|
|
36864
|
-
const diffRef = `./workspaces/${input.intent.workspaceId}/${input.owner}-write-conflicts/${
|
|
37109
|
+
await writeFileAtomic(path115.join(dir, "proposed"), input.proposedContent);
|
|
37110
|
+
const diffRef = `./workspaces/${input.intent.workspaceId}/${input.owner}-write-conflicts/${path115.basename(dir)}/proposed`;
|
|
36865
37111
|
return { ...base, diffRef };
|
|
36866
37112
|
}
|
|
36867
37113
|
|
|
@@ -36911,21 +37157,21 @@ async function resolveWorkspaceModule(cwd, workspaceId) {
|
|
|
36911
37157
|
}
|
|
36912
37158
|
}
|
|
36913
37159
|
async function collectWikiDecisionEntries(cwd) {
|
|
36914
|
-
const dir =
|
|
37160
|
+
const dir = path116.join(cwd, ".metaproject", "wiki", "decisions");
|
|
36915
37161
|
let files;
|
|
36916
37162
|
try {
|
|
36917
|
-
files = (await
|
|
37163
|
+
files = (await readdir19(dir)).filter((name) => name.endsWith(".md"));
|
|
36918
37164
|
} catch {
|
|
36919
37165
|
return [];
|
|
36920
37166
|
}
|
|
36921
37167
|
const entries = [];
|
|
36922
37168
|
for (const file of files) {
|
|
36923
|
-
const absolutePath =
|
|
37169
|
+
const absolutePath = path116.join(dir, file);
|
|
36924
37170
|
try {
|
|
36925
|
-
const content = await
|
|
37171
|
+
const content = await readFile58(absolutePath, "utf8");
|
|
36926
37172
|
entries.push({
|
|
36927
37173
|
absolutePath,
|
|
36928
|
-
relativePath:
|
|
37174
|
+
relativePath: path116.posix.join("decisions", file),
|
|
36929
37175
|
type: extractHeaderField(content, "Type") ?? "decision",
|
|
36930
37176
|
title: extractTitle(content),
|
|
36931
37177
|
version: extractHeaderField(content, "Version") ?? null,
|
|
@@ -37039,7 +37285,7 @@ ${candidates}`;
|
|
|
37039
37285
|
|
|
37040
37286
|
// src/sac/memory-owner-writer.ts
|
|
37041
37287
|
init_write();
|
|
37042
|
-
import { readFile as
|
|
37288
|
+
import { readFile as readFile59 } from "fs/promises";
|
|
37043
37289
|
function renderWrapUpMemoryEntry(input) {
|
|
37044
37290
|
const note = input.note?.trim();
|
|
37045
37291
|
return `# ${input.title}
|
|
@@ -37098,7 +37344,7 @@ function createRealMemoryOwnerWriter(cwd, opts) {
|
|
|
37098
37344
|
},
|
|
37099
37345
|
async recover(intent) {
|
|
37100
37346
|
try {
|
|
37101
|
-
const raw = await
|
|
37347
|
+
const raw = await readFile59(ownerReceiptPath(cwd, "memory", intent.workspaceId, intent.idempotencyKey), "utf8");
|
|
37102
37348
|
return JSON.parse(raw);
|
|
37103
37349
|
} catch {
|
|
37104
37350
|
return;
|
|
@@ -37146,8 +37392,8 @@ function createRealMemoryOwnerWriter(cwd, opts) {
|
|
|
37146
37392
|
// src/sac/wiki-owner-writer.ts
|
|
37147
37393
|
init_fs();
|
|
37148
37394
|
init_guard();
|
|
37149
|
-
import { readFile as
|
|
37150
|
-
import
|
|
37395
|
+
import { readFile as readFile60 } from "fs/promises";
|
|
37396
|
+
import path117 from "path";
|
|
37151
37397
|
function renderWrapUpDecisionPage(input) {
|
|
37152
37398
|
const note = input.note?.trim();
|
|
37153
37399
|
return `# ${input.title}
|
|
@@ -37188,14 +37434,14 @@ export is the source of truth for what actually happened.
|
|
|
37188
37434
|
`;
|
|
37189
37435
|
}
|
|
37190
37436
|
function wikiPageRelativePath(proposalId) {
|
|
37191
|
-
return
|
|
37437
|
+
return path117.posix.join("decisions", `sac-${proposalId}.md`);
|
|
37192
37438
|
}
|
|
37193
37439
|
function createRealWikiOwnerWriter(cwd, opts) {
|
|
37194
37440
|
const now = opts?.now ?? (() => new Date);
|
|
37195
37441
|
const applyStaged = async (staged) => {
|
|
37196
37442
|
const relativePath = staged.relativePath;
|
|
37197
37443
|
const content = staged.content;
|
|
37198
|
-
await writeFileAtomic(
|
|
37444
|
+
await writeFileAtomic(path117.join(cwd, ".metaproject", "wiki", relativePath), content);
|
|
37199
37445
|
return { ok: true };
|
|
37200
37446
|
};
|
|
37201
37447
|
return {
|
|
@@ -37204,7 +37450,7 @@ function createRealWikiOwnerWriter(cwd, opts) {
|
|
|
37204
37450
|
},
|
|
37205
37451
|
async recover(intent) {
|
|
37206
37452
|
try {
|
|
37207
|
-
const raw = await
|
|
37453
|
+
const raw = await readFile60(ownerReceiptPath(cwd, "wiki", intent.workspaceId, intent.idempotencyKey), "utf8");
|
|
37208
37454
|
return JSON.parse(raw);
|
|
37209
37455
|
} catch {
|
|
37210
37456
|
return;
|
|
@@ -37255,7 +37501,7 @@ function createRealWikiOwnerWriter(cwd, opts) {
|
|
|
37255
37501
|
}
|
|
37256
37502
|
|
|
37257
37503
|
// src/sac/skill-owner-writer.ts
|
|
37258
|
-
import { readFile as
|
|
37504
|
+
import { readFile as readFile62 } from "fs/promises";
|
|
37259
37505
|
|
|
37260
37506
|
// src/gdskills/project-skills.ts
|
|
37261
37507
|
init_contained_write();
|
|
@@ -37264,8 +37510,8 @@ init_json();
|
|
|
37264
37510
|
init_guard();
|
|
37265
37511
|
import { createHash as createHash28 } from "crypto";
|
|
37266
37512
|
import { homedir as homedir2 } from "os";
|
|
37267
|
-
import { readFile as
|
|
37268
|
-
import
|
|
37513
|
+
import { readFile as readFile61, stat as stat15 } from "fs/promises";
|
|
37514
|
+
import path118 from "path";
|
|
37269
37515
|
var VERSION = "0.1.0";
|
|
37270
37516
|
var PROJECT_SKILLS_DIR = ".metaproject/project-skills";
|
|
37271
37517
|
var PROJECT_SKILLS_MANIFEST_PATH = ".metaproject/metaproject.json";
|
|
@@ -37295,7 +37541,7 @@ async function assertProjectWritesContained(projectRoot, paths) {
|
|
|
37295
37541
|
}
|
|
37296
37542
|
}
|
|
37297
37543
|
async function createProjectSkill(projectRoot, options) {
|
|
37298
|
-
const metaprojectRoot =
|
|
37544
|
+
const metaprojectRoot = path118.join(projectRoot, ".metaproject");
|
|
37299
37545
|
if (!await pathExists(metaprojectRoot)) {
|
|
37300
37546
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
37301
37547
|
}
|
|
@@ -37306,7 +37552,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
37306
37552
|
const skillName = projectSkillSlug(options.name ?? inferSkillName(options.target));
|
|
37307
37553
|
const format = options.format ?? "auto";
|
|
37308
37554
|
const relativeSkillPath = projectSkillPackagePath(moduleName, skillName);
|
|
37309
|
-
const packageRoot =
|
|
37555
|
+
const packageRoot = path118.join(projectRoot, relativeSkillPath);
|
|
37310
37556
|
await assertProjectWritesContained(projectRoot, projectSkillWritePaths(moduleName, skillName, format));
|
|
37311
37557
|
const origin = options.origin ? options.originContent !== undefined ? {
|
|
37312
37558
|
ref: options.origin.trim(),
|
|
@@ -37317,7 +37563,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
37317
37563
|
const warnings = collectWarnings(evidence, format);
|
|
37318
37564
|
const files = filesForPackage(packageRoot, format);
|
|
37319
37565
|
if (!options.dryRun) {
|
|
37320
|
-
await withFileLock(
|
|
37566
|
+
await withFileLock(path118.join(projectRoot, PROJECT_SKILLS_LOCK_PATH), async () => {
|
|
37321
37567
|
await writeProjectSkillPackage({
|
|
37322
37568
|
projectRoot,
|
|
37323
37569
|
packageRoot,
|
|
@@ -37346,7 +37592,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
37346
37592
|
name: skillName,
|
|
37347
37593
|
target: options.target,
|
|
37348
37594
|
skillPath: relativeSkillPath,
|
|
37349
|
-
files: files.map((filePath) => toPosix(
|
|
37595
|
+
files: files.map((filePath) => toPosix(path118.relative(projectRoot, filePath))),
|
|
37350
37596
|
warnings,
|
|
37351
37597
|
dryRun: options.dryRun === true
|
|
37352
37598
|
};
|
|
@@ -37355,9 +37601,9 @@ var MAX_SKILL_TARGET_LENGTH = 80;
|
|
|
37355
37601
|
function resolveOriginPath(ref, projectRoot) {
|
|
37356
37602
|
const trimmed = ref.trim();
|
|
37357
37603
|
if (trimmed === "~" || trimmed.startsWith("~/")) {
|
|
37358
|
-
return
|
|
37604
|
+
return path118.join(homedir2(), trimmed.slice(1));
|
|
37359
37605
|
}
|
|
37360
|
-
return
|
|
37606
|
+
return path118.resolve(projectRoot, trimmed);
|
|
37361
37607
|
}
|
|
37362
37608
|
function hashOriginContent(content) {
|
|
37363
37609
|
return `sha256:${createHash28("sha256").update(content).digest("hex")}`;
|
|
@@ -37366,7 +37612,7 @@ async function readSkillOrigin(projectRoot, ref, now = () => new Date) {
|
|
|
37366
37612
|
const resolved = resolveOriginPath(ref, projectRoot);
|
|
37367
37613
|
let content;
|
|
37368
37614
|
try {
|
|
37369
|
-
content = await
|
|
37615
|
+
content = await readFile61(resolved, "utf8");
|
|
37370
37616
|
} catch (cause) {
|
|
37371
37617
|
throw new Error(`Cannot read the origin file ${ref} (resolved to ${resolved}): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
37372
37618
|
}
|
|
@@ -37395,16 +37641,16 @@ async function writeProjectSkillPackage({
|
|
|
37395
37641
|
}) {
|
|
37396
37642
|
const packageFormat = format === "single" ? "single" : "package";
|
|
37397
37643
|
const skillContent = renderProjectSkill({ moduleName, skillName, target, note, origin, evidence, packageFormat });
|
|
37398
|
-
const relativeSkillMdPath = toPosix(
|
|
37644
|
+
const relativeSkillMdPath = toPosix(path118.join(path118.relative(projectRoot, packageRoot), "SKILL.md"));
|
|
37399
37645
|
const guard = await guardOutput({ cwd: projectRoot, content: skillContent, target: "skill", source: "generated", path: relativeSkillMdPath });
|
|
37400
37646
|
const output = prepareOutputForPersistence(guard, skillContent);
|
|
37401
37647
|
if (!output.allowed) {
|
|
37402
37648
|
throw new Error(`Project skill blocked by the security gate: ${output.reason}`);
|
|
37403
37649
|
}
|
|
37404
|
-
const packageRel = toPosix(
|
|
37650
|
+
const packageRel = toPosix(path118.relative(projectRoot, packageRoot));
|
|
37405
37651
|
const write = (file, content) => writeContained(projectRoot, `${packageRel}/${file}`, content);
|
|
37406
37652
|
await write("SKILL.md", output.content);
|
|
37407
|
-
if (!await pathExists(
|
|
37653
|
+
if (!await pathExists(path118.join(packageRoot, "skill-changelog.md"))) {
|
|
37408
37654
|
await write("skill-changelog.md", renderSkillChangelog({ moduleName, skillName, target }));
|
|
37409
37655
|
}
|
|
37410
37656
|
if (packageFormat === "package") {
|
|
@@ -37414,10 +37660,10 @@ async function writeProjectSkillPackage({
|
|
|
37414
37660
|
}
|
|
37415
37661
|
}
|
|
37416
37662
|
async function collectEvidence(projectRoot, target) {
|
|
37417
|
-
const absoluteTarget =
|
|
37663
|
+
const absoluteTarget = path118.resolve(projectRoot, target);
|
|
37418
37664
|
const targetExists = await pathExists(absoluteTarget);
|
|
37419
37665
|
const targetKind = await classifyTarget(absoluteTarget, targetExists);
|
|
37420
|
-
const maybeRelativeTarget = targetExists ? toPosix(
|
|
37666
|
+
const maybeRelativeTarget = targetExists ? toPosix(path118.relative(projectRoot, absoluteTarget)) : undefined;
|
|
37421
37667
|
const graphArtifacts = await existingRelativePaths(projectRoot, [
|
|
37422
37668
|
".metaproject/data/gdgraph/artifacts/summary.md",
|
|
37423
37669
|
".metaproject/data/gdgraph/artifacts/module-map.json"
|
|
@@ -37441,13 +37687,13 @@ async function classifyTarget(absoluteTarget, targetExists) {
|
|
|
37441
37687
|
if (!targetExists) {
|
|
37442
37688
|
return "symbol-or-concept";
|
|
37443
37689
|
}
|
|
37444
|
-
const stats = await
|
|
37690
|
+
const stats = await stat15(absoluteTarget);
|
|
37445
37691
|
return stats.isDirectory() ? "directory" : "file";
|
|
37446
37692
|
}
|
|
37447
37693
|
async function existingRelativePaths(projectRoot, candidates) {
|
|
37448
37694
|
const existing = [];
|
|
37449
37695
|
for (const candidate of candidates) {
|
|
37450
|
-
if (await pathExists(
|
|
37696
|
+
if (await pathExists(path118.join(projectRoot, candidate))) {
|
|
37451
37697
|
existing.push(candidate);
|
|
37452
37698
|
}
|
|
37453
37699
|
}
|
|
@@ -37474,17 +37720,17 @@ function collectWarnings(evidence, format) {
|
|
|
37474
37720
|
}
|
|
37475
37721
|
function filesForPackage(packageRoot, format) {
|
|
37476
37722
|
const base = [
|
|
37477
|
-
|
|
37478
|
-
|
|
37723
|
+
path118.join(packageRoot, "SKILL.md"),
|
|
37724
|
+
path118.join(packageRoot, "skill-changelog.md")
|
|
37479
37725
|
];
|
|
37480
37726
|
if (format === "single") {
|
|
37481
37727
|
return base;
|
|
37482
37728
|
}
|
|
37483
37729
|
return [
|
|
37484
37730
|
...base,
|
|
37485
|
-
|
|
37486
|
-
|
|
37487
|
-
|
|
37731
|
+
path118.join(packageRoot, "verification.md"),
|
|
37732
|
+
path118.join(packageRoot, "references", "context.md"),
|
|
37733
|
+
path118.join(packageRoot, "templates", "README.md")
|
|
37488
37734
|
];
|
|
37489
37735
|
}
|
|
37490
37736
|
function renderProjectSkill({
|
|
@@ -37712,7 +37958,7 @@ keryx skills verify ${moduleName}/${skillName}
|
|
|
37712
37958
|
`;
|
|
37713
37959
|
}
|
|
37714
37960
|
async function updateManifest(projectRoot, entry) {
|
|
37715
|
-
const manifestPath =
|
|
37961
|
+
const manifestPath = path118.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
37716
37962
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
37717
37963
|
manifest.modules ??= {};
|
|
37718
37964
|
manifest.modules.gdskills ??= {};
|
|
@@ -37726,8 +37972,8 @@ async function updateManifest(projectRoot, entry) {
|
|
|
37726
37972
|
`);
|
|
37727
37973
|
}
|
|
37728
37974
|
async function updateSkillsCatalog(projectRoot) {
|
|
37729
|
-
const manifestPath =
|
|
37730
|
-
const catalogPath =
|
|
37975
|
+
const manifestPath = path118.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
37976
|
+
const catalogPath = path118.join(projectRoot, PROJECT_SKILLS_CATALOG_PATH);
|
|
37731
37977
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
37732
37978
|
const registry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
37733
37979
|
const rows = registry.length > 0 ? registry.map(projectSkillCatalogRow).join(`
|
|
@@ -37739,7 +37985,7 @@ async function updateSkillsCatalog(projectRoot) {
|
|
|
37739
37985
|
|---|---|---|---|
|
|
37740
37986
|
${rows}
|
|
37741
37987
|
${PROJECT_SKILLS_CATALOG_END}`;
|
|
37742
|
-
const current = await pathExists(catalogPath) ? await
|
|
37988
|
+
const current = await pathExists(catalogPath) ? await readFile61(catalogPath, "utf8") : `# Metaproject Skills Catalog
|
|
37743
37989
|
`;
|
|
37744
37990
|
const start = PROJECT_SKILLS_CATALOG_START;
|
|
37745
37991
|
const end = PROJECT_SKILLS_CATALOG_END;
|
|
@@ -37767,7 +38013,7 @@ function inferModule(target) {
|
|
|
37767
38013
|
}
|
|
37768
38014
|
function inferSkillName(target) {
|
|
37769
38015
|
const normalized = target.trim().replace(/[#:]+/g, "/");
|
|
37770
|
-
const base =
|
|
38016
|
+
const base = path118.basename(normalized).replace(/\.[^.]+$/, "");
|
|
37771
38017
|
return base || "entity";
|
|
37772
38018
|
}
|
|
37773
38019
|
function projectSkillSlug(value) {
|
|
@@ -37814,7 +38060,7 @@ function createRealSkillOwnerWriter(cwd, opts) {
|
|
|
37814
38060
|
},
|
|
37815
38061
|
async recover(intent) {
|
|
37816
38062
|
try {
|
|
37817
|
-
const raw = await
|
|
38063
|
+
const raw = await readFile62(ownerReceiptPath(cwd, "skill", intent.workspaceId, intent.idempotencyKey), "utf8");
|
|
37818
38064
|
return JSON.parse(raw);
|
|
37819
38065
|
} catch {
|
|
37820
38066
|
return;
|
|
@@ -37872,7 +38118,7 @@ class ProposalLifecycleService {
|
|
|
37872
38118
|
now;
|
|
37873
38119
|
constructor(options) {
|
|
37874
38120
|
this.options = options;
|
|
37875
|
-
this.root =
|
|
38121
|
+
this.root = path119.resolve(options.workspaceRoot);
|
|
37876
38122
|
this.now = options.now ?? (() => new Date);
|
|
37877
38123
|
}
|
|
37878
38124
|
async create(input) {
|
|
@@ -37891,13 +38137,13 @@ class ProposalLifecycleService {
|
|
|
37891
38137
|
if (manifest.status === "archived")
|
|
37892
38138
|
throw new ProposalLifecycleError("guard_denied", "workspace is archived");
|
|
37893
38139
|
const file = this.proposalPath(workspaceId, proposal.id);
|
|
37894
|
-
await mkdir34(
|
|
38140
|
+
await mkdir34(path119.dirname(file), { recursive: true, mode: 448 });
|
|
37895
38141
|
return withFileLock(`${file}.lock`, async () => {
|
|
37896
38142
|
const consume = this.options.wrapUpAuthority.consume(input.wrapUp, { actor, workspaceId });
|
|
37897
38143
|
if (consume !== "ok")
|
|
37898
38144
|
throw new ProposalLifecycleError("trusted_wrap_up_required", `trusted wrap-up ${consume}`);
|
|
37899
38145
|
try {
|
|
37900
|
-
await
|
|
38146
|
+
await readFile63(file, "utf8");
|
|
37901
38147
|
throw new ProposalLifecycleError("conflict", "proposal already exists");
|
|
37902
38148
|
} catch (error) {
|
|
37903
38149
|
if (error instanceof ProposalLifecycleError)
|
|
@@ -37919,7 +38165,7 @@ class ProposalLifecycleService {
|
|
|
37919
38165
|
return this.options.workspaces.withAuthorizedActor({ actorContext: actor, workspaceId: input.workspaceId, action: "review", execute: async (manifest) => {
|
|
37920
38166
|
const proposal = await this.loadProposal(input.workspaceId, input.proposalId);
|
|
37921
38167
|
const ledger = this.ledgerPath(input.workspaceId);
|
|
37922
|
-
await mkdir34(
|
|
38168
|
+
await mkdir34(path119.dirname(ledger), { recursive: true, mode: 448 });
|
|
37923
38169
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
37924
38170
|
const records = (await this.records(ledger)).filter((record) => record.proposalId === proposal.id);
|
|
37925
38171
|
const events = records.filter((record) => record.recordType === "proposal-transition");
|
|
@@ -37961,10 +38207,10 @@ class ProposalLifecycleService {
|
|
|
37961
38207
|
} });
|
|
37962
38208
|
}
|
|
37963
38209
|
async listProposedProposals(workspaceId) {
|
|
37964
|
-
const proposalsDir =
|
|
38210
|
+
const proposalsDir = path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals");
|
|
37965
38211
|
let entries;
|
|
37966
38212
|
try {
|
|
37967
|
-
entries = (await
|
|
38213
|
+
entries = (await readdir20(proposalsDir)).filter((name) => name.endsWith(".json"));
|
|
37968
38214
|
} catch (error) {
|
|
37969
38215
|
if (isNotFound(error))
|
|
37970
38216
|
return [];
|
|
@@ -37973,7 +38219,7 @@ class ProposalLifecycleService {
|
|
|
37973
38219
|
const proposals = [];
|
|
37974
38220
|
for (const entry of entries) {
|
|
37975
38221
|
try {
|
|
37976
|
-
const parsed = JSON.parse(await
|
|
38222
|
+
const parsed = JSON.parse(await readFile63(path119.join(proposalsDir, entry), "utf8"));
|
|
37977
38223
|
if (parsed.recordType === "proposal-created")
|
|
37978
38224
|
proposals.push(parsed);
|
|
37979
38225
|
} catch {}
|
|
@@ -38058,7 +38304,7 @@ class ProposalLifecycleService {
|
|
|
38058
38304
|
}
|
|
38059
38305
|
async loadOwnerWriteAttempt(workspaceId, proposalId, key) {
|
|
38060
38306
|
try {
|
|
38061
|
-
return JSON.parse(await
|
|
38307
|
+
return JSON.parse(await readFile63(this.attemptPath(workspaceId, proposalId, key), "utf8"));
|
|
38062
38308
|
} catch (error) {
|
|
38063
38309
|
if (isNotFound(error))
|
|
38064
38310
|
return;
|
|
@@ -38073,7 +38319,7 @@ class ProposalLifecycleService {
|
|
|
38073
38319
|
async ensureReviewDecision(proposal, actor, reviewerAuthority, input, outcome, targetWrite, policyRevision) {
|
|
38074
38320
|
const file = this.decisionPath(proposal.workspaceId, proposal.id, input.idempotencyKey);
|
|
38075
38321
|
try {
|
|
38076
|
-
const existing = JSON.parse(await
|
|
38322
|
+
const existing = JSON.parse(await readFile63(file, "utf8"));
|
|
38077
38323
|
const existingWrite = existing.targetWrite;
|
|
38078
38324
|
if (existing.proposalId !== proposal.id || existing.proposalRevision !== proposal.proposalRevision || existing.workspaceId !== proposal.workspaceId || existing.correlationId !== input.requestCorrelationId || existing.idempotencyKey !== input.idempotencyKey || existing.decision !== outcome) {
|
|
38079
38325
|
throw new ProposalLifecycleError("conflict", "review decision does not match the recovery request");
|
|
@@ -38106,7 +38352,7 @@ class ProposalLifecycleService {
|
|
|
38106
38352
|
const approvalRef = this.approvalRef(proposal.id, input.idempotencyKey);
|
|
38107
38353
|
const approvalPath = this.approvalPath(proposal.workspaceId, proposal.id, input.idempotencyKey);
|
|
38108
38354
|
try {
|
|
38109
|
-
const existing = JSON.parse(await
|
|
38355
|
+
const existing = JSON.parse(await readFile63(approvalPath, "utf8"));
|
|
38110
38356
|
if (existing.proposalId !== proposal.id || existing.proposalRevision !== proposal.proposalRevision || existing.workspaceId !== proposal.workspaceId || existing.correlationId !== input.requestCorrelationId || existing.idempotencyKey !== input.idempotencyKey || existing.policyRevision !== policyRevision || JSON.stringify(existing.reviewer) !== JSON.stringify({ subject: actor.subject, authority: reviewerAuthority }))
|
|
38111
38357
|
throw new ProposalLifecycleError("conflict", "approval does not match the recovery request");
|
|
38112
38358
|
return { approvalRef };
|
|
@@ -38126,7 +38372,7 @@ class ProposalLifecycleService {
|
|
|
38126
38372
|
if (existing)
|
|
38127
38373
|
return { intentRef };
|
|
38128
38374
|
try {
|
|
38129
|
-
const recovered = JSON.parse(await
|
|
38375
|
+
const recovered = JSON.parse(await readFile63(this.intentPath(proposal.workspaceId, proposal.id, input.idempotencyKey), "utf8"));
|
|
38130
38376
|
await this.validateRecord(recovered);
|
|
38131
38377
|
if (recovered.proposalId !== proposal.id || recovered.proposalRevision !== proposal.proposalRevision || recovered.correlationId !== input.requestCorrelationId || recovered.idempotencyKey !== input.idempotencyKey || recovered.workspaceId !== proposal.workspaceId)
|
|
38132
38378
|
throw new ProposalLifecycleError("conflict", "write intent does not match the recovery request");
|
|
@@ -38219,7 +38465,7 @@ class ProposalLifecycleService {
|
|
|
38219
38465
|
}
|
|
38220
38466
|
async loadProposal(workspaceId, proposalId) {
|
|
38221
38467
|
try {
|
|
38222
|
-
const item = JSON.parse(await
|
|
38468
|
+
const item = JSON.parse(await readFile63(this.proposalPath(workspaceId, proposalId), "utf8"));
|
|
38223
38469
|
await this.validateProposal(item);
|
|
38224
38470
|
return item;
|
|
38225
38471
|
} catch (error) {
|
|
@@ -38230,7 +38476,7 @@ class ProposalLifecycleService {
|
|
|
38230
38476
|
}
|
|
38231
38477
|
async records(ledger) {
|
|
38232
38478
|
try {
|
|
38233
|
-
return (await
|
|
38479
|
+
return (await readFile63(ledger, "utf8")).trim().split(`
|
|
38234
38480
|
`).filter(Boolean).map((line) => JSON.parse(line));
|
|
38235
38481
|
} catch (error) {
|
|
38236
38482
|
if (isNotFound(error))
|
|
@@ -38239,35 +38485,35 @@ class ProposalLifecycleService {
|
|
|
38239
38485
|
}
|
|
38240
38486
|
}
|
|
38241
38487
|
proposalPath(workspaceId, proposalId) {
|
|
38242
|
-
return
|
|
38488
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
|
|
38243
38489
|
}
|
|
38244
38490
|
decisionRef(proposalId, key) {
|
|
38245
38491
|
return `./proposals/${proposalId}.${hash(key)}.decision.json`;
|
|
38246
38492
|
}
|
|
38247
38493
|
decisionPath(workspaceId, proposalId, key) {
|
|
38248
|
-
return
|
|
38494
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.decision.json`);
|
|
38249
38495
|
}
|
|
38250
38496
|
approvalRef(proposalId, key) {
|
|
38251
38497
|
return `./proposals/${proposalId}.${hash(key)}.approval.json`;
|
|
38252
38498
|
}
|
|
38253
38499
|
approvalPath(workspaceId, proposalId, key) {
|
|
38254
|
-
return
|
|
38500
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.approval.json`);
|
|
38255
38501
|
}
|
|
38256
38502
|
writeResultPath(workspaceId, proposalId, key) {
|
|
38257
|
-
return
|
|
38503
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.write-result.json`);
|
|
38258
38504
|
}
|
|
38259
38505
|
intentRef(proposalId, key) {
|
|
38260
38506
|
return `./proposals/${proposalId}.${hash(key)}.write-intent.json`;
|
|
38261
38507
|
}
|
|
38262
38508
|
intentPath(workspaceId, proposalId, key) {
|
|
38263
|
-
return
|
|
38509
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.write-intent.json`);
|
|
38264
38510
|
}
|
|
38265
38511
|
attemptPath(workspaceId, proposalId, key) {
|
|
38266
|
-
return
|
|
38512
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.owner-write-attempt.json`);
|
|
38267
38513
|
}
|
|
38268
38514
|
async loadWriteResult(workspaceId, proposalId, key) {
|
|
38269
38515
|
try {
|
|
38270
|
-
return JSON.parse(await
|
|
38516
|
+
return JSON.parse(await readFile63(this.writeResultPath(workspaceId, proposalId, key), "utf8"));
|
|
38271
38517
|
} catch (error) {
|
|
38272
38518
|
if (isNotFound(error))
|
|
38273
38519
|
return;
|
|
@@ -38276,7 +38522,7 @@ class ProposalLifecycleService {
|
|
|
38276
38522
|
}
|
|
38277
38523
|
async writeImmutable(file, record) {
|
|
38278
38524
|
try {
|
|
38279
|
-
const existing = await
|
|
38525
|
+
const existing = await readFile63(file, "utf8");
|
|
38280
38526
|
if (existing !== `${JSON.stringify(record)}
|
|
38281
38527
|
`)
|
|
38282
38528
|
throw new ProposalLifecycleError("conflict", "immutable lifecycle record already exists");
|
|
@@ -38290,7 +38536,7 @@ class ProposalLifecycleService {
|
|
|
38290
38536
|
}
|
|
38291
38537
|
}
|
|
38292
38538
|
ledgerPath(workspaceId) {
|
|
38293
|
-
return
|
|
38539
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "activity.jsonl");
|
|
38294
38540
|
}
|
|
38295
38541
|
timestamp() {
|
|
38296
38542
|
return this.now().toISOString();
|
|
@@ -38371,9 +38617,9 @@ function normalizeProposalLifecycleResult(value) {
|
|
|
38371
38617
|
return JSON.parse(JSON.stringify(value));
|
|
38372
38618
|
}
|
|
38373
38619
|
// src/sac/collaboration-service.ts
|
|
38374
|
-
import { appendFile as appendFile8, mkdir as mkdir35, readFile as
|
|
38620
|
+
import { appendFile as appendFile8, mkdir as mkdir35, readFile as readFile64 } from "fs/promises";
|
|
38375
38621
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
38376
|
-
import
|
|
38622
|
+
import path120 from "path";
|
|
38377
38623
|
class CollaborationServiceError extends Error {
|
|
38378
38624
|
code;
|
|
38379
38625
|
constructor(code, message) {
|
|
@@ -38388,7 +38634,7 @@ class CollaborationService {
|
|
|
38388
38634
|
this.input = input;
|
|
38389
38635
|
}
|
|
38390
38636
|
file(id) {
|
|
38391
|
-
return
|
|
38637
|
+
return path120.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
38392
38638
|
}
|
|
38393
38639
|
async overview(input) {
|
|
38394
38640
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -38397,7 +38643,7 @@ class CollaborationService {
|
|
|
38397
38643
|
async activity(input) {
|
|
38398
38644
|
await this.input.workspaces.show(input);
|
|
38399
38645
|
try {
|
|
38400
|
-
return (await
|
|
38646
|
+
return (await readFile64(this.file(input.workspaceId), "utf8")).trim().split(`
|
|
38401
38647
|
`).filter(Boolean).map((line) => this.validate(JSON.parse(line)));
|
|
38402
38648
|
} catch (error) {
|
|
38403
38649
|
if (typeof error === "object" && error && "code" in error && error.code === "ENOENT")
|
|
@@ -38425,7 +38671,7 @@ class CollaborationService {
|
|
|
38425
38671
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
38426
38672
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
38427
38673
|
});
|
|
38428
|
-
await mkdir35(
|
|
38674
|
+
await mkdir35(path120.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
38429
38675
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
38430
38676
|
`, { mode: 384 });
|
|
38431
38677
|
return normalizeCollaborationResult(activity);
|
|
@@ -38474,14 +38720,14 @@ function normalizeCollaborationResult(value) {
|
|
|
38474
38720
|
}
|
|
38475
38721
|
// src/session/external-slate.ts
|
|
38476
38722
|
init_fs();
|
|
38477
|
-
import { mkdir as mkdir37, readdir as
|
|
38478
|
-
import
|
|
38723
|
+
import { mkdir as mkdir37, readdir as readdir21, readFile as readFile65 } from "fs/promises";
|
|
38724
|
+
import path122 from "path";
|
|
38479
38725
|
|
|
38480
38726
|
// src/sac/machine-wrap-up.ts
|
|
38481
38727
|
init_fs();
|
|
38482
38728
|
import { createHash as createHash30, randomUUID as randomUUID10 } from "crypto";
|
|
38483
38729
|
import { mkdir as mkdir36 } from "fs/promises";
|
|
38484
|
-
import
|
|
38730
|
+
import path121 from "path";
|
|
38485
38731
|
|
|
38486
38732
|
// src/sac/workspace-resolve.ts
|
|
38487
38733
|
init_redact();
|
|
@@ -38622,11 +38868,11 @@ async function resolveMachineWrapUp(input) {
|
|
|
38622
38868
|
`;
|
|
38623
38869
|
const seedsJson = `${JSON.stringify(seedsForKind.map((seed) => ({ text: seed.text, source: describeSource(seed.source) })), null, 2)}
|
|
38624
38870
|
`;
|
|
38625
|
-
const evidenceRelBase =
|
|
38871
|
+
const evidenceRelBase = path121.posix.join(".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
38626
38872
|
const floor = await applyEvidenceRedactionFloor(input.cwd, [
|
|
38627
|
-
{ name: `${input.kind}.diff.txt`, content: diffBody, path:
|
|
38628
|
-
{ name: `${input.kind}.flow.json`, content: flowSnapshotJson, path:
|
|
38629
|
-
{ name: `${input.kind}.seeds.json`, content: seedsJson, path:
|
|
38873
|
+
{ name: `${input.kind}.diff.txt`, content: diffBody, path: path121.posix.join(evidenceRelBase, `${input.kind}.diff.txt`), source: "tool-output" },
|
|
38874
|
+
{ name: `${input.kind}.flow.json`, content: flowSnapshotJson, path: path121.posix.join(evidenceRelBase, `${input.kind}.flow.json`), source: "generated" },
|
|
38875
|
+
{ name: `${input.kind}.seeds.json`, content: seedsJson, path: path121.posix.join(evidenceRelBase, `${input.kind}.seeds.json`), source: "generated" }
|
|
38630
38876
|
]);
|
|
38631
38877
|
if (!floor.ok)
|
|
38632
38878
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
@@ -38680,14 +38926,14 @@ ${safeSeeds.content}`;
|
|
|
38680
38926
|
}
|
|
38681
38927
|
summary = result.text.trim().length > 0 ? result.text.trim() : mechanicalSummary(summaryDiff, course);
|
|
38682
38928
|
}
|
|
38683
|
-
const evidenceDir =
|
|
38929
|
+
const evidenceDir = path121.join(input.cwd, ".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
38684
38930
|
await mkdir36(evidenceDir, { recursive: true });
|
|
38685
38931
|
const diffFile = `${input.kind}.${shortHash}.diff.txt`;
|
|
38686
38932
|
const flowFile = `${input.kind}.${shortHash}.flow.json`;
|
|
38687
38933
|
const seedsFile = `${input.kind}.${shortHash}.seeds.json`;
|
|
38688
|
-
await writeFileAtomic(
|
|
38689
|
-
await writeFileAtomic(
|
|
38690
|
-
await writeFileAtomic(
|
|
38934
|
+
await writeFileAtomic(path121.join(evidenceDir, diffFile), safeDiff.content);
|
|
38935
|
+
await writeFileAtomic(path121.join(evidenceDir, flowFile), safeFlow.content);
|
|
38936
|
+
await writeFileAtomic(path121.join(evidenceDir, seedsFile), safeSeeds.content);
|
|
38691
38937
|
const observedAt = now().toISOString();
|
|
38692
38938
|
const relBase = `./.metaproject/workspaces/${input.workspaceId}/machine-evidence`;
|
|
38693
38939
|
const evidence = [
|
|
@@ -38697,7 +38943,7 @@ ${safeSeeds.content}`;
|
|
|
38697
38943
|
];
|
|
38698
38944
|
if (floor.notice !== undefined) {
|
|
38699
38945
|
const noticeFile = `${input.kind}.${shortHash}.redaction.md`;
|
|
38700
|
-
await writeFileAtomic(
|
|
38946
|
+
await writeFileAtomic(path121.join(evidenceDir, noticeFile), floor.notice);
|
|
38701
38947
|
evidence.push({ kind: REDACTION_NOTICE_KIND, uri: `${relBase}/${noticeFile}`, revision: sha2566(floor.notice), observedAt });
|
|
38702
38948
|
}
|
|
38703
38949
|
return {
|
|
@@ -38716,18 +38962,18 @@ async function writeFlooredArchiveArtifact(params) {
|
|
|
38716
38962
|
{
|
|
38717
38963
|
name: params.filename,
|
|
38718
38964
|
content: params.content,
|
|
38719
|
-
path:
|
|
38965
|
+
path: path121.posix.join("slate-archive", params.filename),
|
|
38720
38966
|
source: "generated"
|
|
38721
38967
|
}
|
|
38722
38968
|
]);
|
|
38723
38969
|
if (!floor.ok)
|
|
38724
38970
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
38725
38971
|
const body = floor.bodies[0];
|
|
38726
|
-
const archiveDir =
|
|
38972
|
+
const archiveDir = path121.join(params.dir, "slate-archive");
|
|
38727
38973
|
await mkdir36(archiveDir, { recursive: true });
|
|
38728
|
-
await writeFileAtomic(
|
|
38974
|
+
await writeFileAtomic(path121.join(archiveDir, params.filename), body.content);
|
|
38729
38975
|
if (floor.notice !== undefined) {
|
|
38730
|
-
await writeFileAtomic(
|
|
38976
|
+
await writeFileAtomic(path121.join(archiveDir, `${params.filename}${ARCHIVE_REDACTION_NOTICE_SUFFIX}`), floor.notice);
|
|
38731
38977
|
}
|
|
38732
38978
|
return { ok: true, altered: body.altered };
|
|
38733
38979
|
}
|
|
@@ -38763,9 +39009,9 @@ async function writeWrapUpOutcomeArtifact(cwd, dir, trigger, now, groups) {
|
|
|
38763
39009
|
return;
|
|
38764
39010
|
const refusalIso = now().toISOString();
|
|
38765
39011
|
const refusalFilename = `${refusalIso.replace(/[:.]/g, "-")}-wrap-up-outcome.json`;
|
|
38766
|
-
const archiveDir =
|
|
39012
|
+
const archiveDir = path121.join(dir, "slate-archive");
|
|
38767
39013
|
await mkdir36(archiveDir, { recursive: true });
|
|
38768
|
-
await writeFileAtomic(
|
|
39014
|
+
await writeFileAtomic(path121.join(archiveDir, refusalFilename), `${JSON.stringify({
|
|
38769
39015
|
recordType: "wrap-up-outcome",
|
|
38770
39016
|
trigger,
|
|
38771
39017
|
generatedAt: refusalIso,
|
|
@@ -38901,7 +39147,7 @@ async function runWrapUp(input) {
|
|
|
38901
39147
|
|
|
38902
39148
|
// src/session/external-slate.ts
|
|
38903
39149
|
function externalSlatesDir(cwd) {
|
|
38904
|
-
return
|
|
39150
|
+
return path122.join(cwd, ".keryx", "external-slates");
|
|
38905
39151
|
}
|
|
38906
39152
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
38907
39153
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -38911,18 +39157,18 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
38911
39157
|
}
|
|
38912
39158
|
function externalSlatePath(cwd, externalSessionId) {
|
|
38913
39159
|
assertValidExternalSessionId(externalSessionId);
|
|
38914
|
-
return
|
|
39160
|
+
return path122.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
38915
39161
|
}
|
|
38916
39162
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
38917
39163
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
38918
39164
|
}
|
|
38919
39165
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
38920
39166
|
assertValidExternalSessionId(externalSessionId);
|
|
38921
|
-
return
|
|
39167
|
+
return path122.join(externalSlatesDir(cwd), externalSessionId);
|
|
38922
39168
|
}
|
|
38923
39169
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
38924
39170
|
try {
|
|
38925
|
-
const raw = await
|
|
39171
|
+
const raw = await readFile65(externalSlatePath(cwd, externalSessionId), "utf8");
|
|
38926
39172
|
return JSON.parse(raw);
|
|
38927
39173
|
} catch (error) {
|
|
38928
39174
|
if (isNotFound(error))
|
|
@@ -38942,7 +39188,7 @@ async function writeExternalSlate(cwd, externalSessionId, update) {
|
|
|
38942
39188
|
}
|
|
38943
39189
|
async function listExternalSlateIds(cwd) {
|
|
38944
39190
|
try {
|
|
38945
|
-
const entries = await
|
|
39191
|
+
const entries = await readdir21(externalSlatesDir(cwd));
|
|
38946
39192
|
return entries.filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -".json".length));
|
|
38947
39193
|
} catch (error) {
|
|
38948
39194
|
if (isNotFound(error))
|
|
@@ -39024,13 +39270,13 @@ __export(exports_service10, {
|
|
|
39024
39270
|
runDoctor: () => runDoctor,
|
|
39025
39271
|
runValidate: () => runValidate
|
|
39026
39272
|
});
|
|
39027
|
-
import
|
|
39028
|
-
import { readFile as
|
|
39273
|
+
import path125 from "path";
|
|
39274
|
+
import { readFile as readFile68 } from "fs/promises";
|
|
39029
39275
|
|
|
39030
39276
|
// src/standard/validate.ts
|
|
39031
39277
|
init_fs();
|
|
39032
|
-
import
|
|
39033
|
-
import { readFile as
|
|
39278
|
+
import path124 from "path";
|
|
39279
|
+
import { readFile as readFile67 } from "fs/promises";
|
|
39034
39280
|
|
|
39035
39281
|
// src/standard/schemas.ts
|
|
39036
39282
|
var MODULE_SCHEMA = {
|
|
@@ -39159,8 +39405,8 @@ var SCHEMA_REGISTRY2 = {
|
|
|
39159
39405
|
// src/standard/profiles.ts
|
|
39160
39406
|
init_fs();
|
|
39161
39407
|
init_entrypoint_targets();
|
|
39162
|
-
import
|
|
39163
|
-
import { readFile as
|
|
39408
|
+
import path123 from "path";
|
|
39409
|
+
import { readFile as readFile66 } from "fs/promises";
|
|
39164
39410
|
var AGENT_MODULES = ["gdgraph", "gdctx", "gdskills", "gdwiki", "memory"];
|
|
39165
39411
|
var CI_MODULES = ["health", "testing"];
|
|
39166
39412
|
var INDEX_LINK = ".metaproject/index.md";
|
|
@@ -39184,11 +39430,11 @@ function enabledModuleKeys(manifest) {
|
|
|
39184
39430
|
}
|
|
39185
39431
|
async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
39186
39432
|
for (const { path: name } of namedEntrypoints(manifest)) {
|
|
39187
|
-
const filePath =
|
|
39433
|
+
const filePath = path123.join(cwd, name);
|
|
39188
39434
|
if (!await pathExists(filePath)) {
|
|
39189
39435
|
continue;
|
|
39190
39436
|
}
|
|
39191
|
-
const content = await
|
|
39437
|
+
const content = await readFile66(filePath, "utf8");
|
|
39192
39438
|
if (content.includes(INDEX_LINK)) {
|
|
39193
39439
|
return true;
|
|
39194
39440
|
}
|
|
@@ -39196,16 +39442,16 @@ async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
|
39196
39442
|
return false;
|
|
39197
39443
|
}
|
|
39198
39444
|
async function minimalSatisfied(cwd) {
|
|
39199
|
-
const metaprojectRoot =
|
|
39445
|
+
const metaprojectRoot = path123.join(cwd, ".metaproject");
|
|
39200
39446
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
39201
39447
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
39202
39448
|
for (const file of requiredFiles) {
|
|
39203
|
-
if (!await pathExists(
|
|
39449
|
+
if (!await pathExists(path123.join(metaprojectRoot, file))) {
|
|
39204
39450
|
return false;
|
|
39205
39451
|
}
|
|
39206
39452
|
}
|
|
39207
39453
|
for (const dir of requiredDirs) {
|
|
39208
|
-
if (!await pathExists(
|
|
39454
|
+
if (!await pathExists(path123.join(metaprojectRoot, dir))) {
|
|
39209
39455
|
return false;
|
|
39210
39456
|
}
|
|
39211
39457
|
}
|
|
@@ -39218,7 +39464,7 @@ async function ciSatisfied(cwd, manifest) {
|
|
|
39218
39464
|
return false;
|
|
39219
39465
|
}
|
|
39220
39466
|
for (const key of ciModules) {
|
|
39221
|
-
if (await pathExists(
|
|
39467
|
+
if (await pathExists(path123.join(cwd, ".metaproject", "data", key, "artifacts"))) {
|
|
39222
39468
|
return true;
|
|
39223
39469
|
}
|
|
39224
39470
|
}
|
|
@@ -39231,7 +39477,7 @@ async function evaluateProfiles(cwd, manifest) {
|
|
|
39231
39477
|
satisfied.push("minimal");
|
|
39232
39478
|
}
|
|
39233
39479
|
const hasAgentModule = AGENT_MODULES.some((key) => new Set(enabledModuleKeys(manifest)).has(key));
|
|
39234
|
-
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(
|
|
39480
|
+
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(path123.join(cwd, ".metaproject", "rules"));
|
|
39235
39481
|
if (agent) {
|
|
39236
39482
|
satisfied.push("agent");
|
|
39237
39483
|
}
|
|
@@ -39400,26 +39646,26 @@ var MANIFEST_TOP_LEVEL_SCHEMA = {
|
|
|
39400
39646
|
async function validateWorkspace2(cwd) {
|
|
39401
39647
|
const errors = [];
|
|
39402
39648
|
const warnings = [];
|
|
39403
|
-
const metaprojectRoot =
|
|
39649
|
+
const metaprojectRoot = path124.join(cwd, ".metaproject");
|
|
39404
39650
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
39405
39651
|
for (const file of requiredFiles) {
|
|
39406
|
-
if (!await pathExists(
|
|
39652
|
+
if (!await pathExists(path124.join(metaprojectRoot, file))) {
|
|
39407
39653
|
errors.push(issue("missing-required-file", `Required file .metaproject/${file} is missing`, `Run \`keryx init\` to scaffold .metaproject/${file}.`));
|
|
39408
39654
|
}
|
|
39409
39655
|
}
|
|
39410
39656
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
39411
39657
|
for (const dir of requiredDirs) {
|
|
39412
|
-
if (!await pathExists(
|
|
39658
|
+
if (!await pathExists(path124.join(metaprojectRoot, dir))) {
|
|
39413
39659
|
errors.push(issue("missing-required-dir", `Required directory .metaproject/${dir}/ is missing`, `Run \`keryx init\` to create .metaproject/${dir}/.`));
|
|
39414
39660
|
}
|
|
39415
39661
|
}
|
|
39416
|
-
const manifestPath =
|
|
39662
|
+
const manifestPath = path124.join(metaprojectRoot, "metaproject.json");
|
|
39417
39663
|
if (!await pathExists(manifestPath)) {
|
|
39418
39664
|
return { ok: errors.length === 0, errors, warnings };
|
|
39419
39665
|
}
|
|
39420
39666
|
let manifest;
|
|
39421
39667
|
try {
|
|
39422
|
-
manifest = JSON.parse(await
|
|
39668
|
+
manifest = JSON.parse(await readFile67(manifestPath, "utf8"));
|
|
39423
39669
|
} catch (error) {
|
|
39424
39670
|
const detail = error instanceof Error ? error.message : String(error);
|
|
39425
39671
|
errors.push(issue("invalid-manifest-json", `.metaproject/metaproject.json is not valid JSON: ${detail}`, "Fix the JSON syntax or regenerate it with `keryx init`."));
|
|
@@ -39445,7 +39691,7 @@ async function validateWorkspace2(cwd) {
|
|
|
39445
39691
|
if (typeof value !== "string") {
|
|
39446
39692
|
continue;
|
|
39447
39693
|
}
|
|
39448
|
-
if (!await pathExists(
|
|
39694
|
+
if (!await pathExists(path124.join(cwd, value))) {
|
|
39449
39695
|
errors.push(issue("missing-declared-path", `Declared path paths.${key} -> ${value} does not exist`, `Create ${value} or remove paths.${key} from metaproject.json.`));
|
|
39450
39696
|
}
|
|
39451
39697
|
}
|
|
@@ -39455,7 +39701,7 @@ async function validateWorkspace2(cwd) {
|
|
|
39455
39701
|
continue;
|
|
39456
39702
|
}
|
|
39457
39703
|
if (typeof entry.manifest === "string") {
|
|
39458
|
-
if (!await pathExists(
|
|
39704
|
+
if (!await pathExists(path124.join(cwd, entry.manifest))) {
|
|
39459
39705
|
errors.push(issue("missing-module-manifest", `Enabled module "${key}" manifest ${entry.manifest} is missing`, `Create ${entry.manifest} or run \`keryx update\` to regenerate module manifests.`));
|
|
39460
39706
|
}
|
|
39461
39707
|
}
|
|
@@ -39464,18 +39710,18 @@ async function validateWorkspace2(cwd) {
|
|
|
39464
39710
|
if (typeof value !== "string") {
|
|
39465
39711
|
continue;
|
|
39466
39712
|
}
|
|
39467
|
-
if (!await pathExists(
|
|
39713
|
+
if (!await pathExists(path124.join(cwd, value))) {
|
|
39468
39714
|
const sink = field === "data" ? warnings : errors;
|
|
39469
39715
|
sink.push(issue("missing-module-path", `Module "${key}" declares ${String(field)} ${value}, which does not exist`, field === "data" ? `Run the module (e.g. \`keryx ${key} ...\`) to generate ${value}, or update modules.${key}.data in metaproject.json.` : `Create ${value} or update modules.${key}.${String(field)} in metaproject.json.`));
|
|
39470
39716
|
}
|
|
39471
39717
|
}
|
|
39472
39718
|
}
|
|
39473
39719
|
for (const { path: name, scope } of namedEntrypoints(manifest)) {
|
|
39474
|
-
const filePath =
|
|
39720
|
+
const filePath = path124.join(cwd, name);
|
|
39475
39721
|
if (!await pathExists(filePath)) {
|
|
39476
39722
|
continue;
|
|
39477
39723
|
}
|
|
39478
|
-
const content = await
|
|
39724
|
+
const content = await readFile67(filePath, "utf8");
|
|
39479
39725
|
if (!content.includes(INDEX_LINK2)) {
|
|
39480
39726
|
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).`));
|
|
39481
39727
|
}
|
|
@@ -40381,12 +40627,12 @@ async function runDoctor(cwd) {
|
|
|
40381
40627
|
return validateWorkspace2(cwd);
|
|
40382
40628
|
}
|
|
40383
40629
|
async function readManifest(cwd) {
|
|
40384
|
-
const manifestPath =
|
|
40630
|
+
const manifestPath = path125.join(cwd, ".metaproject", "metaproject.json");
|
|
40385
40631
|
if (!await pathExists(manifestPath)) {
|
|
40386
40632
|
throw new Error("Metaproject is not initialized (missing .metaproject/metaproject.json). Run `keryx init` first.");
|
|
40387
40633
|
}
|
|
40388
40634
|
try {
|
|
40389
|
-
return JSON.parse(await
|
|
40635
|
+
return JSON.parse(await readFile68(manifestPath, "utf8"));
|
|
40390
40636
|
} catch (error) {
|
|
40391
40637
|
const detail = error instanceof Error ? error.message : String(error);
|
|
40392
40638
|
throw new Error(`Invalid .metaproject/metaproject.json: ${detail}`);
|