@mrciphersmith/keryx 0.3.52 → 0.3.54
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 +7140 -6646
- package/dist/core.js +971 -711
- package/package.json +1 -1
- package/src/gdskills/bundled/skills/core/reviewer-skill-creator/SKILL.detail.md +9 -7
- 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,179 @@ 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 rankOfRef(ref, headTargets) {
|
|
26322
|
+
if (/^refs\/remotes\/[^/]+\/(main|master)$/.test(ref)) {
|
|
26323
|
+
return 0;
|
|
26324
|
+
}
|
|
26325
|
+
return headTargets.has(ref) ? 1 : 2;
|
|
26326
|
+
}
|
|
26327
|
+
function orderRemoteRefs(entries, max = MAX_REFS) {
|
|
26328
|
+
const headTargets = new Set(entries.map((entry) => entry.symref).filter((symref) => symref.length > 0));
|
|
26329
|
+
const rank = (ref) => rankOfRef(ref, headTargets);
|
|
26330
|
+
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));
|
|
26331
|
+
const seenTips = new Set;
|
|
26332
|
+
const refs = [];
|
|
26333
|
+
for (const entry of candidates) {
|
|
26334
|
+
if (seenTips.has(entry.tip)) {
|
|
26335
|
+
continue;
|
|
26336
|
+
}
|
|
26337
|
+
seenTips.add(entry.tip);
|
|
26338
|
+
refs.push(entry.ref);
|
|
26339
|
+
if (refs.length >= max) {
|
|
26340
|
+
break;
|
|
26341
|
+
}
|
|
26342
|
+
}
|
|
26343
|
+
return refs;
|
|
26344
|
+
}
|
|
26345
|
+
async function remoteRefs(cwd) {
|
|
26346
|
+
const result = await runGit2(cwd, ["for-each-ref", "--format=%(objectname) %(refname) %(symref)", "refs/remotes/"]);
|
|
26347
|
+
if (result === undefined || result.code !== 0) {
|
|
26348
|
+
return [];
|
|
26349
|
+
}
|
|
26350
|
+
const entries = [];
|
|
26351
|
+
for (const line of lines(result.stdout)) {
|
|
26352
|
+
const [tip, ref, symref] = line.split(" ");
|
|
26353
|
+
if (tip === undefined || ref === undefined) {
|
|
26354
|
+
continue;
|
|
26355
|
+
}
|
|
26356
|
+
entries.push({ tip, ref, symref: symref ?? "" });
|
|
26357
|
+
}
|
|
26358
|
+
const headTargets = new Set(entries.map((entry) => entry.symref).filter((symref) => symref.length > 0));
|
|
26359
|
+
return orderRemoteRefs(entries).map((ref) => ({ ref, primary: rankOfRef(ref, headTargets) < 2 }));
|
|
26360
|
+
}
|
|
26361
|
+
async function flowDirsOnRef(cwd, ref, flowsPath) {
|
|
26362
|
+
const result = await runGit2(cwd, ["ls-tree", "-z", "-d", "--name-only", "--full-tree", ref, "--", `${flowsPath}/`]);
|
|
26363
|
+
if (result === undefined || result.code !== 0) {
|
|
26364
|
+
return [];
|
|
26365
|
+
}
|
|
26366
|
+
return nulSplit(result.stdout).map((entry) => path55.posix.basename(entry)).filter((name) => /^\d{3}-/.test(name));
|
|
26367
|
+
}
|
|
26368
|
+
async function knownRemoteFlowDirs(cwd) {
|
|
26369
|
+
try {
|
|
26370
|
+
const prefix = await showPrefix(cwd);
|
|
26371
|
+
if (prefix === null) {
|
|
26372
|
+
return [];
|
|
26373
|
+
}
|
|
26374
|
+
const refs = await remoteRefs(cwd);
|
|
26375
|
+
if (refs.length === 0) {
|
|
26376
|
+
return [];
|
|
26377
|
+
}
|
|
26378
|
+
const flowsPath = `${prefix}.metaproject/flows`;
|
|
26379
|
+
const found = new Array(refs.length).fill([]);
|
|
26380
|
+
let next = 0;
|
|
26381
|
+
const worker = async () => {
|
|
26382
|
+
while (next < refs.length) {
|
|
26383
|
+
const index = next;
|
|
26384
|
+
next += 1;
|
|
26385
|
+
const entry = refs[index];
|
|
26386
|
+
if (entry === undefined) {
|
|
26387
|
+
continue;
|
|
26388
|
+
}
|
|
26389
|
+
const short = entry.ref.slice("refs/remotes/".length);
|
|
26390
|
+
found[index] = (await flowDirsOnRef(cwd, entry.ref, flowsPath)).map((dir) => ({
|
|
26391
|
+
ref: short,
|
|
26392
|
+
dir,
|
|
26393
|
+
primary: entry.primary
|
|
26394
|
+
}));
|
|
26395
|
+
}
|
|
26396
|
+
};
|
|
26397
|
+
await Promise.all(Array.from({ length: Math.min(CONCURRENCY, refs.length) }, () => worker()));
|
|
26398
|
+
return found.flat();
|
|
26399
|
+
} catch {
|
|
26400
|
+
return [];
|
|
26401
|
+
}
|
|
26402
|
+
}
|
|
26403
|
+
function flowNumberOfDir(dir) {
|
|
26404
|
+
const dash = dir.indexOf("-");
|
|
26405
|
+
return dash > 0 ? Number(dir.slice(0, dash)) : Number.NaN;
|
|
26406
|
+
}
|
|
26407
|
+
async function remoteFlowNumbers(cwd) {
|
|
26408
|
+
const dirs = await knownRemoteFlowDirs(cwd);
|
|
26409
|
+
const numbers = new Set;
|
|
26410
|
+
for (const { dir } of dirs) {
|
|
26411
|
+
const value = flowNumberOfDir(dir);
|
|
26412
|
+
if (!Number.isNaN(value)) {
|
|
26413
|
+
numbers.add(value);
|
|
26414
|
+
}
|
|
26415
|
+
}
|
|
26416
|
+
return [...numbers].sort((a, b) => a - b);
|
|
26417
|
+
}
|
|
26418
|
+
|
|
26419
|
+
// src/flow/folder-committed.ts
|
|
26420
|
+
var BATCH = 100;
|
|
26421
|
+
async function flowFoldersInHead(cwd, dirs) {
|
|
26422
|
+
const prefix = await showPrefix(cwd);
|
|
26423
|
+
if (prefix === null) {
|
|
26424
|
+
return null;
|
|
26425
|
+
}
|
|
26426
|
+
const committed = new Set;
|
|
26427
|
+
if (dirs.length === 0) {
|
|
26428
|
+
return committed;
|
|
26429
|
+
}
|
|
26430
|
+
const head = await runGit2(cwd, ["rev-parse", "--verify", "-q", "HEAD"]);
|
|
26431
|
+
if (head === undefined) {
|
|
26432
|
+
return null;
|
|
26433
|
+
}
|
|
26434
|
+
if (head.code !== 0) {
|
|
26435
|
+
return committed;
|
|
26436
|
+
}
|
|
26437
|
+
for (let start = 0;start < dirs.length; start += BATCH) {
|
|
26438
|
+
const batch = dirs.slice(start, start + BATCH);
|
|
26439
|
+
const paths = batch.map((dir) => `${prefix}.metaproject/flows/${dir}/flow.json`);
|
|
26440
|
+
const result = await runGit2(cwd, ["ls-tree", "-z", "--name-only", "--full-tree", "HEAD", "--", ...paths]);
|
|
26441
|
+
if (result === undefined) {
|
|
26442
|
+
return null;
|
|
26443
|
+
}
|
|
26444
|
+
if (result.code !== 0) {
|
|
26445
|
+
throw new Error(`could not read HEAD tree (git ls-tree exited ${result.code}); not treating the flow folders as uncommitted`);
|
|
26446
|
+
}
|
|
26447
|
+
const found = new Set(nulSplit(result.stdout));
|
|
26448
|
+
for (const [index, dir] of batch.entries()) {
|
|
26449
|
+
if (found.has(paths[index] ?? "")) {
|
|
26450
|
+
committed.add(dir);
|
|
26451
|
+
}
|
|
26452
|
+
}
|
|
26453
|
+
}
|
|
26454
|
+
return committed;
|
|
26455
|
+
}
|
|
26456
|
+
|
|
26272
26457
|
// src/flow/templates.ts
|
|
26273
26458
|
function renderDescription(title, source) {
|
|
26274
26459
|
return `# ${title}
|
|
@@ -26353,7 +26538,7 @@ function renderJournal(createdAt) {
|
|
|
26353
26538
|
|
|
26354
26539
|
// src/flow/service.ts
|
|
26355
26540
|
async function deriveAcKinds(cwd, dir, flowId) {
|
|
26356
|
-
const { report } = buildAcKindReport(flowId, await
|
|
26541
|
+
const { report } = buildAcKindReport(flowId, await readFile28(acPath(cwd, dir), "utf8"));
|
|
26357
26542
|
return { ...report.criteria };
|
|
26358
26543
|
}
|
|
26359
26544
|
function prMergeState(pr, gates) {
|
|
@@ -26384,6 +26569,7 @@ function prMergeState(pr, gates) {
|
|
|
26384
26569
|
}
|
|
26385
26570
|
var UNCONFIRMED_PREFIX = "unconfirmed: ";
|
|
26386
26571
|
var NO_PR_DETAIL = "no PR recorded";
|
|
26572
|
+
var FOLDER_UNCOMMITTED_PREFIX = "flow folder ";
|
|
26387
26573
|
function completionAdmitted(status, merged) {
|
|
26388
26574
|
return canTransition(status, "completing") || merged && status === "in-progress";
|
|
26389
26575
|
}
|
|
@@ -26409,6 +26595,10 @@ function completionFixHint(gate, flowId) {
|
|
|
26409
26595
|
return "keryx health run";
|
|
26410
26596
|
case "confirmation":
|
|
26411
26597
|
return `keryx flow confirm ${flowId} (in a terminal)`;
|
|
26598
|
+
case "folder-committed": {
|
|
26599
|
+
const dir = gate.detail.startsWith(FOLDER_UNCOMMITTED_PREFIX) ? gate.detail.slice(FOLDER_UNCOMMITTED_PREFIX.length).split(" ")[0] : undefined;
|
|
26600
|
+
return dir ? `git add .metaproject/flows/${dir} && git commit` : undefined;
|
|
26601
|
+
}
|
|
26412
26602
|
default:
|
|
26413
26603
|
return;
|
|
26414
26604
|
}
|
|
@@ -26541,6 +26731,11 @@ function createFlowService(deps) {
|
|
|
26541
26731
|
}
|
|
26542
26732
|
gates.push(taskGate(flow));
|
|
26543
26733
|
gates.push(ownerGate(flow));
|
|
26734
|
+
try {
|
|
26735
|
+
gates.push(await folderCommittedGate(cwd, dir, flow));
|
|
26736
|
+
} catch {
|
|
26737
|
+
gates.push(unevaluableGate("folder-committed"));
|
|
26738
|
+
}
|
|
26544
26739
|
try {
|
|
26545
26740
|
gates.push(await reviewGate({
|
|
26546
26741
|
cwd,
|
|
@@ -26612,11 +26807,14 @@ function createFlowService(deps) {
|
|
|
26612
26807
|
const title = input.title ?? context.issueTitle ?? provisionalTitle;
|
|
26613
26808
|
const scope = await resolveAllocationScope(input.cwd);
|
|
26614
26809
|
return withFileLock(scope.lockPath, async () => {
|
|
26615
|
-
const id = await nextFlowId(input.cwd,
|
|
26810
|
+
const id = await nextFlowId(input.cwd, [
|
|
26811
|
+
...await reservedIds(scope),
|
|
26812
|
+
...await remoteFlowNumbers(input.cwd)
|
|
26813
|
+
]);
|
|
26616
26814
|
const date = now().slice(0, 10);
|
|
26617
26815
|
const slug = slugify(input.slug ?? title);
|
|
26618
26816
|
const dir = `${id}-${date}-${slug}`;
|
|
26619
|
-
const absolute =
|
|
26817
|
+
const absolute = path56.join(flowsRoot(input.cwd), dir);
|
|
26620
26818
|
if (await pathExists(absolute)) {
|
|
26621
26819
|
throw new Error(`Flow directory already exists: ${dir}`);
|
|
26622
26820
|
}
|
|
@@ -26628,6 +26826,7 @@ function createFlowService(deps) {
|
|
|
26628
26826
|
tasks: true,
|
|
26629
26827
|
review: true,
|
|
26630
26828
|
owner: true,
|
|
26829
|
+
folderCommitted: true,
|
|
26631
26830
|
...input.requireConfirmation === true || await readRequireConfirmationDefault(input.cwd) ? { confirmation: true } : {}
|
|
26632
26831
|
},
|
|
26633
26832
|
id,
|
|
@@ -26650,15 +26849,15 @@ function createFlowService(deps) {
|
|
|
26650
26849
|
history: [{ at: createdAt, event: "created" }]
|
|
26651
26850
|
};
|
|
26652
26851
|
const sourceLabel = input.issue ?? "user description";
|
|
26653
|
-
await writeFileAtomic(
|
|
26654
|
-
await writeFileAtomic(
|
|
26655
|
-
await writeFileAtomic(
|
|
26656
|
-
await writeFileAtomic(
|
|
26657
|
-
await writeFileAtomic(
|
|
26658
|
-
await writeFileAtomic(
|
|
26852
|
+
await writeFileAtomic(path56.join(absolute, "description.md"), renderDescription(title, sourceLabel));
|
|
26853
|
+
await writeFileAtomic(path56.join(absolute, "context.md"), context.markdown);
|
|
26854
|
+
await writeFileAtomic(path56.join(absolute, "plan.md"), renderPlan());
|
|
26855
|
+
await writeFileAtomic(path56.join(absolute, "tasks.md"), renderTasksDoc());
|
|
26856
|
+
await writeFileAtomic(path56.join(absolute, "acceptance-criteria.md"), renderAcceptanceCriteria());
|
|
26857
|
+
await writeFileAtomic(path56.join(absolute, "journal.md"), renderJournal(createdAt));
|
|
26659
26858
|
await writeFlow(input.cwd, dir, flow);
|
|
26660
26859
|
await recordAllocation(scope, { id, dir, at: createdAt, project: scope.project });
|
|
26661
|
-
return { flow, dir:
|
|
26860
|
+
return { flow, dir: path56.relative(input.cwd, absolute), contextNotes: context.notes };
|
|
26662
26861
|
});
|
|
26663
26862
|
},
|
|
26664
26863
|
async list({ cwd }) {
|
|
@@ -26883,7 +27082,7 @@ function createFlowService(deps) {
|
|
|
26883
27082
|
},
|
|
26884
27083
|
async acKinds({ cwd, id }) {
|
|
26885
27084
|
const { dir, flow } = await load(cwd, id);
|
|
26886
|
-
return buildAcKindReport(flow.id, await
|
|
27085
|
+
return buildAcKindReport(flow.id, await readFile28(acPath(cwd, dir), "utf8"));
|
|
26887
27086
|
},
|
|
26888
27087
|
async acReseal({ cwd, id, reason }) {
|
|
26889
27088
|
if (!reason?.trim()) {
|
|
@@ -27141,6 +27340,10 @@ function createFlowService(deps) {
|
|
|
27141
27340
|
if ((await reservedIds(scope)).includes(Number(to))) {
|
|
27142
27341
|
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
27342
|
}
|
|
27343
|
+
const remoteUse = (await knownRemoteFlowDirs(cwd)).find((entry) => flowNumberOfDir(entry.dir) === Number(to));
|
|
27344
|
+
if (remoteUse) {
|
|
27345
|
+
throw new Error(`Flow id ${to} is already used on ${remoteUse.ref} (${safeDirName(remoteUse.dir)}) and cannot be reused. ` + "Pick a fresh number.");
|
|
27346
|
+
}
|
|
27144
27347
|
const toDir = `${to}${fromDir.slice(3)}`;
|
|
27145
27348
|
const { flow, reviewRecords } = await withFileLock(flowLockPath(cwd, fromDir), async () => {
|
|
27146
27349
|
const reviewRecords = await moveFlowDirWithReviewRecords({ cwd, from, to, fromDir, toDir });
|
|
@@ -27176,6 +27379,42 @@ function createFlowService(deps) {
|
|
|
27176
27379
|
});
|
|
27177
27380
|
}
|
|
27178
27381
|
}
|
|
27382
|
+
const warnings = [];
|
|
27383
|
+
const remoteDirs = await knownRemoteFlowDirs(cwd);
|
|
27384
|
+
if (remoteDirs.length > 0) {
|
|
27385
|
+
for (const dir of allDirs) {
|
|
27386
|
+
const clashes = remoteDirs.filter((entry) => entry.dir !== dir && flowNumberOfDir(entry.dir) === Number(flowIdOf(dir)));
|
|
27387
|
+
const primary = clashes.find((entry) => entry.primary);
|
|
27388
|
+
const other = clashes[0];
|
|
27389
|
+
if (primary) {
|
|
27390
|
+
issues.push({
|
|
27391
|
+
flow: dir,
|
|
27392
|
+
kind: "duplicate-id",
|
|
27393
|
+
message: `flow id ${flowIdOf(dir)} is also used on ${primary.ref} by a different flow (${safeDirName(primary.dir)})` + ` \u2014 repair with: keryx flow renumber ${dir} --to <free id> --reason "<why>"`
|
|
27394
|
+
});
|
|
27395
|
+
} else if (other) {
|
|
27396
|
+
warnings.push({
|
|
27397
|
+
flow: dir,
|
|
27398
|
+
kind: "branch-duplicate-id",
|
|
27399
|
+
message: `flow id ${flowIdOf(dir)} is also used on ${other.ref} by a different flow (${safeDirName(other.dir)});` + ` ignore it if that branch is stale, otherwise renumber one of them before it merges`
|
|
27400
|
+
});
|
|
27401
|
+
}
|
|
27402
|
+
}
|
|
27403
|
+
}
|
|
27404
|
+
const inHead = await flowFoldersInHead(cwd, allDirs).catch(() => {
|
|
27405
|
+
return null;
|
|
27406
|
+
});
|
|
27407
|
+
if (inHead !== null) {
|
|
27408
|
+
for (const dir of allDirs) {
|
|
27409
|
+
if (!inHead.has(dir)) {
|
|
27410
|
+
warnings.push({
|
|
27411
|
+
flow: dir,
|
|
27412
|
+
kind: "untracked",
|
|
27413
|
+
message: `flow folder ${dir} is not committed: commit it in the same PR as the code`
|
|
27414
|
+
});
|
|
27415
|
+
}
|
|
27416
|
+
}
|
|
27417
|
+
}
|
|
27179
27418
|
for (const dir of allDirs) {
|
|
27180
27419
|
let flow;
|
|
27181
27420
|
try {
|
|
@@ -27192,7 +27431,7 @@ function createFlowService(deps) {
|
|
|
27192
27431
|
issues.push({ flow: dir, kind: "schema", message: `unknown schemaVersion ${flow.schemaVersion}` });
|
|
27193
27432
|
}
|
|
27194
27433
|
try {
|
|
27195
|
-
const raw = JSON.parse(await Bun.file(
|
|
27434
|
+
const raw = JSON.parse(await Bun.file(path56.join(flowsRoot(cwd), dir, "flow.json")).text());
|
|
27196
27435
|
const validation = validateAgainstSchemaObject(flowStateSchema(), raw);
|
|
27197
27436
|
if (!validation.valid) {
|
|
27198
27437
|
issues.push({
|
|
@@ -27203,7 +27442,7 @@ function createFlowService(deps) {
|
|
|
27203
27442
|
}
|
|
27204
27443
|
} catch {}
|
|
27205
27444
|
for (const file of ["description.md", "context.md", "plan.md", "tasks.md", "acceptance-criteria.md", "journal.md"]) {
|
|
27206
|
-
if (!await pathExists(
|
|
27445
|
+
if (!await pathExists(path56.join(flowsRoot(cwd), dir, file))) {
|
|
27207
27446
|
issues.push({ flow: dir, kind: "structure", message: `missing ${file}` });
|
|
27208
27447
|
}
|
|
27209
27448
|
}
|
|
@@ -27237,15 +27476,15 @@ function createFlowService(deps) {
|
|
|
27237
27476
|
});
|
|
27238
27477
|
}
|
|
27239
27478
|
}
|
|
27240
|
-
return { ok: issues.length === 0, issues };
|
|
27479
|
+
return { ok: issues.length === 0, issues, warnings };
|
|
27241
27480
|
}
|
|
27242
27481
|
};
|
|
27243
27482
|
}
|
|
27244
27483
|
async function appendIdMap(cwd, entry) {
|
|
27245
|
-
const file =
|
|
27484
|
+
const file = path56.join(flowsRoot(cwd), "id-map.json");
|
|
27246
27485
|
let entries = [];
|
|
27247
27486
|
if (await pathExists(file)) {
|
|
27248
|
-
const raw = await
|
|
27487
|
+
const raw = await readFile28(file, "utf8");
|
|
27249
27488
|
try {
|
|
27250
27489
|
const parsed = JSON.parse(raw);
|
|
27251
27490
|
if (!Array.isArray(parsed)) {
|
|
@@ -27253,7 +27492,7 @@ async function appendIdMap(cwd, entry) {
|
|
|
27253
27492
|
}
|
|
27254
27493
|
entries = parsed;
|
|
27255
27494
|
} catch (error) {
|
|
27256
|
-
throw new Error(`Cannot read ${
|
|
27495
|
+
throw new Error(`Cannot read ${path56.relative(cwd, file)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
27257
27496
|
}
|
|
27258
27497
|
}
|
|
27259
27498
|
entries.push(entry);
|
|
@@ -27347,6 +27586,27 @@ function ownerGate(flow) {
|
|
|
27347
27586
|
}
|
|
27348
27587
|
return { name: "owner", status: "pass", detail: `owner: ${flow.owner.value}` };
|
|
27349
27588
|
}
|
|
27589
|
+
async function folderCommittedGate(cwd, dir, flow) {
|
|
27590
|
+
if (!flow.gates?.folderCommitted) {
|
|
27591
|
+
return {
|
|
27592
|
+
name: "folder-committed",
|
|
27593
|
+
status: "skipped",
|
|
27594
|
+
detail: "folder-committed gate not enabled for this package (created before the gate); " + "flows created by this keryx version opt in automatically"
|
|
27595
|
+
};
|
|
27596
|
+
}
|
|
27597
|
+
const committed = await flowFoldersInHead(cwd, [dir]);
|
|
27598
|
+
if (committed === null) {
|
|
27599
|
+
return { name: "folder-committed", status: "skipped", detail: "not a git repository; nothing to compare the flow folder with" };
|
|
27600
|
+
}
|
|
27601
|
+
if (!committed.has(dir)) {
|
|
27602
|
+
return {
|
|
27603
|
+
name: "folder-committed",
|
|
27604
|
+
status: "fail",
|
|
27605
|
+
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"
|
|
27606
|
+
};
|
|
27607
|
+
}
|
|
27608
|
+
return { name: "folder-committed", status: "pass", detail: `${dir} is committed in HEAD` };
|
|
27609
|
+
}
|
|
27350
27610
|
function confirmPreconditionError(flow, merged) {
|
|
27351
27611
|
const confirmable = flow.status === "implemented" || flow.status === "in-progress" && merged;
|
|
27352
27612
|
if (!confirmable) {
|
|
@@ -27504,9 +27764,9 @@ __export(exports_service5, {
|
|
|
27504
27764
|
});
|
|
27505
27765
|
|
|
27506
27766
|
// src/gdgraph/build.ts
|
|
27507
|
-
import { mkdir as mkdir21, readdir as
|
|
27767
|
+
import { mkdir as mkdir21, readdir as readdir14, readFile as readFile39, writeFile as writeFile15 } from "fs/promises";
|
|
27508
27768
|
import { existsSync as existsSync7 } from "fs";
|
|
27509
|
-
import
|
|
27769
|
+
import path76 from "path";
|
|
27510
27770
|
|
|
27511
27771
|
// src/gdgraph/types.ts
|
|
27512
27772
|
var UNKNOWN_IMPORT_KIND = "unknown-static";
|
|
@@ -27571,8 +27831,8 @@ async function buildGraph(projectRoot) {
|
|
|
27571
27831
|
const assetNodes = new Map;
|
|
27572
27832
|
const fileRecords = [];
|
|
27573
27833
|
for (const file of files) {
|
|
27574
|
-
const absolutePath =
|
|
27575
|
-
const content = await
|
|
27834
|
+
const absolutePath = path76.join(projectRoot, file);
|
|
27835
|
+
const content = await readFile39(absolutePath, "utf8");
|
|
27576
27836
|
fileRecords.push({ path: file, content });
|
|
27577
27837
|
const language = getLanguage(file);
|
|
27578
27838
|
const resolver = pickResolver(resolvers, language);
|
|
@@ -27624,24 +27884,24 @@ async function collectSourceFiles(projectRoot) {
|
|
|
27624
27884
|
const result = [];
|
|
27625
27885
|
const skippedDirectories = [];
|
|
27626
27886
|
async function walk(relativeDir) {
|
|
27627
|
-
const absoluteDir =
|
|
27628
|
-
const entries = await
|
|
27887
|
+
const absoluteDir = path76.join(projectRoot, relativeDir);
|
|
27888
|
+
const entries = await readdir14(absoluteDir, { withFileTypes: true });
|
|
27629
27889
|
for (const entry of entries) {
|
|
27630
27890
|
if (entry.isDirectory()) {
|
|
27631
27891
|
if (IGNORE_DIRS.has(entry.name)) {
|
|
27632
|
-
skippedDirectories.push(normalizePath2(
|
|
27892
|
+
skippedDirectories.push(normalizePath2(path76.posix.join(relativeDir, entry.name)).replace(/^\.\//, ""));
|
|
27633
27893
|
continue;
|
|
27634
27894
|
}
|
|
27635
|
-
await walk(normalizePath2(
|
|
27895
|
+
await walk(normalizePath2(path76.posix.join(relativeDir, entry.name)));
|
|
27636
27896
|
continue;
|
|
27637
27897
|
}
|
|
27638
27898
|
if (!entry.isFile()) {
|
|
27639
27899
|
continue;
|
|
27640
27900
|
}
|
|
27641
|
-
if (!SOURCE_EXTENSIONS.includes(
|
|
27901
|
+
if (!SOURCE_EXTENSIONS.includes(path76.extname(entry.name))) {
|
|
27642
27902
|
continue;
|
|
27643
27903
|
}
|
|
27644
|
-
result.push(normalizePath2(
|
|
27904
|
+
result.push(normalizePath2(path76.posix.join(relativeDir, entry.name)).replace(/^\.\//, ""));
|
|
27645
27905
|
}
|
|
27646
27906
|
}
|
|
27647
27907
|
await walk(".");
|
|
@@ -27723,8 +27983,8 @@ function resolveImport(projectRoot, fromFile, specifier, fileSet, resolver) {
|
|
|
27723
27983
|
}
|
|
27724
27984
|
function resolveAssetImport(projectRoot, fromFile, specifier, resolver) {
|
|
27725
27985
|
for (const candidate of importCandidateBases(projectRoot, fromFile, specifier, resolver)) {
|
|
27726
|
-
const extension =
|
|
27727
|
-
if (ASSET_EXTENSIONS.includes(extension) && existsSync7(
|
|
27986
|
+
const extension = path76.posix.extname(candidate);
|
|
27987
|
+
if (ASSET_EXTENSIONS.includes(extension) && existsSync7(path76.join(projectRoot, candidate))) {
|
|
27728
27988
|
return candidate;
|
|
27729
27989
|
}
|
|
27730
27990
|
}
|
|
@@ -27733,17 +27993,17 @@ function resolveAssetImport(projectRoot, fromFile, specifier, resolver) {
|
|
|
27733
27993
|
function importCandidateBases(projectRoot, fromFile, specifier, resolver) {
|
|
27734
27994
|
const normalizedSpecifier = stripImportSuffix(specifier);
|
|
27735
27995
|
if (normalizedSpecifier.startsWith(".") && getLanguage(fromFile) !== "python") {
|
|
27736
|
-
const fromDir =
|
|
27737
|
-
return [normalizePath2(
|
|
27996
|
+
const fromDir = path76.posix.dirname(fromFile);
|
|
27997
|
+
return [normalizePath2(path76.posix.normalize(path76.posix.join(fromDir, normalizedSpecifier)))];
|
|
27738
27998
|
}
|
|
27739
|
-
return resolver.candidateBases(normalizedSpecifier, fromFile).map((candidate) => normalizePath2(
|
|
27999
|
+
return resolver.candidateBases(normalizedSpecifier, fromFile).map((candidate) => normalizePath2(path76.relative(projectRoot, path76.join(projectRoot, candidate))));
|
|
27740
28000
|
}
|
|
27741
28001
|
function resolveSourceCandidate(base, fileSet) {
|
|
27742
28002
|
const candidates = [
|
|
27743
28003
|
base,
|
|
27744
28004
|
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) => `${base}${extension}`),
|
|
27745
|
-
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) =>
|
|
27746
|
-
|
|
28005
|
+
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) => path76.posix.join(base, `index${extension}`)),
|
|
28006
|
+
path76.posix.join(base, "__init__.py")
|
|
27747
28007
|
];
|
|
27748
28008
|
return candidates.find((candidate) => fileSet.has(candidate)) ?? null;
|
|
27749
28009
|
}
|
|
@@ -27773,20 +28033,20 @@ async function loadJavaResolver(projectRoot) {
|
|
|
27773
28033
|
return createJavaResolver(roots);
|
|
27774
28034
|
}
|
|
27775
28035
|
async function discoverJavaSourceRoots(projectRoot) {
|
|
27776
|
-
const pomPath =
|
|
28036
|
+
const pomPath = path76.join(projectRoot, "pom.xml");
|
|
27777
28037
|
if (existsSync7(pomPath)) {
|
|
27778
28038
|
try {
|
|
27779
|
-
const roots = parseMavenSourceRoots(await
|
|
28039
|
+
const roots = parseMavenSourceRoots(await readFile39(pomPath, "utf8"));
|
|
27780
28040
|
if (roots.length > 0) {
|
|
27781
28041
|
return roots;
|
|
27782
28042
|
}
|
|
27783
28043
|
} catch {}
|
|
27784
28044
|
}
|
|
27785
28045
|
for (const gradleName of ["build.gradle", "build.gradle.kts"]) {
|
|
27786
|
-
const gradlePath =
|
|
28046
|
+
const gradlePath = path76.join(projectRoot, gradleName);
|
|
27787
28047
|
if (existsSync7(gradlePath)) {
|
|
27788
28048
|
try {
|
|
27789
|
-
const roots = parseGradleSourceRoots(await
|
|
28049
|
+
const roots = parseGradleSourceRoots(await readFile39(gradlePath, "utf8"));
|
|
27790
28050
|
if (roots.length > 0) {
|
|
27791
28051
|
return roots;
|
|
27792
28052
|
}
|
|
@@ -27805,12 +28065,12 @@ function createJavaResolver(sourceRoots) {
|
|
|
27805
28065
|
return [];
|
|
27806
28066
|
}
|
|
27807
28067
|
const relative = specifier.split(".").join("/");
|
|
27808
|
-
return sourceRoots.map((root) => normalizePath2(
|
|
28068
|
+
return sourceRoots.map((root) => normalizePath2(path76.posix.join(root, relative)));
|
|
27809
28069
|
}
|
|
27810
28070
|
};
|
|
27811
28071
|
}
|
|
27812
28072
|
function loadPythonResolver(projectRoot) {
|
|
27813
|
-
const hasSrc = existsSync7(
|
|
28073
|
+
const hasSrc = existsSync7(path76.join(projectRoot, "src"));
|
|
27814
28074
|
return createPythonResolver(hasSrc);
|
|
27815
28075
|
}
|
|
27816
28076
|
function createPythonResolver(hasSrc) {
|
|
@@ -27823,16 +28083,16 @@ function createPythonResolver(hasSrc) {
|
|
|
27823
28083
|
if (specifier.startsWith(".")) {
|
|
27824
28084
|
const dots = /^\.+/.exec(specifier)?.[0].length ?? 0;
|
|
27825
28085
|
const rest = specifier.slice(dots);
|
|
27826
|
-
let baseDir =
|
|
28086
|
+
let baseDir = path76.posix.dirname(fromFile);
|
|
27827
28087
|
for (let level = 1;level < dots; level += 1) {
|
|
27828
|
-
baseDir =
|
|
28088
|
+
baseDir = path76.posix.dirname(baseDir);
|
|
27829
28089
|
}
|
|
27830
28090
|
const suffix = rest ? rest.split(".").join("/") : "";
|
|
27831
|
-
const base = suffix ?
|
|
28091
|
+
const base = suffix ? path76.posix.join(baseDir, suffix) : baseDir;
|
|
27832
28092
|
return [normalizePath2(base)];
|
|
27833
28093
|
}
|
|
27834
28094
|
const relative = specifier.split(".").join("/");
|
|
27835
|
-
return roots.map((root) => normalizePath2(root ?
|
|
28095
|
+
return roots.map((root) => normalizePath2(root ? path76.posix.join(root, relative) : relative));
|
|
27836
28096
|
}
|
|
27837
28097
|
};
|
|
27838
28098
|
}
|
|
@@ -27841,8 +28101,8 @@ function parseMavenSourceRoots(pomXml) {
|
|
|
27841
28101
|
for (const match of pomXml.matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
|
|
27842
28102
|
const moduleDir = match[1]?.trim();
|
|
27843
28103
|
if (moduleDir && !moduleDir.includes("${")) {
|
|
27844
|
-
roots.add(normalizePath2(
|
|
27845
|
-
roots.add(normalizePath2(
|
|
28104
|
+
roots.add(normalizePath2(path76.posix.join(moduleDir, "src/main/java")));
|
|
28105
|
+
roots.add(normalizePath2(path76.posix.join(moduleDir, "src/test/java")));
|
|
27846
28106
|
}
|
|
27847
28107
|
}
|
|
27848
28108
|
for (const tag of ["sourceDirectory", "testSourceDirectory"]) {
|
|
@@ -27875,7 +28135,7 @@ function parseGradleSourceRoots(buildGradle) {
|
|
|
27875
28135
|
var TSCONFIG_EXTENDS_MAX_DEPTH = 10;
|
|
27876
28136
|
async function loadTsconfigResolver(projectRoot) {
|
|
27877
28137
|
const empty = createTsconfigResolver(null, []);
|
|
27878
|
-
const tsconfigPath =
|
|
28138
|
+
const tsconfigPath = path76.join(projectRoot, "tsconfig.json");
|
|
27879
28139
|
if (!existsSync7(tsconfigPath)) {
|
|
27880
28140
|
return empty;
|
|
27881
28141
|
}
|
|
@@ -27888,12 +28148,12 @@ async function loadTsconfigResolver(projectRoot) {
|
|
|
27888
28148
|
return createTsconfigResolver(resolved.baseUrl, paths);
|
|
27889
28149
|
}
|
|
27890
28150
|
function toProjectRelativeDir(projectRoot, absoluteDir) {
|
|
27891
|
-
const relative = normalizePath2(
|
|
28151
|
+
const relative = normalizePath2(path76.relative(projectRoot, absoluteDir)).replace(/^\.\//, "");
|
|
27892
28152
|
return relative === "." ? "" : relative;
|
|
27893
28153
|
}
|
|
27894
28154
|
async function readTsconfigJson(configPath) {
|
|
27895
28155
|
try {
|
|
27896
|
-
const raw = await
|
|
28156
|
+
const raw = await readFile39(configPath, "utf8");
|
|
27897
28157
|
return JSON.parse(stripJsonComments(raw));
|
|
27898
28158
|
} catch {
|
|
27899
28159
|
return null;
|
|
@@ -27903,11 +28163,11 @@ function resolveTsconfigExtendsPath(configDir, extendsSpecifier) {
|
|
|
27903
28163
|
if (!extendsSpecifier.startsWith(".") && !extendsSpecifier.startsWith("/")) {
|
|
27904
28164
|
return null;
|
|
27905
28165
|
}
|
|
27906
|
-
const resolved =
|
|
28166
|
+
const resolved = path76.resolve(configDir, extendsSpecifier);
|
|
27907
28167
|
return resolved.endsWith(".json") ? resolved : `${resolved}.json`;
|
|
27908
28168
|
}
|
|
27909
28169
|
async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
27910
|
-
const resolvedPath =
|
|
28170
|
+
const resolvedPath = path76.resolve(configPath);
|
|
27911
28171
|
if (depth > TSCONFIG_EXTENDS_MAX_DEPTH || visited.has(resolvedPath)) {
|
|
27912
28172
|
return null;
|
|
27913
28173
|
}
|
|
@@ -27918,15 +28178,15 @@ async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
|
27918
28178
|
}
|
|
27919
28179
|
let inherited = null;
|
|
27920
28180
|
if (typeof parsed.extends === "string") {
|
|
27921
|
-
const basePath = resolveTsconfigExtendsPath(
|
|
28181
|
+
const basePath = resolveTsconfigExtendsPath(path76.dirname(resolvedPath), parsed.extends);
|
|
27922
28182
|
if (basePath) {
|
|
27923
28183
|
inherited = await resolveTsconfigOptions(basePath, visited, depth + 1, projectRoot);
|
|
27924
28184
|
}
|
|
27925
28185
|
}
|
|
27926
|
-
const configDir =
|
|
28186
|
+
const configDir = path76.dirname(resolvedPath);
|
|
27927
28187
|
const declaringDir = toProjectRelativeDir(projectRoot, configDir);
|
|
27928
28188
|
const options = parsed.compilerOptions ?? {};
|
|
27929
|
-
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot,
|
|
28189
|
+
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot, path76.resolve(configDir, options.baseUrl)) : null;
|
|
27930
28190
|
const baseUrl = ownBaseUrl ?? inherited?.baseUrl ?? null;
|
|
27931
28191
|
const ownPaths = isRecord4(options.paths) ? Object.entries(options.paths).filter((entry) => Array.isArray(entry[1])).map(([pattern, targets]) => ({
|
|
27932
28192
|
pattern,
|
|
@@ -27955,7 +28215,7 @@ function createTsconfigResolver(baseUrl, mappings) {
|
|
|
27955
28215
|
}
|
|
27956
28216
|
}
|
|
27957
28217
|
if (baseUrl !== null) {
|
|
27958
|
-
candidates.push(normalizePath2(
|
|
28218
|
+
candidates.push(normalizePath2(path76.posix.join(baseUrl, normalizedSpecifier)));
|
|
27959
28219
|
}
|
|
27960
28220
|
return [...new Set(candidates.map((candidate) => candidate.replace(/^\.\//, "")))];
|
|
27961
28221
|
}
|
|
@@ -27975,7 +28235,7 @@ function matchPathPattern(pattern, specifier) {
|
|
|
27975
28235
|
}
|
|
27976
28236
|
function applyPathTarget(base, target, wildcard) {
|
|
27977
28237
|
const replaced = target.includes("*") ? target.replace("*", wildcard) : target;
|
|
27978
|
-
return normalizePath2(
|
|
28238
|
+
return normalizePath2(path76.posix.normalize(path76.posix.join(base, replaced)));
|
|
27979
28239
|
}
|
|
27980
28240
|
function stripJsonComments(source) {
|
|
27981
28241
|
let out = "";
|
|
@@ -28028,21 +28288,21 @@ function isRecord4(value) {
|
|
|
28028
28288
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28029
28289
|
}
|
|
28030
28290
|
async function writeGraph(projectRoot, graph) {
|
|
28031
|
-
const storageDir =
|
|
28032
|
-
const artifactsDir =
|
|
28291
|
+
const storageDir = path76.join(projectRoot, ".metaproject", "data", "gdgraph", "storage");
|
|
28292
|
+
const artifactsDir = path76.join(projectRoot, ".metaproject", "data", "gdgraph", "artifacts");
|
|
28033
28293
|
await mkdir21(storageDir, { recursive: true });
|
|
28034
28294
|
await mkdir21(artifactsDir, { recursive: true });
|
|
28035
|
-
await writeFile15(
|
|
28295
|
+
await writeFile15(path76.join(storageDir, "nodes.jsonl"), graph.nodes.map((node) => JSON.stringify(node)).join(`
|
|
28036
28296
|
`) + `
|
|
28037
28297
|
`, "utf8");
|
|
28038
|
-
await writeFile15(
|
|
28298
|
+
await writeFile15(path76.join(storageDir, "edges.jsonl"), graph.edges.map((edge) => JSON.stringify(edge)).join(`
|
|
28039
28299
|
`) + `
|
|
28040
28300
|
`, "utf8");
|
|
28041
|
-
await writeFile15(
|
|
28301
|
+
await writeFile15(path76.join(artifactsDir, "module-map.json"), JSON.stringify(buildModuleMap(graph), null, 2) + `
|
|
28042
28302
|
`, "utf8");
|
|
28043
28303
|
}
|
|
28044
28304
|
async function writeSummary(projectRoot, graph, collection) {
|
|
28045
|
-
const artifactsDir =
|
|
28305
|
+
const artifactsDir = path76.join(projectRoot, ".metaproject", "data", "gdgraph", "artifacts");
|
|
28046
28306
|
await mkdir21(artifactsDir, { recursive: true });
|
|
28047
28307
|
const unresolved = graph.edges.filter((edge) => edge.kind === "unresolved");
|
|
28048
28308
|
const imports = graph.edges.filter((edge) => edge.kind === "imports");
|
|
@@ -28057,7 +28317,7 @@ async function writeSummary(projectRoot, graph, collection) {
|
|
|
28057
28317
|
`);
|
|
28058
28318
|
const skippedList = collection.skippedDirectories.length > 0 ? collection.skippedDirectories.slice(0, 30).map((dir) => `- \`${dir}\``).join(`
|
|
28059
28319
|
`) : "- none";
|
|
28060
|
-
const summaryPath =
|
|
28320
|
+
const summaryPath = path76.join(artifactsDir, "summary.md");
|
|
28061
28321
|
const content = `# gdgraph Summary
|
|
28062
28322
|
|
|
28063
28323
|
## Stats
|
|
@@ -28138,7 +28398,7 @@ function groupBy(items, getKey) {
|
|
|
28138
28398
|
}
|
|
28139
28399
|
function unresolvedType(specifier) {
|
|
28140
28400
|
const stripped = stripImportSuffix(specifier);
|
|
28141
|
-
const extension =
|
|
28401
|
+
const extension = path76.posix.extname(stripped);
|
|
28142
28402
|
if (extension) {
|
|
28143
28403
|
return extension;
|
|
28144
28404
|
}
|
|
@@ -28148,13 +28408,13 @@ function unresolvedType(specifier) {
|
|
|
28148
28408
|
return "package";
|
|
28149
28409
|
}
|
|
28150
28410
|
function normalizePath2(value) {
|
|
28151
|
-
return value.split(
|
|
28411
|
+
return value.split(path76.sep).join("/");
|
|
28152
28412
|
}
|
|
28153
28413
|
|
|
28154
28414
|
// src/gdgraph/target.ts
|
|
28155
|
-
import
|
|
28415
|
+
import path77 from "path";
|
|
28156
28416
|
function normalizeTarget(target) {
|
|
28157
|
-
return target.replace(/^\.\//, "").split(
|
|
28417
|
+
return target.replace(/^\.\//, "").split(path77.sep).join("/");
|
|
28158
28418
|
}
|
|
28159
28419
|
function resolveGraphTarget(graph, target) {
|
|
28160
28420
|
const normalized = normalizeTarget(target);
|
|
@@ -28246,16 +28506,16 @@ function normalizeDepth(depth) {
|
|
|
28246
28506
|
init_config3();
|
|
28247
28507
|
|
|
28248
28508
|
// src/gdgraph/query.ts
|
|
28249
|
-
import { readFile as
|
|
28250
|
-
import
|
|
28509
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
28510
|
+
import path78 from "path";
|
|
28251
28511
|
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(
|
|
28512
|
+
const storageDir = path78.join(projectRoot, ".metaproject", "data", "gdgraph", "storage");
|
|
28513
|
+
const nodes = await readJsonl(path78.join(storageDir, "nodes.jsonl"));
|
|
28514
|
+
const edges = await readJsonl(path78.join(storageDir, "edges.jsonl"));
|
|
28515
|
+
const symbols = await readJsonl(path78.join(storageDir, "symbols.jsonl"));
|
|
28516
|
+
const calls = await readJsonl(path78.join(storageDir, "calls.jsonl"));
|
|
28517
|
+
const wikiPages = await readJsonl(path78.join(storageDir, "wiki-pages.jsonl"));
|
|
28518
|
+
const describes = await readJsonl(path78.join(storageDir, "describes.jsonl"));
|
|
28259
28519
|
const graph = { nodes, edges };
|
|
28260
28520
|
if (symbols.length > 0) {
|
|
28261
28521
|
graph.symbols = symbols;
|
|
@@ -28315,7 +28575,7 @@ async function bunfigPreloadRoots(projectRoot) {
|
|
|
28315
28575
|
const roots = new Set;
|
|
28316
28576
|
let text;
|
|
28317
28577
|
try {
|
|
28318
|
-
text = await
|
|
28578
|
+
text = await readFile40(path78.join(projectRoot, "bunfig.toml"), "utf8");
|
|
28319
28579
|
} catch {
|
|
28320
28580
|
return roots;
|
|
28321
28581
|
}
|
|
@@ -28329,8 +28589,8 @@ async function bunfigPreloadRoots(projectRoot) {
|
|
|
28329
28589
|
if (raw.length === 0) {
|
|
28330
28590
|
continue;
|
|
28331
28591
|
}
|
|
28332
|
-
const absolute =
|
|
28333
|
-
const relative =
|
|
28592
|
+
const absolute = path78.resolve(projectRoot, raw);
|
|
28593
|
+
const relative = path78.relative(projectRoot, absolute).split(path78.sep).join("/");
|
|
28334
28594
|
roots.add(relative);
|
|
28335
28595
|
}
|
|
28336
28596
|
}
|
|
@@ -28379,7 +28639,7 @@ function getCycles(graph) {
|
|
|
28379
28639
|
async function readJsonl(filePath) {
|
|
28380
28640
|
let content;
|
|
28381
28641
|
try {
|
|
28382
|
-
content = await
|
|
28642
|
+
content = await readFile40(filePath, "utf8");
|
|
28383
28643
|
} catch {
|
|
28384
28644
|
return [];
|
|
28385
28645
|
}
|
|
@@ -28403,7 +28663,7 @@ function canonicalCycle(cycle) {
|
|
|
28403
28663
|
|
|
28404
28664
|
// src/gdgraph/repomap.ts
|
|
28405
28665
|
import { mkdir as mkdir22, writeFile as writeFile16 } from "fs/promises";
|
|
28406
|
-
import
|
|
28666
|
+
import path79 from "path";
|
|
28407
28667
|
|
|
28408
28668
|
// src/gdgraph/pagerank.ts
|
|
28409
28669
|
function personalizedPageRank(nodes, edges, options) {
|
|
@@ -28514,7 +28774,7 @@ function overflowContent(required, missing, budget) {
|
|
|
28514
28774
|
`);
|
|
28515
28775
|
}
|
|
28516
28776
|
function repomapArtifactPath(cwd) {
|
|
28517
|
-
return
|
|
28777
|
+
return path79.join(cwd, ".metaproject", "data", "gdgraph", "artifacts", "repomap.md");
|
|
28518
28778
|
}
|
|
28519
28779
|
function buildRankEdges(graph, config) {
|
|
28520
28780
|
const edges = [];
|
|
@@ -28659,7 +28919,7 @@ function computeRepomap(graph, config, options = {}) {
|
|
|
28659
28919
|
async function writeRepomap(cwd, graph, config, options = {}) {
|
|
28660
28920
|
const result = computeRepomap(graph, config, options);
|
|
28661
28921
|
const artifactPath = repomapArtifactPath(cwd);
|
|
28662
|
-
await mkdir22(
|
|
28922
|
+
await mkdir22(path79.dirname(artifactPath), { recursive: true });
|
|
28663
28923
|
await writeFile16(artifactPath, result.content, "utf8");
|
|
28664
28924
|
return { ...result, path: artifactPath };
|
|
28665
28925
|
}
|
|
@@ -28741,18 +29001,18 @@ init_section_tombstone();
|
|
|
28741
29001
|
|
|
28742
29002
|
// src/forgetting/journal.ts
|
|
28743
29003
|
init_fs();
|
|
28744
|
-
import { appendFile as appendFile4, mkdir as mkdir23, readFile as
|
|
28745
|
-
import
|
|
29004
|
+
import { appendFile as appendFile4, mkdir as mkdir23, readFile as readFile41, stat as stat12 } from "fs/promises";
|
|
29005
|
+
import path80 from "path";
|
|
28746
29006
|
function forgettingDataDir(cwd) {
|
|
28747
|
-
return
|
|
29007
|
+
return path80.join(cwd, ".metaproject", "data", "forgetting");
|
|
28748
29008
|
}
|
|
28749
29009
|
function deletionJournalPath(cwd) {
|
|
28750
|
-
return
|
|
29010
|
+
return path80.join(forgettingDataDir(cwd), "journal.jsonl");
|
|
28751
29011
|
}
|
|
28752
29012
|
async function readDeletionJournal(cwd) {
|
|
28753
29013
|
const file = deletionJournalPath(cwd);
|
|
28754
29014
|
try {
|
|
28755
|
-
await
|
|
29015
|
+
await stat12(file);
|
|
28756
29016
|
} catch (error) {
|
|
28757
29017
|
if (isNotFound(error)) {
|
|
28758
29018
|
return { state: "absent", path: file };
|
|
@@ -28766,7 +29026,7 @@ async function readDeletionJournal(cwd) {
|
|
|
28766
29026
|
}
|
|
28767
29027
|
let content;
|
|
28768
29028
|
try {
|
|
28769
|
-
content = await
|
|
29029
|
+
content = await readFile41(file, "utf8");
|
|
28770
29030
|
} catch (error) {
|
|
28771
29031
|
const message = error instanceof Error ? error.message : String(error);
|
|
28772
29032
|
return {
|
|
@@ -29166,12 +29426,12 @@ __export(exports_service7, {
|
|
|
29166
29426
|
gateExitCode: () => gateExitCode,
|
|
29167
29427
|
readWikiFreshnessMetric: () => readWikiFreshnessMetric
|
|
29168
29428
|
});
|
|
29169
|
-
import
|
|
29429
|
+
import path100 from "path";
|
|
29170
29430
|
|
|
29171
29431
|
// src/health/config.ts
|
|
29172
29432
|
init_fs();
|
|
29173
29433
|
init_json();
|
|
29174
|
-
import
|
|
29434
|
+
import path81 from "path";
|
|
29175
29435
|
var DEFAULT_HEALTH_CONFIG = {
|
|
29176
29436
|
schemaVersion: 2,
|
|
29177
29437
|
ignore: {
|
|
@@ -29227,7 +29487,7 @@ var DEFAULT_HEALTH_CONFIG = {
|
|
|
29227
29487
|
}
|
|
29228
29488
|
};
|
|
29229
29489
|
function configPath2(cwd) {
|
|
29230
|
-
return
|
|
29490
|
+
return path81.join(cwd, ".metaproject", "health.config.json");
|
|
29231
29491
|
}
|
|
29232
29492
|
var STRICTEST_GATE = {
|
|
29233
29493
|
failOnPriorities: ["P0", "P1", "P2", "P3"],
|
|
@@ -29278,7 +29538,7 @@ async function loadHealthConfig(cwd) {
|
|
|
29278
29538
|
// src/health/run.ts
|
|
29279
29539
|
init_fs();
|
|
29280
29540
|
import { mkdir as mkdir27, writeFile as writeFile20 } from "fs/promises";
|
|
29281
|
-
import
|
|
29541
|
+
import path99 from "path";
|
|
29282
29542
|
|
|
29283
29543
|
// src/metrics/provenance.ts
|
|
29284
29544
|
async function collectGitProvenance(cwd) {
|
|
@@ -29378,12 +29638,12 @@ function computeGate2(input) {
|
|
|
29378
29638
|
}
|
|
29379
29639
|
|
|
29380
29640
|
// src/health/scopes.ts
|
|
29381
|
-
import
|
|
29641
|
+
import path84 from "path";
|
|
29382
29642
|
|
|
29383
29643
|
// src/health/util.ts
|
|
29384
29644
|
init_fs();
|
|
29385
|
-
import { mkdir as mkdir24, readFile as
|
|
29386
|
-
import
|
|
29645
|
+
import { mkdir as mkdir24, readFile as readFile42, readdir as readdir15, writeFile as writeFile17 } from "fs/promises";
|
|
29646
|
+
import path82 from "path";
|
|
29387
29647
|
var IGNORED_DIRS = new Set([
|
|
29388
29648
|
"node_modules",
|
|
29389
29649
|
".git",
|
|
@@ -29427,14 +29687,14 @@ async function toolVersion(command, cwd) {
|
|
|
29427
29687
|
}
|
|
29428
29688
|
}
|
|
29429
29689
|
function dataRoot3(cwd) {
|
|
29430
|
-
return
|
|
29690
|
+
return path82.join(cwd, ".metaproject", "data", "health");
|
|
29431
29691
|
}
|
|
29432
29692
|
async function writeRaw(cwd, source, content, stamp) {
|
|
29433
|
-
const dir =
|
|
29693
|
+
const dir = path82.join(dataRoot3(cwd), "raw", source);
|
|
29434
29694
|
await mkdir24(dir, { recursive: true });
|
|
29435
|
-
const file =
|
|
29695
|
+
const file = path82.join(dir, `${stamp}.log`);
|
|
29436
29696
|
await writeFile17(file, content, "utf8");
|
|
29437
|
-
return
|
|
29697
|
+
return path82.relative(cwd, file);
|
|
29438
29698
|
}
|
|
29439
29699
|
async function listSourceFiles(cwd, ignorePaths = []) {
|
|
29440
29700
|
const { files } = await listSourceFilesWithReasons(cwd, ignorePaths);
|
|
@@ -29452,9 +29712,9 @@ async function listSourceFilesWithReasons(cwd, ignorePaths = []) {
|
|
|
29452
29712
|
async function walk2(root, dir, out, incompleteReasons) {
|
|
29453
29713
|
let entries;
|
|
29454
29714
|
try {
|
|
29455
|
-
entries = await
|
|
29715
|
+
entries = await readdir15(dir, { withFileTypes: true });
|
|
29456
29716
|
} catch (error) {
|
|
29457
|
-
incompleteReasons.push(`${
|
|
29717
|
+
incompleteReasons.push(`${path82.relative(root, dir) || "."}: ${error instanceof Error ? error.message : String(error)}`);
|
|
29458
29718
|
return;
|
|
29459
29719
|
}
|
|
29460
29720
|
for (const entry of entries) {
|
|
@@ -29462,14 +29722,14 @@ async function walk2(root, dir, out, incompleteReasons) {
|
|
|
29462
29722
|
if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {}
|
|
29463
29723
|
continue;
|
|
29464
29724
|
}
|
|
29465
|
-
const abs =
|
|
29725
|
+
const abs = path82.join(dir, entry.name);
|
|
29466
29726
|
if (entry.isDirectory()) {
|
|
29467
29727
|
if (IGNORED_DIRS.has(entry.name)) {
|
|
29468
29728
|
continue;
|
|
29469
29729
|
}
|
|
29470
29730
|
await walk2(root, abs, out, incompleteReasons);
|
|
29471
|
-
} else if (entry.isFile() && SOURCE_EXT.has(
|
|
29472
|
-
out.push(
|
|
29731
|
+
} else if (entry.isFile() && SOURCE_EXT.has(path82.extname(entry.name))) {
|
|
29732
|
+
out.push(path82.relative(root, abs));
|
|
29473
29733
|
}
|
|
29474
29734
|
}
|
|
29475
29735
|
}
|
|
@@ -29484,7 +29744,7 @@ function moduleOfFile(file) {
|
|
|
29484
29744
|
return null;
|
|
29485
29745
|
}
|
|
29486
29746
|
function isSourceFile(file) {
|
|
29487
|
-
return SOURCE_EXT.has(
|
|
29747
|
+
return SOURCE_EXT.has(path82.extname(file));
|
|
29488
29748
|
}
|
|
29489
29749
|
function matchesAnyPattern(file, patterns) {
|
|
29490
29750
|
const normalized = file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -29583,8 +29843,8 @@ function rankHotspots(files, churn, sourceAnalysis) {
|
|
|
29583
29843
|
|
|
29584
29844
|
// src/health/source-analysis.ts
|
|
29585
29845
|
init_fs();
|
|
29586
|
-
import { readFile as
|
|
29587
|
-
import
|
|
29846
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
29847
|
+
import path83 from "path";
|
|
29588
29848
|
|
|
29589
29849
|
// src/health/metrics/complexity.ts
|
|
29590
29850
|
var CONTROL_KEYWORDS = new Set([
|
|
@@ -29818,11 +30078,11 @@ async function analyzeSourceFiles(cwd, sourceFiles) {
|
|
|
29818
30078
|
return new Map(entries.filter((entry) => entry !== null));
|
|
29819
30079
|
}
|
|
29820
30080
|
async function analyzeSourceFile(cwd, file) {
|
|
29821
|
-
const abs =
|
|
30081
|
+
const abs = path83.join(cwd, file);
|
|
29822
30082
|
if (!await pathExists(abs)) {
|
|
29823
30083
|
return null;
|
|
29824
30084
|
}
|
|
29825
|
-
const content = await
|
|
30085
|
+
const content = await readFile43(abs, "utf8");
|
|
29826
30086
|
return [
|
|
29827
30087
|
file,
|
|
29828
30088
|
{
|
|
@@ -29891,14 +30151,14 @@ async function computeMetrics(input) {
|
|
|
29891
30151
|
}
|
|
29892
30152
|
const components = new Map;
|
|
29893
30153
|
for (const file of sourceFiles) {
|
|
29894
|
-
const dir =
|
|
30154
|
+
const dir = path84.posix.dirname(file);
|
|
29895
30155
|
if (dir === "." || dir === moduleOfFile(file)) {
|
|
29896
30156
|
continue;
|
|
29897
30157
|
}
|
|
29898
30158
|
components.set(dir, [...components.get(dir) ?? [], file]);
|
|
29899
30159
|
}
|
|
29900
30160
|
for (const [name, files] of [...components.entries()].sort()) {
|
|
29901
|
-
const compFindings = findings.filter((f) => f.file &&
|
|
30161
|
+
const compFindings = findings.filter((f) => f.file && path84.posix.dirname(f.file) === name);
|
|
29902
30162
|
scopes.push(build("component", `component:${name}`, name, files, compFindings, averageCoverage(files, coverage)));
|
|
29903
30163
|
}
|
|
29904
30164
|
const findingFiles = findings.map((f) => f.file).filter((f) => Boolean(f));
|
|
@@ -29942,10 +30202,10 @@ function tally2(values) {
|
|
|
29942
30202
|
|
|
29943
30203
|
// src/health/baseline.ts
|
|
29944
30204
|
init_fs();
|
|
29945
|
-
import { mkdir as mkdir25, readFile as
|
|
29946
|
-
import
|
|
30205
|
+
import { mkdir as mkdir25, readFile as readFile44, writeFile as writeFile18 } from "fs/promises";
|
|
30206
|
+
import path85 from "path";
|
|
29947
30207
|
function baselinePath(cwd) {
|
|
29948
|
-
return
|
|
30208
|
+
return path85.join(cwd, ".metaproject", "health", "baselines", "scores.json");
|
|
29949
30209
|
}
|
|
29950
30210
|
async function loadBaseline(cwd) {
|
|
29951
30211
|
const file = baselinePath(cwd);
|
|
@@ -29953,7 +30213,7 @@ async function loadBaseline(cwd) {
|
|
|
29953
30213
|
return new Map;
|
|
29954
30214
|
}
|
|
29955
30215
|
try {
|
|
29956
|
-
const data = JSON.parse(await
|
|
30216
|
+
const data = JSON.parse(await readFile44(file, "utf8"));
|
|
29957
30217
|
return new Map(Object.entries(data.scopes ?? {}));
|
|
29958
30218
|
} catch {
|
|
29959
30219
|
return new Map;
|
|
@@ -29977,7 +30237,7 @@ async function writeBaseline(cwd, metrics, generatedAt, selector) {
|
|
|
29977
30237
|
scopes[key] = value;
|
|
29978
30238
|
}
|
|
29979
30239
|
const file = baselinePath(cwd);
|
|
29980
|
-
await mkdir25(
|
|
30240
|
+
await mkdir25(path85.dirname(file), { recursive: true });
|
|
29981
30241
|
await writeFile18(file, `${JSON.stringify({ generatedAt, scopes }, null, 2)}
|
|
29982
30242
|
`, "utf8");
|
|
29983
30243
|
return updated;
|
|
@@ -30030,11 +30290,11 @@ async function getChurn(cwd, windowDays) {
|
|
|
30030
30290
|
}
|
|
30031
30291
|
|
|
30032
30292
|
// src/health/metrics/wiki-freshness.ts
|
|
30033
|
-
import { readFile as
|
|
30034
|
-
import
|
|
30293
|
+
import { readFile as readFile45, stat as stat13 } from "fs/promises";
|
|
30294
|
+
import path86 from "path";
|
|
30035
30295
|
var DEFAULT_STALE_EVIDENCE_DAYS = 7;
|
|
30036
30296
|
function freshnessReportPath(cwd) {
|
|
30037
|
-
return
|
|
30297
|
+
return path86.join(cwd, ".metaproject", "data", "wiki", "freshness", "latest.json");
|
|
30038
30298
|
}
|
|
30039
30299
|
async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
30040
30300
|
const file = freshnessReportPath(cwd);
|
|
@@ -30042,7 +30302,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
30042
30302
|
const now = (options.now ?? (() => new Date))();
|
|
30043
30303
|
let raw;
|
|
30044
30304
|
try {
|
|
30045
|
-
raw = await
|
|
30305
|
+
raw = await readFile45(file, "utf8");
|
|
30046
30306
|
} catch {
|
|
30047
30307
|
return {
|
|
30048
30308
|
status: "no-report",
|
|
@@ -30055,7 +30315,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
30055
30315
|
} catch {
|
|
30056
30316
|
return {
|
|
30057
30317
|
status: "unreadable-report",
|
|
30058
|
-
reason: `${
|
|
30318
|
+
reason: `${path86.relative(cwd, file)} is not valid JSON; re-run \`keryx wiki freshness\`.`
|
|
30059
30319
|
};
|
|
30060
30320
|
}
|
|
30061
30321
|
const report = parsed;
|
|
@@ -30065,7 +30325,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
30065
30325
|
if (pagesTotal === null || pagesFresh === null) {
|
|
30066
30326
|
return {
|
|
30067
30327
|
status: "unreadable-report",
|
|
30068
|
-
reason: `${
|
|
30328
|
+
reason: `${path86.relative(cwd, file)} is missing its totals; re-run \`keryx wiki freshness\`.`
|
|
30069
30329
|
};
|
|
30070
30330
|
}
|
|
30071
30331
|
const pagesUndecidable = numberOr2(totals.pagesUndecidable) ?? 0;
|
|
@@ -30102,7 +30362,7 @@ async function reportAgeDays(file, generatedAt, now) {
|
|
|
30102
30362
|
return Math.floor((now.getTime() - stamped) / 86400000);
|
|
30103
30363
|
}
|
|
30104
30364
|
try {
|
|
30105
|
-
const info = await
|
|
30365
|
+
const info = await stat13(file);
|
|
30106
30366
|
return Math.floor((now.getTime() - info.mtimeMs) / 86400000);
|
|
30107
30367
|
} catch {
|
|
30108
30368
|
return null;
|
|
@@ -30119,12 +30379,12 @@ function renderWikiFreshnessLine(metric) {
|
|
|
30119
30379
|
}
|
|
30120
30380
|
|
|
30121
30381
|
// src/health/sources/helpers.ts
|
|
30122
|
-
import
|
|
30382
|
+
import path87 from "path";
|
|
30123
30383
|
import { existsSync as existsSync8 } from "fs";
|
|
30124
30384
|
class NoImportError extends Error {
|
|
30125
30385
|
}
|
|
30126
30386
|
function resolveBin(cwd, name) {
|
|
30127
|
-
const local =
|
|
30387
|
+
const local = path87.join(cwd, "node_modules", ".bin", name);
|
|
30128
30388
|
if (existsSync8(local)) {
|
|
30129
30389
|
return local;
|
|
30130
30390
|
}
|
|
@@ -30205,13 +30465,13 @@ async function getComplexityFindings(cwd, sourceFiles, config, sourceAnalysis) {
|
|
|
30205
30465
|
|
|
30206
30466
|
// src/health/metrics/coverage.ts
|
|
30207
30467
|
import { existsSync as existsSync9 } from "fs";
|
|
30208
|
-
import
|
|
30468
|
+
import path88 from "path";
|
|
30209
30469
|
var SUMMARY_PATHS = [
|
|
30210
30470
|
"coverage/coverage-summary.json",
|
|
30211
30471
|
"coverage/coverage-final.json"
|
|
30212
30472
|
];
|
|
30213
30473
|
async function getCoverage(cwd) {
|
|
30214
|
-
const summary = SUMMARY_PATHS.map((rel) =>
|
|
30474
|
+
const summary = SUMMARY_PATHS.map((rel) => path88.join(cwd, rel)).find((abs) => existsSync9(abs));
|
|
30215
30475
|
if (!summary) {
|
|
30216
30476
|
return { status: "missing", total: null, byFile: new Map };
|
|
30217
30477
|
}
|
|
@@ -30232,7 +30492,7 @@ async function getCoverage(cwd) {
|
|
|
30232
30492
|
total = pct;
|
|
30233
30493
|
continue;
|
|
30234
30494
|
}
|
|
30235
|
-
const relative =
|
|
30495
|
+
const relative = path88.isAbsolute(key) ? path88.relative(cwd, key) : key;
|
|
30236
30496
|
byFile.set(relative.replace(/\\/g, "/"), pct);
|
|
30237
30497
|
}
|
|
30238
30498
|
return { status: "available", total, byFile };
|
|
@@ -30364,17 +30624,17 @@ function renderWikiFreshness(metric) {
|
|
|
30364
30624
|
|
|
30365
30625
|
// src/health/skills.ts
|
|
30366
30626
|
init_fs();
|
|
30367
|
-
import { readFile as
|
|
30368
|
-
import
|
|
30627
|
+
import { readFile as readFile46 } from "fs/promises";
|
|
30628
|
+
import path89 from "path";
|
|
30369
30629
|
var EMPTY = { skills: [], skillForFile: () => null };
|
|
30370
30630
|
async function loadSkillOwnership(cwd) {
|
|
30371
|
-
const manifestPath =
|
|
30631
|
+
const manifestPath = path89.join(cwd, ".metaproject", "metaproject.json");
|
|
30372
30632
|
if (!await pathExists(manifestPath)) {
|
|
30373
30633
|
return EMPTY;
|
|
30374
30634
|
}
|
|
30375
30635
|
let registry;
|
|
30376
30636
|
try {
|
|
30377
|
-
const manifest = JSON.parse(await
|
|
30637
|
+
const manifest = JSON.parse(await readFile46(manifestPath, "utf8"));
|
|
30378
30638
|
registry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
30379
30639
|
} catch {
|
|
30380
30640
|
return EMPTY;
|
|
@@ -30406,7 +30666,7 @@ function normalize(value) {
|
|
|
30406
30666
|
|
|
30407
30667
|
// src/health/sources/eslint.ts
|
|
30408
30668
|
import { existsSync as existsSync10 } from "fs";
|
|
30409
|
-
import
|
|
30669
|
+
import path90 from "path";
|
|
30410
30670
|
var CONFIG_FILES = [
|
|
30411
30671
|
".eslintrc",
|
|
30412
30672
|
".eslintrc.js",
|
|
@@ -30420,7 +30680,7 @@ var CONFIG_FILES = [
|
|
|
30420
30680
|
"eslint.config.ts"
|
|
30421
30681
|
];
|
|
30422
30682
|
function hasConfig(cwd) {
|
|
30423
|
-
return CONFIG_FILES.some((file) => existsSync10(
|
|
30683
|
+
return CONFIG_FILES.some((file) => existsSync10(path90.join(cwd, file)));
|
|
30424
30684
|
}
|
|
30425
30685
|
var eslintAdapter = {
|
|
30426
30686
|
id: "eslint",
|
|
@@ -30445,7 +30705,7 @@ var eslintAdapter = {
|
|
|
30445
30705
|
};
|
|
30446
30706
|
},
|
|
30447
30707
|
async import(ctx) {
|
|
30448
|
-
const report =
|
|
30708
|
+
const report = path90.join(ctx.cwd, "eslint-report.json");
|
|
30449
30709
|
if (!existsSync10(report)) {
|
|
30450
30710
|
throw new NoImportError("no eslint-report.json");
|
|
30451
30711
|
}
|
|
@@ -30472,7 +30732,7 @@ var eslintAdapter = {
|
|
|
30472
30732
|
}
|
|
30473
30733
|
const findings = [];
|
|
30474
30734
|
for (const file of data) {
|
|
30475
|
-
const relative =
|
|
30735
|
+
const relative = path90.isAbsolute(file.filePath) ? path90.relative(ctx.cwd, file.filePath) : file.filePath;
|
|
30476
30736
|
for (const message of file.messages ?? []) {
|
|
30477
30737
|
const isError = message.severity === 2;
|
|
30478
30738
|
findings.push(makeFinding({
|
|
@@ -30504,9 +30764,9 @@ var eslintAdapter = {
|
|
|
30504
30764
|
|
|
30505
30765
|
// src/health/sources/typescript.ts
|
|
30506
30766
|
import { existsSync as existsSync11 } from "fs";
|
|
30507
|
-
import
|
|
30767
|
+
import path91 from "path";
|
|
30508
30768
|
function hasTsconfig(cwd) {
|
|
30509
|
-
return existsSync11(
|
|
30769
|
+
return existsSync11(path91.join(cwd, "tsconfig.json"));
|
|
30510
30770
|
}
|
|
30511
30771
|
var typescriptAdapter = {
|
|
30512
30772
|
id: "typescript",
|
|
@@ -30581,8 +30841,8 @@ __export(exports_service6, {
|
|
|
30581
30841
|
testingDataRoot: () => testingDataRoot
|
|
30582
30842
|
});
|
|
30583
30843
|
import { existsSync as existsSync12 } from "fs";
|
|
30584
|
-
import { readFile as
|
|
30585
|
-
import
|
|
30844
|
+
import { readFile as readFile49, readdir as readdir16 } from "fs/promises";
|
|
30845
|
+
import path96 from "path";
|
|
30586
30846
|
|
|
30587
30847
|
// src/lib/git-env.ts
|
|
30588
30848
|
var GIT_DISCOVERY_OVERRIDE_VARS = [
|
|
@@ -30606,8 +30866,8 @@ function withoutGitDiscoveryOverrides(env) {
|
|
|
30606
30866
|
// src/metrics/lifecycle.ts
|
|
30607
30867
|
init_fs();
|
|
30608
30868
|
init_guard();
|
|
30609
|
-
import { readFile as
|
|
30610
|
-
import
|
|
30869
|
+
import { readFile as readFile47 } from "fs/promises";
|
|
30870
|
+
import path92 from "path";
|
|
30611
30871
|
|
|
30612
30872
|
// src/metrics/record.ts
|
|
30613
30873
|
var RELIABILITIES = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -30622,16 +30882,16 @@ var RETRY_TYPES = new Set([
|
|
|
30622
30882
|
|
|
30623
30883
|
// src/metrics/lifecycle.ts
|
|
30624
30884
|
async function readArtifactPointer(root, expected) {
|
|
30625
|
-
const pointerFile =
|
|
30885
|
+
const pointerFile = path92.join(root, "latest.json");
|
|
30626
30886
|
if (!await pathExists(pointerFile))
|
|
30627
30887
|
return { status: "missing", pointer: null };
|
|
30628
30888
|
let pointer;
|
|
30629
30889
|
try {
|
|
30630
|
-
pointer = JSON.parse(await
|
|
30890
|
+
pointer = JSON.parse(await readFile47(pointerFile, "utf8"));
|
|
30631
30891
|
} catch {
|
|
30632
30892
|
return { status: "stale", pointer: null, reason: "latest pointer is invalid JSON" };
|
|
30633
30893
|
}
|
|
30634
|
-
const recordFile = pointer.record ?
|
|
30894
|
+
const recordFile = pointer.record ? path92.join(root, pointer.record) : null;
|
|
30635
30895
|
if (!recordFile || !isPathInside(root, recordFile) || !await pathExists(recordFile)) {
|
|
30636
30896
|
return { status: "stale", pointer, reason: "pointer record is missing" };
|
|
30637
30897
|
}
|
|
@@ -30647,9 +30907,9 @@ init_guard();
|
|
|
30647
30907
|
// src/testing/capability.ts
|
|
30648
30908
|
init_fs();
|
|
30649
30909
|
init_json();
|
|
30650
|
-
import
|
|
30910
|
+
import path93 from "path";
|
|
30651
30911
|
async function isTestingCapabilityEnabled(cwd, capability) {
|
|
30652
|
-
const manifestPath =
|
|
30912
|
+
const manifestPath = path93.join(cwd, ".metaproject", "metaproject.json");
|
|
30653
30913
|
if (!await pathExists(manifestPath)) {
|
|
30654
30914
|
return false;
|
|
30655
30915
|
}
|
|
@@ -30674,11 +30934,11 @@ async function isTestingCapabilityEnabled(cwd, capability) {
|
|
|
30674
30934
|
// src/testing/coverage-map.ts
|
|
30675
30935
|
init_fs();
|
|
30676
30936
|
init_guard();
|
|
30677
|
-
import { readFile as
|
|
30678
|
-
import
|
|
30937
|
+
import { readFile as readFile48, mkdir as mkdir26, writeFile as writeFile19 } from "fs/promises";
|
|
30938
|
+
import path94 from "path";
|
|
30679
30939
|
var COVERAGE_MAP_SCHEMA_VERSION = 1;
|
|
30680
30940
|
function coverageMapPath(cwd, cfg) {
|
|
30681
|
-
return
|
|
30941
|
+
return path94.isAbsolute(cfg.coverageMap.artifact) ? cfg.coverageMap.artifact : path94.join(cwd, cfg.coverageMap.artifact);
|
|
30682
30942
|
}
|
|
30683
30943
|
async function loadCoverageMap(cwd, cfg) {
|
|
30684
30944
|
const file = coverageMapPath(cwd, cfg);
|
|
@@ -30686,7 +30946,7 @@ async function loadCoverageMap(cwd, cfg) {
|
|
|
30686
30946
|
return null;
|
|
30687
30947
|
}
|
|
30688
30948
|
try {
|
|
30689
|
-
const parsed = JSON.parse(await
|
|
30949
|
+
const parsed = JSON.parse(await readFile48(file, "utf8"));
|
|
30690
30950
|
if (!parsed || typeof parsed !== "object" || typeof parsed.map !== "object") {
|
|
30691
30951
|
return null;
|
|
30692
30952
|
}
|
|
@@ -30752,20 +31012,20 @@ function normalizePath4(file) {
|
|
|
30752
31012
|
}
|
|
30753
31013
|
|
|
30754
31014
|
// src/testing/selection.ts
|
|
30755
|
-
import
|
|
31015
|
+
import path95 from "path";
|
|
30756
31016
|
var TEST_FILE_RE = /(^|\/)(__tests__\/.*|.*\.(test|spec)\.[cm]?[tj]sx?$|e2e\/.*\.[cm]?[tj]sx?$|tests\/.*\.[cm]?[tj]sx?$)/;
|
|
30757
31017
|
function normalizePath5(file) {
|
|
30758
31018
|
return file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
30759
31019
|
}
|
|
30760
31020
|
function relatedByNamingAndDirectory(target, testFiles) {
|
|
30761
31021
|
const normalized = normalizePath5(target);
|
|
30762
|
-
const ext =
|
|
31022
|
+
const ext = path95.extname(normalized);
|
|
30763
31023
|
const withoutExt = ext ? normalized.slice(0, -ext.length) : normalized;
|
|
30764
|
-
const base =
|
|
30765
|
-
const dir =
|
|
31024
|
+
const base = path95.basename(withoutExt);
|
|
31025
|
+
const dir = path95.dirname(normalized);
|
|
30766
31026
|
return testFiles.filter((file) => {
|
|
30767
|
-
const fileDir =
|
|
30768
|
-
const fileBase =
|
|
31027
|
+
const fileDir = path95.dirname(file);
|
|
31028
|
+
const fileBase = path95.basename(file);
|
|
30769
31029
|
return file.startsWith(`${withoutExt}.`) || file.includes(`${withoutExt}.`) || fileDir === dir && fileBase.startsWith(`${base}.`) || fileDir.startsWith(dir) && fileBase.includes(base);
|
|
30770
31030
|
}).sort();
|
|
30771
31031
|
}
|
|
@@ -30895,7 +31155,7 @@ var DEFAULT_TESTING_CONFIG = {
|
|
|
30895
31155
|
}
|
|
30896
31156
|
};
|
|
30897
31157
|
function testingDataRoot(cwd) {
|
|
30898
|
-
return
|
|
31158
|
+
return path96.join(cwd, ".metaproject", "data", "testing");
|
|
30899
31159
|
}
|
|
30900
31160
|
async function computeTestingContext(cwd) {
|
|
30901
31161
|
const { files, incompleteReasons } = await listProjectFiles(cwd);
|
|
@@ -31070,33 +31330,33 @@ async function runTesting(input) {
|
|
|
31070
31330
|
return runResult;
|
|
31071
31331
|
}
|
|
31072
31332
|
async function loadTestingContext(cwd) {
|
|
31073
|
-
const file =
|
|
31333
|
+
const file = path96.join(testingDataRoot(cwd), "context.json");
|
|
31074
31334
|
if (!await pathExists(file)) {
|
|
31075
31335
|
return null;
|
|
31076
31336
|
}
|
|
31077
31337
|
try {
|
|
31078
|
-
return JSON.parse(await
|
|
31338
|
+
return JSON.parse(await readFile49(file, "utf8"));
|
|
31079
31339
|
} catch {
|
|
31080
31340
|
return null;
|
|
31081
31341
|
}
|
|
31082
31342
|
}
|
|
31083
31343
|
async function loadTestingReport(cwd) {
|
|
31084
|
-
const file =
|
|
31344
|
+
const file = path96.join(testingDataRoot(cwd), "artifacts", "latest.json");
|
|
31085
31345
|
if (!await pathExists(file)) {
|
|
31086
31346
|
return null;
|
|
31087
31347
|
}
|
|
31088
31348
|
try {
|
|
31089
|
-
const latest = JSON.parse(await
|
|
31349
|
+
const latest = JSON.parse(await readFile49(file, "utf8"));
|
|
31090
31350
|
if (typeof latest.record === "string") {
|
|
31091
|
-
const pointer = await readArtifactPointer(
|
|
31351
|
+
const pointer = await readArtifactPointer(path96.join(testingDataRoot(cwd), "artifacts"), await collectGitProvenance(cwd));
|
|
31092
31352
|
if (pointer.status !== "fresh")
|
|
31093
31353
|
return null;
|
|
31094
|
-
const recordFile =
|
|
31095
|
-
if (!isPathInside(
|
|
31354
|
+
const recordFile = path96.join(testingDataRoot(cwd), "artifacts", latest.record);
|
|
31355
|
+
if (!isPathInside(path96.join(testingDataRoot(cwd), "artifacts"), recordFile))
|
|
31096
31356
|
return null;
|
|
31097
31357
|
if (!await pathExists(recordFile))
|
|
31098
31358
|
return null;
|
|
31099
|
-
return JSON.parse(await
|
|
31359
|
+
return JSON.parse(await readFile49(recordFile, "utf8"));
|
|
31100
31360
|
}
|
|
31101
31361
|
return latest;
|
|
31102
31362
|
} catch {
|
|
@@ -31139,28 +31399,28 @@ async function ensureContext(cwd) {
|
|
|
31139
31399
|
}
|
|
31140
31400
|
async function writeContext(cwd, context) {
|
|
31141
31401
|
const root = testingDataRoot(cwd);
|
|
31142
|
-
const rel = (p) =>
|
|
31402
|
+
const rel = (p) => path96.relative(cwd, p);
|
|
31143
31403
|
await mkdirContained(cwd, rel(root));
|
|
31144
|
-
await writeContained(cwd, rel(
|
|
31404
|
+
await writeContained(cwd, rel(path96.join(root, "context.json")), `${JSON.stringify(context, null, 2)}
|
|
31145
31405
|
`);
|
|
31146
|
-
await writeContained(cwd, rel(
|
|
31147
|
-
await writeContained(cwd, rel(
|
|
31406
|
+
await writeContained(cwd, rel(path96.join(root, "context.md")), renderContextMarkdown(context));
|
|
31407
|
+
await writeContained(cwd, rel(path96.join(root, "recommendations.md")), renderRecommendationsMarkdown(context));
|
|
31148
31408
|
}
|
|
31149
31409
|
async function writeReport(cwd, report) {
|
|
31150
31410
|
const root = testingDataRoot(cwd);
|
|
31151
|
-
const artifacts =
|
|
31152
|
-
const history =
|
|
31153
|
-
const rel = (p) =>
|
|
31411
|
+
const artifacts = path96.join(root, "artifacts");
|
|
31412
|
+
const history = path96.join(root, "history");
|
|
31413
|
+
const rel = (p) => path96.relative(cwd, p);
|
|
31154
31414
|
await mkdirContained(cwd, rel(artifacts));
|
|
31155
31415
|
await mkdirContained(cwd, rel(history));
|
|
31156
31416
|
const stamp = report.generatedAt.replace(/[:.]/g, "-");
|
|
31157
|
-
const json =
|
|
31158
|
-
const markdown =
|
|
31417
|
+
const json = path96.join(artifacts, "latest.json");
|
|
31418
|
+
const markdown = path96.join(artifacts, "latest.md");
|
|
31159
31419
|
const serialized = `${JSON.stringify(report, null, 2)}
|
|
31160
31420
|
`;
|
|
31161
31421
|
if (report.runId) {
|
|
31162
|
-
const runJson =
|
|
31163
|
-
const runMarkdown =
|
|
31422
|
+
const runJson = path96.join(artifacts, "runs", `${report.runId}.json`);
|
|
31423
|
+
const runMarkdown = path96.join(artifacts, "runs", `${report.runId}.md`);
|
|
31164
31424
|
if (await pathExists(runJson))
|
|
31165
31425
|
throw new Error(`immutable testing run already exists: ${report.runId}`);
|
|
31166
31426
|
await writeContained(cwd, rel(runJson), serialized);
|
|
@@ -31171,26 +31431,26 @@ async function writeReport(cwd, report) {
|
|
|
31171
31431
|
branch: report.provenance?.branch ?? null,
|
|
31172
31432
|
worktree: report.provenance?.worktree ?? null,
|
|
31173
31433
|
generated_at: report.generatedAt,
|
|
31174
|
-
record:
|
|
31434
|
+
record: path96.posix.join("runs", `${report.runId}.json`)
|
|
31175
31435
|
}, null, 2)}
|
|
31176
31436
|
`);
|
|
31177
31437
|
} else {
|
|
31178
31438
|
await writeContained(cwd, rel(json), serialized);
|
|
31179
31439
|
}
|
|
31180
31440
|
await writeContained(cwd, rel(markdown), renderReportMarkdown3(report));
|
|
31181
|
-
await writeContained(cwd, rel(
|
|
31441
|
+
await writeContained(cwd, rel(path96.join(history, `${stamp}.json`)), serialized);
|
|
31182
31442
|
return {
|
|
31183
|
-
markdownPath:
|
|
31184
|
-
jsonPath:
|
|
31443
|
+
markdownPath: path96.relative(cwd, markdown),
|
|
31444
|
+
jsonPath: path96.relative(cwd, json)
|
|
31185
31445
|
};
|
|
31186
31446
|
}
|
|
31187
31447
|
async function writeRawLog(cwd, raw) {
|
|
31188
|
-
const logs =
|
|
31189
|
-
const rel = (p) =>
|
|
31448
|
+
const logs = path96.join(testingDataRoot(cwd), "logs");
|
|
31449
|
+
const rel = (p) => path96.relative(cwd, p);
|
|
31190
31450
|
await mkdirContained(cwd, rel(logs));
|
|
31191
|
-
const latest =
|
|
31451
|
+
const latest = path96.join(logs, "latest.raw.log");
|
|
31192
31452
|
await writeContained(cwd, rel(latest), raw);
|
|
31193
|
-
return
|
|
31453
|
+
return path96.relative(cwd, latest);
|
|
31194
31454
|
}
|
|
31195
31455
|
async function listProjectFiles(cwd) {
|
|
31196
31456
|
const out = [];
|
|
@@ -31201,13 +31461,13 @@ async function listProjectFiles(cwd) {
|
|
|
31201
31461
|
async function walk3(root, dir, out, incompleteReasons) {
|
|
31202
31462
|
let entries;
|
|
31203
31463
|
try {
|
|
31204
|
-
entries = await
|
|
31464
|
+
entries = await readdir16(dir, { withFileTypes: true });
|
|
31205
31465
|
} catch (error) {
|
|
31206
|
-
incompleteReasons.push(`${normalizePath6(
|
|
31466
|
+
incompleteReasons.push(`${normalizePath6(path96.relative(root, dir)) || "."}: ${error instanceof Error ? error.message : String(error)}`);
|
|
31207
31467
|
return;
|
|
31208
31468
|
}
|
|
31209
31469
|
for (const entry of entries) {
|
|
31210
|
-
const abs =
|
|
31470
|
+
const abs = path96.join(dir, entry.name);
|
|
31211
31471
|
if (entry.isDirectory()) {
|
|
31212
31472
|
if (IGNORED_DIRS2.has(entry.name) || entry.name.startsWith(".tmp-")) {
|
|
31213
31473
|
continue;
|
|
@@ -31216,28 +31476,28 @@ async function walk3(root, dir, out, incompleteReasons) {
|
|
|
31216
31476
|
continue;
|
|
31217
31477
|
}
|
|
31218
31478
|
if (entry.isFile()) {
|
|
31219
|
-
out.push(normalizePath6(
|
|
31479
|
+
out.push(normalizePath6(path96.relative(root, abs)));
|
|
31220
31480
|
}
|
|
31221
31481
|
}
|
|
31222
31482
|
}
|
|
31223
31483
|
async function readPackageJson(cwd) {
|
|
31224
|
-
const file =
|
|
31484
|
+
const file = path96.join(cwd, "package.json");
|
|
31225
31485
|
if (!await pathExists(file)) {
|
|
31226
31486
|
return null;
|
|
31227
31487
|
}
|
|
31228
31488
|
try {
|
|
31229
|
-
return JSON.parse(await
|
|
31489
|
+
return JSON.parse(await readFile49(file, "utf8"));
|
|
31230
31490
|
} catch {
|
|
31231
31491
|
return null;
|
|
31232
31492
|
}
|
|
31233
31493
|
}
|
|
31234
31494
|
async function loadTestingConfig(cwd) {
|
|
31235
|
-
const file =
|
|
31495
|
+
const file = path96.join(cwd, ".metaproject", "testing.config.json");
|
|
31236
31496
|
if (!await pathExists(file)) {
|
|
31237
31497
|
return DEFAULT_TESTING_CONFIG;
|
|
31238
31498
|
}
|
|
31239
31499
|
try {
|
|
31240
|
-
const parsed = JSON.parse(await
|
|
31500
|
+
const parsed = JSON.parse(await readFile49(file, "utf8"));
|
|
31241
31501
|
return {
|
|
31242
31502
|
schemaVersion: parsed.schemaVersion ?? DEFAULT_TESTING_CONFIG.schemaVersion,
|
|
31243
31503
|
enabled: parsed.enabled ?? DEFAULT_TESTING_CONFIG.enabled,
|
|
@@ -31297,11 +31557,11 @@ function detectFrameworks(input) {
|
|
|
31297
31557
|
async function extractConventions(cwd, files) {
|
|
31298
31558
|
const conventions = new Set;
|
|
31299
31559
|
for (const file of files.slice(0, 40)) {
|
|
31300
|
-
const abs =
|
|
31560
|
+
const abs = path96.join(cwd, file);
|
|
31301
31561
|
if (!await pathExists(abs)) {
|
|
31302
31562
|
continue;
|
|
31303
31563
|
}
|
|
31304
|
-
const text = await
|
|
31564
|
+
const text = await readFile49(abs, "utf8");
|
|
31305
31565
|
for (const line of text.split(`
|
|
31306
31566
|
`)) {
|
|
31307
31567
|
if (/test|testing|spec|coverage|playwright|vitest|jest|bun:test/i.test(line)) {
|
|
@@ -31427,11 +31687,11 @@ async function findTestsByImportedTargets(cwd, targets, testFiles) {
|
|
|
31427
31687
|
const targetKeys = new Set(targets.map((target) => modulePathKey(normalizePath6(target))));
|
|
31428
31688
|
const selected = new Set;
|
|
31429
31689
|
for (const testFile of testFiles) {
|
|
31430
|
-
const absoluteTest =
|
|
31690
|
+
const absoluteTest = path96.join(cwd, testFile);
|
|
31431
31691
|
if (!await pathExists(absoluteTest)) {
|
|
31432
31692
|
continue;
|
|
31433
31693
|
}
|
|
31434
|
-
const content = await
|
|
31694
|
+
const content = await readFile49(absoluteTest, "utf8");
|
|
31435
31695
|
const imports = extractImportPaths(content);
|
|
31436
31696
|
if (imports.length === 0) {
|
|
31437
31697
|
continue;
|
|
@@ -31461,10 +31721,10 @@ function resolveImportPath(cwd, testFile, importPath) {
|
|
|
31461
31721
|
if (!importPath.startsWith(".")) {
|
|
31462
31722
|
return null;
|
|
31463
31723
|
}
|
|
31464
|
-
const importerDir =
|
|
31465
|
-
const absoluteResolved =
|
|
31466
|
-
const relative =
|
|
31467
|
-
if (relative.startsWith(`..${
|
|
31724
|
+
const importerDir = path96.dirname(testFile);
|
|
31725
|
+
const absoluteResolved = path96.resolve(path96.join(cwd, importerDir), importPath);
|
|
31726
|
+
const relative = path96.relative(cwd, absoluteResolved);
|
|
31727
|
+
if (relative.startsWith(`..${path96.sep}`) || relative === "..") {
|
|
31468
31728
|
return null;
|
|
31469
31729
|
}
|
|
31470
31730
|
const relativeNormalized = normalizePath6(relative);
|
|
@@ -31516,13 +31776,13 @@ function resolveTestCommand(cwd, context, input) {
|
|
|
31516
31776
|
}
|
|
31517
31777
|
var BUN_LOCKFILES = ["bun.lock", "bun.lockb"];
|
|
31518
31778
|
function detectPackageManager(cwd) {
|
|
31519
|
-
if (resolveBunBin() && BUN_LOCKFILES.some((name) => pathExistsSync(
|
|
31779
|
+
if (resolveBunBin() && BUN_LOCKFILES.some((name) => pathExistsSync(path96.join(cwd, name)))) {
|
|
31520
31780
|
return "bun";
|
|
31521
31781
|
}
|
|
31522
|
-
if (Bun.which("pnpm") && pathExistsSync(
|
|
31782
|
+
if (Bun.which("pnpm") && pathExistsSync(path96.join(cwd, "pnpm-lock.yaml"))) {
|
|
31523
31783
|
return "pnpm";
|
|
31524
31784
|
}
|
|
31525
|
-
if (Bun.which("yarn") && pathExistsSync(
|
|
31785
|
+
if (Bun.which("yarn") && pathExistsSync(path96.join(cwd, "yarn.lock"))) {
|
|
31526
31786
|
return "yarn";
|
|
31527
31787
|
}
|
|
31528
31788
|
return "npm";
|
|
@@ -31862,7 +32122,7 @@ async function compatibleReportForHealth(ctx) {
|
|
|
31862
32122
|
|
|
31863
32123
|
// src/health/sources/dependency-audit.ts
|
|
31864
32124
|
import { existsSync as existsSync13 } from "fs";
|
|
31865
|
-
import
|
|
32125
|
+
import path97 from "path";
|
|
31866
32126
|
function auditSeverityToPriority(severity) {
|
|
31867
32127
|
if (severity === "critical" || severity === "high")
|
|
31868
32128
|
return "P0";
|
|
@@ -31981,7 +32241,7 @@ var LOCKFILE_COMMANDS = [
|
|
|
31981
32241
|
];
|
|
31982
32242
|
function auditCommand(cwd) {
|
|
31983
32243
|
for (const entry of LOCKFILE_COMMANDS) {
|
|
31984
|
-
if (!entry.lockfiles.some((name) => existsSync13(
|
|
32244
|
+
if (!entry.lockfiles.some((name) => existsSync13(path97.join(cwd, name))))
|
|
31985
32245
|
continue;
|
|
31986
32246
|
const bin = resolveBin(cwd, entry.bin);
|
|
31987
32247
|
if (bin)
|
|
@@ -32040,13 +32300,13 @@ var dependencyAuditAdapter = {
|
|
|
32040
32300
|
|
|
32041
32301
|
// src/health/sources/sonarqube.ts
|
|
32042
32302
|
import { existsSync as existsSync14 } from "fs";
|
|
32043
|
-
import
|
|
32303
|
+
import path98 from "path";
|
|
32044
32304
|
var REPORT_FILES = ["sonar-issues.json", ".metaproject/data/health/sonar-issues.json"];
|
|
32045
32305
|
function reportPath(cwd) {
|
|
32046
32306
|
const fromEnv = process.env.SONAR_ISSUES_FILE;
|
|
32047
32307
|
const candidates = fromEnv ? [fromEnv, ...REPORT_FILES] : REPORT_FILES;
|
|
32048
32308
|
for (const rel of candidates) {
|
|
32049
|
-
const abs =
|
|
32309
|
+
const abs = path98.isAbsolute(rel) ? rel : path98.join(cwd, rel);
|
|
32050
32310
|
if (existsSync14(abs)) {
|
|
32051
32311
|
return abs;
|
|
32052
32312
|
}
|
|
@@ -32130,7 +32390,7 @@ var sonarqubeAdapter = {
|
|
|
32130
32390
|
function relativizeComponent(component, cwd) {
|
|
32131
32391
|
const colon = component.indexOf(":");
|
|
32132
32392
|
const value = colon >= 0 ? component.slice(colon + 1) : component;
|
|
32133
|
-
return
|
|
32393
|
+
return path98.isAbsolute(value) ? path98.relative(cwd, value) : value;
|
|
32134
32394
|
}
|
|
32135
32395
|
|
|
32136
32396
|
// src/health/sources/index.ts
|
|
@@ -32467,19 +32727,19 @@ function filteredInfo(source, cfg) {
|
|
|
32467
32727
|
};
|
|
32468
32728
|
}
|
|
32469
32729
|
async function writeOutputs(cwd, report, config, stamp) {
|
|
32470
|
-
const artifacts =
|
|
32471
|
-
const history =
|
|
32730
|
+
const artifacts = path99.join(dataRoot3(cwd), "artifacts");
|
|
32731
|
+
const history = path99.join(dataRoot3(cwd), "history");
|
|
32472
32732
|
await mkdir27(artifacts, { recursive: true });
|
|
32473
32733
|
await mkdir27(history, { recursive: true });
|
|
32474
|
-
const markdown =
|
|
32475
|
-
const json =
|
|
32476
|
-
const historyJson =
|
|
32734
|
+
const markdown = path99.join(artifacts, "latest.md");
|
|
32735
|
+
const json = path99.join(artifacts, "latest.json");
|
|
32736
|
+
const historyJson = path99.join(history, `${stamp}.json`);
|
|
32477
32737
|
const serialized = `${JSON.stringify(report, null, 2)}
|
|
32478
32738
|
`;
|
|
32479
32739
|
await writeFile20(markdown, renderReportMarkdown2(report, config), "utf8");
|
|
32480
32740
|
if (report.runId) {
|
|
32481
|
-
const runJson =
|
|
32482
|
-
const runMarkdown =
|
|
32741
|
+
const runJson = path99.join(artifacts, "runs", `${report.runId}.json`);
|
|
32742
|
+
const runMarkdown = path99.join(artifacts, "runs", `${report.runId}.md`);
|
|
32483
32743
|
if (await pathExists(runJson))
|
|
32484
32744
|
throw new Error(`immutable health run already exists: ${report.runId}`);
|
|
32485
32745
|
await writeFileAtomic(runJson, serialized);
|
|
@@ -32490,7 +32750,7 @@ async function writeOutputs(cwd, report, config, stamp) {
|
|
|
32490
32750
|
branch: report.provenance?.branch ?? null,
|
|
32491
32751
|
worktree: report.provenance?.worktree ?? null,
|
|
32492
32752
|
generated_at: report.generatedAt,
|
|
32493
|
-
record:
|
|
32753
|
+
record: path99.posix.join("runs", `${report.runId}.json`)
|
|
32494
32754
|
}, null, 2)}
|
|
32495
32755
|
`);
|
|
32496
32756
|
} else {
|
|
@@ -32498,8 +32758,8 @@ async function writeOutputs(cwd, report, config, stamp) {
|
|
|
32498
32758
|
}
|
|
32499
32759
|
await writeFile20(historyJson, serialized, "utf8");
|
|
32500
32760
|
return {
|
|
32501
|
-
markdownPath:
|
|
32502
|
-
jsonPath:
|
|
32761
|
+
markdownPath: path99.relative(cwd, markdown),
|
|
32762
|
+
jsonPath: path99.relative(cwd, json)
|
|
32503
32763
|
};
|
|
32504
32764
|
}
|
|
32505
32765
|
async function resolveChanged(cwd, selector) {
|
|
@@ -32552,7 +32812,7 @@ function hasGateShape(value) {
|
|
|
32552
32812
|
return Array.isArray(report.metrics) && Array.isArray(report.sources) && Array.isArray(report.findings);
|
|
32553
32813
|
}
|
|
32554
32814
|
async function readLatest(cwd) {
|
|
32555
|
-
const file =
|
|
32815
|
+
const file = path100.join(dataRoot3(cwd), "artifacts", "latest.json");
|
|
32556
32816
|
if (!await pathExists(file)) {
|
|
32557
32817
|
return null;
|
|
32558
32818
|
}
|
|
@@ -32563,11 +32823,11 @@ async function readLatest(cwd) {
|
|
|
32563
32823
|
const latest = read.value;
|
|
32564
32824
|
const record = latest.record;
|
|
32565
32825
|
if (typeof record === "string") {
|
|
32566
|
-
const pointer = await readArtifactPointer(
|
|
32826
|
+
const pointer = await readArtifactPointer(path100.join(dataRoot3(cwd), "artifacts"), await collectGitProvenance(cwd));
|
|
32567
32827
|
if (pointer.status !== "fresh")
|
|
32568
32828
|
return null;
|
|
32569
|
-
const recordFile =
|
|
32570
|
-
if (!isPathInside(
|
|
32829
|
+
const recordFile = path100.join(dataRoot3(cwd), "artifacts", record);
|
|
32830
|
+
if (!isPathInside(path100.join(dataRoot3(cwd), "artifacts"), recordFile))
|
|
32571
32831
|
return null;
|
|
32572
32832
|
if (!await pathExists(recordFile))
|
|
32573
32833
|
return null;
|
|
@@ -32640,7 +32900,7 @@ function createCodeHealthService() {
|
|
|
32640
32900
|
return {
|
|
32641
32901
|
run: (input) => runHealth(input),
|
|
32642
32902
|
async status(input) {
|
|
32643
|
-
const enabled = await pathExists(
|
|
32903
|
+
const enabled = await pathExists(path100.join(input.cwd, ".metaproject", "health.config.json"));
|
|
32644
32904
|
const latest = await readLatest(input.cwd);
|
|
32645
32905
|
const project = latest?.metrics.find((m) => m.key === "project") ?? null;
|
|
32646
32906
|
const decliningScopes = latest ? latest.metrics.filter((m) => m.regression_score > 0).length : 0;
|
|
@@ -32798,10 +33058,10 @@ function renderEvidenceBlock(evidences, siblings) {
|
|
|
32798
33058
|
init_fs();
|
|
32799
33059
|
init_contained_write();
|
|
32800
33060
|
init_json();
|
|
32801
|
-
import
|
|
33061
|
+
import path101 from "path";
|
|
32802
33062
|
import { createHash as createHash16 } from "crypto";
|
|
32803
33063
|
function impactEvidenceDataRoot(root) {
|
|
32804
|
-
return
|
|
33064
|
+
return path101.join(root, ".metaproject", "data", "security", "impact-evidence");
|
|
32805
33065
|
}
|
|
32806
33066
|
function sanitizeSessionId(sessionId) {
|
|
32807
33067
|
const cleaned = sessionId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -32813,7 +33073,7 @@ function sessionStateFilename(sessionId) {
|
|
|
32813
33073
|
return `${sanitized}-${suffix}.json`;
|
|
32814
33074
|
}
|
|
32815
33075
|
function sessionStatePath(root, sessionId) {
|
|
32816
|
-
return
|
|
33076
|
+
return path101.join(impactEvidenceDataRoot(root), "sessions", sessionStateFilename(sessionId));
|
|
32817
33077
|
}
|
|
32818
33078
|
async function loadSessionState(root, sessionId) {
|
|
32819
33079
|
const file = sessionStatePath(root, sessionId);
|
|
@@ -32832,11 +33092,11 @@ async function loadSessionState(root, sessionId) {
|
|
|
32832
33092
|
}
|
|
32833
33093
|
async function saveSessionState(root, sessionId, state) {
|
|
32834
33094
|
const file = sessionStatePath(root, sessionId);
|
|
32835
|
-
await writeContained(root,
|
|
33095
|
+
await writeContained(root, path101.relative(root, file), `${JSON.stringify(state, null, 2)}
|
|
32836
33096
|
`);
|
|
32837
33097
|
}
|
|
32838
33098
|
function logPath(root) {
|
|
32839
|
-
return
|
|
33099
|
+
return path101.join(impactEvidenceDataRoot(root), "log.jsonl");
|
|
32840
33100
|
}
|
|
32841
33101
|
async function appendLogRecord(root, record) {
|
|
32842
33102
|
const full = {
|
|
@@ -32847,7 +33107,7 @@ async function appendLogRecord(root, record) {
|
|
|
32847
33107
|
...record.detail !== undefined ? { detail: record.detail } : {}
|
|
32848
33108
|
};
|
|
32849
33109
|
const file = logPath(root);
|
|
32850
|
-
await appendContained(root,
|
|
33110
|
+
await appendContained(root, path101.relative(root, file), `${JSON.stringify(full)}
|
|
32851
33111
|
`);
|
|
32852
33112
|
return full;
|
|
32853
33113
|
}
|
|
@@ -32874,7 +33134,7 @@ init_command_risk();
|
|
|
32874
33134
|
init_shell_syntax();
|
|
32875
33135
|
init_fs();
|
|
32876
33136
|
init_service2();
|
|
32877
|
-
import
|
|
33137
|
+
import path102 from "path";
|
|
32878
33138
|
import { createHash as createHash17 } from "crypto";
|
|
32879
33139
|
import { realpathSync as realpathSync2 } from "fs";
|
|
32880
33140
|
function impactEvidenceHookClass(strict) {
|
|
@@ -32890,29 +33150,29 @@ function realpathNearestExisting(p) {
|
|
|
32890
33150
|
for (;; ) {
|
|
32891
33151
|
try {
|
|
32892
33152
|
const real = realpathSync2(current);
|
|
32893
|
-
return tail.length > 0 ?
|
|
33153
|
+
return tail.length > 0 ? path102.join(real, ...tail) : real;
|
|
32894
33154
|
} catch {
|
|
32895
|
-
const parent =
|
|
33155
|
+
const parent = path102.dirname(current);
|
|
32896
33156
|
if (parent === current) {
|
|
32897
|
-
return tail.length > 0 ?
|
|
33157
|
+
return tail.length > 0 ? path102.join(current, ...tail) : current;
|
|
32898
33158
|
}
|
|
32899
|
-
tail.unshift(
|
|
33159
|
+
tail.unshift(path102.basename(current));
|
|
32900
33160
|
current = parent;
|
|
32901
33161
|
}
|
|
32902
33162
|
}
|
|
32903
33163
|
}
|
|
32904
33164
|
function normalizeRequestFiles(root, files) {
|
|
32905
|
-
const rootReal = realpathNearestExisting(
|
|
33165
|
+
const rootReal = realpathNearestExisting(path102.resolve(root));
|
|
32906
33166
|
const normalized = [];
|
|
32907
33167
|
const rejected = [];
|
|
32908
33168
|
for (const file of files) {
|
|
32909
|
-
const lexicalAbsolute =
|
|
33169
|
+
const lexicalAbsolute = path102.isAbsolute(file) ? path102.resolve(file) : path102.resolve(rootReal, toPosix(file).replace(/^\.\//, ""));
|
|
32910
33170
|
const effective = realpathNearestExisting(lexicalAbsolute);
|
|
32911
33171
|
if (!isPathInside(rootReal, effective)) {
|
|
32912
33172
|
rejected.push(file);
|
|
32913
33173
|
continue;
|
|
32914
33174
|
}
|
|
32915
|
-
normalized.push(toPosix(
|
|
33175
|
+
normalized.push(toPosix(path102.relative(rootReal, effective)));
|
|
32916
33176
|
}
|
|
32917
33177
|
return { files: [...new Set(normalized)], rejected };
|
|
32918
33178
|
}
|
|
@@ -33198,7 +33458,7 @@ __export(exports_service8, {
|
|
|
33198
33458
|
createJobService: () => createJobService
|
|
33199
33459
|
});
|
|
33200
33460
|
import { copyFile, mkdir as mkdir29 } from "fs/promises";
|
|
33201
|
-
import
|
|
33461
|
+
import path105 from "path";
|
|
33202
33462
|
|
|
33203
33463
|
// src/job/types.ts
|
|
33204
33464
|
var JOB_INTENTS = ["implement", "analyze", "review", "custom"];
|
|
@@ -33338,16 +33598,16 @@ function defaultPlan(intent) {
|
|
|
33338
33598
|
init_validator();
|
|
33339
33599
|
init_contracts();
|
|
33340
33600
|
init_fs();
|
|
33341
|
-
import { appendFile as appendFile5, mkdir as mkdir28, readFile as
|
|
33342
|
-
import
|
|
33601
|
+
import { appendFile as appendFile5, mkdir as mkdir28, readFile as readFile51, readdir as readdir17 } from "fs/promises";
|
|
33602
|
+
import path104 from "path";
|
|
33343
33603
|
function jobsRoot(cwd) {
|
|
33344
|
-
return
|
|
33604
|
+
return path104.join(cwd, ".metaproject", "jobs");
|
|
33345
33605
|
}
|
|
33346
33606
|
function jobDir(cwd, name) {
|
|
33347
|
-
return
|
|
33607
|
+
return path104.join(jobsRoot(cwd), name);
|
|
33348
33608
|
}
|
|
33349
33609
|
function jobStatePath(cwd, name) {
|
|
33350
|
-
return
|
|
33610
|
+
return path104.join(jobDir(cwd, name), "state.json");
|
|
33351
33611
|
}
|
|
33352
33612
|
var JOB_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
33353
33613
|
function assertJobName(name) {
|
|
@@ -33364,7 +33624,7 @@ async function listJobNames(cwd) {
|
|
|
33364
33624
|
if (!await pathExists(root)) {
|
|
33365
33625
|
return [];
|
|
33366
33626
|
}
|
|
33367
|
-
const entries = await
|
|
33627
|
+
const entries = await readdir17(root, { withFileTypes: true });
|
|
33368
33628
|
return entries.filter((entry) => entry.isDirectory() && JOB_NAME_PATTERN.test(entry.name)).map((entry) => entry.name).sort();
|
|
33369
33629
|
}
|
|
33370
33630
|
async function readJob(cwd, name) {
|
|
@@ -33373,7 +33633,7 @@ async function readJob(cwd, name) {
|
|
|
33373
33633
|
const known = await listJobNames(cwd);
|
|
33374
33634
|
throw new Error(`Job not found: ${name}.` + (known.length > 0 ? ` Known: ${known.join(", ")}` : " Run: keryx job list"));
|
|
33375
33635
|
}
|
|
33376
|
-
return JSON.parse(await
|
|
33636
|
+
return JSON.parse(await readFile51(file, "utf8"));
|
|
33377
33637
|
}
|
|
33378
33638
|
async function writeJob(cwd, name, state) {
|
|
33379
33639
|
const schema = await loadSchema("job-orchestrator-state");
|
|
@@ -33384,12 +33644,12 @@ async function writeJob(cwd, name, state) {
|
|
|
33384
33644
|
`));
|
|
33385
33645
|
}
|
|
33386
33646
|
const file = jobStatePath(cwd, name);
|
|
33387
|
-
await mkdir28(
|
|
33647
|
+
await mkdir28(path104.dirname(file), { recursive: true });
|
|
33388
33648
|
await writeFileAtomic(file, `${JSON.stringify(state, null, 2)}
|
|
33389
33649
|
`);
|
|
33390
33650
|
}
|
|
33391
33651
|
async function appendJournal2(cwd, name, at, line) {
|
|
33392
|
-
await appendFile5(
|
|
33652
|
+
await appendFile5(path104.join(jobDir(cwd, name), "journal.md"), `- ${at} - ${line}
|
|
33393
33653
|
`, "utf8");
|
|
33394
33654
|
}
|
|
33395
33655
|
function renderJournal2(name, at) {
|
|
@@ -33410,7 +33670,7 @@ function assertDocumentType(raw) {
|
|
|
33410
33670
|
return raw;
|
|
33411
33671
|
}
|
|
33412
33672
|
function documentFileName(type, sourcePath) {
|
|
33413
|
-
const extension =
|
|
33673
|
+
const extension = path104.extname(sourcePath) || ".md";
|
|
33414
33674
|
return `${type}${extension}`;
|
|
33415
33675
|
}
|
|
33416
33676
|
|
|
@@ -33418,7 +33678,7 @@ function documentFileName(type, sourcePath) {
|
|
|
33418
33678
|
function createJobService(deps) {
|
|
33419
33679
|
const now = () => deps.now().toISOString();
|
|
33420
33680
|
function lockPath(cwd, name) {
|
|
33421
|
-
return
|
|
33681
|
+
return path105.join(jobsRoot(cwd), `.job-lock-${name}`);
|
|
33422
33682
|
}
|
|
33423
33683
|
async function mutate(cwd, name, run) {
|
|
33424
33684
|
assertJobName(name);
|
|
@@ -33467,20 +33727,20 @@ function createJobService(deps) {
|
|
|
33467
33727
|
},
|
|
33468
33728
|
plan: { steps: defaultPlan(intent) },
|
|
33469
33729
|
documentation: {
|
|
33470
|
-
job_path:
|
|
33730
|
+
job_path: path105.join(".metaproject", "jobs", name),
|
|
33471
33731
|
documents_created: []
|
|
33472
33732
|
},
|
|
33473
33733
|
metrics: { steps: [] },
|
|
33474
|
-
jobs_root:
|
|
33734
|
+
jobs_root: path105.join(".metaproject", "jobs"),
|
|
33475
33735
|
updated_at: createdAt
|
|
33476
33736
|
};
|
|
33477
33737
|
const first = firstOpenStep(state.plan.steps);
|
|
33478
33738
|
if (first) {
|
|
33479
33739
|
state.plan.current_step = first.id;
|
|
33480
33740
|
}
|
|
33481
|
-
await writeFileAtomic(
|
|
33741
|
+
await writeFileAtomic(path105.join(absolute, "journal.md"), renderJournal2(name, createdAt));
|
|
33482
33742
|
await writeJob(input.cwd, name, state);
|
|
33483
|
-
return { state, dir:
|
|
33743
|
+
return { state, dir: path105.relative(input.cwd, absolute) };
|
|
33484
33744
|
},
|
|
33485
33745
|
async status({ cwd, name }) {
|
|
33486
33746
|
assertJobName(name);
|
|
@@ -33492,7 +33752,7 @@ function createJobService(deps) {
|
|
|
33492
33752
|
}
|
|
33493
33753
|
return {
|
|
33494
33754
|
state,
|
|
33495
|
-
dir:
|
|
33755
|
+
dir: path105.join(".metaproject", "jobs", name),
|
|
33496
33756
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
33497
33757
|
stepsTotal: state.plan.steps.length,
|
|
33498
33758
|
nextStep: firstOpenStep(state.plan.steps),
|
|
@@ -33549,23 +33809,23 @@ function createJobService(deps) {
|
|
|
33549
33809
|
if (!input.file) {
|
|
33550
33810
|
throw new Error("Missing --file. Usage: keryx job document <name> --type <type> --file <path>");
|
|
33551
33811
|
}
|
|
33552
|
-
const source =
|
|
33812
|
+
const source = path105.isAbsolute(input.file) ? input.file : path105.join(input.cwd, input.file);
|
|
33553
33813
|
if (!await pathExists(source)) {
|
|
33554
33814
|
throw new Error(`--file not found: ${input.file}. Write the document first, then record it.`);
|
|
33555
33815
|
}
|
|
33556
33816
|
let recorded;
|
|
33557
33817
|
await mutate(input.cwd, input.name, async (state) => {
|
|
33558
33818
|
const fileName = documentFileName(type, source);
|
|
33559
|
-
const target =
|
|
33819
|
+
const target = path105.join(jobDir(input.cwd, state.job_name), fileName);
|
|
33560
33820
|
await copyFile(source, target);
|
|
33561
33821
|
state.documentation ??= {};
|
|
33562
|
-
state.documentation.job_path ??=
|
|
33822
|
+
state.documentation.job_path ??= path105.join(".metaproject", "jobs", state.job_name);
|
|
33563
33823
|
const created = state.documentation.documents_created ?? [];
|
|
33564
33824
|
state.documentation.documents_created = created.includes(fileName) ? created : [...created, fileName];
|
|
33565
33825
|
recorded = {
|
|
33566
33826
|
state,
|
|
33567
33827
|
document: fileName,
|
|
33568
|
-
path:
|
|
33828
|
+
path: path105.join(".metaproject", "jobs", state.job_name, fileName)
|
|
33569
33829
|
};
|
|
33570
33830
|
return { state, event: "document", detail: `${type} -> ${fileName}` };
|
|
33571
33831
|
});
|
|
@@ -33600,7 +33860,7 @@ function createJobService(deps) {
|
|
|
33600
33860
|
name: state.job_name,
|
|
33601
33861
|
phase: state.phase,
|
|
33602
33862
|
intent: state.intent,
|
|
33603
|
-
dir:
|
|
33863
|
+
dir: path105.join(".metaproject", "jobs", name),
|
|
33604
33864
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
33605
33865
|
stepsTotal: state.plan.steps.length,
|
|
33606
33866
|
nextStep: firstOpenStep(state.plan.steps)?.id ?? null
|
|
@@ -33704,13 +33964,13 @@ function normalizeWorkspaceReference(raw) {
|
|
|
33704
33964
|
|
|
33705
33965
|
// src/sac/fwk-service.ts
|
|
33706
33966
|
init_assembly();
|
|
33707
|
-
import { appendFile as appendFile6, mkdir as mkdir31, open as open3, readFile as
|
|
33708
|
-
import
|
|
33967
|
+
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";
|
|
33968
|
+
import path109 from "path";
|
|
33709
33969
|
|
|
33710
33970
|
// src/sac/index.ts
|
|
33711
|
-
import { access as access3, readFile as
|
|
33971
|
+
import { access as access3, readFile as readFile52, realpath as realpath12 } from "fs/promises";
|
|
33712
33972
|
import { createHash as createHash18 } from "crypto";
|
|
33713
|
-
import
|
|
33973
|
+
import path106 from "path";
|
|
33714
33974
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
33715
33975
|
var idPattern = /^[a-z][a-z0-9-]{2,63}$/;
|
|
33716
33976
|
var subjectPattern = /^(?:user|team|service|agent):[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
@@ -33728,21 +33988,21 @@ var normativeSchemaFiles = {
|
|
|
33728
33988
|
"review-decision": "review-decision.schema.json"
|
|
33729
33989
|
};
|
|
33730
33990
|
var normativeSchemas = new Map;
|
|
33731
|
-
var NORMATIVE_SCHEMA_DIR =
|
|
33991
|
+
var NORMATIVE_SCHEMA_DIR = path106.join("docs", "requirements", "shared-agent-context", "schemas");
|
|
33732
33992
|
async function resolveSacNormativeSchemaPath(fileName, searchFrom = [fileURLToPath3(new URL(".", import.meta.url)), process.cwd()]) {
|
|
33733
33993
|
const seen = new Set;
|
|
33734
33994
|
for (const start of searchFrom) {
|
|
33735
|
-
let dir =
|
|
33995
|
+
let dir = path106.resolve(start);
|
|
33736
33996
|
for (let i = 0;i < 10; i++) {
|
|
33737
33997
|
if (seen.has(dir))
|
|
33738
33998
|
break;
|
|
33739
33999
|
seen.add(dir);
|
|
33740
|
-
const candidate =
|
|
34000
|
+
const candidate = path106.join(dir, NORMATIVE_SCHEMA_DIR, fileName);
|
|
33741
34001
|
try {
|
|
33742
34002
|
await access3(candidate);
|
|
33743
34003
|
return candidate;
|
|
33744
34004
|
} catch {}
|
|
33745
|
-
const parent =
|
|
34005
|
+
const parent = path106.dirname(dir);
|
|
33746
34006
|
if (parent === dir)
|
|
33747
34007
|
break;
|
|
33748
34008
|
dir = parent;
|
|
@@ -33788,7 +34048,7 @@ function isRecord5(value) {
|
|
|
33788
34048
|
async function loadNormativeSchema(schema) {
|
|
33789
34049
|
let pending = normativeSchemas.get(schema);
|
|
33790
34050
|
if (!pending) {
|
|
33791
|
-
pending = resolveSacNormativeSchemaPath(normativeSchemaFiles[schema]).then((file) =>
|
|
34051
|
+
pending = resolveSacNormativeSchemaPath(normativeSchemaFiles[schema]).then((file) => readFile52(file, "utf8")).then((source) => JSON.parse(source));
|
|
33792
34052
|
normativeSchemas.set(schema, pending);
|
|
33793
34053
|
}
|
|
33794
34054
|
return pending;
|
|
@@ -34411,20 +34671,20 @@ class SacReferenceError extends Error {
|
|
|
34411
34671
|
code = "unsafe_workspace_reference";
|
|
34412
34672
|
}
|
|
34413
34673
|
async function resolveWorkspaceReference(input) {
|
|
34414
|
-
if (!workspacePathPattern.test(input.uri) ||
|
|
34674
|
+
if (!workspacePathPattern.test(input.uri) || path106.isAbsolute(input.uri) || /^[a-z][a-z0-9+.-]*:/i.test(input.uri) || input.uri.includes("\\"))
|
|
34415
34675
|
throw new SacReferenceError("unsafe workspace reference");
|
|
34416
|
-
const lexicalRoot =
|
|
34417
|
-
const root = await
|
|
34418
|
-
const candidate =
|
|
34419
|
-
if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${
|
|
34676
|
+
const lexicalRoot = path106.resolve(input.workspaceRoot);
|
|
34677
|
+
const root = await realpath12(lexicalRoot);
|
|
34678
|
+
const candidate = path106.resolve(lexicalRoot, input.uri.slice(2));
|
|
34679
|
+
if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${path106.sep}`))
|
|
34420
34680
|
throw new SacReferenceError("workspace reference escapes root");
|
|
34421
34681
|
let resolved;
|
|
34422
34682
|
try {
|
|
34423
|
-
resolved = await
|
|
34683
|
+
resolved = await realpath12(candidate);
|
|
34424
34684
|
} catch {
|
|
34425
34685
|
throw new SacReferenceError("workspace reference is not resolvable");
|
|
34426
34686
|
}
|
|
34427
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
34687
|
+
if (resolved !== root && !resolved.startsWith(`${root}${path106.sep}`))
|
|
34428
34688
|
throw new SacReferenceError("workspace reference escapes root after realpath");
|
|
34429
34689
|
return candidate;
|
|
34430
34690
|
}
|
|
@@ -34478,18 +34738,18 @@ async function evaluateStrictSacGuard(input) {
|
|
|
34478
34738
|
|
|
34479
34739
|
// src/sac/workspace-service.ts
|
|
34480
34740
|
init_fs();
|
|
34481
|
-
import { mkdir as mkdir30, readdir as
|
|
34741
|
+
import { mkdir as mkdir30, readdir as readdir18, readFile as readFile53 } from "fs/promises";
|
|
34482
34742
|
import { createHash as createHash19, randomUUID as randomUUID3 } from "crypto";
|
|
34483
|
-
import
|
|
34743
|
+
import path108 from "path";
|
|
34484
34744
|
|
|
34485
34745
|
// src/sac/secure-resource-read.ts
|
|
34486
34746
|
init_contained_read();
|
|
34487
34747
|
init_descriptor_read();
|
|
34488
|
-
import
|
|
34748
|
+
import path107 from "path";
|
|
34489
34749
|
function readWorkspaceFileNoFollow(workspaceRoot, absolutePath) {
|
|
34490
|
-
const relativePath =
|
|
34491
|
-
const components = relativePath.split(
|
|
34492
|
-
if (!relativePath ||
|
|
34750
|
+
const relativePath = path107.relative(workspaceRoot, absolutePath);
|
|
34751
|
+
const components = relativePath.split(path107.sep);
|
|
34752
|
+
if (!relativePath || path107.isAbsolute(relativePath) || components.some((component) => !component || component === "." || component === "..")) {
|
|
34493
34753
|
throw new Error("safe source path is not workspace-relative");
|
|
34494
34754
|
}
|
|
34495
34755
|
return readDescriptorChain(workspaceRoot, absolutePath, {
|
|
@@ -34516,8 +34776,8 @@ class WorkspaceService {
|
|
|
34516
34776
|
now;
|
|
34517
34777
|
constructor(options) {
|
|
34518
34778
|
this.options = options;
|
|
34519
|
-
this.root =
|
|
34520
|
-
this.storageRoot =
|
|
34779
|
+
this.root = path108.resolve(options.workspaceRoot);
|
|
34780
|
+
this.storageRoot = path108.resolve(options.storageRoot ?? path108.join(this.root, ".metaproject", "workspaces"));
|
|
34521
34781
|
this.now = options.now ?? (() => new Date);
|
|
34522
34782
|
}
|
|
34523
34783
|
async create(input) {
|
|
@@ -34540,7 +34800,7 @@ class WorkspaceService {
|
|
|
34540
34800
|
await mkdir30(this.storageRoot, { recursive: true, mode: 448 });
|
|
34541
34801
|
await withFileLock(this.lockPath(manifest.id), async () => {
|
|
34542
34802
|
try {
|
|
34543
|
-
await
|
|
34803
|
+
await readFile53(this.manifestPath(manifest.id), "utf8");
|
|
34544
34804
|
throw new WorkspaceServiceError("conflict", "workspace already exists");
|
|
34545
34805
|
} catch (error) {
|
|
34546
34806
|
if (error instanceof WorkspaceServiceError || !isNotFound(error))
|
|
@@ -34569,7 +34829,7 @@ class WorkspaceService {
|
|
|
34569
34829
|
} catch {
|
|
34570
34830
|
return [];
|
|
34571
34831
|
}
|
|
34572
|
-
const entries = await
|
|
34832
|
+
const entries = await readdir18(this.storageRoot, { withFileTypes: true });
|
|
34573
34833
|
const visible = [];
|
|
34574
34834
|
for (const entry of entries) {
|
|
34575
34835
|
if (!entry.isDirectory())
|
|
@@ -34830,7 +35090,7 @@ class WorkspaceService {
|
|
|
34830
35090
|
throw new WorkspaceServiceError("not_found", "workspace not found");
|
|
34831
35091
|
let parsed;
|
|
34832
35092
|
try {
|
|
34833
|
-
parsed = JSON.parse(await
|
|
35093
|
+
parsed = JSON.parse(await readFile53(this.manifestPath(id), "utf8"));
|
|
34834
35094
|
} catch (error) {
|
|
34835
35095
|
if (isNotFound(error))
|
|
34836
35096
|
throw new WorkspaceServiceError("not_found", "workspace not found");
|
|
@@ -34842,13 +35102,13 @@ class WorkspaceService {
|
|
|
34842
35102
|
return parsed;
|
|
34843
35103
|
}
|
|
34844
35104
|
workspaceDir(id) {
|
|
34845
|
-
return
|
|
35105
|
+
return path108.join(this.storageRoot, id);
|
|
34846
35106
|
}
|
|
34847
35107
|
manifestPath(id) {
|
|
34848
|
-
return
|
|
35108
|
+
return path108.join(this.workspaceDir(id), "workspace.json");
|
|
34849
35109
|
}
|
|
34850
35110
|
lockPath(id) {
|
|
34851
|
-
return
|
|
35111
|
+
return path108.join(this.storageRoot, `.${id}.lock`);
|
|
34852
35112
|
}
|
|
34853
35113
|
timestamp() {
|
|
34854
35114
|
return this.now().toISOString();
|
|
@@ -35457,7 +35717,7 @@ var ledgerDigestChunkBytes = 64 * 1024;
|
|
|
35457
35717
|
var isMissing = (error) => error instanceof Error && error.code === "ENOENT";
|
|
35458
35718
|
var checkpointHash = (body) => createHash22("sha256").update(JSON.stringify(body), "utf8").digest("hex");
|
|
35459
35719
|
async function ledgerByteLength(ledger) {
|
|
35460
|
-
const value = await
|
|
35720
|
+
const value = await stat14(ledger, { bigint: true });
|
|
35461
35721
|
if (value.size > BigInt(Number.MAX_SAFE_INTEGER))
|
|
35462
35722
|
throw new Error("invalid access receipt ledger: ledger-too-large");
|
|
35463
35723
|
return Number(value.size);
|
|
@@ -35503,7 +35763,7 @@ function parseCheckpoint(value) {
|
|
|
35503
35763
|
}
|
|
35504
35764
|
async function readCheckpoint(checkpointPath) {
|
|
35505
35765
|
try {
|
|
35506
|
-
const raw = await
|
|
35766
|
+
const raw = await readFile54(checkpointPath, "utf8");
|
|
35507
35767
|
try {
|
|
35508
35768
|
const checkpoint = parseCheckpoint(JSON.parse(raw));
|
|
35509
35769
|
return checkpoint ? { present: true, checkpoint } : { present: true };
|
|
@@ -35571,7 +35831,7 @@ async function fastCheckpointState(ledger, checkpoint, ledgerBytes) {
|
|
|
35571
35831
|
}
|
|
35572
35832
|
}
|
|
35573
35833
|
async function auditLedger(ledger, checkpointPath, verifier) {
|
|
35574
|
-
const bytes = await
|
|
35834
|
+
const bytes = await readFile54(ledger);
|
|
35575
35835
|
const raw = bytes.toString("utf8");
|
|
35576
35836
|
if (raw.length > 0 && !raw.endsWith(`
|
|
35577
35837
|
`))
|
|
@@ -35621,7 +35881,7 @@ var isEvaluationReport = (report) => report.schemaVersion === "1.0" && asString2
|
|
|
35621
35881
|
var isConfigRecord = (value) => typeof value.enabled === "boolean" && typeof value.killSwitch === "boolean";
|
|
35622
35882
|
async function readPinnedJson(input) {
|
|
35623
35883
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input.workspaceRoot, kind: "artifact", uri: input.uri });
|
|
35624
|
-
const raw = await
|
|
35884
|
+
const raw = await readFile54(ref, "utf8");
|
|
35625
35885
|
const digest = createHash22("sha256").update(raw).digest("hex");
|
|
35626
35886
|
if (digest !== input.digest)
|
|
35627
35887
|
throw new Error("policy experiment pinned artifact digest mismatch");
|
|
@@ -35629,7 +35889,7 @@ async function readPinnedJson(input) {
|
|
|
35629
35889
|
}
|
|
35630
35890
|
async function readWorkspaceJson(input) {
|
|
35631
35891
|
const ref = await resolveWorkspaceReference({ workspaceRoot: input.workspaceRoot, kind: "artifact", uri: input.uri });
|
|
35632
|
-
const raw = await
|
|
35892
|
+
const raw = await readFile54(ref, "utf8");
|
|
35633
35893
|
return { artifact: JSON.parse(raw), raw };
|
|
35634
35894
|
}
|
|
35635
35895
|
function hasReportIntegrity(report) {
|
|
@@ -35637,10 +35897,10 @@ function hasReportIntegrity(report) {
|
|
|
35637
35897
|
return reportHashPattern.test(reportDigest) && sha2564(body) === reportDigest;
|
|
35638
35898
|
}
|
|
35639
35899
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
35640
|
-
const configPath =
|
|
35900
|
+
const configPath = path109.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
35641
35901
|
let configJson;
|
|
35642
35902
|
try {
|
|
35643
|
-
const raw = await
|
|
35903
|
+
const raw = await readFile54(configPath, "utf8").catch((error) => {
|
|
35644
35904
|
if (isMissing(error))
|
|
35645
35905
|
return;
|
|
35646
35906
|
throw error;
|
|
@@ -35835,9 +36095,9 @@ class FwkReadService {
|
|
|
35835
36095
|
const recordedAt = nowIso(now);
|
|
35836
36096
|
const id = `receipt-${randomUUID4().replace(/-/g, "").slice(0, 16)}`;
|
|
35837
36097
|
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(
|
|
36098
|
+
const ledger = path109.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
|
|
36099
|
+
const checkpointPath = path109.join(path109.dirname(ledger), "access-receipts.checkpoint.json");
|
|
36100
|
+
await mkdir31(path109.dirname(ledger), { recursive: true, mode: 448 });
|
|
35841
36101
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
35842
36102
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
35843
36103
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -35940,8 +36200,8 @@ function normalizeFwkResult(result) {
|
|
|
35940
36200
|
// src/sac/proposal-lifecycle.ts
|
|
35941
36201
|
init_fs();
|
|
35942
36202
|
import { createHash as createHash29, randomUUID as randomUUID7 } from "crypto";
|
|
35943
|
-
import { appendFile as appendFile7, mkdir as mkdir34, readdir as
|
|
35944
|
-
import
|
|
36203
|
+
import { appendFile as appendFile7, mkdir as mkdir34, readdir as readdir20, readFile as readFile63 } from "fs/promises";
|
|
36204
|
+
import path119 from "path";
|
|
35945
36205
|
|
|
35946
36206
|
// src/sac/trusted-wrap-up.ts
|
|
35947
36207
|
import { createHash as createHash23, randomUUID as randomUUID5 } from "crypto";
|
|
@@ -36013,7 +36273,7 @@ function bindingHash(owner, intent) {
|
|
|
36013
36273
|
// src/sac/session-wrap-up.ts
|
|
36014
36274
|
import { createHash as createHash25 } from "crypto";
|
|
36015
36275
|
import { mkdir as mkdir33, writeFile as writeFile22 } from "fs/promises";
|
|
36016
|
-
import
|
|
36276
|
+
import path113 from "path";
|
|
36017
36277
|
|
|
36018
36278
|
// src/session/store.ts
|
|
36019
36279
|
init_config_dir();
|
|
@@ -36025,12 +36285,12 @@ import {
|
|
|
36025
36285
|
renameSync as renameSync2,
|
|
36026
36286
|
writeFileSync as writeFileSync2
|
|
36027
36287
|
} from "fs";
|
|
36028
|
-
import
|
|
36288
|
+
import path112 from "path";
|
|
36029
36289
|
|
|
36030
36290
|
// src/session/paths.ts
|
|
36031
36291
|
import { existsSync as existsSync16 } from "fs";
|
|
36032
36292
|
import { homedir } from "os";
|
|
36033
|
-
import
|
|
36293
|
+
import path110 from "path";
|
|
36034
36294
|
function keryxDataDir(override) {
|
|
36035
36295
|
if (override !== undefined && override.length > 0) {
|
|
36036
36296
|
return override;
|
|
@@ -36042,22 +36302,22 @@ function keryxDataDir(override) {
|
|
|
36042
36302
|
const home = homedir();
|
|
36043
36303
|
if (process.platform === "win32") {
|
|
36044
36304
|
const appData = process.env.APPDATA;
|
|
36045
|
-
const base = appData !== undefined && appData.length > 0 ? appData :
|
|
36046
|
-
return
|
|
36305
|
+
const base = appData !== undefined && appData.length > 0 ? appData : path110.join(home, "AppData", "Roaming");
|
|
36306
|
+
return path110.join(base, "keryx");
|
|
36047
36307
|
}
|
|
36048
36308
|
const xdg = process.env.XDG_DATA_HOME;
|
|
36049
|
-
const base = xdg !== undefined && xdg.length > 0 ? xdg :
|
|
36050
|
-
return
|
|
36309
|
+
const base = xdg !== undefined && xdg.length > 0 ? xdg : path110.join(home, ".local", "share");
|
|
36310
|
+
return path110.join(base, "keryx");
|
|
36051
36311
|
}
|
|
36052
36312
|
function resolveProjectRoot2(cwd) {
|
|
36053
|
-
const abs =
|
|
36313
|
+
const abs = path110.resolve(cwd);
|
|
36054
36314
|
let dir = abs;
|
|
36055
36315
|
for (;; ) {
|
|
36056
|
-
const gitPath =
|
|
36316
|
+
const gitPath = path110.join(dir, ".git");
|
|
36057
36317
|
if (existsSync16(gitPath)) {
|
|
36058
36318
|
return dir;
|
|
36059
36319
|
}
|
|
36060
|
-
const parent =
|
|
36320
|
+
const parent = path110.dirname(dir);
|
|
36061
36321
|
if (parent === dir) {
|
|
36062
36322
|
break;
|
|
36063
36323
|
}
|
|
@@ -36066,12 +36326,12 @@ function resolveProjectRoot2(cwd) {
|
|
|
36066
36326
|
return abs;
|
|
36067
36327
|
}
|
|
36068
36328
|
function projectKeyFromPath(projectPath) {
|
|
36069
|
-
const abs =
|
|
36329
|
+
const abs = path110.resolve(projectPath);
|
|
36070
36330
|
const encoded = encodeURIComponent(abs);
|
|
36071
36331
|
if (encoded.length <= 200) {
|
|
36072
36332
|
return encoded;
|
|
36073
36333
|
}
|
|
36074
|
-
const base =
|
|
36334
|
+
const base = path110.basename(abs).replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40);
|
|
36075
36335
|
let h = 0;
|
|
36076
36336
|
for (let i = 0;i < abs.length; i++) {
|
|
36077
36337
|
h = Math.imul(31, h) + abs.charCodeAt(i) | 0;
|
|
@@ -36081,17 +36341,17 @@ function projectKeyFromPath(projectPath) {
|
|
|
36081
36341
|
}
|
|
36082
36342
|
function projectSessionsDir(projectPath, dataDir) {
|
|
36083
36343
|
const key = projectKeyFromPath(resolveProjectRoot2(projectPath));
|
|
36084
|
-
return
|
|
36344
|
+
return path110.join(keryxDataDir(dataDir), "sessions", key);
|
|
36085
36345
|
}
|
|
36086
36346
|
function sessionDir(projectPath, sessionId, dataDir) {
|
|
36087
|
-
return
|
|
36347
|
+
return path110.join(projectSessionsDir(projectPath, dataDir), sessionId);
|
|
36088
36348
|
}
|
|
36089
36349
|
|
|
36090
36350
|
// src/session/slate.ts
|
|
36091
36351
|
init_fs();
|
|
36092
36352
|
init_assembly();
|
|
36093
|
-
import { mkdir as mkdir32, readFile as
|
|
36094
|
-
import
|
|
36353
|
+
import { mkdir as mkdir32, readFile as readFile55, rm as rm7 } from "fs/promises";
|
|
36354
|
+
import path111 from "path";
|
|
36095
36355
|
init_redact();
|
|
36096
36356
|
var SLATE_SEED_KINDS = [
|
|
36097
36357
|
"decision",
|
|
@@ -36106,14 +36366,14 @@ function isSlateSeedKind(value) {
|
|
|
36106
36366
|
}
|
|
36107
36367
|
var SEED_TEXT_MAX_LENGTH = 4000;
|
|
36108
36368
|
function slatePath(dir) {
|
|
36109
|
-
return
|
|
36369
|
+
return path111.join(dir, "slate.json");
|
|
36110
36370
|
}
|
|
36111
36371
|
function slateLockPath(dir) {
|
|
36112
36372
|
return `${slatePath(dir)}.lock`;
|
|
36113
36373
|
}
|
|
36114
36374
|
async function readSlate(dir) {
|
|
36115
36375
|
try {
|
|
36116
|
-
const raw = await
|
|
36376
|
+
const raw = await readFile55(slatePath(dir), "utf8");
|
|
36117
36377
|
return JSON.parse(raw);
|
|
36118
36378
|
} catch (error) {
|
|
36119
36379
|
if (isNotFound(error))
|
|
@@ -36329,11 +36589,11 @@ function listSessions(cwd, dataDir) {
|
|
|
36329
36589
|
if (name.startsWith(".")) {
|
|
36330
36590
|
continue;
|
|
36331
36591
|
}
|
|
36332
|
-
const summary = readSummaryFile(
|
|
36592
|
+
const summary = readSummaryFile(path112.join(root, name, "summary.json"));
|
|
36333
36593
|
if (summary === undefined) {
|
|
36334
36594
|
continue;
|
|
36335
36595
|
}
|
|
36336
|
-
if (
|
|
36596
|
+
if (path112.resolve(summary.projectPath) !== path112.resolve(projectPath)) {
|
|
36337
36597
|
continue;
|
|
36338
36598
|
}
|
|
36339
36599
|
out.push(summary);
|
|
@@ -36366,15 +36626,15 @@ async function readSessionSlate(cwd, sessionId, dataDir) {
|
|
|
36366
36626
|
}
|
|
36367
36627
|
function loadContext(cwd, sessionId, dataDir) {
|
|
36368
36628
|
const dir = sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir);
|
|
36369
|
-
const contextPath =
|
|
36629
|
+
const contextPath = path112.join(dir, "context.jsonl");
|
|
36370
36630
|
if (existsSync17(contextPath)) {
|
|
36371
36631
|
return readJsonl2(contextPath);
|
|
36372
36632
|
}
|
|
36373
|
-
return readJsonl2(
|
|
36633
|
+
return readJsonl2(path112.join(dir, "transcript.jsonl"));
|
|
36374
36634
|
}
|
|
36375
36635
|
function loadArchive(cwd, sessionId, dataDir, onDegraded) {
|
|
36376
36636
|
const dir = sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir);
|
|
36377
|
-
const archivePath =
|
|
36637
|
+
const archivePath = path112.join(dir, "archive.jsonl");
|
|
36378
36638
|
if (existsSync17(archivePath)) {
|
|
36379
36639
|
try {
|
|
36380
36640
|
const archive = readJsonl2(archivePath);
|
|
@@ -36558,7 +36818,7 @@ function sessionEvidenceRef(workspaceId, sessionId) {
|
|
|
36558
36818
|
}
|
|
36559
36819
|
async function resolveSessionWrapUp(input) {
|
|
36560
36820
|
const now = input.now ?? (() => new Date);
|
|
36561
|
-
const sessionId =
|
|
36821
|
+
const sessionId = path113.posix.basename(input.sourceRef, ".md");
|
|
36562
36822
|
const summary = findSession(input.cwd, sessionId);
|
|
36563
36823
|
if (summary === undefined || input.sourceRef !== sessionEvidenceRef(input.workspaceId, summary.id)) {
|
|
36564
36824
|
throw new SessionWrapUpError("session_not_found", `no session matching "${input.sourceRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
@@ -36575,7 +36835,7 @@ async function resolveSessionWrapUp(input) {
|
|
|
36575
36835
|
throw new SessionWrapUpError("session_unreadable", `session "${summary.id}" could not be read: ${cause.message}`);
|
|
36576
36836
|
}
|
|
36577
36837
|
const relPath = sessionEvidenceRef(input.workspaceId, summary.id).slice(2);
|
|
36578
|
-
const evidenceDir =
|
|
36838
|
+
const evidenceDir = path113.dirname(relPath);
|
|
36579
36839
|
const slate = await readSessionSlate(input.cwd, summary.id);
|
|
36580
36840
|
const diff = await gitDiff(input.cwd);
|
|
36581
36841
|
const diffBody = diff.kind === "measured" ? diff.text : unmeasurableDiffBody(diff.detail);
|
|
@@ -36597,23 +36857,23 @@ async function resolveSessionWrapUp(input) {
|
|
|
36597
36857
|
""
|
|
36598
36858
|
].join(`
|
|
36599
36859
|
`);
|
|
36600
|
-
const wrapUpRelPath =
|
|
36601
|
-
const diffRelPath =
|
|
36602
|
-
const noticeRelPath =
|
|
36860
|
+
const wrapUpRelPath = path113.join(evidenceDir, `${summary.id}.wrap-up.md`);
|
|
36861
|
+
const diffRelPath = path113.join(evidenceDir, `${summary.id}.diff.txt`);
|
|
36862
|
+
const noticeRelPath = path113.join(evidenceDir, `${summary.id}.redaction.md`);
|
|
36603
36863
|
const floor = await applyEvidenceRedactionFloor(input.cwd, [
|
|
36604
|
-
{ name:
|
|
36605
|
-
{ name:
|
|
36606
|
-
{ name:
|
|
36864
|
+
{ name: path113.basename(relPath), content: markdown, path: relPath, source: "generated" },
|
|
36865
|
+
{ name: path113.basename(wrapUpRelPath), content: wrapUpMarkdown, path: wrapUpRelPath, source: "generated" },
|
|
36866
|
+
{ name: path113.basename(diffRelPath), content: diffBody, path: diffRelPath, source: "tool-output" }
|
|
36607
36867
|
]);
|
|
36608
36868
|
if (!floor.ok)
|
|
36609
36869
|
throw new SessionWrapUpError("security_denied", floor.reason);
|
|
36610
36870
|
const [safeSession, safeWrapUp, safeDiff] = floor.bodies;
|
|
36611
|
-
await mkdir33(
|
|
36612
|
-
await writeFile22(
|
|
36613
|
-
await writeFile22(
|
|
36614
|
-
await writeFile22(
|
|
36871
|
+
await mkdir33(path113.join(input.cwd, evidenceDir), { recursive: true });
|
|
36872
|
+
await writeFile22(path113.join(input.cwd, relPath), safeSession.content, "utf8");
|
|
36873
|
+
await writeFile22(path113.join(input.cwd, wrapUpRelPath), safeWrapUp.content, "utf8");
|
|
36874
|
+
await writeFile22(path113.join(input.cwd, diffRelPath), safeDiff.content, "utf8");
|
|
36615
36875
|
if (floor.notice !== undefined)
|
|
36616
|
-
await writeFile22(
|
|
36876
|
+
await writeFile22(path113.join(input.cwd, noticeRelPath), floor.notice, "utf8");
|
|
36617
36877
|
const observedAt = now().toISOString();
|
|
36618
36878
|
const evidence = [
|
|
36619
36879
|
{ kind: "wrap-up", uri: `./${wrapUpRelPath}`, revision: createHash25("sha256").update(safeWrapUp.content).digest("hex"), observedAt },
|
|
@@ -36635,14 +36895,14 @@ async function resolveSessionWrapUp(input) {
|
|
|
36635
36895
|
// src/sac/review-confirm-token.ts
|
|
36636
36896
|
init_fs();
|
|
36637
36897
|
import { createHash as createHash26, randomBytes as randomBytes5 } from "crypto";
|
|
36638
|
-
import { readFile as
|
|
36639
|
-
import
|
|
36898
|
+
import { readFile as readFile56 } from "fs/promises";
|
|
36899
|
+
import path114 from "path";
|
|
36640
36900
|
var CONFIRM_TOKEN_TTL_MS = 2 * 60000;
|
|
36641
36901
|
function confirmTokenPath2(cwd, workspaceId, proposalId) {
|
|
36642
|
-
return
|
|
36902
|
+
return path114.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.confirm-token.json`);
|
|
36643
36903
|
}
|
|
36644
36904
|
function confirmReceiptPath(cwd, workspaceId, proposalId, idempotencyKey) {
|
|
36645
|
-
return
|
|
36905
|
+
return path114.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${sha2565(idempotencyKey)}.confirm-receipt.json`);
|
|
36646
36906
|
}
|
|
36647
36907
|
function sha2565(text) {
|
|
36648
36908
|
return createHash26("sha256").update(text).digest("hex");
|
|
@@ -36658,7 +36918,7 @@ async function consumeConfirmToken(cwd, workspaceId, proposalId, idempotencyKey,
|
|
|
36658
36918
|
return { ok: false, reason: "token_required" };
|
|
36659
36919
|
let stored;
|
|
36660
36920
|
try {
|
|
36661
|
-
stored = JSON.parse(await
|
|
36921
|
+
stored = JSON.parse(await readFile56(file, "utf8"));
|
|
36662
36922
|
} catch (error) {
|
|
36663
36923
|
return { ok: false, reason: isNotFound(error) ? "token_required" : "token_invalid" };
|
|
36664
36924
|
}
|
|
@@ -36681,28 +36941,28 @@ async function consumeConfirmToken(cwd, workspaceId, proposalId, idempotencyKey,
|
|
|
36681
36941
|
init_dedup();
|
|
36682
36942
|
init_store();
|
|
36683
36943
|
init_config();
|
|
36684
|
-
import { readFile as
|
|
36944
|
+
import { readFile as readFile58, readdir as readdir19 } from "fs/promises";
|
|
36685
36945
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
36686
|
-
import
|
|
36946
|
+
import path116 from "path";
|
|
36687
36947
|
|
|
36688
36948
|
// src/sac/proposal-evidence.ts
|
|
36689
36949
|
init_fs();
|
|
36690
36950
|
import { createHash as createHash27 } from "crypto";
|
|
36691
|
-
import { readFile as
|
|
36692
|
-
import
|
|
36951
|
+
import { readFile as readFile57, rm as rm8 } from "fs/promises";
|
|
36952
|
+
import path115 from "path";
|
|
36693
36953
|
function proposalPath(cwd, workspaceId, proposalId) {
|
|
36694
|
-
return
|
|
36954
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
|
|
36695
36955
|
}
|
|
36696
36956
|
function ownerReceiptPath(cwd, owner, workspaceId, idempotencyKey) {
|
|
36697
|
-
return
|
|
36957
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-receipts`, `${idempotencyKey}.json`);
|
|
36698
36958
|
}
|
|
36699
36959
|
function proposalNotePath(cwd, workspaceId, proposalId) {
|
|
36700
|
-
return
|
|
36960
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.note.txt`);
|
|
36701
36961
|
}
|
|
36702
36962
|
async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
36703
36963
|
let proposal;
|
|
36704
36964
|
try {
|
|
36705
|
-
proposal = JSON.parse(await
|
|
36965
|
+
proposal = JSON.parse(await readFile57(proposalPath(cwd, workspaceId, proposalId), "utf8"));
|
|
36706
36966
|
} catch {
|
|
36707
36967
|
return { ok: false, code: "proposal_record_unreadable" };
|
|
36708
36968
|
}
|
|
@@ -36711,7 +36971,7 @@ async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
|
36711
36971
|
return { ok: false, code: "no_evidence_to_write" };
|
|
36712
36972
|
let content;
|
|
36713
36973
|
try {
|
|
36714
|
-
content = await
|
|
36974
|
+
content = await readFile57(path115.join(cwd, evidence.uri), "utf8");
|
|
36715
36975
|
} catch {
|
|
36716
36976
|
return { ok: false, code: "evidence_file_unreadable" };
|
|
36717
36977
|
}
|
|
@@ -36721,7 +36981,7 @@ async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
|
36721
36981
|
return { proposal, evidence, content };
|
|
36722
36982
|
}
|
|
36723
36983
|
async function readSidecarNote(cwd, workspaceId, proposalId) {
|
|
36724
|
-
return
|
|
36984
|
+
return readFile57(proposalNotePath(cwd, workspaceId, proposalId), "utf8").catch(() => {
|
|
36725
36985
|
return;
|
|
36726
36986
|
});
|
|
36727
36987
|
}
|
|
@@ -36730,7 +36990,7 @@ function digestOf2(content) {
|
|
|
36730
36990
|
}
|
|
36731
36991
|
async function readTargetVersion(absolutePath) {
|
|
36732
36992
|
try {
|
|
36733
|
-
return digestOf2(await
|
|
36993
|
+
return digestOf2(await readFile57(absolutePath, "utf8"));
|
|
36734
36994
|
} catch (error) {
|
|
36735
36995
|
if (isNotFound(error))
|
|
36736
36996
|
return null;
|
|
@@ -36741,26 +37001,26 @@ function targetRelPath(targetRef) {
|
|
|
36741
37001
|
return targetRef.replace(/^\.\//, "");
|
|
36742
37002
|
}
|
|
36743
37003
|
function targetAbsolutePath(cwd, targetRef) {
|
|
36744
|
-
return
|
|
37004
|
+
return path115.join(cwd, ".metaproject", targetRelPath(targetRef));
|
|
36745
37005
|
}
|
|
36746
37006
|
function targetKey(targetRef) {
|
|
36747
37007
|
return digestOf2(targetRelPath(targetRef)).slice(0, 32);
|
|
36748
37008
|
}
|
|
36749
37009
|
function ownerTargetVersionPath(cwd, owner, workspaceId, targetRef) {
|
|
36750
|
-
return
|
|
37010
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-target-versions`, `${targetKey(targetRef)}.json`);
|
|
36751
37011
|
}
|
|
36752
37012
|
function ownerTargetLockPath(cwd, owner, targetRef) {
|
|
36753
|
-
return
|
|
37013
|
+
return path115.join(cwd, ".metaproject", "data", "sac", "target-locks", owner, `${targetKey(targetRef)}.lock`);
|
|
36754
37014
|
}
|
|
36755
37015
|
function ownerStagingDir(cwd, owner, workspaceId, idempotencyKey) {
|
|
36756
|
-
return
|
|
37016
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-staging`, digestOf2(idempotencyKey).slice(0, 32));
|
|
36757
37017
|
}
|
|
36758
37018
|
function ownerConflictDir(cwd, owner, workspaceId, idempotencyKey) {
|
|
36759
|
-
return
|
|
37019
|
+
return path115.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-conflicts`, digestOf2(idempotencyKey).slice(0, 32));
|
|
36760
37020
|
}
|
|
36761
37021
|
async function readJson2(file) {
|
|
36762
37022
|
try {
|
|
36763
|
-
return JSON.parse(await
|
|
37023
|
+
return JSON.parse(await readFile57(file, "utf8"));
|
|
36764
37024
|
} catch {
|
|
36765
37025
|
return;
|
|
36766
37026
|
}
|
|
@@ -36827,7 +37087,7 @@ async function applyGuardedTargetWrite(input) {
|
|
|
36827
37087
|
staged: input.staged,
|
|
36828
37088
|
stagedAt: receipt.completedAt
|
|
36829
37089
|
};
|
|
36830
|
-
await writeFileAtomic(
|
|
37090
|
+
await writeFileAtomic(path115.join(ownerStagingDir(cwd, owner, intent.workspaceId, intent.idempotencyKey), "plan.json"), `${JSON.stringify(staged, null, 2)}
|
|
36831
37091
|
`);
|
|
36832
37092
|
const applied = await input.apply(input.staged);
|
|
36833
37093
|
if (!applied.ok) {
|
|
@@ -36853,15 +37113,15 @@ async function recoverStagedOwnerWrite(input) {
|
|
|
36853
37113
|
});
|
|
36854
37114
|
}
|
|
36855
37115
|
async function readStagedOwnerWrite(cwd, owner, workspaceId, idempotencyKey) {
|
|
36856
|
-
return readJson2(
|
|
37116
|
+
return readJson2(path115.join(ownerStagingDir(cwd, owner, workspaceId, idempotencyKey), "plan.json"));
|
|
36857
37117
|
}
|
|
36858
37118
|
async function recordConflict(input) {
|
|
36859
37119
|
const base = { targetRef: input.targetRef, expectedVersion: input.expectedVersion, currentVersion: input.currentVersion };
|
|
36860
37120
|
if (input.proposedContent === undefined)
|
|
36861
37121
|
return base;
|
|
36862
37122
|
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/${
|
|
37123
|
+
await writeFileAtomic(path115.join(dir, "proposed"), input.proposedContent);
|
|
37124
|
+
const diffRef = `./workspaces/${input.intent.workspaceId}/${input.owner}-write-conflicts/${path115.basename(dir)}/proposed`;
|
|
36865
37125
|
return { ...base, diffRef };
|
|
36866
37126
|
}
|
|
36867
37127
|
|
|
@@ -36911,21 +37171,21 @@ async function resolveWorkspaceModule(cwd, workspaceId) {
|
|
|
36911
37171
|
}
|
|
36912
37172
|
}
|
|
36913
37173
|
async function collectWikiDecisionEntries(cwd) {
|
|
36914
|
-
const dir =
|
|
37174
|
+
const dir = path116.join(cwd, ".metaproject", "wiki", "decisions");
|
|
36915
37175
|
let files;
|
|
36916
37176
|
try {
|
|
36917
|
-
files = (await
|
|
37177
|
+
files = (await readdir19(dir)).filter((name) => name.endsWith(".md"));
|
|
36918
37178
|
} catch {
|
|
36919
37179
|
return [];
|
|
36920
37180
|
}
|
|
36921
37181
|
const entries = [];
|
|
36922
37182
|
for (const file of files) {
|
|
36923
|
-
const absolutePath =
|
|
37183
|
+
const absolutePath = path116.join(dir, file);
|
|
36924
37184
|
try {
|
|
36925
|
-
const content = await
|
|
37185
|
+
const content = await readFile58(absolutePath, "utf8");
|
|
36926
37186
|
entries.push({
|
|
36927
37187
|
absolutePath,
|
|
36928
|
-
relativePath:
|
|
37188
|
+
relativePath: path116.posix.join("decisions", file),
|
|
36929
37189
|
type: extractHeaderField(content, "Type") ?? "decision",
|
|
36930
37190
|
title: extractTitle(content),
|
|
36931
37191
|
version: extractHeaderField(content, "Version") ?? null,
|
|
@@ -37039,7 +37299,7 @@ ${candidates}`;
|
|
|
37039
37299
|
|
|
37040
37300
|
// src/sac/memory-owner-writer.ts
|
|
37041
37301
|
init_write();
|
|
37042
|
-
import { readFile as
|
|
37302
|
+
import { readFile as readFile59 } from "fs/promises";
|
|
37043
37303
|
function renderWrapUpMemoryEntry(input) {
|
|
37044
37304
|
const note = input.note?.trim();
|
|
37045
37305
|
return `# ${input.title}
|
|
@@ -37098,7 +37358,7 @@ function createRealMemoryOwnerWriter(cwd, opts) {
|
|
|
37098
37358
|
},
|
|
37099
37359
|
async recover(intent) {
|
|
37100
37360
|
try {
|
|
37101
|
-
const raw = await
|
|
37361
|
+
const raw = await readFile59(ownerReceiptPath(cwd, "memory", intent.workspaceId, intent.idempotencyKey), "utf8");
|
|
37102
37362
|
return JSON.parse(raw);
|
|
37103
37363
|
} catch {
|
|
37104
37364
|
return;
|
|
@@ -37146,8 +37406,8 @@ function createRealMemoryOwnerWriter(cwd, opts) {
|
|
|
37146
37406
|
// src/sac/wiki-owner-writer.ts
|
|
37147
37407
|
init_fs();
|
|
37148
37408
|
init_guard();
|
|
37149
|
-
import { readFile as
|
|
37150
|
-
import
|
|
37409
|
+
import { readFile as readFile60 } from "fs/promises";
|
|
37410
|
+
import path117 from "path";
|
|
37151
37411
|
function renderWrapUpDecisionPage(input) {
|
|
37152
37412
|
const note = input.note?.trim();
|
|
37153
37413
|
return `# ${input.title}
|
|
@@ -37188,14 +37448,14 @@ export is the source of truth for what actually happened.
|
|
|
37188
37448
|
`;
|
|
37189
37449
|
}
|
|
37190
37450
|
function wikiPageRelativePath(proposalId) {
|
|
37191
|
-
return
|
|
37451
|
+
return path117.posix.join("decisions", `sac-${proposalId}.md`);
|
|
37192
37452
|
}
|
|
37193
37453
|
function createRealWikiOwnerWriter(cwd, opts) {
|
|
37194
37454
|
const now = opts?.now ?? (() => new Date);
|
|
37195
37455
|
const applyStaged = async (staged) => {
|
|
37196
37456
|
const relativePath = staged.relativePath;
|
|
37197
37457
|
const content = staged.content;
|
|
37198
|
-
await writeFileAtomic(
|
|
37458
|
+
await writeFileAtomic(path117.join(cwd, ".metaproject", "wiki", relativePath), content);
|
|
37199
37459
|
return { ok: true };
|
|
37200
37460
|
};
|
|
37201
37461
|
return {
|
|
@@ -37204,7 +37464,7 @@ function createRealWikiOwnerWriter(cwd, opts) {
|
|
|
37204
37464
|
},
|
|
37205
37465
|
async recover(intent) {
|
|
37206
37466
|
try {
|
|
37207
|
-
const raw = await
|
|
37467
|
+
const raw = await readFile60(ownerReceiptPath(cwd, "wiki", intent.workspaceId, intent.idempotencyKey), "utf8");
|
|
37208
37468
|
return JSON.parse(raw);
|
|
37209
37469
|
} catch {
|
|
37210
37470
|
return;
|
|
@@ -37255,7 +37515,7 @@ function createRealWikiOwnerWriter(cwd, opts) {
|
|
|
37255
37515
|
}
|
|
37256
37516
|
|
|
37257
37517
|
// src/sac/skill-owner-writer.ts
|
|
37258
|
-
import { readFile as
|
|
37518
|
+
import { readFile as readFile62 } from "fs/promises";
|
|
37259
37519
|
|
|
37260
37520
|
// src/gdskills/project-skills.ts
|
|
37261
37521
|
init_contained_write();
|
|
@@ -37264,8 +37524,8 @@ init_json();
|
|
|
37264
37524
|
init_guard();
|
|
37265
37525
|
import { createHash as createHash28 } from "crypto";
|
|
37266
37526
|
import { homedir as homedir2 } from "os";
|
|
37267
|
-
import { readFile as
|
|
37268
|
-
import
|
|
37527
|
+
import { readFile as readFile61, stat as stat15 } from "fs/promises";
|
|
37528
|
+
import path118 from "path";
|
|
37269
37529
|
var VERSION = "0.1.0";
|
|
37270
37530
|
var PROJECT_SKILLS_DIR = ".metaproject/project-skills";
|
|
37271
37531
|
var PROJECT_SKILLS_MANIFEST_PATH = ".metaproject/metaproject.json";
|
|
@@ -37295,7 +37555,7 @@ async function assertProjectWritesContained(projectRoot, paths) {
|
|
|
37295
37555
|
}
|
|
37296
37556
|
}
|
|
37297
37557
|
async function createProjectSkill(projectRoot, options) {
|
|
37298
|
-
const metaprojectRoot =
|
|
37558
|
+
const metaprojectRoot = path118.join(projectRoot, ".metaproject");
|
|
37299
37559
|
if (!await pathExists(metaprojectRoot)) {
|
|
37300
37560
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
37301
37561
|
}
|
|
@@ -37306,7 +37566,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
37306
37566
|
const skillName = projectSkillSlug(options.name ?? inferSkillName(options.target));
|
|
37307
37567
|
const format = options.format ?? "auto";
|
|
37308
37568
|
const relativeSkillPath = projectSkillPackagePath(moduleName, skillName);
|
|
37309
|
-
const packageRoot =
|
|
37569
|
+
const packageRoot = path118.join(projectRoot, relativeSkillPath);
|
|
37310
37570
|
await assertProjectWritesContained(projectRoot, projectSkillWritePaths(moduleName, skillName, format));
|
|
37311
37571
|
const origin = options.origin ? options.originContent !== undefined ? {
|
|
37312
37572
|
ref: options.origin.trim(),
|
|
@@ -37317,7 +37577,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
37317
37577
|
const warnings = collectWarnings(evidence, format);
|
|
37318
37578
|
const files = filesForPackage(packageRoot, format);
|
|
37319
37579
|
if (!options.dryRun) {
|
|
37320
|
-
await withFileLock(
|
|
37580
|
+
await withFileLock(path118.join(projectRoot, PROJECT_SKILLS_LOCK_PATH), async () => {
|
|
37321
37581
|
await writeProjectSkillPackage({
|
|
37322
37582
|
projectRoot,
|
|
37323
37583
|
packageRoot,
|
|
@@ -37346,7 +37606,7 @@ async function createProjectSkill(projectRoot, options) {
|
|
|
37346
37606
|
name: skillName,
|
|
37347
37607
|
target: options.target,
|
|
37348
37608
|
skillPath: relativeSkillPath,
|
|
37349
|
-
files: files.map((filePath) => toPosix(
|
|
37609
|
+
files: files.map((filePath) => toPosix(path118.relative(projectRoot, filePath))),
|
|
37350
37610
|
warnings,
|
|
37351
37611
|
dryRun: options.dryRun === true
|
|
37352
37612
|
};
|
|
@@ -37355,9 +37615,9 @@ var MAX_SKILL_TARGET_LENGTH = 80;
|
|
|
37355
37615
|
function resolveOriginPath(ref, projectRoot) {
|
|
37356
37616
|
const trimmed = ref.trim();
|
|
37357
37617
|
if (trimmed === "~" || trimmed.startsWith("~/")) {
|
|
37358
|
-
return
|
|
37618
|
+
return path118.join(homedir2(), trimmed.slice(1));
|
|
37359
37619
|
}
|
|
37360
|
-
return
|
|
37620
|
+
return path118.resolve(projectRoot, trimmed);
|
|
37361
37621
|
}
|
|
37362
37622
|
function hashOriginContent(content) {
|
|
37363
37623
|
return `sha256:${createHash28("sha256").update(content).digest("hex")}`;
|
|
@@ -37366,7 +37626,7 @@ async function readSkillOrigin(projectRoot, ref, now = () => new Date) {
|
|
|
37366
37626
|
const resolved = resolveOriginPath(ref, projectRoot);
|
|
37367
37627
|
let content;
|
|
37368
37628
|
try {
|
|
37369
|
-
content = await
|
|
37629
|
+
content = await readFile61(resolved, "utf8");
|
|
37370
37630
|
} catch (cause) {
|
|
37371
37631
|
throw new Error(`Cannot read the origin file ${ref} (resolved to ${resolved}): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
37372
37632
|
}
|
|
@@ -37395,16 +37655,16 @@ async function writeProjectSkillPackage({
|
|
|
37395
37655
|
}) {
|
|
37396
37656
|
const packageFormat = format === "single" ? "single" : "package";
|
|
37397
37657
|
const skillContent = renderProjectSkill({ moduleName, skillName, target, note, origin, evidence, packageFormat });
|
|
37398
|
-
const relativeSkillMdPath = toPosix(
|
|
37658
|
+
const relativeSkillMdPath = toPosix(path118.join(path118.relative(projectRoot, packageRoot), "SKILL.md"));
|
|
37399
37659
|
const guard = await guardOutput({ cwd: projectRoot, content: skillContent, target: "skill", source: "generated", path: relativeSkillMdPath });
|
|
37400
37660
|
const output = prepareOutputForPersistence(guard, skillContent);
|
|
37401
37661
|
if (!output.allowed) {
|
|
37402
37662
|
throw new Error(`Project skill blocked by the security gate: ${output.reason}`);
|
|
37403
37663
|
}
|
|
37404
|
-
const packageRel = toPosix(
|
|
37664
|
+
const packageRel = toPosix(path118.relative(projectRoot, packageRoot));
|
|
37405
37665
|
const write = (file, content) => writeContained(projectRoot, `${packageRel}/${file}`, content);
|
|
37406
37666
|
await write("SKILL.md", output.content);
|
|
37407
|
-
if (!await pathExists(
|
|
37667
|
+
if (!await pathExists(path118.join(packageRoot, "skill-changelog.md"))) {
|
|
37408
37668
|
await write("skill-changelog.md", renderSkillChangelog({ moduleName, skillName, target }));
|
|
37409
37669
|
}
|
|
37410
37670
|
if (packageFormat === "package") {
|
|
@@ -37414,10 +37674,10 @@ async function writeProjectSkillPackage({
|
|
|
37414
37674
|
}
|
|
37415
37675
|
}
|
|
37416
37676
|
async function collectEvidence(projectRoot, target) {
|
|
37417
|
-
const absoluteTarget =
|
|
37677
|
+
const absoluteTarget = path118.resolve(projectRoot, target);
|
|
37418
37678
|
const targetExists = await pathExists(absoluteTarget);
|
|
37419
37679
|
const targetKind = await classifyTarget(absoluteTarget, targetExists);
|
|
37420
|
-
const maybeRelativeTarget = targetExists ? toPosix(
|
|
37680
|
+
const maybeRelativeTarget = targetExists ? toPosix(path118.relative(projectRoot, absoluteTarget)) : undefined;
|
|
37421
37681
|
const graphArtifacts = await existingRelativePaths(projectRoot, [
|
|
37422
37682
|
".metaproject/data/gdgraph/artifacts/summary.md",
|
|
37423
37683
|
".metaproject/data/gdgraph/artifacts/module-map.json"
|
|
@@ -37441,13 +37701,13 @@ async function classifyTarget(absoluteTarget, targetExists) {
|
|
|
37441
37701
|
if (!targetExists) {
|
|
37442
37702
|
return "symbol-or-concept";
|
|
37443
37703
|
}
|
|
37444
|
-
const stats = await
|
|
37704
|
+
const stats = await stat15(absoluteTarget);
|
|
37445
37705
|
return stats.isDirectory() ? "directory" : "file";
|
|
37446
37706
|
}
|
|
37447
37707
|
async function existingRelativePaths(projectRoot, candidates) {
|
|
37448
37708
|
const existing = [];
|
|
37449
37709
|
for (const candidate of candidates) {
|
|
37450
|
-
if (await pathExists(
|
|
37710
|
+
if (await pathExists(path118.join(projectRoot, candidate))) {
|
|
37451
37711
|
existing.push(candidate);
|
|
37452
37712
|
}
|
|
37453
37713
|
}
|
|
@@ -37474,17 +37734,17 @@ function collectWarnings(evidence, format) {
|
|
|
37474
37734
|
}
|
|
37475
37735
|
function filesForPackage(packageRoot, format) {
|
|
37476
37736
|
const base = [
|
|
37477
|
-
|
|
37478
|
-
|
|
37737
|
+
path118.join(packageRoot, "SKILL.md"),
|
|
37738
|
+
path118.join(packageRoot, "skill-changelog.md")
|
|
37479
37739
|
];
|
|
37480
37740
|
if (format === "single") {
|
|
37481
37741
|
return base;
|
|
37482
37742
|
}
|
|
37483
37743
|
return [
|
|
37484
37744
|
...base,
|
|
37485
|
-
|
|
37486
|
-
|
|
37487
|
-
|
|
37745
|
+
path118.join(packageRoot, "verification.md"),
|
|
37746
|
+
path118.join(packageRoot, "references", "context.md"),
|
|
37747
|
+
path118.join(packageRoot, "templates", "README.md")
|
|
37488
37748
|
];
|
|
37489
37749
|
}
|
|
37490
37750
|
function renderProjectSkill({
|
|
@@ -37712,7 +37972,7 @@ keryx skills verify ${moduleName}/${skillName}
|
|
|
37712
37972
|
`;
|
|
37713
37973
|
}
|
|
37714
37974
|
async function updateManifest(projectRoot, entry) {
|
|
37715
|
-
const manifestPath =
|
|
37975
|
+
const manifestPath = path118.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
37716
37976
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
37717
37977
|
manifest.modules ??= {};
|
|
37718
37978
|
manifest.modules.gdskills ??= {};
|
|
@@ -37726,8 +37986,8 @@ async function updateManifest(projectRoot, entry) {
|
|
|
37726
37986
|
`);
|
|
37727
37987
|
}
|
|
37728
37988
|
async function updateSkillsCatalog(projectRoot) {
|
|
37729
|
-
const manifestPath =
|
|
37730
|
-
const catalogPath =
|
|
37989
|
+
const manifestPath = path118.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
37990
|
+
const catalogPath = path118.join(projectRoot, PROJECT_SKILLS_CATALOG_PATH);
|
|
37731
37991
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
37732
37992
|
const registry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
37733
37993
|
const rows = registry.length > 0 ? registry.map(projectSkillCatalogRow).join(`
|
|
@@ -37739,7 +37999,7 @@ async function updateSkillsCatalog(projectRoot) {
|
|
|
37739
37999
|
|---|---|---|---|
|
|
37740
38000
|
${rows}
|
|
37741
38001
|
${PROJECT_SKILLS_CATALOG_END}`;
|
|
37742
|
-
const current = await pathExists(catalogPath) ? await
|
|
38002
|
+
const current = await pathExists(catalogPath) ? await readFile61(catalogPath, "utf8") : `# Metaproject Skills Catalog
|
|
37743
38003
|
`;
|
|
37744
38004
|
const start = PROJECT_SKILLS_CATALOG_START;
|
|
37745
38005
|
const end = PROJECT_SKILLS_CATALOG_END;
|
|
@@ -37767,7 +38027,7 @@ function inferModule(target) {
|
|
|
37767
38027
|
}
|
|
37768
38028
|
function inferSkillName(target) {
|
|
37769
38029
|
const normalized = target.trim().replace(/[#:]+/g, "/");
|
|
37770
|
-
const base =
|
|
38030
|
+
const base = path118.basename(normalized).replace(/\.[^.]+$/, "");
|
|
37771
38031
|
return base || "entity";
|
|
37772
38032
|
}
|
|
37773
38033
|
function projectSkillSlug(value) {
|
|
@@ -37814,7 +38074,7 @@ function createRealSkillOwnerWriter(cwd, opts) {
|
|
|
37814
38074
|
},
|
|
37815
38075
|
async recover(intent) {
|
|
37816
38076
|
try {
|
|
37817
|
-
const raw = await
|
|
38077
|
+
const raw = await readFile62(ownerReceiptPath(cwd, "skill", intent.workspaceId, intent.idempotencyKey), "utf8");
|
|
37818
38078
|
return JSON.parse(raw);
|
|
37819
38079
|
} catch {
|
|
37820
38080
|
return;
|
|
@@ -37872,7 +38132,7 @@ class ProposalLifecycleService {
|
|
|
37872
38132
|
now;
|
|
37873
38133
|
constructor(options) {
|
|
37874
38134
|
this.options = options;
|
|
37875
|
-
this.root =
|
|
38135
|
+
this.root = path119.resolve(options.workspaceRoot);
|
|
37876
38136
|
this.now = options.now ?? (() => new Date);
|
|
37877
38137
|
}
|
|
37878
38138
|
async create(input) {
|
|
@@ -37891,13 +38151,13 @@ class ProposalLifecycleService {
|
|
|
37891
38151
|
if (manifest.status === "archived")
|
|
37892
38152
|
throw new ProposalLifecycleError("guard_denied", "workspace is archived");
|
|
37893
38153
|
const file = this.proposalPath(workspaceId, proposal.id);
|
|
37894
|
-
await mkdir34(
|
|
38154
|
+
await mkdir34(path119.dirname(file), { recursive: true, mode: 448 });
|
|
37895
38155
|
return withFileLock(`${file}.lock`, async () => {
|
|
37896
38156
|
const consume = this.options.wrapUpAuthority.consume(input.wrapUp, { actor, workspaceId });
|
|
37897
38157
|
if (consume !== "ok")
|
|
37898
38158
|
throw new ProposalLifecycleError("trusted_wrap_up_required", `trusted wrap-up ${consume}`);
|
|
37899
38159
|
try {
|
|
37900
|
-
await
|
|
38160
|
+
await readFile63(file, "utf8");
|
|
37901
38161
|
throw new ProposalLifecycleError("conflict", "proposal already exists");
|
|
37902
38162
|
} catch (error) {
|
|
37903
38163
|
if (error instanceof ProposalLifecycleError)
|
|
@@ -37919,7 +38179,7 @@ class ProposalLifecycleService {
|
|
|
37919
38179
|
return this.options.workspaces.withAuthorizedActor({ actorContext: actor, workspaceId: input.workspaceId, action: "review", execute: async (manifest) => {
|
|
37920
38180
|
const proposal = await this.loadProposal(input.workspaceId, input.proposalId);
|
|
37921
38181
|
const ledger = this.ledgerPath(input.workspaceId);
|
|
37922
|
-
await mkdir34(
|
|
38182
|
+
await mkdir34(path119.dirname(ledger), { recursive: true, mode: 448 });
|
|
37923
38183
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
37924
38184
|
const records = (await this.records(ledger)).filter((record) => record.proposalId === proposal.id);
|
|
37925
38185
|
const events = records.filter((record) => record.recordType === "proposal-transition");
|
|
@@ -37961,10 +38221,10 @@ class ProposalLifecycleService {
|
|
|
37961
38221
|
} });
|
|
37962
38222
|
}
|
|
37963
38223
|
async listProposedProposals(workspaceId) {
|
|
37964
|
-
const proposalsDir =
|
|
38224
|
+
const proposalsDir = path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals");
|
|
37965
38225
|
let entries;
|
|
37966
38226
|
try {
|
|
37967
|
-
entries = (await
|
|
38227
|
+
entries = (await readdir20(proposalsDir)).filter((name) => name.endsWith(".json"));
|
|
37968
38228
|
} catch (error) {
|
|
37969
38229
|
if (isNotFound(error))
|
|
37970
38230
|
return [];
|
|
@@ -37973,7 +38233,7 @@ class ProposalLifecycleService {
|
|
|
37973
38233
|
const proposals = [];
|
|
37974
38234
|
for (const entry of entries) {
|
|
37975
38235
|
try {
|
|
37976
|
-
const parsed = JSON.parse(await
|
|
38236
|
+
const parsed = JSON.parse(await readFile63(path119.join(proposalsDir, entry), "utf8"));
|
|
37977
38237
|
if (parsed.recordType === "proposal-created")
|
|
37978
38238
|
proposals.push(parsed);
|
|
37979
38239
|
} catch {}
|
|
@@ -38058,7 +38318,7 @@ class ProposalLifecycleService {
|
|
|
38058
38318
|
}
|
|
38059
38319
|
async loadOwnerWriteAttempt(workspaceId, proposalId, key) {
|
|
38060
38320
|
try {
|
|
38061
|
-
return JSON.parse(await
|
|
38321
|
+
return JSON.parse(await readFile63(this.attemptPath(workspaceId, proposalId, key), "utf8"));
|
|
38062
38322
|
} catch (error) {
|
|
38063
38323
|
if (isNotFound(error))
|
|
38064
38324
|
return;
|
|
@@ -38073,7 +38333,7 @@ class ProposalLifecycleService {
|
|
|
38073
38333
|
async ensureReviewDecision(proposal, actor, reviewerAuthority, input, outcome, targetWrite, policyRevision) {
|
|
38074
38334
|
const file = this.decisionPath(proposal.workspaceId, proposal.id, input.idempotencyKey);
|
|
38075
38335
|
try {
|
|
38076
|
-
const existing = JSON.parse(await
|
|
38336
|
+
const existing = JSON.parse(await readFile63(file, "utf8"));
|
|
38077
38337
|
const existingWrite = existing.targetWrite;
|
|
38078
38338
|
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
38339
|
throw new ProposalLifecycleError("conflict", "review decision does not match the recovery request");
|
|
@@ -38106,7 +38366,7 @@ class ProposalLifecycleService {
|
|
|
38106
38366
|
const approvalRef = this.approvalRef(proposal.id, input.idempotencyKey);
|
|
38107
38367
|
const approvalPath = this.approvalPath(proposal.workspaceId, proposal.id, input.idempotencyKey);
|
|
38108
38368
|
try {
|
|
38109
|
-
const existing = JSON.parse(await
|
|
38369
|
+
const existing = JSON.parse(await readFile63(approvalPath, "utf8"));
|
|
38110
38370
|
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
38371
|
throw new ProposalLifecycleError("conflict", "approval does not match the recovery request");
|
|
38112
38372
|
return { approvalRef };
|
|
@@ -38126,7 +38386,7 @@ class ProposalLifecycleService {
|
|
|
38126
38386
|
if (existing)
|
|
38127
38387
|
return { intentRef };
|
|
38128
38388
|
try {
|
|
38129
|
-
const recovered = JSON.parse(await
|
|
38389
|
+
const recovered = JSON.parse(await readFile63(this.intentPath(proposal.workspaceId, proposal.id, input.idempotencyKey), "utf8"));
|
|
38130
38390
|
await this.validateRecord(recovered);
|
|
38131
38391
|
if (recovered.proposalId !== proposal.id || recovered.proposalRevision !== proposal.proposalRevision || recovered.correlationId !== input.requestCorrelationId || recovered.idempotencyKey !== input.idempotencyKey || recovered.workspaceId !== proposal.workspaceId)
|
|
38132
38392
|
throw new ProposalLifecycleError("conflict", "write intent does not match the recovery request");
|
|
@@ -38219,7 +38479,7 @@ class ProposalLifecycleService {
|
|
|
38219
38479
|
}
|
|
38220
38480
|
async loadProposal(workspaceId, proposalId) {
|
|
38221
38481
|
try {
|
|
38222
|
-
const item = JSON.parse(await
|
|
38482
|
+
const item = JSON.parse(await readFile63(this.proposalPath(workspaceId, proposalId), "utf8"));
|
|
38223
38483
|
await this.validateProposal(item);
|
|
38224
38484
|
return item;
|
|
38225
38485
|
} catch (error) {
|
|
@@ -38230,7 +38490,7 @@ class ProposalLifecycleService {
|
|
|
38230
38490
|
}
|
|
38231
38491
|
async records(ledger) {
|
|
38232
38492
|
try {
|
|
38233
|
-
return (await
|
|
38493
|
+
return (await readFile63(ledger, "utf8")).trim().split(`
|
|
38234
38494
|
`).filter(Boolean).map((line) => JSON.parse(line));
|
|
38235
38495
|
} catch (error) {
|
|
38236
38496
|
if (isNotFound(error))
|
|
@@ -38239,35 +38499,35 @@ class ProposalLifecycleService {
|
|
|
38239
38499
|
}
|
|
38240
38500
|
}
|
|
38241
38501
|
proposalPath(workspaceId, proposalId) {
|
|
38242
|
-
return
|
|
38502
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
|
|
38243
38503
|
}
|
|
38244
38504
|
decisionRef(proposalId, key) {
|
|
38245
38505
|
return `./proposals/${proposalId}.${hash(key)}.decision.json`;
|
|
38246
38506
|
}
|
|
38247
38507
|
decisionPath(workspaceId, proposalId, key) {
|
|
38248
|
-
return
|
|
38508
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.decision.json`);
|
|
38249
38509
|
}
|
|
38250
38510
|
approvalRef(proposalId, key) {
|
|
38251
38511
|
return `./proposals/${proposalId}.${hash(key)}.approval.json`;
|
|
38252
38512
|
}
|
|
38253
38513
|
approvalPath(workspaceId, proposalId, key) {
|
|
38254
|
-
return
|
|
38514
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.approval.json`);
|
|
38255
38515
|
}
|
|
38256
38516
|
writeResultPath(workspaceId, proposalId, key) {
|
|
38257
|
-
return
|
|
38517
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.write-result.json`);
|
|
38258
38518
|
}
|
|
38259
38519
|
intentRef(proposalId, key) {
|
|
38260
38520
|
return `./proposals/${proposalId}.${hash(key)}.write-intent.json`;
|
|
38261
38521
|
}
|
|
38262
38522
|
intentPath(workspaceId, proposalId, key) {
|
|
38263
|
-
return
|
|
38523
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.write-intent.json`);
|
|
38264
38524
|
}
|
|
38265
38525
|
attemptPath(workspaceId, proposalId, key) {
|
|
38266
|
-
return
|
|
38526
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash(key)}.owner-write-attempt.json`);
|
|
38267
38527
|
}
|
|
38268
38528
|
async loadWriteResult(workspaceId, proposalId, key) {
|
|
38269
38529
|
try {
|
|
38270
|
-
return JSON.parse(await
|
|
38530
|
+
return JSON.parse(await readFile63(this.writeResultPath(workspaceId, proposalId, key), "utf8"));
|
|
38271
38531
|
} catch (error) {
|
|
38272
38532
|
if (isNotFound(error))
|
|
38273
38533
|
return;
|
|
@@ -38276,7 +38536,7 @@ class ProposalLifecycleService {
|
|
|
38276
38536
|
}
|
|
38277
38537
|
async writeImmutable(file, record) {
|
|
38278
38538
|
try {
|
|
38279
|
-
const existing = await
|
|
38539
|
+
const existing = await readFile63(file, "utf8");
|
|
38280
38540
|
if (existing !== `${JSON.stringify(record)}
|
|
38281
38541
|
`)
|
|
38282
38542
|
throw new ProposalLifecycleError("conflict", "immutable lifecycle record already exists");
|
|
@@ -38290,7 +38550,7 @@ class ProposalLifecycleService {
|
|
|
38290
38550
|
}
|
|
38291
38551
|
}
|
|
38292
38552
|
ledgerPath(workspaceId) {
|
|
38293
|
-
return
|
|
38553
|
+
return path119.join(this.root, ".metaproject", "workspaces", workspaceId, "activity.jsonl");
|
|
38294
38554
|
}
|
|
38295
38555
|
timestamp() {
|
|
38296
38556
|
return this.now().toISOString();
|
|
@@ -38371,9 +38631,9 @@ function normalizeProposalLifecycleResult(value) {
|
|
|
38371
38631
|
return JSON.parse(JSON.stringify(value));
|
|
38372
38632
|
}
|
|
38373
38633
|
// src/sac/collaboration-service.ts
|
|
38374
|
-
import { appendFile as appendFile8, mkdir as mkdir35, readFile as
|
|
38634
|
+
import { appendFile as appendFile8, mkdir as mkdir35, readFile as readFile64 } from "fs/promises";
|
|
38375
38635
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
38376
|
-
import
|
|
38636
|
+
import path120 from "path";
|
|
38377
38637
|
class CollaborationServiceError extends Error {
|
|
38378
38638
|
code;
|
|
38379
38639
|
constructor(code, message) {
|
|
@@ -38388,7 +38648,7 @@ class CollaborationService {
|
|
|
38388
38648
|
this.input = input;
|
|
38389
38649
|
}
|
|
38390
38650
|
file(id) {
|
|
38391
|
-
return
|
|
38651
|
+
return path120.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
38392
38652
|
}
|
|
38393
38653
|
async overview(input) {
|
|
38394
38654
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -38397,7 +38657,7 @@ class CollaborationService {
|
|
|
38397
38657
|
async activity(input) {
|
|
38398
38658
|
await this.input.workspaces.show(input);
|
|
38399
38659
|
try {
|
|
38400
|
-
return (await
|
|
38660
|
+
return (await readFile64(this.file(input.workspaceId), "utf8")).trim().split(`
|
|
38401
38661
|
`).filter(Boolean).map((line) => this.validate(JSON.parse(line)));
|
|
38402
38662
|
} catch (error) {
|
|
38403
38663
|
if (typeof error === "object" && error && "code" in error && error.code === "ENOENT")
|
|
@@ -38425,7 +38685,7 @@ class CollaborationService {
|
|
|
38425
38685
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
38426
38686
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
38427
38687
|
});
|
|
38428
|
-
await mkdir35(
|
|
38688
|
+
await mkdir35(path120.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
38429
38689
|
await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
38430
38690
|
`, { mode: 384 });
|
|
38431
38691
|
return normalizeCollaborationResult(activity);
|
|
@@ -38474,14 +38734,14 @@ function normalizeCollaborationResult(value) {
|
|
|
38474
38734
|
}
|
|
38475
38735
|
// src/session/external-slate.ts
|
|
38476
38736
|
init_fs();
|
|
38477
|
-
import { mkdir as mkdir37, readdir as
|
|
38478
|
-
import
|
|
38737
|
+
import { mkdir as mkdir37, readdir as readdir21, readFile as readFile65 } from "fs/promises";
|
|
38738
|
+
import path122 from "path";
|
|
38479
38739
|
|
|
38480
38740
|
// src/sac/machine-wrap-up.ts
|
|
38481
38741
|
init_fs();
|
|
38482
38742
|
import { createHash as createHash30, randomUUID as randomUUID10 } from "crypto";
|
|
38483
38743
|
import { mkdir as mkdir36 } from "fs/promises";
|
|
38484
|
-
import
|
|
38744
|
+
import path121 from "path";
|
|
38485
38745
|
|
|
38486
38746
|
// src/sac/workspace-resolve.ts
|
|
38487
38747
|
init_redact();
|
|
@@ -38622,11 +38882,11 @@ async function resolveMachineWrapUp(input) {
|
|
|
38622
38882
|
`;
|
|
38623
38883
|
const seedsJson = `${JSON.stringify(seedsForKind.map((seed) => ({ text: seed.text, source: describeSource(seed.source) })), null, 2)}
|
|
38624
38884
|
`;
|
|
38625
|
-
const evidenceRelBase =
|
|
38885
|
+
const evidenceRelBase = path121.posix.join(".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
38626
38886
|
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:
|
|
38887
|
+
{ name: `${input.kind}.diff.txt`, content: diffBody, path: path121.posix.join(evidenceRelBase, `${input.kind}.diff.txt`), source: "tool-output" },
|
|
38888
|
+
{ name: `${input.kind}.flow.json`, content: flowSnapshotJson, path: path121.posix.join(evidenceRelBase, `${input.kind}.flow.json`), source: "generated" },
|
|
38889
|
+
{ name: `${input.kind}.seeds.json`, content: seedsJson, path: path121.posix.join(evidenceRelBase, `${input.kind}.seeds.json`), source: "generated" }
|
|
38630
38890
|
]);
|
|
38631
38891
|
if (!floor.ok)
|
|
38632
38892
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
@@ -38680,14 +38940,14 @@ ${safeSeeds.content}`;
|
|
|
38680
38940
|
}
|
|
38681
38941
|
summary = result.text.trim().length > 0 ? result.text.trim() : mechanicalSummary(summaryDiff, course);
|
|
38682
38942
|
}
|
|
38683
|
-
const evidenceDir =
|
|
38943
|
+
const evidenceDir = path121.join(input.cwd, ".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
38684
38944
|
await mkdir36(evidenceDir, { recursive: true });
|
|
38685
38945
|
const diffFile = `${input.kind}.${shortHash}.diff.txt`;
|
|
38686
38946
|
const flowFile = `${input.kind}.${shortHash}.flow.json`;
|
|
38687
38947
|
const seedsFile = `${input.kind}.${shortHash}.seeds.json`;
|
|
38688
|
-
await writeFileAtomic(
|
|
38689
|
-
await writeFileAtomic(
|
|
38690
|
-
await writeFileAtomic(
|
|
38948
|
+
await writeFileAtomic(path121.join(evidenceDir, diffFile), safeDiff.content);
|
|
38949
|
+
await writeFileAtomic(path121.join(evidenceDir, flowFile), safeFlow.content);
|
|
38950
|
+
await writeFileAtomic(path121.join(evidenceDir, seedsFile), safeSeeds.content);
|
|
38691
38951
|
const observedAt = now().toISOString();
|
|
38692
38952
|
const relBase = `./.metaproject/workspaces/${input.workspaceId}/machine-evidence`;
|
|
38693
38953
|
const evidence = [
|
|
@@ -38697,7 +38957,7 @@ ${safeSeeds.content}`;
|
|
|
38697
38957
|
];
|
|
38698
38958
|
if (floor.notice !== undefined) {
|
|
38699
38959
|
const noticeFile = `${input.kind}.${shortHash}.redaction.md`;
|
|
38700
|
-
await writeFileAtomic(
|
|
38960
|
+
await writeFileAtomic(path121.join(evidenceDir, noticeFile), floor.notice);
|
|
38701
38961
|
evidence.push({ kind: REDACTION_NOTICE_KIND, uri: `${relBase}/${noticeFile}`, revision: sha2566(floor.notice), observedAt });
|
|
38702
38962
|
}
|
|
38703
38963
|
return {
|
|
@@ -38716,18 +38976,18 @@ async function writeFlooredArchiveArtifact(params) {
|
|
|
38716
38976
|
{
|
|
38717
38977
|
name: params.filename,
|
|
38718
38978
|
content: params.content,
|
|
38719
|
-
path:
|
|
38979
|
+
path: path121.posix.join("slate-archive", params.filename),
|
|
38720
38980
|
source: "generated"
|
|
38721
38981
|
}
|
|
38722
38982
|
]);
|
|
38723
38983
|
if (!floor.ok)
|
|
38724
38984
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
38725
38985
|
const body = floor.bodies[0];
|
|
38726
|
-
const archiveDir =
|
|
38986
|
+
const archiveDir = path121.join(params.dir, "slate-archive");
|
|
38727
38987
|
await mkdir36(archiveDir, { recursive: true });
|
|
38728
|
-
await writeFileAtomic(
|
|
38988
|
+
await writeFileAtomic(path121.join(archiveDir, params.filename), body.content);
|
|
38729
38989
|
if (floor.notice !== undefined) {
|
|
38730
|
-
await writeFileAtomic(
|
|
38990
|
+
await writeFileAtomic(path121.join(archiveDir, `${params.filename}${ARCHIVE_REDACTION_NOTICE_SUFFIX}`), floor.notice);
|
|
38731
38991
|
}
|
|
38732
38992
|
return { ok: true, altered: body.altered };
|
|
38733
38993
|
}
|
|
@@ -38763,9 +39023,9 @@ async function writeWrapUpOutcomeArtifact(cwd, dir, trigger, now, groups) {
|
|
|
38763
39023
|
return;
|
|
38764
39024
|
const refusalIso = now().toISOString();
|
|
38765
39025
|
const refusalFilename = `${refusalIso.replace(/[:.]/g, "-")}-wrap-up-outcome.json`;
|
|
38766
|
-
const archiveDir =
|
|
39026
|
+
const archiveDir = path121.join(dir, "slate-archive");
|
|
38767
39027
|
await mkdir36(archiveDir, { recursive: true });
|
|
38768
|
-
await writeFileAtomic(
|
|
39028
|
+
await writeFileAtomic(path121.join(archiveDir, refusalFilename), `${JSON.stringify({
|
|
38769
39029
|
recordType: "wrap-up-outcome",
|
|
38770
39030
|
trigger,
|
|
38771
39031
|
generatedAt: refusalIso,
|
|
@@ -38901,7 +39161,7 @@ async function runWrapUp(input) {
|
|
|
38901
39161
|
|
|
38902
39162
|
// src/session/external-slate.ts
|
|
38903
39163
|
function externalSlatesDir(cwd) {
|
|
38904
|
-
return
|
|
39164
|
+
return path122.join(cwd, ".keryx", "external-slates");
|
|
38905
39165
|
}
|
|
38906
39166
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
38907
39167
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -38911,18 +39171,18 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
38911
39171
|
}
|
|
38912
39172
|
function externalSlatePath(cwd, externalSessionId) {
|
|
38913
39173
|
assertValidExternalSessionId(externalSessionId);
|
|
38914
|
-
return
|
|
39174
|
+
return path122.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
38915
39175
|
}
|
|
38916
39176
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
38917
39177
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
38918
39178
|
}
|
|
38919
39179
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
38920
39180
|
assertValidExternalSessionId(externalSessionId);
|
|
38921
|
-
return
|
|
39181
|
+
return path122.join(externalSlatesDir(cwd), externalSessionId);
|
|
38922
39182
|
}
|
|
38923
39183
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
38924
39184
|
try {
|
|
38925
|
-
const raw = await
|
|
39185
|
+
const raw = await readFile65(externalSlatePath(cwd, externalSessionId), "utf8");
|
|
38926
39186
|
return JSON.parse(raw);
|
|
38927
39187
|
} catch (error) {
|
|
38928
39188
|
if (isNotFound(error))
|
|
@@ -38942,7 +39202,7 @@ async function writeExternalSlate(cwd, externalSessionId, update) {
|
|
|
38942
39202
|
}
|
|
38943
39203
|
async function listExternalSlateIds(cwd) {
|
|
38944
39204
|
try {
|
|
38945
|
-
const entries = await
|
|
39205
|
+
const entries = await readdir21(externalSlatesDir(cwd));
|
|
38946
39206
|
return entries.filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -".json".length));
|
|
38947
39207
|
} catch (error) {
|
|
38948
39208
|
if (isNotFound(error))
|
|
@@ -39024,13 +39284,13 @@ __export(exports_service10, {
|
|
|
39024
39284
|
runDoctor: () => runDoctor,
|
|
39025
39285
|
runValidate: () => runValidate
|
|
39026
39286
|
});
|
|
39027
|
-
import
|
|
39028
|
-
import { readFile as
|
|
39287
|
+
import path125 from "path";
|
|
39288
|
+
import { readFile as readFile68 } from "fs/promises";
|
|
39029
39289
|
|
|
39030
39290
|
// src/standard/validate.ts
|
|
39031
39291
|
init_fs();
|
|
39032
|
-
import
|
|
39033
|
-
import { readFile as
|
|
39292
|
+
import path124 from "path";
|
|
39293
|
+
import { readFile as readFile67 } from "fs/promises";
|
|
39034
39294
|
|
|
39035
39295
|
// src/standard/schemas.ts
|
|
39036
39296
|
var MODULE_SCHEMA = {
|
|
@@ -39159,8 +39419,8 @@ var SCHEMA_REGISTRY2 = {
|
|
|
39159
39419
|
// src/standard/profiles.ts
|
|
39160
39420
|
init_fs();
|
|
39161
39421
|
init_entrypoint_targets();
|
|
39162
|
-
import
|
|
39163
|
-
import { readFile as
|
|
39422
|
+
import path123 from "path";
|
|
39423
|
+
import { readFile as readFile66 } from "fs/promises";
|
|
39164
39424
|
var AGENT_MODULES = ["gdgraph", "gdctx", "gdskills", "gdwiki", "memory"];
|
|
39165
39425
|
var CI_MODULES = ["health", "testing"];
|
|
39166
39426
|
var INDEX_LINK = ".metaproject/index.md";
|
|
@@ -39184,11 +39444,11 @@ function enabledModuleKeys(manifest) {
|
|
|
39184
39444
|
}
|
|
39185
39445
|
async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
39186
39446
|
for (const { path: name } of namedEntrypoints(manifest)) {
|
|
39187
|
-
const filePath =
|
|
39447
|
+
const filePath = path123.join(cwd, name);
|
|
39188
39448
|
if (!await pathExists(filePath)) {
|
|
39189
39449
|
continue;
|
|
39190
39450
|
}
|
|
39191
|
-
const content = await
|
|
39451
|
+
const content = await readFile66(filePath, "utf8");
|
|
39192
39452
|
if (content.includes(INDEX_LINK)) {
|
|
39193
39453
|
return true;
|
|
39194
39454
|
}
|
|
@@ -39196,16 +39456,16 @@ async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
|
39196
39456
|
return false;
|
|
39197
39457
|
}
|
|
39198
39458
|
async function minimalSatisfied(cwd) {
|
|
39199
|
-
const metaprojectRoot =
|
|
39459
|
+
const metaprojectRoot = path123.join(cwd, ".metaproject");
|
|
39200
39460
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
39201
39461
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
39202
39462
|
for (const file of requiredFiles) {
|
|
39203
|
-
if (!await pathExists(
|
|
39463
|
+
if (!await pathExists(path123.join(metaprojectRoot, file))) {
|
|
39204
39464
|
return false;
|
|
39205
39465
|
}
|
|
39206
39466
|
}
|
|
39207
39467
|
for (const dir of requiredDirs) {
|
|
39208
|
-
if (!await pathExists(
|
|
39468
|
+
if (!await pathExists(path123.join(metaprojectRoot, dir))) {
|
|
39209
39469
|
return false;
|
|
39210
39470
|
}
|
|
39211
39471
|
}
|
|
@@ -39218,7 +39478,7 @@ async function ciSatisfied(cwd, manifest) {
|
|
|
39218
39478
|
return false;
|
|
39219
39479
|
}
|
|
39220
39480
|
for (const key of ciModules) {
|
|
39221
|
-
if (await pathExists(
|
|
39481
|
+
if (await pathExists(path123.join(cwd, ".metaproject", "data", key, "artifacts"))) {
|
|
39222
39482
|
return true;
|
|
39223
39483
|
}
|
|
39224
39484
|
}
|
|
@@ -39231,7 +39491,7 @@ async function evaluateProfiles(cwd, manifest) {
|
|
|
39231
39491
|
satisfied.push("minimal");
|
|
39232
39492
|
}
|
|
39233
39493
|
const hasAgentModule = AGENT_MODULES.some((key) => new Set(enabledModuleKeys(manifest)).has(key));
|
|
39234
|
-
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(
|
|
39494
|
+
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(path123.join(cwd, ".metaproject", "rules"));
|
|
39235
39495
|
if (agent) {
|
|
39236
39496
|
satisfied.push("agent");
|
|
39237
39497
|
}
|
|
@@ -39400,26 +39660,26 @@ var MANIFEST_TOP_LEVEL_SCHEMA = {
|
|
|
39400
39660
|
async function validateWorkspace2(cwd) {
|
|
39401
39661
|
const errors = [];
|
|
39402
39662
|
const warnings = [];
|
|
39403
|
-
const metaprojectRoot =
|
|
39663
|
+
const metaprojectRoot = path124.join(cwd, ".metaproject");
|
|
39404
39664
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
39405
39665
|
for (const file of requiredFiles) {
|
|
39406
|
-
if (!await pathExists(
|
|
39666
|
+
if (!await pathExists(path124.join(metaprojectRoot, file))) {
|
|
39407
39667
|
errors.push(issue("missing-required-file", `Required file .metaproject/${file} is missing`, `Run \`keryx init\` to scaffold .metaproject/${file}.`));
|
|
39408
39668
|
}
|
|
39409
39669
|
}
|
|
39410
39670
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
39411
39671
|
for (const dir of requiredDirs) {
|
|
39412
|
-
if (!await pathExists(
|
|
39672
|
+
if (!await pathExists(path124.join(metaprojectRoot, dir))) {
|
|
39413
39673
|
errors.push(issue("missing-required-dir", `Required directory .metaproject/${dir}/ is missing`, `Run \`keryx init\` to create .metaproject/${dir}/.`));
|
|
39414
39674
|
}
|
|
39415
39675
|
}
|
|
39416
|
-
const manifestPath =
|
|
39676
|
+
const manifestPath = path124.join(metaprojectRoot, "metaproject.json");
|
|
39417
39677
|
if (!await pathExists(manifestPath)) {
|
|
39418
39678
|
return { ok: errors.length === 0, errors, warnings };
|
|
39419
39679
|
}
|
|
39420
39680
|
let manifest;
|
|
39421
39681
|
try {
|
|
39422
|
-
manifest = JSON.parse(await
|
|
39682
|
+
manifest = JSON.parse(await readFile67(manifestPath, "utf8"));
|
|
39423
39683
|
} catch (error) {
|
|
39424
39684
|
const detail = error instanceof Error ? error.message : String(error);
|
|
39425
39685
|
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 +39705,7 @@ async function validateWorkspace2(cwd) {
|
|
|
39445
39705
|
if (typeof value !== "string") {
|
|
39446
39706
|
continue;
|
|
39447
39707
|
}
|
|
39448
|
-
if (!await pathExists(
|
|
39708
|
+
if (!await pathExists(path124.join(cwd, value))) {
|
|
39449
39709
|
errors.push(issue("missing-declared-path", `Declared path paths.${key} -> ${value} does not exist`, `Create ${value} or remove paths.${key} from metaproject.json.`));
|
|
39450
39710
|
}
|
|
39451
39711
|
}
|
|
@@ -39455,7 +39715,7 @@ async function validateWorkspace2(cwd) {
|
|
|
39455
39715
|
continue;
|
|
39456
39716
|
}
|
|
39457
39717
|
if (typeof entry.manifest === "string") {
|
|
39458
|
-
if (!await pathExists(
|
|
39718
|
+
if (!await pathExists(path124.join(cwd, entry.manifest))) {
|
|
39459
39719
|
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
39720
|
}
|
|
39461
39721
|
}
|
|
@@ -39464,18 +39724,18 @@ async function validateWorkspace2(cwd) {
|
|
|
39464
39724
|
if (typeof value !== "string") {
|
|
39465
39725
|
continue;
|
|
39466
39726
|
}
|
|
39467
|
-
if (!await pathExists(
|
|
39727
|
+
if (!await pathExists(path124.join(cwd, value))) {
|
|
39468
39728
|
const sink = field === "data" ? warnings : errors;
|
|
39469
39729
|
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
39730
|
}
|
|
39471
39731
|
}
|
|
39472
39732
|
}
|
|
39473
39733
|
for (const { path: name, scope } of namedEntrypoints(manifest)) {
|
|
39474
|
-
const filePath =
|
|
39734
|
+
const filePath = path124.join(cwd, name);
|
|
39475
39735
|
if (!await pathExists(filePath)) {
|
|
39476
39736
|
continue;
|
|
39477
39737
|
}
|
|
39478
|
-
const content = await
|
|
39738
|
+
const content = await readFile67(filePath, "utf8");
|
|
39479
39739
|
if (!content.includes(INDEX_LINK2)) {
|
|
39480
39740
|
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
39741
|
}
|
|
@@ -40381,12 +40641,12 @@ async function runDoctor(cwd) {
|
|
|
40381
40641
|
return validateWorkspace2(cwd);
|
|
40382
40642
|
}
|
|
40383
40643
|
async function readManifest(cwd) {
|
|
40384
|
-
const manifestPath =
|
|
40644
|
+
const manifestPath = path125.join(cwd, ".metaproject", "metaproject.json");
|
|
40385
40645
|
if (!await pathExists(manifestPath)) {
|
|
40386
40646
|
throw new Error("Metaproject is not initialized (missing .metaproject/metaproject.json). Run `keryx init` first.");
|
|
40387
40647
|
}
|
|
40388
40648
|
try {
|
|
40389
|
-
return JSON.parse(await
|
|
40649
|
+
return JSON.parse(await readFile68(manifestPath, "utf8"));
|
|
40390
40650
|
} catch (error) {
|
|
40391
40651
|
const detail = error instanceof Error ? error.message : String(error);
|
|
40392
40652
|
throw new Error(`Invalid .metaproject/metaproject.json: ${detail}`);
|