@mrciphersmith/keryx 0.3.132 → 0.3.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/cli.js +6910 -5415
- package/dist/core.js +773 -667
- package/package.json +2 -2
- package/src/gdskills/bundled/install-manifest.json +2 -1
- package/src/gdskills/bundled/skills/review/review-degradation/SKILL.md +193 -0
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +20 -2
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +14 -14
- package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +12 -0
- package/src/gdskills/contracts/review-finding.schema.json +23 -0
package/dist/core.js
CHANGED
|
@@ -1078,6 +1078,12 @@ var init_catalog = __esm(() => {
|
|
|
1078
1078
|
"Anchor class_scope on the callers that break, never on the changed line.",
|
|
1079
1079
|
"Say when the radius could not answer \u2014 a non-code change has an empty one."
|
|
1080
1080
|
]),
|
|
1081
|
+
skill("review-degradation", "review", ["full"], "Ask whether the code is worse after a change than on the base branch (behaviour, defaults, speed, structure) and whether that can be shown, over the diff plus every consumer of each changed shared symbol.", [
|
|
1082
|
+
"Read the diff against the description before reading any consumer.",
|
|
1083
|
+
"Trace the consumers the code graph lists for each changed shared export; the set is the graph's, not yours.",
|
|
1084
|
+
"Name one degradation class and one evidence kind per finding; a major or blocker without evidence is lowered to info at ingest.",
|
|
1085
|
+
"Say what was traced when nothing was found, and say when the consumer cap dropped any."
|
|
1086
|
+
]),
|
|
1081
1087
|
skill("review-pr-contract", "review", ["recommended", "full"], "Compare what the PR body claims (checklist boxes, stated changes) with the diff, and look for documentation outside the diff that the change made stale.", [
|
|
1082
1088
|
"Work every checklist box in pr-context.md as a row: quoted box, state, quoted diff line, expectation.",
|
|
1083
1089
|
"File a finding only where a quoted box or claim disagrees with a quoted diff or document line.",
|
|
@@ -9733,6 +9739,7 @@ var init_skill_length_ceilings = __esm(() => {
|
|
|
9733
9739
|
"review/review-backend": 361,
|
|
9734
9740
|
"review/review-clean-code": 538,
|
|
9735
9741
|
"review/review-core-boundaries": 158,
|
|
9742
|
+
"review/review-degradation": 193,
|
|
9736
9743
|
"review/review-flow-graph": 183,
|
|
9737
9744
|
"review/review-frontend": 607,
|
|
9738
9745
|
"review/review-frontend-conventions": 206,
|
|
@@ -16164,7 +16171,7 @@ var init_guard = __esm(() => {
|
|
|
16164
16171
|
|
|
16165
16172
|
// src/lib/keryx-home.ts
|
|
16166
16173
|
import os2 from "os";
|
|
16167
|
-
import
|
|
16174
|
+
import path59 from "path";
|
|
16168
16175
|
function resolveKeryxHomeDir(env, homeDir) {
|
|
16169
16176
|
if (homeDir !== undefined)
|
|
16170
16177
|
return homeDir;
|
|
@@ -16173,24 +16180,24 @@ function resolveKeryxHomeDir(env, homeDir) {
|
|
|
16173
16180
|
}
|
|
16174
16181
|
function userStoreRoot(env = process.env, homeDir) {
|
|
16175
16182
|
const home = resolveKeryxHomeDir(env, homeDir);
|
|
16176
|
-
return
|
|
16183
|
+
return path59.join(home, USER_STORE_DIRNAME);
|
|
16177
16184
|
}
|
|
16178
16185
|
function userStorePaths(env = process.env, homeDir) {
|
|
16179
16186
|
const root = userStoreRoot(env, homeDir);
|
|
16180
|
-
const state =
|
|
16187
|
+
const state = path59.join(root, "state");
|
|
16181
16188
|
return {
|
|
16182
16189
|
root,
|
|
16183
|
-
skills:
|
|
16184
|
-
agents:
|
|
16185
|
-
memory:
|
|
16186
|
-
learning:
|
|
16187
|
-
learningPatterns:
|
|
16188
|
-
bundles:
|
|
16189
|
-
appliedState:
|
|
16190
|
-
hooksJson:
|
|
16191
|
-
externalSkillImports:
|
|
16190
|
+
skills: path59.join(root, "skills"),
|
|
16191
|
+
agents: path59.join(root, "agents"),
|
|
16192
|
+
memory: path59.join(root, "memory"),
|
|
16193
|
+
learning: path59.join(root, "learning"),
|
|
16194
|
+
learningPatterns: path59.join(root, "learning", "patterns"),
|
|
16195
|
+
bundles: path59.join(root, "bundles"),
|
|
16196
|
+
appliedState: path59.join(root, "bundles", "applied-state.json"),
|
|
16197
|
+
hooksJson: path59.join(root, "hooks.json"),
|
|
16198
|
+
externalSkillImports: path59.join(root, "skills", "external-imports.json"),
|
|
16192
16199
|
state,
|
|
16193
|
-
externalImportsKey:
|
|
16200
|
+
externalImportsKey: path59.join(state, "external-imports.key")
|
|
16194
16201
|
};
|
|
16195
16202
|
}
|
|
16196
16203
|
var USER_STORE_DIRNAME = ".keryx";
|
|
@@ -16297,12 +16304,12 @@ var init_types2 = __esm(() => {
|
|
|
16297
16304
|
|
|
16298
16305
|
// src/memory/store.ts
|
|
16299
16306
|
import { lstat as lstat8, readdir as readdir13, readFile as readFile28 } from "fs/promises";
|
|
16300
|
-
import
|
|
16307
|
+
import path60 from "path";
|
|
16301
16308
|
function decodeStrictUtf8(bytes) {
|
|
16302
16309
|
return STRICT_UTF8_DECODER.decode(bytes);
|
|
16303
16310
|
}
|
|
16304
16311
|
function memoryRoot(cwd) {
|
|
16305
|
-
return
|
|
16312
|
+
return path60.join(cwd, ".metaproject", "memory");
|
|
16306
16313
|
}
|
|
16307
16314
|
function memoryRootFor(scope, cwd, env = process.env, homeDir) {
|
|
16308
16315
|
return scope === "user" ? userStorePaths(env, homeDir).memory : memoryRoot(cwd);
|
|
@@ -16311,7 +16318,7 @@ async function collectEntries(cwd) {
|
|
|
16311
16318
|
const root = memoryRoot(cwd);
|
|
16312
16319
|
const entries = [];
|
|
16313
16320
|
for (const { type, folder } of MEMORY_TYPES) {
|
|
16314
|
-
const dir =
|
|
16321
|
+
const dir = path60.join(root, folder);
|
|
16315
16322
|
let dirStats;
|
|
16316
16323
|
try {
|
|
16317
16324
|
dirStats = await lstat8(dir);
|
|
@@ -16325,7 +16332,7 @@ async function collectEntries(cwd) {
|
|
|
16325
16332
|
if (!name.endsWith(".md")) {
|
|
16326
16333
|
continue;
|
|
16327
16334
|
}
|
|
16328
|
-
const abs =
|
|
16335
|
+
const abs = path60.join(dir, name);
|
|
16329
16336
|
let stats;
|
|
16330
16337
|
try {
|
|
16331
16338
|
stats = await lstat8(abs);
|
|
@@ -16360,7 +16367,7 @@ async function collectEntriesStrict(root) {
|
|
|
16360
16367
|
return { status: "incomplete", entries: [], problems: [{ path: ".", reason: "not-a-regular-file" }] };
|
|
16361
16368
|
}
|
|
16362
16369
|
for (const { type, folder } of MEMORY_TYPES) {
|
|
16363
|
-
const dir =
|
|
16370
|
+
const dir = path60.join(root, folder);
|
|
16364
16371
|
let dirStats;
|
|
16365
16372
|
try {
|
|
16366
16373
|
dirStats = await lstat8(dir);
|
|
@@ -16384,7 +16391,7 @@ async function collectEntriesStrict(root) {
|
|
|
16384
16391
|
}
|
|
16385
16392
|
for (const name of names) {
|
|
16386
16393
|
const relativePath = `${folder}/${name}`;
|
|
16387
|
-
const abs =
|
|
16394
|
+
const abs = path60.join(dir, name);
|
|
16388
16395
|
let entryStats;
|
|
16389
16396
|
try {
|
|
16390
16397
|
entryStats = await lstat8(abs);
|
|
@@ -16476,7 +16483,7 @@ async function collectUnexpectedMarkdown(dir, relativeDir, problems) {
|
|
|
16476
16483
|
return;
|
|
16477
16484
|
}
|
|
16478
16485
|
for (const name of names) {
|
|
16479
|
-
const abs =
|
|
16486
|
+
const abs = path60.join(dir, name);
|
|
16480
16487
|
const relativePath = `${relativeDir}/${name}`;
|
|
16481
16488
|
let stats;
|
|
16482
16489
|
try {
|
|
@@ -16510,7 +16517,7 @@ async function collectUnexpectedTopLevel(root, problems) {
|
|
|
16510
16517
|
if (MEMORY_TYPE_FOLDERS.has(name)) {
|
|
16511
16518
|
continue;
|
|
16512
16519
|
}
|
|
16513
|
-
const abs =
|
|
16520
|
+
const abs = path60.join(root, name);
|
|
16514
16521
|
let stats;
|
|
16515
16522
|
try {
|
|
16516
16523
|
stats = await lstat8(abs);
|
|
@@ -16879,9 +16886,9 @@ var init_validation = __esm(() => {
|
|
|
16879
16886
|
});
|
|
16880
16887
|
|
|
16881
16888
|
// src/memory/config.ts
|
|
16882
|
-
import
|
|
16889
|
+
import path64 from "path";
|
|
16883
16890
|
function memoryConfigPath(cwd) {
|
|
16884
|
-
return
|
|
16891
|
+
return path64.join(cwd, ".metaproject", "memory.config.json");
|
|
16885
16892
|
}
|
|
16886
16893
|
async function loadMemoryConfig(cwd) {
|
|
16887
16894
|
const file = memoryConfigPath(cwd);
|
|
@@ -17009,9 +17016,9 @@ function titleSimilarity(a, b) {
|
|
|
17009
17016
|
// src/memory/report.ts
|
|
17010
17017
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
17011
17018
|
import { access as access2, mkdir as mkdir10, readdir as readdir15, rename as rename4, rm as rm4, stat as stat10, writeFile as writeFile7 } from "fs/promises";
|
|
17012
|
-
import
|
|
17019
|
+
import path65 from "path";
|
|
17013
17020
|
function runtimeRoot(cwd) {
|
|
17014
|
-
return
|
|
17021
|
+
return path65.join(cwd, ".metaproject", "runtime", "memory");
|
|
17015
17022
|
}
|
|
17016
17023
|
function bounded(value, maximum) {
|
|
17017
17024
|
if (Buffer.byteLength(value, "utf8") <= maximum)
|
|
@@ -17048,8 +17055,8 @@ function formatScope(scopes) {
|
|
|
17048
17055
|
return parts.length > 0 ? parts.join(", ") : null;
|
|
17049
17056
|
}
|
|
17050
17057
|
function relativeMemoryPath(value) {
|
|
17051
|
-
const normalized = value.split(
|
|
17052
|
-
if (!normalized ||
|
|
17058
|
+
const normalized = value.split(path65.sep).join("/");
|
|
17059
|
+
if (!normalized || path65.isAbsolute(value) || normalized.startsWith("../") || normalized.includes("/../")) {
|
|
17053
17060
|
throw new Error("Memory report paths must be memory-root-relative");
|
|
17054
17061
|
}
|
|
17055
17062
|
return bounded(normalized, MAX_PATH_LENGTH);
|
|
@@ -17156,7 +17163,7 @@ function validateMemorySearchReport(value) {
|
|
|
17156
17163
|
for (const key of Object.keys(item))
|
|
17157
17164
|
if (!allowedResult.has(key))
|
|
17158
17165
|
errors.push(`unexpected result property: ${key}`);
|
|
17159
|
-
if (typeof item.path !== "string" || !item.path || item.path.length > MAX_PATH_LENGTH ||
|
|
17166
|
+
if (typeof item.path !== "string" || !item.path || item.path.length > MAX_PATH_LENGTH || path65.isAbsolute(item.path) || item.path.includes(".."))
|
|
17160
17167
|
errors.push("result path is invalid");
|
|
17161
17168
|
if (typeof item.title !== "string" || item.title.length > MAX_TITLE_LENGTH)
|
|
17162
17169
|
errors.push("result title is invalid");
|
|
@@ -17243,26 +17250,26 @@ class MemoryReportStore {
|
|
|
17243
17250
|
throw new Error("Invalid memory report run ID");
|
|
17244
17251
|
const report = renderMemorySearchReport({ runId, generatedAt: this.clock(), search: input.search, filters: input.filters ?? {} });
|
|
17245
17252
|
const root = runtimeRoot(input.cwd);
|
|
17246
|
-
const searchRoot =
|
|
17247
|
-
const tempRoot =
|
|
17248
|
-
const destination =
|
|
17253
|
+
const searchRoot = path65.join(root, "search");
|
|
17254
|
+
const tempRoot = path65.join(root, "tmp");
|
|
17255
|
+
const destination = path65.join(searchRoot, runId);
|
|
17249
17256
|
if (await exists(destination))
|
|
17250
17257
|
throw new Error(`Memory report run already exists: ${runId}`);
|
|
17251
17258
|
await mkdir10(searchRoot, { recursive: true });
|
|
17252
17259
|
await mkdir10(tempRoot, { recursive: true });
|
|
17253
17260
|
const staleBefore = this.clock().getTime() - STALE_STAGING_MS;
|
|
17254
17261
|
for (const name of await readdir15(tempRoot)) {
|
|
17255
|
-
const candidate =
|
|
17262
|
+
const candidate = path65.join(tempRoot, name);
|
|
17256
17263
|
if ((await stat10(candidate)).mtimeMs <= staleBefore)
|
|
17257
17264
|
await rm4(candidate, { recursive: true, force: true });
|
|
17258
17265
|
}
|
|
17259
|
-
const staging =
|
|
17266
|
+
const staging = path65.join(tempRoot, `${runId}.${randomUUID2()}`);
|
|
17260
17267
|
try {
|
|
17261
17268
|
await mkdir10(staging);
|
|
17262
17269
|
await Promise.all([
|
|
17263
|
-
writeFile7(
|
|
17270
|
+
writeFile7(path65.join(staging, "report.json"), `${JSON.stringify(report, null, 2)}
|
|
17264
17271
|
`, "utf8"),
|
|
17265
|
-
writeFile7(
|
|
17272
|
+
writeFile7(path65.join(staging, "report.md"), renderMemorySearchReportMarkdown(report), "utf8")
|
|
17266
17273
|
]);
|
|
17267
17274
|
if (await exists(destination))
|
|
17268
17275
|
throw new Error(`Memory report run already exists: ${runId}`);
|
|
@@ -17273,8 +17280,8 @@ class MemoryReportStore {
|
|
|
17273
17280
|
}
|
|
17274
17281
|
return {
|
|
17275
17282
|
runId,
|
|
17276
|
-
markdownPath:
|
|
17277
|
-
jsonPath:
|
|
17283
|
+
markdownPath: path65.relative(input.cwd, path65.join(destination, "report.md")),
|
|
17284
|
+
jsonPath: path65.relative(input.cwd, path65.join(destination, "report.json"))
|
|
17278
17285
|
};
|
|
17279
17286
|
}
|
|
17280
17287
|
}
|
|
@@ -17531,22 +17538,22 @@ var init_search = __esm(() => {
|
|
|
17531
17538
|
});
|
|
17532
17539
|
|
|
17533
17540
|
// src/gdgraph/data-dir.ts
|
|
17534
|
-
import
|
|
17541
|
+
import path79 from "path";
|
|
17535
17542
|
function gdgraphDataDir(projectRoot, dataDir) {
|
|
17536
|
-
return dataDir ??
|
|
17543
|
+
return dataDir ?? path79.join(projectRoot, ".metaproject", "data", "gdgraph");
|
|
17537
17544
|
}
|
|
17538
17545
|
function gdgraphStorageDir(projectRoot, dataDir) {
|
|
17539
|
-
return
|
|
17546
|
+
return path79.join(gdgraphDataDir(projectRoot, dataDir), "storage");
|
|
17540
17547
|
}
|
|
17541
17548
|
function gdgraphArtifactsDir(projectRoot, dataDir) {
|
|
17542
|
-
return
|
|
17549
|
+
return path79.join(gdgraphDataDir(projectRoot, dataDir), "artifacts");
|
|
17543
17550
|
}
|
|
17544
17551
|
var init_data_dir = () => {};
|
|
17545
17552
|
|
|
17546
17553
|
// src/gdgraph/config.ts
|
|
17547
|
-
import
|
|
17554
|
+
import path80 from "path";
|
|
17548
17555
|
function gdgraphConfigPath(cwd) {
|
|
17549
|
-
return
|
|
17556
|
+
return path80.join(cwd, ".metaproject", "gdgraph.config.json");
|
|
17550
17557
|
}
|
|
17551
17558
|
function mergeGdgraphConfig(parsed) {
|
|
17552
17559
|
const base = DEFAULT_GDGRAPH_CONFIG;
|
|
@@ -18151,7 +18158,7 @@ var init_extract = __esm(() => {
|
|
|
18151
18158
|
});
|
|
18152
18159
|
|
|
18153
18160
|
// src/gdgraph/treesitter/grammars.ts
|
|
18154
|
-
import
|
|
18161
|
+
import path81 from "path";
|
|
18155
18162
|
function grammarAssetId(language) {
|
|
18156
18163
|
return `tree-sitter-${language}`;
|
|
18157
18164
|
}
|
|
@@ -18167,7 +18174,7 @@ function symbolLanguage(language) {
|
|
|
18167
18174
|
async function resolveGrammar(cwd, language, grammarsPath) {
|
|
18168
18175
|
const id = grammarAssetId(language);
|
|
18169
18176
|
const lock = await loadAssetsLock(cwd);
|
|
18170
|
-
const overrides = grammarsPath ? { [id]: { path:
|
|
18177
|
+
const overrides = grammarsPath ? { [id]: { path: path81.join(grammarsPath, `${id}.wasm`) } } : undefined;
|
|
18171
18178
|
const registry = registryFromLock(lock, overrides);
|
|
18172
18179
|
const resolved = await resolveAsset(registry, id);
|
|
18173
18180
|
if (!resolved) {
|
|
@@ -18197,7 +18204,7 @@ function toGrammarLanguages(languages) {
|
|
|
18197
18204
|
return out;
|
|
18198
18205
|
}
|
|
18199
18206
|
function grammarForFile(file, available) {
|
|
18200
|
-
const ext =
|
|
18207
|
+
const ext = path81.extname(file);
|
|
18201
18208
|
const preference = ext === ".tsx" ? ["tsx", "typescript"] : ext === ".ts" ? ["typescript"] : ext === ".jsx" || ext === ".js" || ext === ".mjs" || ext === ".cjs" ? ["javascript"] : ext === ".java" ? ["java"] : ext === ".py" ? ["python"] : [];
|
|
18202
18209
|
for (const language of preference) {
|
|
18203
18210
|
if (available.includes(language)) {
|
|
@@ -18459,7 +18466,7 @@ var init_adapter = __esm(() => {
|
|
|
18459
18466
|
|
|
18460
18467
|
// src/gdgraph/enrich.ts
|
|
18461
18468
|
import { mkdir as mkdir14, writeFile as writeFile8 } from "fs/promises";
|
|
18462
|
-
import
|
|
18469
|
+
import path82 from "path";
|
|
18463
18470
|
async function enrichBuildWithSymbols(cwd, files, resolve, dataDir) {
|
|
18464
18471
|
const config = await loadGdgraphConfig(cwd);
|
|
18465
18472
|
const treesitterConfig = {
|
|
@@ -18477,10 +18484,10 @@ async function enrichBuildWithSymbols(cwd, files, resolve, dataDir) {
|
|
|
18477
18484
|
async function writeSymbolLayer(cwd, layer, dataDir) {
|
|
18478
18485
|
const storageDir = gdgraphStorageDir(cwd, dataDir);
|
|
18479
18486
|
await mkdir14(storageDir, { recursive: true });
|
|
18480
|
-
await writeFile8(
|
|
18487
|
+
await writeFile8(path82.join(storageDir, "symbols.jsonl"), layer.symbols.map((symbol) => JSON.stringify(symbol)).join(`
|
|
18481
18488
|
`) + `
|
|
18482
18489
|
`, "utf8");
|
|
18483
|
-
await writeFile8(
|
|
18490
|
+
await writeFile8(path82.join(storageDir, "calls.jsonl"), layer.calls.map((call) => JSON.stringify(call)).join(`
|
|
18484
18491
|
`) + `
|
|
18485
18492
|
`, "utf8");
|
|
18486
18493
|
}
|
|
@@ -18674,10 +18681,10 @@ var init_describes = () => {};
|
|
|
18674
18681
|
|
|
18675
18682
|
// src/sync/provenance.ts
|
|
18676
18683
|
import { mkdir as mkdir15, readFile as readFile40, writeFile as writeFile9 } from "fs/promises";
|
|
18677
|
-
import
|
|
18684
|
+
import path83 from "path";
|
|
18678
18685
|
import { spawn } from "child_process";
|
|
18679
18686
|
function provenancePath(cwd, module) {
|
|
18680
|
-
return
|
|
18687
|
+
return path83.join(cwd, ".metaproject", "data", module, ".provenance.json");
|
|
18681
18688
|
}
|
|
18682
18689
|
function gitCmdResult(cwd, args) {
|
|
18683
18690
|
return new Promise((resolve) => {
|
|
@@ -18722,7 +18729,7 @@ async function recordProvenance(cwd, module, at) {
|
|
|
18722
18729
|
if (!head)
|
|
18723
18730
|
return;
|
|
18724
18731
|
const file = provenancePath(cwd, module);
|
|
18725
|
-
await mkdir15(
|
|
18732
|
+
await mkdir15(path83.dirname(file), { recursive: true });
|
|
18726
18733
|
const provenance = { commit: head.commit, branch: head.branch, builtAt: at };
|
|
18727
18734
|
await writeFile9(file, `${JSON.stringify(provenance, null, 2)}
|
|
18728
18735
|
`, "utf8");
|
|
@@ -18748,7 +18755,7 @@ var init_provenance = __esm(() => {
|
|
|
18748
18755
|
// src/gdgraph/staleness.ts
|
|
18749
18756
|
import { spawn as spawn2 } from "child_process";
|
|
18750
18757
|
import { stat as stat11 } from "fs/promises";
|
|
18751
|
-
import
|
|
18758
|
+
import path84 from "path";
|
|
18752
18759
|
function gitStatusPorcelain(cwd) {
|
|
18753
18760
|
return new Promise((resolve) => {
|
|
18754
18761
|
try {
|
|
@@ -18771,10 +18778,10 @@ function gitStatusPorcelain(cwd) {
|
|
|
18771
18778
|
});
|
|
18772
18779
|
}
|
|
18773
18780
|
function nodesJsonlPath(cwd) {
|
|
18774
|
-
return
|
|
18781
|
+
return path84.join(cwd, ".metaproject", "data", "gdgraph", "storage", "nodes.jsonl");
|
|
18775
18782
|
}
|
|
18776
18783
|
function gdgraphConfigPath2(cwd) {
|
|
18777
|
-
return
|
|
18784
|
+
return path84.join(cwd, ".metaproject", "gdgraph.config.json");
|
|
18778
18785
|
}
|
|
18779
18786
|
function categorizeStatusEntries(entries, rootPrefix) {
|
|
18780
18787
|
const metaprojectPrefix = `${rootPrefix}.metaproject/`;
|
|
@@ -18869,7 +18876,7 @@ async function checkGraphStaleness(cwd, statusObservation) {
|
|
|
18869
18876
|
}
|
|
18870
18877
|
} else {
|
|
18871
18878
|
provenanceMissing = true;
|
|
18872
|
-
const logsHeadStat = await stat11(
|
|
18879
|
+
const logsHeadStat = await stat11(path84.join(cwd, ".git", "logs", "HEAD")).catch(() => null);
|
|
18873
18880
|
if (logsHeadStat && logsHeadStat.mtimeMs > nodesStat.mtimeMs) {
|
|
18874
18881
|
reasons.push("repo HEAD moved since the graph was built (no build provenance recorded; inferred from .git/logs/HEAD)");
|
|
18875
18882
|
}
|
|
@@ -18897,7 +18904,7 @@ async function checkGraphStaleness(cwd, statusObservation) {
|
|
|
18897
18904
|
if (projectRelative === null) {
|
|
18898
18905
|
continue;
|
|
18899
18906
|
}
|
|
18900
|
-
const fileStat = await stat11(
|
|
18907
|
+
const fileStat = await stat11(path84.join(cwd, projectRelative)).catch(() => null);
|
|
18901
18908
|
if (fileStat && fileStat.mtimeMs > nodesStat.mtimeMs) {
|
|
18902
18909
|
postBuildEdits.push(displayPath);
|
|
18903
18910
|
}
|
|
@@ -19033,15 +19040,15 @@ var init_types3 = __esm(() => {
|
|
|
19033
19040
|
|
|
19034
19041
|
// src/wiki/collect.ts
|
|
19035
19042
|
import { readFile as readFile41, readdir as readdir16 } from "fs/promises";
|
|
19036
|
-
import
|
|
19043
|
+
import path85 from "path";
|
|
19037
19044
|
function wikiRootPath(cwd) {
|
|
19038
|
-
return
|
|
19045
|
+
return path85.join(cwd, ".metaproject", "wiki");
|
|
19039
19046
|
}
|
|
19040
19047
|
async function collectPages(cwd) {
|
|
19041
19048
|
const root = wikiRootPath(cwd);
|
|
19042
19049
|
const pages = [];
|
|
19043
19050
|
for (const { type, folder } of WIKI_PAGE_TYPES) {
|
|
19044
|
-
const dir =
|
|
19051
|
+
const dir = path85.join(root, folder);
|
|
19045
19052
|
let names;
|
|
19046
19053
|
try {
|
|
19047
19054
|
names = await readdir16(dir);
|
|
@@ -19056,7 +19063,7 @@ async function collectPages(cwd) {
|
|
|
19056
19063
|
if (!entry.endsWith(".md")) {
|
|
19057
19064
|
continue;
|
|
19058
19065
|
}
|
|
19059
|
-
const absolutePath =
|
|
19066
|
+
const absolutePath = path85.join(dir, entry);
|
|
19060
19067
|
let content;
|
|
19061
19068
|
try {
|
|
19062
19069
|
content = await readFile41(absolutePath, "utf8");
|
|
@@ -19917,9 +19924,9 @@ var MEMORY_EMBEDDING_ID = "memory.embedding", DEFAULT_EMBED_DIMS = 96;
|
|
|
19917
19924
|
// src/memory/embedding/index.ts
|
|
19918
19925
|
import { createHash as createHash14 } from "crypto";
|
|
19919
19926
|
import { mkdir as mkdir16, readFile as readFile42, writeFile as writeFile10 } from "fs/promises";
|
|
19920
|
-
import
|
|
19927
|
+
import path86 from "path";
|
|
19921
19928
|
function embeddingsDir(cwd) {
|
|
19922
|
-
return
|
|
19929
|
+
return path86.join(cwd, ".metaproject", "data", "memory", "embeddings");
|
|
19923
19930
|
}
|
|
19924
19931
|
function entryText(entry) {
|
|
19925
19932
|
return `${entry.title}
|
|
@@ -19948,17 +19955,17 @@ async function buildEmbeddingIndex(cwd, entries, embedder, model, now) {
|
|
|
19948
19955
|
};
|
|
19949
19956
|
const dir = embeddingsDir(cwd);
|
|
19950
19957
|
await mkdir16(dir, { recursive: true });
|
|
19951
|
-
await writeFile10(
|
|
19958
|
+
await writeFile10(path86.join(dir, "index.meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
19952
19959
|
`, "utf8");
|
|
19953
|
-
await writeFile10(
|
|
19960
|
+
await writeFile10(path86.join(dir, "vectors.jsonl"), records.map((record) => JSON.stringify(record)).join(`
|
|
19954
19961
|
`) + (records.length > 0 ? `
|
|
19955
19962
|
` : ""), "utf8");
|
|
19956
19963
|
return toIndex(meta, records);
|
|
19957
19964
|
}
|
|
19958
19965
|
async function loadEmbeddingIndex(cwd) {
|
|
19959
19966
|
const dir = embeddingsDir(cwd);
|
|
19960
|
-
const metaPath =
|
|
19961
|
-
const vectorsPath =
|
|
19967
|
+
const metaPath = path86.join(dir, "index.meta.json");
|
|
19968
|
+
const vectorsPath = path86.join(dir, "vectors.jsonl");
|
|
19962
19969
|
if (!await pathExists(metaPath) || !await pathExists(vectorsPath)) {
|
|
19963
19970
|
return null;
|
|
19964
19971
|
}
|
|
@@ -20039,10 +20046,10 @@ var init_embedding = __esm(() => {
|
|
|
20039
20046
|
});
|
|
20040
20047
|
|
|
20041
20048
|
// src/wiki/section-tombstone.ts
|
|
20042
|
-
import
|
|
20049
|
+
import path87 from "path";
|
|
20043
20050
|
import { stat as stat12 } from "fs/promises";
|
|
20044
20051
|
function sectionRegistryPath(cwd) {
|
|
20045
|
-
return
|
|
20052
|
+
return path87.join(cwd, ".metaproject", "wiki", ".sections.json");
|
|
20046
20053
|
}
|
|
20047
20054
|
function emptySectionRegistry() {
|
|
20048
20055
|
return { version: SECTION_REGISTRY_VERSION, entries: [], tombstones: [], lifted: [] };
|
|
@@ -20344,10 +20351,10 @@ var init_section_tombstone = __esm(() => {
|
|
|
20344
20351
|
|
|
20345
20352
|
// src/memory/cross-layer.ts
|
|
20346
20353
|
import { readFile as readFile43 } from "fs/promises";
|
|
20347
|
-
import
|
|
20354
|
+
import path88 from "path";
|
|
20348
20355
|
function extractWikiReferences(entryRelativePath, entryAbsolutePath, content, wikiRoot) {
|
|
20349
20356
|
const references = [];
|
|
20350
|
-
const entryDir =
|
|
20357
|
+
const entryDir = path88.dirname(entryAbsolutePath);
|
|
20351
20358
|
const lines = content.split(`
|
|
20352
20359
|
`);
|
|
20353
20360
|
for (let index = 0;index < lines.length; index += 1) {
|
|
@@ -20362,16 +20369,16 @@ function extractWikiReferences(entryRelativePath, entryAbsolutePath, content, wi
|
|
|
20362
20369
|
if (withoutAnchor.length === 0) {
|
|
20363
20370
|
continue;
|
|
20364
20371
|
}
|
|
20365
|
-
const absolute =
|
|
20366
|
-
const relativeToWiki =
|
|
20367
|
-
if (relativeToWiki.startsWith("..") ||
|
|
20372
|
+
const absolute = path88.resolve(entryDir, withoutAnchor);
|
|
20373
|
+
const relativeToWiki = path88.relative(wikiRoot, absolute);
|
|
20374
|
+
if (relativeToWiki.startsWith("..") || path88.isAbsolute(relativeToWiki)) {
|
|
20368
20375
|
continue;
|
|
20369
20376
|
}
|
|
20370
20377
|
references.push({
|
|
20371
20378
|
entry: entryRelativePath,
|
|
20372
20379
|
kind: "page-path",
|
|
20373
20380
|
raw: href,
|
|
20374
|
-
target: relativeToWiki.split(
|
|
20381
|
+
target: relativeToWiki.split(path88.sep).join("/"),
|
|
20375
20382
|
line
|
|
20376
20383
|
});
|
|
20377
20384
|
}
|
|
@@ -20391,7 +20398,7 @@ function isDangling(finding) {
|
|
|
20391
20398
|
return finding.state !== "intact";
|
|
20392
20399
|
}
|
|
20393
20400
|
async function loadWikiKnowledgeView(cwd) {
|
|
20394
|
-
const wikiRoot =
|
|
20401
|
+
const wikiRoot = path88.join(cwd, ".metaproject", "wiki");
|
|
20395
20402
|
const pages = await collectPages(cwd);
|
|
20396
20403
|
const index = buildSectionIndex(await Promise.all(pages.map(async (page) => ({
|
|
20397
20404
|
page,
|
|
@@ -20609,7 +20616,7 @@ function round2(value) {
|
|
|
20609
20616
|
var init_dedup = () => {};
|
|
20610
20617
|
|
|
20611
20618
|
// src/memory/check.ts
|
|
20612
|
-
import
|
|
20619
|
+
import path89 from "path";
|
|
20613
20620
|
async function checkMemory(cwd, config) {
|
|
20614
20621
|
const entries = await collectEntries(cwd);
|
|
20615
20622
|
const issues = [];
|
|
@@ -20625,7 +20632,7 @@ async function checkMemory(cwd, config) {
|
|
|
20625
20632
|
}
|
|
20626
20633
|
for (const file of entry.scopes.files) {
|
|
20627
20634
|
const clean = file.replace(/^`|`$/g, "");
|
|
20628
|
-
if (!await pathExists(
|
|
20635
|
+
if (!await pathExists(path89.resolve(cwd, clean))) {
|
|
20629
20636
|
issues.push({ path: entry.relativePath, kind: "link", message: `related file not found: ${clean}` });
|
|
20630
20637
|
}
|
|
20631
20638
|
}
|
|
@@ -20680,12 +20687,12 @@ var init_check = __esm(() => {
|
|
|
20680
20687
|
|
|
20681
20688
|
// src/memory/write.ts
|
|
20682
20689
|
import { mkdir as mkdir17, open as open2, readFile as readFile44, rename as rename5, rm as rm5, writeFile as writeFile11 } from "fs/promises";
|
|
20683
|
-
import
|
|
20690
|
+
import path90 from "path";
|
|
20684
20691
|
async function resolveCanonicalEntryPath(cwd, relativePath) {
|
|
20685
|
-
const root =
|
|
20686
|
-
const absolutePath =
|
|
20687
|
-
const normalized = toPosix2(
|
|
20688
|
-
if (!normalized || normalized.startsWith("../") ||
|
|
20692
|
+
const root = path90.resolve(memoryRoot(cwd));
|
|
20693
|
+
const absolutePath = path90.resolve(root, relativePath);
|
|
20694
|
+
const normalized = toPosix2(path90.relative(root, absolutePath));
|
|
20695
|
+
if (!normalized || normalized.startsWith("../") || path90.isAbsolute(normalized) || !normalized.endsWith(".md")) {
|
|
20689
20696
|
return null;
|
|
20690
20697
|
}
|
|
20691
20698
|
return { absolutePath, relativePath: normalized };
|
|
@@ -20781,9 +20788,9 @@ function validateNextEntry(relativePath, content) {
|
|
|
20781
20788
|
return null;
|
|
20782
20789
|
}
|
|
20783
20790
|
async function replaceAtomically(target, content) {
|
|
20784
|
-
const dir =
|
|
20791
|
+
const dir = path90.dirname(target);
|
|
20785
20792
|
await mkdir17(dir, { recursive: true });
|
|
20786
|
-
const tmp =
|
|
20793
|
+
const tmp = path90.join(dir, `.${path90.basename(target)}.keryx-tmp-${process.pid}-${Math.random().toString(16).slice(2)}`);
|
|
20787
20794
|
try {
|
|
20788
20795
|
await writeFile11(tmp, content, "utf8");
|
|
20789
20796
|
const file = await open2(tmp, "r");
|
|
@@ -20819,7 +20826,7 @@ function header(content, name) {
|
|
|
20819
20826
|
return content.match(new RegExp(`^${name}:\\s*(.+)$`, "mi"))?.[1]?.trim() ?? null;
|
|
20820
20827
|
}
|
|
20821
20828
|
function toPosix2(value) {
|
|
20822
|
-
return value.split(
|
|
20829
|
+
return value.split(path90.sep).join("/");
|
|
20823
20830
|
}
|
|
20824
20831
|
function message(cause) {
|
|
20825
20832
|
return cause instanceof Error ? cause.message : String(cause);
|
|
@@ -20833,9 +20840,9 @@ var init_write = __esm(() => {
|
|
|
20833
20840
|
// src/memory/ingest.ts
|
|
20834
20841
|
import { existsSync as existsSync10 } from "fs";
|
|
20835
20842
|
import { readFile as readFile45 } from "fs/promises";
|
|
20836
|
-
import
|
|
20843
|
+
import path91 from "path";
|
|
20837
20844
|
async function ingestMemory(cwd, source, filePath, config, now) {
|
|
20838
|
-
const absolute =
|
|
20845
|
+
const absolute = path91.resolve(cwd, filePath);
|
|
20839
20846
|
if (!await pathExists(absolute)) {
|
|
20840
20847
|
throw new Error(`Ingest source not found: ${filePath}`);
|
|
20841
20848
|
}
|
|
@@ -20852,8 +20859,8 @@ async function ingestMemory(cwd, source, filePath, config, now) {
|
|
|
20852
20859
|
const securityWarnings = [];
|
|
20853
20860
|
const securitySkipped = [];
|
|
20854
20861
|
let skippedDuplicates = 0;
|
|
20855
|
-
const dir =
|
|
20856
|
-
const link =
|
|
20862
|
+
const dir = path91.join(memoryRoot(cwd), folder);
|
|
20863
|
+
const link = path91.relative(cwd, absolute);
|
|
20857
20864
|
const date = dateString(now);
|
|
20858
20865
|
for (const text of candidates) {
|
|
20859
20866
|
const title = toTitle(text);
|
|
@@ -21034,7 +21041,7 @@ function uniqueSlug(dir, title) {
|
|
|
21034
21041
|
const base = slugify2(title) || "entry";
|
|
21035
21042
|
let slug = base;
|
|
21036
21043
|
let counter = 2;
|
|
21037
|
-
while (existsSync10(
|
|
21044
|
+
while (existsSync10(path91.join(dir, `${slug}.md`))) {
|
|
21038
21045
|
slug = `${base}-${counter}`;
|
|
21039
21046
|
counter += 1;
|
|
21040
21047
|
}
|
|
@@ -21069,7 +21076,7 @@ var init_ingest = __esm(() => {
|
|
|
21069
21076
|
|
|
21070
21077
|
// src/memory/supersede.ts
|
|
21071
21078
|
import { readFile as readFile46 } from "fs/promises";
|
|
21072
|
-
import
|
|
21079
|
+
import path92 from "path";
|
|
21073
21080
|
async function supersedeEntry(input, now) {
|
|
21074
21081
|
const date = input.date ?? now.toISOString().slice(0, 10);
|
|
21075
21082
|
const today = now.toISOString().slice(0, 10);
|
|
@@ -21129,12 +21136,12 @@ async function supersedeEntry(input, now) {
|
|
|
21129
21136
|
async function resolveEntryPath(cwd, raw) {
|
|
21130
21137
|
const root = memoryRoot(cwd);
|
|
21131
21138
|
const candidates = [
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
|
|
21139
|
+
path92.resolve(cwd, raw),
|
|
21140
|
+
path92.resolve(root, raw),
|
|
21141
|
+
path92.isAbsolute(raw) ? raw : path92.join(cwd, raw)
|
|
21135
21142
|
];
|
|
21136
21143
|
for (const absolute of candidates) {
|
|
21137
|
-
const confined = await resolveCanonicalEntryPath(cwd,
|
|
21144
|
+
const confined = await resolveCanonicalEntryPath(cwd, path92.relative(root, absolute));
|
|
21138
21145
|
if (confined && await pathExists(confined.absolutePath)) {
|
|
21139
21146
|
return { absolute: confined.absolutePath, relative: confined.relativePath };
|
|
21140
21147
|
}
|
|
@@ -21330,9 +21337,9 @@ __export(exports_service3, {
|
|
|
21330
21337
|
});
|
|
21331
21338
|
import { createHash as createHash15 } from "crypto";
|
|
21332
21339
|
import { mkdir as mkdir18, readFile as readFile47, writeFile as writeFile12 } from "fs/promises";
|
|
21333
|
-
import
|
|
21340
|
+
import path93 from "path";
|
|
21334
21341
|
function dataRoot2(cwd) {
|
|
21335
|
-
return
|
|
21342
|
+
return path93.join(cwd, ".metaproject", "data", "memory");
|
|
21336
21343
|
}
|
|
21337
21344
|
function createMemoryService() {
|
|
21338
21345
|
const reportStore = createMemoryReportStore();
|
|
@@ -21347,9 +21354,9 @@ function createMemoryService() {
|
|
|
21347
21354
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
21348
21355
|
throw new Error(`Invalid slug: ${slug}. Use lowercase letters, digits, and hyphens.`);
|
|
21349
21356
|
}
|
|
21350
|
-
const dir =
|
|
21351
|
-
const filePath =
|
|
21352
|
-
const relativePath =
|
|
21357
|
+
const dir = path93.join(memoryRoot(input.cwd), typeConfig.folder);
|
|
21358
|
+
const filePath = path93.join(dir, `${slug}.md`);
|
|
21359
|
+
const relativePath = path93.relative(input.cwd, filePath);
|
|
21353
21360
|
if (await pathExists(filePath) && !input.force) {
|
|
21354
21361
|
throw new Error(`Entry already exists: ${relativePath}. Use --force to overwrite.`);
|
|
21355
21362
|
}
|
|
@@ -21380,9 +21387,9 @@ function createMemoryService() {
|
|
|
21380
21387
|
async index(input) {
|
|
21381
21388
|
const entries = await collectEntries(input.cwd);
|
|
21382
21389
|
const generatedAt = new Date().toISOString();
|
|
21383
|
-
const indexDir = input.indexDir ??
|
|
21390
|
+
const indexDir = input.indexDir ?? path93.join(dataRoot2(input.cwd), "index");
|
|
21384
21391
|
await mkdir18(indexDir, { recursive: true });
|
|
21385
|
-
const indexPath =
|
|
21392
|
+
const indexPath = path93.join(indexDir, "index.json");
|
|
21386
21393
|
await writeFile12(indexPath, `${JSON.stringify({
|
|
21387
21394
|
catalogVersion: 1,
|
|
21388
21395
|
sourceFingerprint: createHash15("sha256").update(JSON.stringify(entries.map((e) => ({
|
|
@@ -21403,7 +21410,7 @@ function createMemoryService() {
|
|
|
21403
21410
|
}, null, 2)}
|
|
21404
21411
|
`, "utf8");
|
|
21405
21412
|
const result = {
|
|
21406
|
-
path:
|
|
21413
|
+
path: path93.relative(input.cwd, indexPath),
|
|
21407
21414
|
entryCount: entries.length,
|
|
21408
21415
|
generatedAt
|
|
21409
21416
|
};
|
|
@@ -21416,7 +21423,7 @@ function createMemoryService() {
|
|
|
21416
21423
|
const built = await buildEmbeddingIndex(input.cwd, entries, embedder.embed, embedder.model, new Date);
|
|
21417
21424
|
result.embeddings = {
|
|
21418
21425
|
built: true,
|
|
21419
|
-
path:
|
|
21426
|
+
path: path93.relative(input.cwd, embeddingsDir(input.cwd)),
|
|
21420
21427
|
vectorCount: built.meta.entryCount,
|
|
21421
21428
|
model: built.meta.model
|
|
21422
21429
|
};
|
|
@@ -22018,7 +22025,7 @@ var init_ask = __esm(() => {
|
|
|
22018
22025
|
});
|
|
22019
22026
|
|
|
22020
22027
|
// src/wiki/backlinks.ts
|
|
22021
|
-
import
|
|
22028
|
+
import path94 from "path";
|
|
22022
22029
|
function extractCodePaths(content) {
|
|
22023
22030
|
const out = [];
|
|
22024
22031
|
let match;
|
|
@@ -22048,8 +22055,8 @@ function resolveLink(fromRepoPath, raw) {
|
|
|
22048
22055
|
const clean = (raw.split("#")[0] ?? "").trim();
|
|
22049
22056
|
if (!clean)
|
|
22050
22057
|
return "";
|
|
22051
|
-
const fromDir =
|
|
22052
|
-
return
|
|
22058
|
+
const fromDir = path94.posix.dirname(fromRepoPath.split(path94.sep).join("/"));
|
|
22059
|
+
return path94.posix.normalize(path94.posix.join(fromDir, clean));
|
|
22053
22060
|
}
|
|
22054
22061
|
function buildBacklinkIndex(pages) {
|
|
22055
22062
|
const index = new Map;
|
|
@@ -22063,18 +22070,18 @@ function buildBacklinkIndex(pages) {
|
|
|
22063
22070
|
}
|
|
22064
22071
|
};
|
|
22065
22072
|
for (const page of pages) {
|
|
22066
|
-
const from = page.repoPath.split(
|
|
22073
|
+
const from = page.repoPath.split(path94.sep).join("/");
|
|
22067
22074
|
for (const raw of extractLinks(page.content))
|
|
22068
22075
|
add(resolveLink(from, raw), from);
|
|
22069
22076
|
for (const raw of extractCodePaths(page.content))
|
|
22070
|
-
add(
|
|
22077
|
+
add(path94.posix.normalize(raw), from);
|
|
22071
22078
|
}
|
|
22072
22079
|
for (const list of index.values())
|
|
22073
22080
|
list.sort();
|
|
22074
22081
|
return index;
|
|
22075
22082
|
}
|
|
22076
22083
|
function backlinksFor(index, targetRepoPath) {
|
|
22077
|
-
const key =
|
|
22084
|
+
const key = path94.posix.normalize(targetRepoPath.split(path94.sep).join("/"));
|
|
22078
22085
|
return index.get(key) ?? [];
|
|
22079
22086
|
}
|
|
22080
22087
|
var LINK, CODE_SPAN, FILE_LIKE;
|
|
@@ -22087,7 +22094,7 @@ var init_backlinks = __esm(() => {
|
|
|
22087
22094
|
// src/ctx/assembly.ts
|
|
22088
22095
|
import { createHash as createHash16 } from "crypto";
|
|
22089
22096
|
import { mkdir as mkdir19, rename as rename6, writeFile as writeFile13 } from "fs/promises";
|
|
22090
|
-
import
|
|
22097
|
+
import path95 from "path";
|
|
22091
22098
|
function assembleContext(input) {
|
|
22092
22099
|
let items = 0;
|
|
22093
22100
|
let tokens = 0;
|
|
@@ -22131,9 +22138,9 @@ async function recordNoContentContext(input) {
|
|
|
22131
22138
|
return assembly;
|
|
22132
22139
|
}
|
|
22133
22140
|
async function recordContextAssembly(input) {
|
|
22134
|
-
const directory =
|
|
22141
|
+
const directory = path95.join(input.workspaceRoot, ".metaproject", "context-operations", "traces");
|
|
22135
22142
|
await mkdir19(directory, { recursive: true });
|
|
22136
|
-
const target =
|
|
22143
|
+
const target = path95.join(directory, path95.basename(input.assembly.traceRef));
|
|
22137
22144
|
const temporary = `${target}.${process.pid}.tmp`;
|
|
22138
22145
|
await writeFile13(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 })}
|
|
22139
22146
|
`, { mode: 384 });
|
|
@@ -22533,9 +22540,9 @@ var init_evidence = __esm(() => {
|
|
|
22533
22540
|
});
|
|
22534
22541
|
|
|
22535
22542
|
// src/wiki/config.ts
|
|
22536
|
-
import
|
|
22543
|
+
import path96 from "path";
|
|
22537
22544
|
function wikiConfigPath(cwd) {
|
|
22538
|
-
return
|
|
22545
|
+
return path96.join(cwd, ".metaproject", "wiki.config.json");
|
|
22539
22546
|
}
|
|
22540
22547
|
function mergeWikiConfig(parsed) {
|
|
22541
22548
|
const base = DEFAULT_WIKI_CONFIG;
|
|
@@ -22613,12 +22620,12 @@ var init_config4 = __esm(() => {
|
|
|
22613
22620
|
// src/wiki/history.ts
|
|
22614
22621
|
import { createHash as createHash17, randomBytes as randomBytes5 } from "crypto";
|
|
22615
22622
|
import { appendFile as appendFile4, mkdir as mkdir20, open as open3, readFile as readFile49, rm as rm6, stat as stat13, writeFile as writeFile14 } from "fs/promises";
|
|
22616
|
-
import
|
|
22623
|
+
import path97 from "path";
|
|
22617
22624
|
function wikiRootPath2(cwd) {
|
|
22618
|
-
return
|
|
22625
|
+
return path97.join(cwd, ".metaproject", "wiki");
|
|
22619
22626
|
}
|
|
22620
22627
|
function wikiHistoryRoot(cwd) {
|
|
22621
|
-
return
|
|
22628
|
+
return path97.join(cwd, ".metaproject", "data", "gdwiki", "history");
|
|
22622
22629
|
}
|
|
22623
22630
|
function newWikiRunId(now = new Date) {
|
|
22624
22631
|
const stamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
|
|
@@ -22639,29 +22646,29 @@ async function openWikiWriteContext(cwd, command) {
|
|
|
22639
22646
|
return createWikiWriteContext(cwd, command, { keep: (await loadWikiConfig(cwd)).history.keep });
|
|
22640
22647
|
}
|
|
22641
22648
|
function wikiPageKey(cwd, absolutePath) {
|
|
22642
|
-
const relative =
|
|
22643
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
22649
|
+
const relative = path97.relative(wikiRootPath2(cwd), path97.resolve(absolutePath));
|
|
22650
|
+
if (relative === "" || relative.startsWith("..") || path97.isAbsolute(relative)) {
|
|
22644
22651
|
throw new Error(`not a wiki page: ${absolutePath}`);
|
|
22645
22652
|
}
|
|
22646
22653
|
return toPosix(relative);
|
|
22647
22654
|
}
|
|
22648
22655
|
function assertPageKey(page) {
|
|
22649
22656
|
const segments = page.split("/");
|
|
22650
|
-
if (page.length === 0 || page.includes("\\") ||
|
|
22657
|
+
if (page.length === 0 || page.includes("\\") || path97.isAbsolute(page) || segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
22651
22658
|
throw new Error(`not a wiki page path: ${page}`);
|
|
22652
22659
|
}
|
|
22653
22660
|
return page;
|
|
22654
22661
|
}
|
|
22655
22662
|
function pageHistoryDir(cwd, page) {
|
|
22656
|
-
return
|
|
22663
|
+
return path97.join(wikiHistoryRoot(cwd), ...assertPageKey(page).replace(/\.md$/i, "").split("/"));
|
|
22657
22664
|
}
|
|
22658
22665
|
function pagePath(cwd, page) {
|
|
22659
|
-
return
|
|
22666
|
+
return path97.join(wikiRootPath2(cwd), ...assertPageKey(page).split("/"));
|
|
22660
22667
|
}
|
|
22661
22668
|
function versionFilePath(cwd, page, file) {
|
|
22662
22669
|
const dir = pageHistoryDir(cwd, page);
|
|
22663
|
-
const target =
|
|
22664
|
-
if (!VERSION_FILE.test(file) ||
|
|
22670
|
+
const target = path97.join(dir, file);
|
|
22671
|
+
if (!VERSION_FILE.test(file) || path97.dirname(target) !== dir) {
|
|
22665
22672
|
throw new Error(`history index names an invalid version file for ${page}: ${file}`);
|
|
22666
22673
|
}
|
|
22667
22674
|
return target;
|
|
@@ -22734,7 +22741,7 @@ function parsePageHistoryIndex(markdown, page) {
|
|
|
22734
22741
|
return { page, rows };
|
|
22735
22742
|
}
|
|
22736
22743
|
async function readPageHistory(cwd, page) {
|
|
22737
|
-
const indexPath =
|
|
22744
|
+
const indexPath = path97.join(pageHistoryDir(cwd, page), INDEX_FILE);
|
|
22738
22745
|
const raw = await readBufferOrNull(indexPath);
|
|
22739
22746
|
if (raw === null)
|
|
22740
22747
|
return null;
|
|
@@ -22746,8 +22753,8 @@ async function readPageHistory(cwd, page) {
|
|
|
22746
22753
|
}
|
|
22747
22754
|
async function storeVersion(cwd, page, version, at, content) {
|
|
22748
22755
|
const fileName = `${versionLabel(version)}-${at.replace(/[:.]/g, "-")}.md`;
|
|
22749
|
-
const target =
|
|
22750
|
-
await mkdir20(
|
|
22756
|
+
const target = path97.join(pageHistoryDir(cwd, page), fileName);
|
|
22757
|
+
await mkdir20(path97.dirname(target), { recursive: true });
|
|
22751
22758
|
await writeFile14(target, content);
|
|
22752
22759
|
if (sha2562(await readFile49(target)) !== sha2562(content)) {
|
|
22753
22760
|
throw new Error(`history: stored version does not match the page bytes: ${target}`);
|
|
@@ -22785,7 +22792,7 @@ function planPrune(history, keep, maxRows = MAX_INDEX_ROWS) {
|
|
|
22785
22792
|
}
|
|
22786
22793
|
async function ensureHistoryRoot(cwd) {
|
|
22787
22794
|
const root = wikiHistoryRoot(cwd);
|
|
22788
|
-
const ignore =
|
|
22795
|
+
const ignore = path97.join(root, ".gitignore");
|
|
22789
22796
|
if (await readBufferOrNull(ignore))
|
|
22790
22797
|
return;
|
|
22791
22798
|
await mkdir20(root, { recursive: true });
|
|
@@ -22815,14 +22822,14 @@ async function endsWithoutNewline(file) {
|
|
|
22815
22822
|
}
|
|
22816
22823
|
async function appendRun(cwd, entry) {
|
|
22817
22824
|
const root = wikiHistoryRoot(cwd);
|
|
22818
|
-
const file =
|
|
22825
|
+
const file = path97.join(root, RUNS_FILE);
|
|
22819
22826
|
const lead = await endsWithoutNewline(file) ? `
|
|
22820
22827
|
` : "";
|
|
22821
22828
|
await appendFile4(file, `${lead}${JSON.stringify(entry)}
|
|
22822
22829
|
`, "utf8");
|
|
22823
22830
|
if ((await stat13(file)).size <= MAX_RUNS_BYTES)
|
|
22824
22831
|
return;
|
|
22825
|
-
await withFileLock(
|
|
22832
|
+
await withFileLock(path97.join(root, ".runs.lock"), async () => {
|
|
22826
22833
|
if ((await stat13(file)).size <= MAX_RUNS_BYTES)
|
|
22827
22834
|
return;
|
|
22828
22835
|
const lines = (await readFile49(file, "utf8")).split(`
|
|
@@ -22836,7 +22843,7 @@ async function mutatePage(ctx, absolutePath, next) {
|
|
|
22836
22843
|
const page = wikiPageKey(ctx.cwd, absolutePath);
|
|
22837
22844
|
const dir = pageHistoryDir(ctx.cwd, page);
|
|
22838
22845
|
await ensureHistoryRoot(ctx.cwd);
|
|
22839
|
-
return withFileLock(
|
|
22846
|
+
return withFileLock(path97.join(path97.dirname(dir), `.${path97.basename(dir)}.lock`), async () => {
|
|
22840
22847
|
const live = await readBufferOrNull(absolutePath);
|
|
22841
22848
|
if (next === null ? live === null : live !== null && live.equals(next)) {
|
|
22842
22849
|
return { changed: false };
|
|
@@ -22860,7 +22867,7 @@ async function mutatePage(ctx, absolutePath, next) {
|
|
|
22860
22867
|
if (next === null) {
|
|
22861
22868
|
await rm6(absolutePath, { force: true });
|
|
22862
22869
|
} else {
|
|
22863
|
-
await mkdir20(
|
|
22870
|
+
await mkdir20(path97.dirname(absolutePath), { recursive: true });
|
|
22864
22871
|
await writeFile14(absolutePath, next);
|
|
22865
22872
|
}
|
|
22866
22873
|
} catch (error) {
|
|
@@ -22868,7 +22875,7 @@ async function mutatePage(ctx, absolutePath, next) {
|
|
|
22868
22875
|
await rm6(versionFilePath(ctx.cwd, page, row.file), { force: true });
|
|
22869
22876
|
throw error;
|
|
22870
22877
|
}
|
|
22871
|
-
await writeFileAtomic(
|
|
22878
|
+
await writeFileAtomic(path97.join(dir, INDEX_FILE), renderPageHistoryIndex(history));
|
|
22872
22879
|
for (const file of pruned)
|
|
22873
22880
|
await rm6(versionFilePath(ctx.cwd, page, file), { force: true });
|
|
22874
22881
|
return { changed: true, action };
|
|
@@ -22881,7 +22888,7 @@ async function deleteWikiPage(ctx, absolutePath) {
|
|
|
22881
22888
|
return mutatePage(ctx, absolutePath, null);
|
|
22882
22889
|
}
|
|
22883
22890
|
async function readWikiRuns(cwd) {
|
|
22884
|
-
const raw = await readBufferOrNull(
|
|
22891
|
+
const raw = await readBufferOrNull(path97.join(wikiHistoryRoot(cwd), RUNS_FILE));
|
|
22885
22892
|
if (raw === null)
|
|
22886
22893
|
return [];
|
|
22887
22894
|
const entries = [];
|
|
@@ -23573,7 +23580,7 @@ var init_page_invariants = __esm(() => {
|
|
|
23573
23580
|
});
|
|
23574
23581
|
|
|
23575
23582
|
// src/wiki/readme-links.ts
|
|
23576
|
-
import
|
|
23583
|
+
import path98 from "path";
|
|
23577
23584
|
function rewriteReadmeLinks(text, readmeDir, pageLink) {
|
|
23578
23585
|
return text.replace(MARKDOWN_LINK, (whole, bang, label, target) => {
|
|
23579
23586
|
if (target === "" || HAS_SCHEME.test(target)) {
|
|
@@ -23583,7 +23590,7 @@ function rewriteReadmeLinks(text, readmeDir, pageLink) {
|
|
|
23583
23590
|
if (filePart === "") {
|
|
23584
23591
|
return label;
|
|
23585
23592
|
}
|
|
23586
|
-
const resolved =
|
|
23593
|
+
const resolved = path98.posix.normalize(filePart.startsWith("/") ? filePart.replace(/^\/+/, "") : path98.posix.join(readmeDir, filePart));
|
|
23587
23594
|
const codePath = resolved.replace(/\/+$/, "");
|
|
23588
23595
|
if (codePath === "" || codePath === "." || codePath === ".." || codePath.startsWith("../")) {
|
|
23589
23596
|
return label;
|
|
@@ -23598,12 +23605,12 @@ function rewriteReadmeLinks(text, readmeDir, pageLink) {
|
|
|
23598
23605
|
});
|
|
23599
23606
|
}
|
|
23600
23607
|
function moduleOfTarget(filePart, codePath) {
|
|
23601
|
-
const base =
|
|
23608
|
+
const base = path98.posix.basename(codePath);
|
|
23602
23609
|
if (filePart.endsWith("/") || !/\.[A-Za-z0-9]+$/.test(base)) {
|
|
23603
23610
|
return codePath;
|
|
23604
23611
|
}
|
|
23605
23612
|
if (DOC_ENTRY.test(base)) {
|
|
23606
|
-
const dir =
|
|
23613
|
+
const dir = path98.posix.dirname(codePath);
|
|
23607
23614
|
return dir === "." ? null : dir;
|
|
23608
23615
|
}
|
|
23609
23616
|
return null;
|
|
@@ -24009,15 +24016,15 @@ __export(exports_service4, {
|
|
|
24009
24016
|
import { spawn as spawn3 } from "child_process";
|
|
24010
24017
|
import { createHash as createHash19 } from "crypto";
|
|
24011
24018
|
import { mkdir as mkdir21, readFile as readFile50, readdir as readdir17, writeFile as writeFile15 } from "fs/promises";
|
|
24012
|
-
import
|
|
24019
|
+
import path99 from "path";
|
|
24013
24020
|
function wikiRootPath3(cwd) {
|
|
24014
|
-
return
|
|
24021
|
+
return path99.join(cwd, ".metaproject", "wiki");
|
|
24015
24022
|
}
|
|
24016
24023
|
function dataRootPath(cwd) {
|
|
24017
|
-
return
|
|
24024
|
+
return path99.join(cwd, ".metaproject", "data", "gdwiki");
|
|
24018
24025
|
}
|
|
24019
24026
|
function linkCheckReportPath(cwd) {
|
|
24020
|
-
return
|
|
24027
|
+
return path99.join(dataRootPath(cwd), "link-check", "latest.md");
|
|
24021
24028
|
}
|
|
24022
24029
|
async function wikiStatus(cwd) {
|
|
24023
24030
|
const root = wikiRootPath3(cwd);
|
|
@@ -24025,7 +24032,7 @@ async function wikiStatus(cwd) {
|
|
|
24025
24032
|
const pages = enabled ? await collectPages(cwd) : [];
|
|
24026
24033
|
return {
|
|
24027
24034
|
enabled,
|
|
24028
|
-
wikiRoot:
|
|
24035
|
+
wikiRoot: path99.relative(cwd, root),
|
|
24029
24036
|
totalPages: pages.length,
|
|
24030
24037
|
countsByType: WIKI_PAGE_TYPES.map(({ type }) => ({
|
|
24031
24038
|
type,
|
|
@@ -24056,9 +24063,9 @@ async function wikiCreatePage(input) {
|
|
|
24056
24063
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(input.slug)) {
|
|
24057
24064
|
throw new Error(`Invalid slug: ${input.slug}. Use lowercase letters, digits, and hyphens.`);
|
|
24058
24065
|
}
|
|
24059
|
-
const dir =
|
|
24060
|
-
const filePath =
|
|
24061
|
-
const relativePath =
|
|
24066
|
+
const dir = path99.join(wikiRootPath3(input.cwd), typeConfig.folder);
|
|
24067
|
+
const filePath = path99.join(dir, `${input.slug}.md`);
|
|
24068
|
+
const relativePath = path99.relative(input.cwd, filePath);
|
|
24062
24069
|
if (await pathExists(filePath) && !input.force) {
|
|
24063
24070
|
throw new Error(`Page already exists: ${relativePath}. Use --force to overwrite.`);
|
|
24064
24071
|
}
|
|
@@ -24070,7 +24077,7 @@ async function wikiCreatePage(input) {
|
|
|
24070
24077
|
}
|
|
24071
24078
|
async function wikiGenerateIndex(cwd, history) {
|
|
24072
24079
|
const root = wikiRootPath3(cwd);
|
|
24073
|
-
const indexPath =
|
|
24080
|
+
const indexPath = path99.join(root, "index.md");
|
|
24074
24081
|
const pages = await collectPages(cwd);
|
|
24075
24082
|
const generatedAt = new Date().toISOString();
|
|
24076
24083
|
const managedBlock = `${WIKI_INDEX_BEGIN}
|
|
@@ -24087,7 +24094,7 @@ ${managedBlock}
|
|
|
24087
24094
|
`;
|
|
24088
24095
|
await writeWikiPage(history ?? await openWikiWriteContext(cwd, "wiki index"), indexPath, next);
|
|
24089
24096
|
return {
|
|
24090
|
-
path:
|
|
24097
|
+
path: path99.relative(cwd, indexPath),
|
|
24091
24098
|
pageCount: pages.length,
|
|
24092
24099
|
generatedAt
|
|
24093
24100
|
};
|
|
@@ -24095,13 +24102,13 @@ ${managedBlock}
|
|
|
24095
24102
|
async function wikiCheckLinks(cwd) {
|
|
24096
24103
|
const root = wikiRootPath3(cwd);
|
|
24097
24104
|
const allFiles = await pathExists(root) ? await walkMarkdown(root) : [];
|
|
24098
|
-
const files = allFiles.filter((absolutePath) => !
|
|
24105
|
+
const files = allFiles.filter((absolutePath) => !path99.relative(root, absolutePath).startsWith(`templates${path99.sep}`));
|
|
24099
24106
|
const broken = [];
|
|
24100
24107
|
let checkedLinks = 0;
|
|
24101
24108
|
let skippedExternal = 0;
|
|
24102
24109
|
for (const absolutePath of files) {
|
|
24103
24110
|
const content = await readFile50(absolutePath, "utf8");
|
|
24104
|
-
const pageRelative =
|
|
24111
|
+
const pageRelative = path99.relative(cwd, absolutePath);
|
|
24105
24112
|
for (const target of extractLinkTargets(content)) {
|
|
24106
24113
|
if (EXTERNAL_LINK.test(target)) {
|
|
24107
24114
|
skippedExternal += 1;
|
|
@@ -24112,7 +24119,7 @@ async function wikiCheckLinks(cwd) {
|
|
|
24112
24119
|
continue;
|
|
24113
24120
|
}
|
|
24114
24121
|
checkedLinks += 1;
|
|
24115
|
-
const resolved =
|
|
24122
|
+
const resolved = path99.resolve(path99.dirname(absolutePath), filePart);
|
|
24116
24123
|
if (!await pathExists(resolved)) {
|
|
24117
24124
|
broken.push({
|
|
24118
24125
|
page: pageRelative,
|
|
@@ -24124,7 +24131,7 @@ async function wikiCheckLinks(cwd) {
|
|
|
24124
24131
|
}
|
|
24125
24132
|
const generatedAt = new Date().toISOString();
|
|
24126
24133
|
const reportPath = linkCheckReportPath(cwd);
|
|
24127
|
-
await mkdir21(
|
|
24134
|
+
await mkdir21(path99.dirname(reportPath), { recursive: true });
|
|
24128
24135
|
await writeFile15(reportPath, renderLinkCheckReport({
|
|
24129
24136
|
generatedAt,
|
|
24130
24137
|
checkedPages: files.length,
|
|
@@ -24133,7 +24140,7 @@ async function wikiCheckLinks(cwd) {
|
|
|
24133
24140
|
broken
|
|
24134
24141
|
}), "utf8");
|
|
24135
24142
|
return {
|
|
24136
|
-
reportPath:
|
|
24143
|
+
reportPath: path99.relative(cwd, reportPath),
|
|
24137
24144
|
checkedPages: files.length,
|
|
24138
24145
|
checkedLinks,
|
|
24139
24146
|
skippedExternal,
|
|
@@ -24184,7 +24191,7 @@ async function wikiValidate(cwd) {
|
|
|
24184
24191
|
}
|
|
24185
24192
|
if (await isIndexStale(cwd, pages)) {
|
|
24186
24193
|
issues.push({
|
|
24187
|
-
page:
|
|
24194
|
+
page: path99.join(path99.relative(cwd, wikiRootPath3(cwd)), "index.md"),
|
|
24188
24195
|
kind: "index",
|
|
24189
24196
|
message: "index out of date, run `keryx wiki index`"
|
|
24190
24197
|
});
|
|
@@ -24230,7 +24237,7 @@ function collectCommandLabel(input) {
|
|
|
24230
24237
|
return ["wiki collect", ...flags].join(" ");
|
|
24231
24238
|
}
|
|
24232
24239
|
async function wikiPruneOrphans(cwd, history) {
|
|
24233
|
-
const componentsDir =
|
|
24240
|
+
const componentsDir = path99.join(wikiRootPath3(cwd), "components");
|
|
24234
24241
|
if (!await pathExists(componentsDir)) {
|
|
24235
24242
|
return { pruned: [], orphanedAccepted: [] };
|
|
24236
24243
|
}
|
|
@@ -24250,15 +24257,15 @@ async function wikiPruneOrphans(cwd, history) {
|
|
|
24250
24257
|
if (validSlugs.has(slug)) {
|
|
24251
24258
|
continue;
|
|
24252
24259
|
}
|
|
24253
|
-
const filePath =
|
|
24260
|
+
const filePath = path99.join(componentsDir, entry);
|
|
24254
24261
|
const current = await readFile50(filePath, "utf8");
|
|
24255
24262
|
const isUnmodifiedDraft = /\nStatus:\s*draft\s*\n/.test(current) && current.includes("Generated by `keryx wiki collect`");
|
|
24256
24263
|
if (isUnmodifiedDraft) {
|
|
24257
24264
|
run ??= history ?? await openWikiWriteContext(cwd, "wiki prune");
|
|
24258
24265
|
await deleteWikiPage(run, filePath);
|
|
24259
|
-
pruned.push(
|
|
24266
|
+
pruned.push(path99.relative(cwd, filePath));
|
|
24260
24267
|
} else {
|
|
24261
|
-
orphanedAccepted.push(
|
|
24268
|
+
orphanedAccepted.push(path99.relative(cwd, filePath));
|
|
24262
24269
|
}
|
|
24263
24270
|
}
|
|
24264
24271
|
return { pruned, orphanedAccepted };
|
|
@@ -24268,7 +24275,7 @@ async function validModuleNames(cwd, dataDir) {
|
|
|
24268
24275
|
return counts === undefined ? undefined : new Set(counts.keys());
|
|
24269
24276
|
}
|
|
24270
24277
|
async function moduleFileCounts(cwd, dataDir) {
|
|
24271
|
-
const nodesPath =
|
|
24278
|
+
const nodesPath = path99.join(gdgraphStorageDir(cwd, dataDir), "nodes.jsonl");
|
|
24272
24279
|
if (!await pathExists(nodesPath)) {
|
|
24273
24280
|
return;
|
|
24274
24281
|
}
|
|
@@ -24339,9 +24346,9 @@ async function wikiEvidence(input) {
|
|
|
24339
24346
|
});
|
|
24340
24347
|
}
|
|
24341
24348
|
function resolveEvidenceScope(cwd, override) {
|
|
24342
|
-
const absolute =
|
|
24349
|
+
const absolute = path99.resolve(cwd);
|
|
24343
24350
|
return {
|
|
24344
|
-
projectId: override?.projectId ?? (
|
|
24351
|
+
projectId: override?.projectId ?? (path99.basename(absolute) || "project"),
|
|
24345
24352
|
checkoutId: override?.checkoutId ?? `checkout:${createHash19("sha256").update(absolute).digest("hex").slice(0, 16)}`,
|
|
24346
24353
|
...override?.workspaceId ? { workspaceId: override.workspaceId } : {},
|
|
24347
24354
|
...override?.taskId ? { taskId: override.taskId } : {}
|
|
@@ -24356,7 +24363,7 @@ function isMachineAuthoredPage(pageType, relativePath, content) {
|
|
|
24356
24363
|
if (source === null) {
|
|
24357
24364
|
return false;
|
|
24358
24365
|
}
|
|
24359
|
-
const normalised = relativePath.split(
|
|
24366
|
+
const normalised = relativePath.split(path99.sep).join("/").replace(/^wiki\//, "");
|
|
24360
24367
|
return WIKI_PAGE_PRODUCERS.some((producer) => producer.source === source && producer.pageType === pageType && producer.path.test(normalised) && /^-[ \t]+Link:.*\bsha256\b/m.test(content));
|
|
24361
24368
|
}
|
|
24362
24369
|
function createGdWikiService() {
|
|
@@ -24372,8 +24379,8 @@ function createGdWikiService() {
|
|
|
24372
24379
|
};
|
|
24373
24380
|
}
|
|
24374
24381
|
async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules, dataDir) {
|
|
24375
|
-
const nodesPath =
|
|
24376
|
-
const edgesPath =
|
|
24382
|
+
const nodesPath = path99.join(gdgraphStorageDir(cwd, dataDir), "nodes.jsonl");
|
|
24383
|
+
const edgesPath = path99.join(gdgraphStorageDir(cwd, dataDir), "edges.jsonl");
|
|
24377
24384
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
24378
24385
|
return [];
|
|
24379
24386
|
}
|
|
@@ -24460,7 +24467,7 @@ async function collectGraphWikiCandidates(cwd, generatedAt, limit, onlyModules,
|
|
|
24460
24467
|
};
|
|
24461
24468
|
const moduleCandidates = [];
|
|
24462
24469
|
const pageModules = new Set(topModules.map((entry) => entry.name));
|
|
24463
|
-
const componentsDir =
|
|
24470
|
+
const componentsDir = path99.join(wikiRootPath3(cwd), "components");
|
|
24464
24471
|
const existingSlugs = new Set((await pathExists(componentsDir) ? await readdir17(componentsDir) : []).filter((entry) => entry.endsWith(".md")).map((entry) => entry.slice(0, -3)));
|
|
24465
24472
|
const pageLink = (name) => {
|
|
24466
24473
|
const slug = slugifyPath(name);
|
|
@@ -24568,7 +24575,7 @@ function topCounts(bucket, limit) {
|
|
|
24568
24575
|
return [...bucket.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit);
|
|
24569
24576
|
}
|
|
24570
24577
|
async function readModuleReadme(cwd, moduleName, pageLink) {
|
|
24571
|
-
const readmePath =
|
|
24578
|
+
const readmePath = path99.join(cwd, ...moduleName.split("/"), "README.md");
|
|
24572
24579
|
if (!await pathExists(readmePath)) {
|
|
24573
24580
|
return null;
|
|
24574
24581
|
}
|
|
@@ -24589,7 +24596,7 @@ async function extractModuleExports(cwd, files) {
|
|
|
24589
24596
|
const declRe = /export\s+(?:async\s+)?(?:default\s+)?(?:const|let|var|function|class|type|interface|enum)\s+([A-Za-z_$][\w$]*)/g;
|
|
24590
24597
|
const braceRe = /export\s*\{([^}]+)\}/g;
|
|
24591
24598
|
for (const file of files.slice(0, 4)) {
|
|
24592
|
-
const filePath =
|
|
24599
|
+
const filePath = path99.join(cwd, ...file.split("/"));
|
|
24593
24600
|
if (!await pathExists(filePath)) {
|
|
24594
24601
|
continue;
|
|
24595
24602
|
}
|
|
@@ -24620,7 +24627,7 @@ async function extractModuleExports(cwd, files) {
|
|
|
24620
24627
|
}
|
|
24621
24628
|
async function extractModuleApi(cwd, files, dataDir) {
|
|
24622
24629
|
const names = await extractModuleExports(cwd, files);
|
|
24623
|
-
const symbolsPath =
|
|
24630
|
+
const symbolsPath = path99.join(gdgraphStorageDir(cwd, dataDir), "symbols.jsonl");
|
|
24624
24631
|
if (names.length === 0 || !await pathExists(symbolsPath)) {
|
|
24625
24632
|
return names.map((name) => "`" + name + "`");
|
|
24626
24633
|
}
|
|
@@ -24646,7 +24653,7 @@ async function extractModuleApi(cwd, files, dataDir) {
|
|
|
24646
24653
|
});
|
|
24647
24654
|
}
|
|
24648
24655
|
async function collectHealthWikiCandidates(cwd, generatedAt) {
|
|
24649
|
-
const healthPath =
|
|
24656
|
+
const healthPath = path99.join(cwd, ".metaproject", "data", "health", "artifacts", "latest.json");
|
|
24650
24657
|
if (!await pathExists(healthPath)) {
|
|
24651
24658
|
return [];
|
|
24652
24659
|
}
|
|
@@ -24688,7 +24695,7 @@ async function collectHealthWikiCandidates(cwd, generatedAt) {
|
|
|
24688
24695
|
}];
|
|
24689
24696
|
}
|
|
24690
24697
|
async function collectTestingWikiCandidates(cwd, generatedAt) {
|
|
24691
|
-
const contextPath =
|
|
24698
|
+
const contextPath = path99.join(cwd, ".metaproject", "data", "testing", "context.md");
|
|
24692
24699
|
if (!await pathExists(contextPath)) {
|
|
24693
24700
|
return [];
|
|
24694
24701
|
}
|
|
@@ -24719,8 +24726,8 @@ async function writeCollectedPage(cwd, candidate, force, history) {
|
|
|
24719
24726
|
if (!folder) {
|
|
24720
24727
|
throw new Error(`Unsupported collected wiki type: ${candidate.type}`);
|
|
24721
24728
|
}
|
|
24722
|
-
const filePath =
|
|
24723
|
-
const relativePath =
|
|
24729
|
+
const filePath = path99.join(wikiRootPath3(cwd), folder, `${candidate.slug}.md`);
|
|
24730
|
+
const relativePath = path99.relative(cwd, filePath);
|
|
24724
24731
|
const exists = await pathExists(filePath);
|
|
24725
24732
|
if (exists && !force) {
|
|
24726
24733
|
return { path: relativePath, type: candidate.type, source: candidate.source, action: "skipped" };
|
|
@@ -24771,7 +24778,7 @@ async function writeCollectedPage(cwd, candidate, force, history) {
|
|
|
24771
24778
|
async function wikiPagesForFile(cwd, targetRepoPath) {
|
|
24772
24779
|
const pages = await collectPages(cwd);
|
|
24773
24780
|
const refs = await Promise.all(pages.map(async (page) => ({
|
|
24774
|
-
repoPath:
|
|
24781
|
+
repoPath: path99.relative(cwd, page.absolutePath).split(path99.sep).join("/"),
|
|
24775
24782
|
content: await readFile50(page.absolutePath, "utf8")
|
|
24776
24783
|
})));
|
|
24777
24784
|
return backlinksFor(buildBacklinkIndex(refs), targetRepoPath);
|
|
@@ -24810,7 +24817,7 @@ function renderIndexBody(pages, generatedAt) {
|
|
|
24810
24817
|
`).trimEnd();
|
|
24811
24818
|
}
|
|
24812
24819
|
async function isIndexStale(cwd, pages) {
|
|
24813
|
-
const indexPath =
|
|
24820
|
+
const indexPath = path99.join(wikiRootPath3(cwd), "index.md");
|
|
24814
24821
|
if (!await pathExists(indexPath)) {
|
|
24815
24822
|
return true;
|
|
24816
24823
|
}
|
|
@@ -24828,7 +24835,7 @@ async function walkMarkdown(root) {
|
|
|
24828
24835
|
const results = [];
|
|
24829
24836
|
const entries = await readdir17(root, { withFileTypes: true });
|
|
24830
24837
|
for (const entry of entries) {
|
|
24831
|
-
const absolutePath =
|
|
24838
|
+
const absolutePath = path99.join(root, entry.name);
|
|
24832
24839
|
if (entry.isDirectory()) {
|
|
24833
24840
|
results.push(...await walkMarkdown(absolutePath));
|
|
24834
24841
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -24885,7 +24892,7 @@ ${JSON.stringify(state, null, 2)}
|
|
|
24885
24892
|
`;
|
|
24886
24893
|
}
|
|
24887
24894
|
async function readIndexGeneratedAt(cwd) {
|
|
24888
|
-
const indexPath =
|
|
24895
|
+
const indexPath = path99.join(wikiRootPath3(cwd), "index.md");
|
|
24889
24896
|
if (!await pathExists(indexPath)) {
|
|
24890
24897
|
return null;
|
|
24891
24898
|
}
|
|
@@ -25125,7 +25132,7 @@ async function validateStructure(cwd, pages, issues) {
|
|
|
25125
25132
|
if (pattern.includes("*")) {
|
|
25126
25133
|
continue;
|
|
25127
25134
|
}
|
|
25128
|
-
if (!await pathExists(
|
|
25135
|
+
if (!await pathExists(path99.join(cwd, pattern))) {
|
|
25129
25136
|
issues.push({
|
|
25130
25137
|
page: page.relativePath,
|
|
25131
25138
|
kind: "describes",
|
|
@@ -25195,7 +25202,7 @@ async function validatePageInvariants(cwd, pages, issues) {
|
|
|
25195
25202
|
}
|
|
25196
25203
|
}
|
|
25197
25204
|
function toPosixPath(value) {
|
|
25198
|
-
return value.split(
|
|
25205
|
+
return value.split(path99.sep).join("/");
|
|
25199
25206
|
}
|
|
25200
25207
|
function compareSemver(a, b) {
|
|
25201
25208
|
const pa = a.split(".").map(Number);
|
|
@@ -25240,7 +25247,7 @@ var init_service4 = __esm(() => {
|
|
|
25240
25247
|
// src/gdgraph/wiki-layer.ts
|
|
25241
25248
|
import { createHash as createHash20 } from "crypto";
|
|
25242
25249
|
import { mkdir as mkdir22, stat as stat14, writeFile as writeFile16 } from "fs/promises";
|
|
25243
|
-
import
|
|
25250
|
+
import path100 from "path";
|
|
25244
25251
|
function storageDir(projectRoot, dataDir) {
|
|
25245
25252
|
return gdgraphStorageDir(projectRoot, dataDir);
|
|
25246
25253
|
}
|
|
@@ -25308,11 +25315,11 @@ async function enrichBuildWithWikiLayer(input) {
|
|
|
25308
25315
|
const mtimes = new Map;
|
|
25309
25316
|
for (const record of input.fileRecords) {
|
|
25310
25317
|
try {
|
|
25311
|
-
const info = await stat14(
|
|
25318
|
+
const info = await stat14(path100.join(input.projectRoot, record.path));
|
|
25312
25319
|
mtimes.set(record.path, info.mtimeMs);
|
|
25313
25320
|
} catch {}
|
|
25314
25321
|
}
|
|
25315
|
-
await writeFile16(
|
|
25322
|
+
await writeFile16(path100.join(dir, BUILD_MANIFEST_FILE), `${JSON.stringify({ version: 1, files: computeFingerprints(input.fileRecords, mtimes) }, null, 2)}
|
|
25316
25323
|
`, "utf8");
|
|
25317
25324
|
const validModules = input.validModules !== undefined ? input.validModules : await (input.loadValidModules ?? ((root) => defaultLoadValidModules(root, input.dataDir)))(input.projectRoot);
|
|
25318
25325
|
const wikiPages = await collectPages(input.projectRoot);
|
|
@@ -25337,8 +25344,8 @@ async function enrichBuildWithWikiLayer(input) {
|
|
|
25337
25344
|
version: page.version
|
|
25338
25345
|
}))
|
|
25339
25346
|
});
|
|
25340
|
-
await writeJsonl(
|
|
25341
|
-
await writeJsonl(
|
|
25347
|
+
await writeJsonl(path100.join(dir, WIKI_PAGES_FILE), layer.pages);
|
|
25348
|
+
await writeJsonl(path100.join(dir, DESCRIBES_FILE), layer.describes);
|
|
25342
25349
|
return layer;
|
|
25343
25350
|
}
|
|
25344
25351
|
async function defaultLoadValidModules(projectRoot, dataDir) {
|
|
@@ -25434,7 +25441,7 @@ function personalizedPageRank(nodes, edges, options) {
|
|
|
25434
25441
|
|
|
25435
25442
|
// src/gdgraph/repomap.ts
|
|
25436
25443
|
import { mkdir as mkdir24, writeFile as writeFile18 } from "fs/promises";
|
|
25437
|
-
import
|
|
25444
|
+
import path103 from "path";
|
|
25438
25445
|
function estimateTokens2(text) {
|
|
25439
25446
|
return Math.ceil(text.length / 4);
|
|
25440
25447
|
}
|
|
@@ -25465,7 +25472,7 @@ function overflowContent(required, missing, budget) {
|
|
|
25465
25472
|
`);
|
|
25466
25473
|
}
|
|
25467
25474
|
function repomapArtifactPath(cwd) {
|
|
25468
|
-
return
|
|
25475
|
+
return path103.join(cwd, ".metaproject", "data", "gdgraph", "artifacts", "repomap.md");
|
|
25469
25476
|
}
|
|
25470
25477
|
function buildRankEdges(graph, config) {
|
|
25471
25478
|
const edges = [];
|
|
@@ -25602,7 +25609,7 @@ function computeRepomap(graph, config, options = {}) {
|
|
|
25602
25609
|
async function writeRepomap(cwd, graph, config, options = {}) {
|
|
25603
25610
|
const result = computeRepomap(graph, config, options);
|
|
25604
25611
|
const artifactPath = repomapArtifactPath(cwd);
|
|
25605
|
-
await mkdir24(
|
|
25612
|
+
await mkdir24(path103.dirname(artifactPath), { recursive: true });
|
|
25606
25613
|
await writeFile18(artifactPath, result.content, "utf8");
|
|
25607
25614
|
return { ...result, path: artifactPath };
|
|
25608
25615
|
}
|
|
@@ -25622,7 +25629,7 @@ var init_repomap = __esm(() => {
|
|
|
25622
25629
|
// src/gdskills/contracts.ts
|
|
25623
25630
|
import { readFile as readFile62 } from "fs/promises";
|
|
25624
25631
|
import { existsSync as existsSync20 } from "fs";
|
|
25625
|
-
import
|
|
25632
|
+
import path127 from "path";
|
|
25626
25633
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
25627
25634
|
async function loadSchema(name) {
|
|
25628
25635
|
const contract = CONTRACTS.find((entry) => entry.name === name);
|
|
@@ -25633,10 +25640,10 @@ async function loadSchema(name) {
|
|
|
25633
25640
|
return JSON.parse(raw);
|
|
25634
25641
|
}
|
|
25635
25642
|
function contractPath(contract) {
|
|
25636
|
-
const here =
|
|
25643
|
+
const here = path127.dirname(fileURLToPath2(import.meta.url));
|
|
25637
25644
|
if (contract.sourcePath) {
|
|
25638
|
-
for (const root of [
|
|
25639
|
-
const candidate =
|
|
25645
|
+
for (const root of [path127.join(here, "..", ".."), path127.join(here, ".."), here]) {
|
|
25646
|
+
const candidate = path127.resolve(root, contract.sourcePath);
|
|
25640
25647
|
if (existsSync20(candidate)) {
|
|
25641
25648
|
return candidate;
|
|
25642
25649
|
}
|
|
@@ -25646,7 +25653,7 @@ function contractPath(contract) {
|
|
|
25646
25653
|
if (existsSync20(directPath)) {
|
|
25647
25654
|
return directPath;
|
|
25648
25655
|
}
|
|
25649
|
-
const packagedSourcePath =
|
|
25656
|
+
const packagedSourcePath = path127.join(here, "..", "src", "gdskills", "contracts", contract.fileName);
|
|
25650
25657
|
if (existsSync20(packagedSourcePath)) {
|
|
25651
25658
|
return packagedSourcePath;
|
|
25652
25659
|
}
|
|
@@ -25829,7 +25836,7 @@ var init_config_dir = __esm(() => {
|
|
|
25829
25836
|
// src/session/preservation.ts
|
|
25830
25837
|
import { createHash as createHash23, randomUUID as randomUUID3 } from "crypto";
|
|
25831
25838
|
import { closeSync, fsyncSync, mkdirSync as mkdirSync3, openSync, readdirSync as readdirSync3, readFileSync as readFileSync13, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
25832
|
-
import
|
|
25839
|
+
import path130 from "path";
|
|
25833
25840
|
function syncDir(dir) {
|
|
25834
25841
|
if (process.platform === "win32")
|
|
25835
25842
|
return;
|
|
@@ -25841,7 +25848,7 @@ function syncDir(dir) {
|
|
|
25841
25848
|
}
|
|
25842
25849
|
}
|
|
25843
25850
|
function writePreservationFile(file, body) {
|
|
25844
|
-
const dir =
|
|
25851
|
+
const dir = path130.dirname(file);
|
|
25845
25852
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
25846
25853
|
const tmp = `${file}.${randomUUID3()}.tmp`;
|
|
25847
25854
|
const fd = openSync(tmp, "wx", 384);
|
|
@@ -25866,11 +25873,11 @@ function writePreservationFile(file, body) {
|
|
|
25866
25873
|
}
|
|
25867
25874
|
}
|
|
25868
25875
|
function readPreservation(dir, snapshotId) {
|
|
25869
|
-
const base =
|
|
25876
|
+
const base = path130.join(dir, "preservation");
|
|
25870
25877
|
let id = snapshotId;
|
|
25871
25878
|
if (id === undefined) {
|
|
25872
25879
|
try {
|
|
25873
|
-
id = readFileSync13(
|
|
25880
|
+
id = readFileSync13(path130.join(base, "CURRENT"), "utf8");
|
|
25874
25881
|
} catch (e) {
|
|
25875
25882
|
if (e.code === "ENOENT")
|
|
25876
25883
|
return;
|
|
@@ -25879,7 +25886,7 @@ function readPreservation(dir, snapshotId) {
|
|
|
25879
25886
|
}
|
|
25880
25887
|
if (!/^[a-f0-9]{64}$/.test(id))
|
|
25881
25888
|
throw new Error("invalid preservation identity");
|
|
25882
|
-
const read = readTranscriptFile(
|
|
25889
|
+
const read = readTranscriptFile(path130.join(base, "generations", `${id}.json`));
|
|
25883
25890
|
if (!read.ok)
|
|
25884
25891
|
throw new Error(`snapshot unreadable: ${read.reason}`);
|
|
25885
25892
|
if (hash(read.text) !== id)
|
|
@@ -25897,9 +25904,9 @@ function readPreservation(dir, snapshotId) {
|
|
|
25897
25904
|
return parsed;
|
|
25898
25905
|
}
|
|
25899
25906
|
function readPendingHistory(dir) {
|
|
25900
|
-
let read = readConfigFile(
|
|
25907
|
+
let read = readConfigFile(path130.join(dir, "preservation", "HISTORY"));
|
|
25901
25908
|
if (!read.ok && read.reason === "absent") {
|
|
25902
|
-
read = readConfigFile(
|
|
25909
|
+
read = readConfigFile(path130.join(dir, "preservation", "MIRRORING"));
|
|
25903
25910
|
}
|
|
25904
25911
|
if (!read.ok) {
|
|
25905
25912
|
if (read.reason === "absent")
|
|
@@ -25923,12 +25930,12 @@ var init_preservation = __esm(() => {
|
|
|
25923
25930
|
|
|
25924
25931
|
// src/session/slate.ts
|
|
25925
25932
|
import { mkdir as mkdir33, readFile as readFile66, readdir as readdir23, stat as stat17 } from "fs/promises";
|
|
25926
|
-
import
|
|
25933
|
+
import path135 from "path";
|
|
25927
25934
|
function isSlateSeedKind(value) {
|
|
25928
25935
|
return typeof value === "string" && SLATE_SEED_KINDS.includes(value);
|
|
25929
25936
|
}
|
|
25930
25937
|
function slatePath(dir) {
|
|
25931
|
-
return
|
|
25938
|
+
return path135.join(dir, "slate.json");
|
|
25932
25939
|
}
|
|
25933
25940
|
function slateLockPath(dir) {
|
|
25934
25941
|
return `${slatePath(dir)}.lock`;
|
|
@@ -26394,7 +26401,7 @@ __export(exports_service10, {
|
|
|
26394
26401
|
init_fs();
|
|
26395
26402
|
init_validator();
|
|
26396
26403
|
import { mkdir as mkdir13, readFile as readFile39 } from "fs/promises";
|
|
26397
|
-
import
|
|
26404
|
+
import path78 from "path";
|
|
26398
26405
|
|
|
26399
26406
|
// src/flow/machine.ts
|
|
26400
26407
|
var TRANSITIONS = {
|
|
@@ -26593,7 +26600,7 @@ function evaluateTaskGate(tasks) {
|
|
|
26593
26600
|
}
|
|
26594
26601
|
|
|
26595
26602
|
// src/review/coverage-complete.ts
|
|
26596
|
-
import
|
|
26603
|
+
import path54 from "path";
|
|
26597
26604
|
|
|
26598
26605
|
// src/lib/shell-quote.ts
|
|
26599
26606
|
function shellQuote(value) {
|
|
@@ -26611,7 +26618,7 @@ init_fs();
|
|
|
26611
26618
|
import { createHash as createHash8 } from "crypto";
|
|
26612
26619
|
import { existsSync as existsSync9 } from "fs";
|
|
26613
26620
|
init_fs();
|
|
26614
|
-
import
|
|
26621
|
+
import path53 from "path";
|
|
26615
26622
|
|
|
26616
26623
|
// src/review/blast-radius.ts
|
|
26617
26624
|
init_affected();
|
|
@@ -27552,7 +27559,7 @@ var encoder = new TextEncoder;
|
|
|
27552
27559
|
// src/review/coverage.ts
|
|
27553
27560
|
import { existsSync as existsSync8 } from "fs";
|
|
27554
27561
|
import { readFile as readFile23, realpath as realpath13 } from "fs/promises";
|
|
27555
|
-
import
|
|
27562
|
+
import path52 from "path";
|
|
27556
27563
|
|
|
27557
27564
|
// src/review/git-base.ts
|
|
27558
27565
|
var SHA = /^[0-9a-f]{7,64}$/i;
|
|
@@ -29321,6 +29328,85 @@ async function pendingMutants(packageDir) {
|
|
|
29321
29328
|
}
|
|
29322
29329
|
return unjudgedMutants(packageDir, specs);
|
|
29323
29330
|
}
|
|
29331
|
+
// src/review/role-switches.ts
|
|
29332
|
+
init_fs();
|
|
29333
|
+
init_json();
|
|
29334
|
+
import path51 from "path";
|
|
29335
|
+
var OPTIONAL_BY_DEFAULT = ["review-clean-code", "review-style"];
|
|
29336
|
+
var ROUND_ROLES_FILE = "round-roles.json";
|
|
29337
|
+
var ROLE_LINE_PREFIX = "\u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0435 \u0440\u043E\u043B\u0438 \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B\u0438\u0441\u044C:";
|
|
29338
|
+
var names = (value, field, warnings) => {
|
|
29339
|
+
if (value === undefined)
|
|
29340
|
+
return [];
|
|
29341
|
+
if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
|
|
29342
|
+
warnings.push(`routing.config.json reviewRoles.${field} must be a list of role names; it is ignored`);
|
|
29343
|
+
return [];
|
|
29344
|
+
}
|
|
29345
|
+
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
29346
|
+
};
|
|
29347
|
+
async function readRoleConfig(projectRoot) {
|
|
29348
|
+
const none = { disabled: [], enabled: [], warnings: [] };
|
|
29349
|
+
const file = path51.join(projectRoot, "routing.config.json");
|
|
29350
|
+
if (!await pathExists(file))
|
|
29351
|
+
return none;
|
|
29352
|
+
const read = await readJsonObjectFile(file);
|
|
29353
|
+
if (read.state !== "object")
|
|
29354
|
+
return { ...none, warnings: [`${file} could not be read as a JSON object (${read.state}); reviewRoles is ignored`] };
|
|
29355
|
+
const own = read.value.reviewRoles;
|
|
29356
|
+
if (own === undefined)
|
|
29357
|
+
return none;
|
|
29358
|
+
if (own === null || typeof own !== "object" || Array.isArray(own))
|
|
29359
|
+
return { ...none, warnings: ["routing.config.json reviewRoles must be an object with disabled and enabled lists; it is ignored"] };
|
|
29360
|
+
const fields = own;
|
|
29361
|
+
const warnings = [];
|
|
29362
|
+
return { disabled: names(fields.disabled, "disabled", warnings), enabled: names(fields.enabled, "enabled", warnings), warnings };
|
|
29363
|
+
}
|
|
29364
|
+
function decideRoleSwitches(input) {
|
|
29365
|
+
const { roster, config, optIn, protectedRoles } = input;
|
|
29366
|
+
const known = new Set(roster);
|
|
29367
|
+
const warnings = [...config.warnings];
|
|
29368
|
+
const off = new Map;
|
|
29369
|
+
for (const name of OPTIONAL_BY_DEFAULT)
|
|
29370
|
+
off.set(name, `optional role, off by default (--style turns it on)`);
|
|
29371
|
+
if (input.configIgnored !== undefined && config.disabled.length > 0)
|
|
29372
|
+
warnings.push(`reviewRoles.disabled not applied: ${input.configIgnored}`);
|
|
29373
|
+
else {
|
|
29374
|
+
for (const name of config.disabled) {
|
|
29375
|
+
if (protectedRoles.has(name))
|
|
29376
|
+
warnings.push(`reviewRoles.disabled: ${name} is a core role that always runs; it stays on`);
|
|
29377
|
+
else if (!known.has(name))
|
|
29378
|
+
warnings.push(`reviewRoles.disabled: unknown role "${name}"; ignored`);
|
|
29379
|
+
else
|
|
29380
|
+
off.set(name, `disabled by routing.config.json reviewRoles.disabled (--with-role ${name} turns it on)`);
|
|
29381
|
+
}
|
|
29382
|
+
}
|
|
29383
|
+
for (const name of config.enabled) {
|
|
29384
|
+
if (!known.has(name) && !OPTIONAL_BY_DEFAULT.includes(name))
|
|
29385
|
+
warnings.push(`reviewRoles.enabled: unknown role "${name}"; ignored`);
|
|
29386
|
+
else
|
|
29387
|
+
off.delete(name);
|
|
29388
|
+
}
|
|
29389
|
+
for (const name of optIn) {
|
|
29390
|
+
if (!known.has(name) && !OPTIONAL_BY_DEFAULT.includes(name))
|
|
29391
|
+
warnings.push(`--with-role: unknown role "${name}"; ignored`);
|
|
29392
|
+
else
|
|
29393
|
+
off.delete(name);
|
|
29394
|
+
}
|
|
29395
|
+
for (const name of [...off.keys()])
|
|
29396
|
+
if (!known.has(name))
|
|
29397
|
+
off.delete(name);
|
|
29398
|
+
return { off, warnings };
|
|
29399
|
+
}
|
|
29400
|
+
async function packageEnabledRoles(packageDir) {
|
|
29401
|
+
const read = await readJsonObjectFile(path51.join(packageDir, ROUND_ROLES_FILE)).catch(() => {
|
|
29402
|
+
return;
|
|
29403
|
+
});
|
|
29404
|
+
if (read?.state !== "object" || !Array.isArray(read.value.enabled))
|
|
29405
|
+
return [];
|
|
29406
|
+
return read.value.enabled.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
29407
|
+
}
|
|
29408
|
+
var LINE = new RegExp(`^${ROLE_LINE_PREFIX}.*$`, "m");
|
|
29409
|
+
|
|
29324
29410
|
// src/review/coverage.ts
|
|
29325
29411
|
var LIGHT_MAX_FILES = 5;
|
|
29326
29412
|
var LIGHT_MAX_CHANGED_LINES = 300;
|
|
@@ -29332,8 +29418,8 @@ function overlayChangedByRound(reviewer, context) {
|
|
|
29332
29418
|
return;
|
|
29333
29419
|
if (context.files === undefined)
|
|
29334
29420
|
return `${reviewer.name} is a project overlay and the round's changed files are not recorded, so it cannot be ruled out that this branch wrote it`;
|
|
29335
|
-
const absolute =
|
|
29336
|
-
const dir =
|
|
29421
|
+
const absolute = path52.isAbsolute(reviewer.path) ? reviewer.path : path52.resolve(context.root ?? process.cwd(), reviewer.path);
|
|
29422
|
+
const dir = path52.relative(context.root ?? process.cwd(), path52.dirname(absolute)).split(path52.sep).join("/");
|
|
29337
29423
|
const changed = context.files.find((file) => file === dir || file.startsWith(`${dir}/`));
|
|
29338
29424
|
return changed === undefined ? undefined : `${reviewer.name} is a project overlay this round changes (${changed})`;
|
|
29339
29425
|
}
|
|
@@ -29398,7 +29484,7 @@ async function autoDepth(root, input) {
|
|
|
29398
29484
|
}
|
|
29399
29485
|
var ROUND_DEPTH_FILE = "round-depth.json";
|
|
29400
29486
|
async function packageDepthBasis(packageDir) {
|
|
29401
|
-
const manifest = await readFile23(
|
|
29487
|
+
const manifest = await readFile23(path52.join(packageDir, "manifest.json"), "utf8").then((t) => JSON.parse(t)).catch(() => {
|
|
29402
29488
|
return;
|
|
29403
29489
|
});
|
|
29404
29490
|
const head = manifest?.target?.head;
|
|
@@ -29407,7 +29493,7 @@ async function packageDepthBasis(packageDir) {
|
|
|
29407
29493
|
}
|
|
29408
29494
|
async function packageChangedLines(packageDir) {
|
|
29409
29495
|
try {
|
|
29410
|
-
const parsed = JSON.parse(await readFile23(
|
|
29496
|
+
const parsed = JSON.parse(await readFile23(path52.join(packageDir, "scope-files.json"), "utf8"));
|
|
29411
29497
|
return typeof parsed?.changedLines === "number" && Number.isFinite(parsed.changedLines) && parsed.changedLines >= 0 ? parsed.changedLines : undefined;
|
|
29412
29498
|
} catch {
|
|
29413
29499
|
return;
|
|
@@ -29416,7 +29502,7 @@ async function packageChangedLines(packageDir) {
|
|
|
29416
29502
|
async function packageRoundDepth(packageDir) {
|
|
29417
29503
|
let parsed;
|
|
29418
29504
|
try {
|
|
29419
|
-
parsed = JSON.parse(await readFile23(
|
|
29505
|
+
parsed = JSON.parse(await readFile23(path52.join(packageDir, ROUND_DEPTH_FILE), "utf8"));
|
|
29420
29506
|
} catch {
|
|
29421
29507
|
return;
|
|
29422
29508
|
}
|
|
@@ -29469,7 +29555,7 @@ function derivedFromFiles(files) {
|
|
|
29469
29555
|
return picked;
|
|
29470
29556
|
}
|
|
29471
29557
|
async function readSkill(root, relativePath) {
|
|
29472
|
-
const file =
|
|
29558
|
+
const file = path52.join(root, relativePath, "SKILL.md");
|
|
29473
29559
|
return existsSync8(file) ? readFile23(file, "utf8").catch(() => {
|
|
29474
29560
|
return;
|
|
29475
29561
|
}) : undefined;
|
|
@@ -29579,6 +29665,21 @@ async function requiredReviewers(root, mode, files, options = {}) {
|
|
|
29579
29665
|
projectGates.set(reviewer.name, reviewer.paths.map(globToRegExp2));
|
|
29580
29666
|
}
|
|
29581
29667
|
const filesKnown = files !== undefined;
|
|
29668
|
+
const switches = decideRoleSwitches({
|
|
29669
|
+
roster,
|
|
29670
|
+
config: await readRoleConfig(root),
|
|
29671
|
+
optIn: options.enabledRoles ?? [],
|
|
29672
|
+
protectedRoles: NEVER_SUPERSEDED,
|
|
29673
|
+
configIgnored: files?.some((file) => file === "routing.config.json") ? "this round changes routing.config.json, so a branch cannot switch roles off for its own review" : undefined
|
|
29674
|
+
});
|
|
29675
|
+
const rolesOff = [...switches.off.keys()].sort();
|
|
29676
|
+
const warnings = switches.warnings;
|
|
29677
|
+
const switchedOff = (name) => switches.off.has(name);
|
|
29678
|
+
for (const name of roster)
|
|
29679
|
+
if (switchedOff(name))
|
|
29680
|
+
excluded.push({ reviewer: name, reason: switches.off.get(name) });
|
|
29681
|
+
roster.splice(0, roster.length, ...roster.filter((name) => !switchedOff(name)));
|
|
29682
|
+
const switchFields = { ...warnings.length === 0 ? {} : { warnings }, ...rolesOff.length === 0 ? {} : { rolesOff } };
|
|
29582
29683
|
if (options.depth === "light") {
|
|
29583
29684
|
const conventions = lightConventionsReviewer(roster, inventory);
|
|
29584
29685
|
const keep = new Set([...LIGHT_FIXED_REVIEWERS, ...conventions === undefined ? [] : [conventions]]);
|
|
@@ -29591,7 +29692,7 @@ async function requiredReviewers(root, mode, files, options = {}) {
|
|
|
29591
29692
|
excluded.push({ reviewer: name, reason: "light round: not part of the light set" });
|
|
29592
29693
|
const notes = [];
|
|
29593
29694
|
const kept = applySupersedes(light, inventory, excluded, { root, files }, notes).sort();
|
|
29594
|
-
return { mode, depth: "light", required: kept, excluded, filesKnown, ...notes.length === 0 ? {} : { notes } };
|
|
29695
|
+
return { mode, depth: "light", required: kept, excluded, filesKnown, ...notes.length === 0 ? {} : { notes }, ...switchFields };
|
|
29595
29696
|
}
|
|
29596
29697
|
const gated = (name) => {
|
|
29597
29698
|
if (!filesKnown)
|
|
@@ -29633,7 +29734,7 @@ async function requiredReviewers(root, mode, files, options = {}) {
|
|
|
29633
29734
|
}
|
|
29634
29735
|
const notes = [];
|
|
29635
29736
|
const collapsed = applyOverlapGroups(applySupersedes(required, inventory, excluded, { root, files }, notes), inventory, files, excluded, { root }, notes).sort();
|
|
29636
|
-
return { mode, depth: "full", required: collapsed, excluded, filesKnown, ...notes.length === 0 ? {} : { notes } };
|
|
29737
|
+
return { mode, depth: "full", required: collapsed, excluded, filesKnown, ...notes.length === 0 ? {} : { notes }, ...switchFields };
|
|
29637
29738
|
}
|
|
29638
29739
|
async function gitDiffAt(cwd, base, head, pathspec) {
|
|
29639
29740
|
try {
|
|
@@ -29698,7 +29799,7 @@ async function packageDiffStats(options) {
|
|
|
29698
29799
|
return packageDiffStatsCached({ packageDir: options.packageDir, root: options.root, target: { ...target, head: target.head } });
|
|
29699
29800
|
}
|
|
29700
29801
|
async function readPackageTarget(packageDir) {
|
|
29701
|
-
const manifest = await readFile23(
|
|
29802
|
+
const manifest = await readFile23(path52.join(packageDir, "manifest.json"), "utf8").then((t) => JSON.parse(t)).catch(() => {
|
|
29702
29803
|
return;
|
|
29703
29804
|
});
|
|
29704
29805
|
const target = asRecord(asRecord(manifest)?.target) ?? {};
|
|
@@ -29737,8 +29838,8 @@ async function diffOfTarget({ packageDir, root, target }) {
|
|
|
29737
29838
|
const resolved = await resolveDiffBases(cwd, target);
|
|
29738
29839
|
if ("error" in resolved)
|
|
29739
29840
|
return { error: `${UNRESOLVED}: ${resolved.error} (${where(target.recordedBase)})` };
|
|
29740
|
-
const realRoot = await realpath13(
|
|
29741
|
-
const pathspec = notReviewPackagesAt(
|
|
29841
|
+
const realRoot = await realpath13(path52.resolve(root)).catch(() => path52.resolve(root));
|
|
29842
|
+
const pathspec = notReviewPackagesAt(path52.relative(cwd, realRoot));
|
|
29742
29843
|
const results = await Promise.all(resolved.bases.map((base) => gitDiffAt(cwd, base, target.head, pathspec)));
|
|
29743
29844
|
const diffs = [];
|
|
29744
29845
|
for (const [index, result] of results.entries()) {
|
|
@@ -29770,7 +29871,7 @@ async function effectiveDepth(record, options) {
|
|
|
29770
29871
|
}
|
|
29771
29872
|
async function packageHead(packageDir) {
|
|
29772
29873
|
try {
|
|
29773
|
-
const manifest = JSON.parse(await readFile23(
|
|
29874
|
+
const manifest = JSON.parse(await readFile23(path52.join(packageDir, "manifest.json"), "utf8"));
|
|
29774
29875
|
return typeof manifest.target?.head === "string" && manifest.target.head.trim() !== "" ? manifest.target.head.trim() : undefined;
|
|
29775
29876
|
} catch {
|
|
29776
29877
|
return;
|
|
@@ -29779,7 +29880,8 @@ async function packageHead(packageDir) {
|
|
|
29779
29880
|
async function reviewersLeftOut(packageDir, selected) {
|
|
29780
29881
|
const need = await requiredReviewers(projectRootOfPackage(packageDir), "all", await packageScopeFiles(packageDir), {
|
|
29781
29882
|
blastRadius: await packageHasBlastRadius(packageDir),
|
|
29782
|
-
prContext: packageHasPrContext(packageDir)
|
|
29883
|
+
prContext: packageHasPrContext(packageDir),
|
|
29884
|
+
enabledRoles: await packageEnabledRoles(packageDir)
|
|
29783
29885
|
});
|
|
29784
29886
|
return { required: need.required, missing: selected === undefined ? need.required : need.required.filter((role) => !selected.includes(role)) };
|
|
29785
29887
|
}
|
|
@@ -29800,7 +29902,7 @@ async function unconfirmedDecision(options, kind, selected) {
|
|
|
29800
29902
|
return `${stale}${writersFor(kind, dir)}`;
|
|
29801
29903
|
}
|
|
29802
29904
|
function writersFor(kind, packageDir) {
|
|
29803
|
-
return decisionWriters(kind, packageDir === undefined ? "<review-dir>" :
|
|
29905
|
+
return decisionWriters(kind, packageDir === undefined ? "<review-dir>" : path52.relative(process.cwd(), packageDir) || ".");
|
|
29804
29906
|
}
|
|
29805
29907
|
async function mutationErrors(record, options, depth) {
|
|
29806
29908
|
const pending = options.pendingMutants ?? 0;
|
|
@@ -29826,14 +29928,14 @@ async function mutationErrors(record, options, depth) {
|
|
|
29826
29928
|
}
|
|
29827
29929
|
async function packageHasBlastRadius(packageDir, name = "blast-radius.json") {
|
|
29828
29930
|
try {
|
|
29829
|
-
const parsed = JSON.parse(await readFile23(
|
|
29931
|
+
const parsed = JSON.parse(await readFile23(path52.join(packageDir, name), "utf8"));
|
|
29830
29932
|
return Array.isArray(parsed?.files) && Array.isArray(parsed?.changedFiles);
|
|
29831
29933
|
} catch {
|
|
29832
29934
|
return false;
|
|
29833
29935
|
}
|
|
29834
29936
|
}
|
|
29835
29937
|
function packageHasPrContext(packageDir, name = "pr-context.md") {
|
|
29836
|
-
return existsSync8(
|
|
29938
|
+
return existsSync8(path52.join(packageDir, name));
|
|
29837
29939
|
}
|
|
29838
29940
|
async function coverageErrors(dispatch, options) {
|
|
29839
29941
|
const record = asRecord(dispatch);
|
|
@@ -29865,7 +29967,8 @@ async function reviewerSetErrors(dispatch, options, effective) {
|
|
|
29865
29967
|
const need = await requiredReviewers(options.root, record.mode, options.files, {
|
|
29866
29968
|
blastRadius: options.blastRadius,
|
|
29867
29969
|
prContext: options.prContext === true && dispatchKnowsRole(record, PR_CONTRACT),
|
|
29868
|
-
depth: effective?.depth ?? "full"
|
|
29970
|
+
depth: effective?.depth ?? "full",
|
|
29971
|
+
enabledRoles: options.packageDir === undefined ? [] : await packageEnabledRoles(options.packageDir)
|
|
29869
29972
|
});
|
|
29870
29973
|
const selected = new Set(Array.isArray(record.selected) ? record.selected.filter(text) : []);
|
|
29871
29974
|
const skips = dispatchSkips(record).filter((skip) => need.required.includes(skip.reviewer) && !selected.has(skip.reviewer));
|
|
@@ -29925,20 +30028,20 @@ function dispatchSkips(dispatch) {
|
|
|
29925
30028
|
}
|
|
29926
30029
|
async function packageScopeFiles(packageDir) {
|
|
29927
30030
|
try {
|
|
29928
|
-
const parsed = JSON.parse(await readFile23(
|
|
30031
|
+
const parsed = JSON.parse(await readFile23(path52.join(packageDir, "scope-files.json"), "utf8"));
|
|
29929
30032
|
return Array.isArray(parsed?.files) && parsed.files.every((f) => typeof f === "string") ? parsed.files : undefined;
|
|
29930
30033
|
} catch {
|
|
29931
30034
|
return;
|
|
29932
30035
|
}
|
|
29933
30036
|
}
|
|
29934
30037
|
function projectRootOfPackage(packageDir) {
|
|
29935
|
-
const resolved =
|
|
29936
|
-
const parts = resolved.split(
|
|
30038
|
+
const resolved = path52.resolve(packageDir);
|
|
30039
|
+
const parts = resolved.split(path52.sep);
|
|
29937
30040
|
const at = parts.lastIndexOf(".metaproject");
|
|
29938
30041
|
if (at > 0)
|
|
29939
|
-
return parts.slice(0, at).join(
|
|
30042
|
+
return parts.slice(0, at).join(path52.sep) || path52.sep;
|
|
29940
30043
|
const found = resolveProjectRoot(resolved);
|
|
29941
|
-
return found !== resolved ? found :
|
|
30044
|
+
return found !== resolved ? found : path52.resolve(resolved, "..", "..", "..");
|
|
29942
30045
|
}
|
|
29943
30046
|
|
|
29944
30047
|
// src/review/conform-state.ts
|
|
@@ -29962,6 +30065,7 @@ var IMPACT_TEMPLATE = "A high-risk hunk with no test in the same diff exercising
|
|
|
29962
30065
|
// src/review/slice-routing.ts
|
|
29963
30066
|
var ALWAYS_ROLE = "review-logic";
|
|
29964
30067
|
var REGRESSION_ROLE = "review-regression";
|
|
30068
|
+
var DEGRADATION_ROLE = "review-degradation";
|
|
29965
30069
|
var DEEP_ROLES = [...CORE_MANDATORY_REVIEWER_IDS, REGRESSION_ROLE];
|
|
29966
30070
|
var DOC_FILE = /(\.(md|mdx|rst|txt|adoc)$|(^|\/)docs?\/)/i;
|
|
29967
30071
|
var STYLE_FILE = /(\.(css|scss|sass|less|styl)$|(^|\/)(styles?|themes?)(\/|\.)|\.(styles?|theme)\.[jt]sx?$)/i;
|
|
@@ -29981,6 +30085,7 @@ var SECURITY_PRINCIPLE = new RegExp([
|
|
|
29981
30085
|
String.raw`process\.env|\bBun\.env\b|\benv\[`,
|
|
29982
30086
|
String.raw`\bfetch\(|\bhttps?\.(get|request)|\bnet\.|\bWebSocket\b|\bsocket\b|new URL\(`
|
|
29983
30087
|
].join("|"));
|
|
30088
|
+
var SHARED_SYMBOL_CHANGE = /^[+-]\s*export\s+(\*|\{|default\b|declare\b|abstract\b|(async\s+)?(function|class|interface|type|const|let|var|enum)\b)/m;
|
|
29984
30089
|
var ENTRY_FILE = /(^|\/)(index|mod|main)\.[cm]?[jt]sx?$/;
|
|
29985
30090
|
var ROLE_RULES = [
|
|
29986
30091
|
{ role: "review-testing-practices", why: "test file", path: (f) => isTestFilePath(f) || BUNDLED_PATH_GATES["review-testing-practices"](f) || /\.stories\.[jt]sx?$/.test(f) },
|
|
@@ -29997,6 +30102,7 @@ var ROLE_RULES = [
|
|
|
29997
30102
|
{ role: "review-architecture", why: "agent instructions (skill, rule, AGENTS/CLAUDE file, prompt)", path: (f) => AGENT_INSTRUCTION_FILE.test(f) },
|
|
29998
30103
|
{ role: "review-architecture", why: "contract or schema", path: (f) => CONTRACT_FILE.test(f) },
|
|
29999
30104
|
{ role: "review-architecture", why: "exported signature or contract type changed", content: /^[+-]\s*export\s+(declare\s+)?(type|interface|abstract\s+class|enum)\b|^[+-]\s*export\s+(async\s+)?function\s+\w+\s*[<(]/m },
|
|
30105
|
+
{ role: DEGRADATION_ROLE, why: "shared (exported) symbol changed", content: SHARED_SYMBOL_CHANGE, codeOnly: true },
|
|
30000
30106
|
{ role: "review-security-code", why: "process, argv, git, input-derived path, env or network", content: SECURITY_PRINCIPLE, codeOnly: true },
|
|
30001
30107
|
{ role: "review-security-code", why: "process, filesystem write or trust-sensitive code", content: /\b(exec|execSync|execFile|spawn|spawnSync|Bun\.spawn|child_process|eval|new Function|innerHTML|dangerouslySetInnerHTML|password|passwd|secret|token|credential|api[_-]?key|sanitiz\w*|escape\w*|redact\w*|untrusted|trust\w*|permission\w*|allow-?list|deny-?list|sandbox\w*|cookie|cors|csrf|jwt|writeFile\w*|rm|unlink|rmdir|chmod|symlink\w*|realpath)\b/i, codeOnly: true },
|
|
30002
30108
|
{ role: "review-security-code", why: "auth, crypto or secret path", path: (f) => /\b(auth|login|session|permission|acl|rbac|credential|crypto|cipher|secret|security|trust|sandbox)\w*/i.test(f) && !isTestFilePath(f) && !DOC_FILE.test(f) },
|
|
@@ -30093,14 +30199,14 @@ var CHUNK_STATE_FILE = "chunk-state.json";
|
|
|
30093
30199
|
var CHUNK_REVIEWER = BLAST_RADIUS_SCOPE_REVIEWER;
|
|
30094
30200
|
var readJson = (file) => readJsonFileOr(file, undefined);
|
|
30095
30201
|
async function readChunkPlan(packageDir) {
|
|
30096
|
-
const plan = await readJson(
|
|
30202
|
+
const plan = await readJson(path53.join(packageDir, CHUNKS_FILE));
|
|
30097
30203
|
return plan !== undefined && Array.isArray(plan.chunks) ? plan : undefined;
|
|
30098
30204
|
}
|
|
30099
30205
|
function emptyChunkState() {
|
|
30100
30206
|
return { schemaVersion: 1, retry: emptyRetryState(), derived: [], outcomes: {} };
|
|
30101
30207
|
}
|
|
30102
30208
|
async function readChunkState(packageDir) {
|
|
30103
|
-
const state = await readJson(
|
|
30209
|
+
const state = await readJson(path53.join(packageDir, CHUNK_STATE_FILE));
|
|
30104
30210
|
return state !== undefined && state.retry !== undefined && Array.isArray(state.derived) && state.outcomes !== undefined ? state : emptyChunkState();
|
|
30105
30211
|
}
|
|
30106
30212
|
var textOf = (entry) => `${entry.files.join(`
|
|
@@ -30197,7 +30303,7 @@ async function computeChunkPass(packageDir) {
|
|
|
30197
30303
|
return pass;
|
|
30198
30304
|
}
|
|
30199
30305
|
function hasChunkPlan(packageDir) {
|
|
30200
|
-
return existsSync9(
|
|
30306
|
+
return existsSync9(path53.join(packageDir, CHUNKS_FILE));
|
|
30201
30307
|
}
|
|
30202
30308
|
|
|
30203
30309
|
// src/review/coverage-complete.ts
|
|
@@ -30207,7 +30313,7 @@ var SLICE_MANIFEST_COPY = "slice-manifest.json";
|
|
|
30207
30313
|
var readJson2 = (file) => readJsonFileOr(file, undefined);
|
|
30208
30314
|
var text2 = (value) => typeof value === "string" && value.trim().length > 0;
|
|
30209
30315
|
async function readManifest(packageDir) {
|
|
30210
|
-
for (const candidate of [
|
|
30316
|
+
for (const candidate of [path54.join(packageDir, "slices", "manifest.json"), path54.join(packageDir, SLICE_MANIFEST_COPY)]) {
|
|
30211
30317
|
const found = await readJson2(candidate);
|
|
30212
30318
|
if (found !== undefined && Array.isArray(found.slices))
|
|
30213
30319
|
return found;
|
|
@@ -30245,9 +30351,9 @@ function scopeBRunComplete(dispatch) {
|
|
|
30245
30351
|
async function packageRecordsCoverage(packageDir, research) {
|
|
30246
30352
|
if (hasChunkPlan(packageDir))
|
|
30247
30353
|
return true;
|
|
30248
|
-
if (await readJson2(
|
|
30354
|
+
if (await readJson2(path54.join(packageDir, COVERAGE_FILE)) !== undefined)
|
|
30249
30355
|
return true;
|
|
30250
|
-
const ledger = research ?? await readJson2(
|
|
30356
|
+
const ledger = research ?? await readJson2(path54.join(packageDir, "research.json"));
|
|
30251
30357
|
return dispatchRunRecords(asRecord(ledger)?.dispatch).some((run) => Array.isArray(run.coveredSlices));
|
|
30252
30358
|
}
|
|
30253
30359
|
function readCoverageWaiver(dispatch) {
|
|
@@ -30261,11 +30367,11 @@ function readCoverageWaiver(dispatch) {
|
|
|
30261
30367
|
return { operatorQuote: waiver.operatorQuote, time: waiver.time, files, ...via === undefined ? {} : { confirmedVia: via }, ...cardPressFields({ pressedBy, pressedAt }) };
|
|
30262
30368
|
}
|
|
30263
30369
|
async function computeCoverage(packageDir, options = {}) {
|
|
30264
|
-
const research = options.research ?? await readJson2(
|
|
30370
|
+
const research = options.research ?? await readJson2(path54.join(packageDir, "research.json"));
|
|
30265
30371
|
const dispatch = asRecord(asRecord(research)?.dispatch);
|
|
30266
|
-
const radius = await readJson2(
|
|
30372
|
+
const radius = await readJson2(path54.join(packageDir, "blast-radius.json"));
|
|
30267
30373
|
const manifest = await readManifest(packageDir);
|
|
30268
|
-
const retry = await readJson2(
|
|
30374
|
+
const retry = await readJson2(path54.join(packageDir, "retry-state.json"));
|
|
30269
30375
|
const scopeA = await packageScopeFiles(packageDir) ?? [];
|
|
30270
30376
|
const chunkPass = hasChunkPlan(packageDir) ? await computeChunkPass(packageDir) : null;
|
|
30271
30377
|
const radiusFiles = (radius?.files ?? []).map((entry) => entry.file).filter(text2);
|
|
@@ -30353,7 +30459,7 @@ async function coverageGateErrors(packageDir, research) {
|
|
|
30353
30459
|
const uncovered = report.files.filter((entry) => entry.status === "uncovered");
|
|
30354
30460
|
const first = uncovered.slice(0, FIRST_FILES).map((entry) => entry.file);
|
|
30355
30461
|
const more = uncovered.length > FIRST_FILES ? `, \u2026 (+${uncovered.length - FIRST_FILES})` : "";
|
|
30356
|
-
const dir = shellWord(
|
|
30462
|
+
const dir = shellWord(path54.relative(process.cwd(), packageDir) || ".");
|
|
30357
30463
|
const chunkWork = uncovered.some((entry) => entry.scope === "B");
|
|
30358
30464
|
const sliceWork = uncovered.some((entry) => entry.scope === "A");
|
|
30359
30465
|
const commands = [
|
|
@@ -30422,7 +30528,7 @@ function dispatchErrors(value) {
|
|
|
30422
30528
|
|
|
30423
30529
|
// src/review/research.ts
|
|
30424
30530
|
import { readFile as readFile24 } from "fs/promises";
|
|
30425
|
-
import
|
|
30531
|
+
import path55 from "path";
|
|
30426
30532
|
function researchErrors(value, findings) {
|
|
30427
30533
|
const ledger = asRecord(value);
|
|
30428
30534
|
if (ledger === undefined)
|
|
@@ -30486,7 +30592,7 @@ async function packageResearchErrors(packageDir, artifacts, findings, options =
|
|
|
30486
30592
|
if (a.research !== "research.json")
|
|
30487
30593
|
return ["invalid research artifact locator"];
|
|
30488
30594
|
try {
|
|
30489
|
-
const ledger = JSON.parse(await readFile24(
|
|
30595
|
+
const ledger = JSON.parse(await readFile24(path55.join(packageDir, "research.json"), "utf8"));
|
|
30490
30596
|
const recorded = ledger?.dispatch !== undefined;
|
|
30491
30597
|
const checked = a.reviewerDispatch !== undefined || recorded;
|
|
30492
30598
|
const mismatch = recorded && a.reviewerDispatch === undefined ? ['research.json records a reviewer dispatch but manifest.json does not declare it: restore artifacts.reviewerDispatch "research.json"'] : [];
|
|
@@ -30549,11 +30655,11 @@ var CARRIED_PREMISE_CONTEXT = new Set(["judge", "model", "operator"]);
|
|
|
30549
30655
|
// src/flow/review-gate.ts
|
|
30550
30656
|
init_fs();
|
|
30551
30657
|
import { readFile as readFile27 } from "fs/promises";
|
|
30552
|
-
import
|
|
30658
|
+
import path58 from "path";
|
|
30553
30659
|
|
|
30554
30660
|
// src/review/package-location.ts
|
|
30555
30661
|
import { readdir as readdir12, readFile as readFile25 } from "fs/promises";
|
|
30556
|
-
import
|
|
30662
|
+
import path56 from "path";
|
|
30557
30663
|
|
|
30558
30664
|
// src/lib/map-bounded.ts
|
|
30559
30665
|
async function mapBounded(items, limit, map) {
|
|
@@ -30569,15 +30675,15 @@ async function mapBounded(items, limit, map) {
|
|
|
30569
30675
|
|
|
30570
30676
|
// src/review/package-location.ts
|
|
30571
30677
|
function reviewsRoot(cwd) {
|
|
30572
|
-
return
|
|
30678
|
+
return path56.join(cwd, ".metaproject", "reviews");
|
|
30573
30679
|
}
|
|
30574
30680
|
function flowReviewsRoot(cwd, flowDir) {
|
|
30575
|
-
return
|
|
30681
|
+
return path56.join(flowsRoot(cwd), flowDir, "reviews");
|
|
30576
30682
|
}
|
|
30577
30683
|
async function readManifestHead(packageDir) {
|
|
30578
30684
|
let manifest;
|
|
30579
30685
|
try {
|
|
30580
|
-
manifest = JSON.parse(await readFile25(
|
|
30686
|
+
manifest = JSON.parse(await readFile25(path56.join(packageDir, "manifest.json"), "utf8"));
|
|
30581
30687
|
} catch {
|
|
30582
30688
|
return;
|
|
30583
30689
|
}
|
|
@@ -30589,13 +30695,13 @@ async function readManifestHead(packageDir) {
|
|
|
30589
30695
|
return { reviewId: str(fields.reviewId), mode: str(fields.mode), status: str(fields.status), flowId: str(flow.id), flowPath: str(flow.path) };
|
|
30590
30696
|
}
|
|
30591
30697
|
function boundToFlow(head, flowDir) {
|
|
30592
|
-
if (head.flowPath !== undefined &&
|
|
30698
|
+
if (head.flowPath !== undefined && path56.posix.normalize(head.flowPath.split(path56.sep).join("/")) === path56.posix.join(".metaproject", "flows", flowDir))
|
|
30593
30699
|
return true;
|
|
30594
30700
|
return head.flowId !== undefined && head.flowId !== "" && (flowDir === head.flowId || flowDir.startsWith(`${head.flowId}-`));
|
|
30595
30701
|
}
|
|
30596
30702
|
async function dirsUnder(root) {
|
|
30597
30703
|
try {
|
|
30598
|
-
return (await readdir12(root, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
30704
|
+
return (await readdir12(root, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path56.join(root, entry.name));
|
|
30599
30705
|
} catch {
|
|
30600
30706
|
return [];
|
|
30601
30707
|
}
|
|
@@ -30615,8 +30721,8 @@ async function flowPackageDirs(cwd, flowDir, standalone) {
|
|
|
30615
30721
|
return [...own, ...heads.filter(({ head }) => boundToFlow(head, flowDir)).map(({ dir }) => dir)];
|
|
30616
30722
|
}
|
|
30617
30723
|
var REVIEW_PACKAGE_MANIFEST_GLOBS = [
|
|
30618
|
-
|
|
30619
|
-
|
|
30724
|
+
path56.posix.join(".metaproject", "reviews", "*", "manifest.json"),
|
|
30725
|
+
path56.posix.join(".metaproject", "flows", "*", "reviews", "*", "manifest.json")
|
|
30620
30726
|
];
|
|
30621
30727
|
|
|
30622
30728
|
// src/review/loop.ts
|
|
@@ -30660,12 +30766,12 @@ function normalizeText(value) {
|
|
|
30660
30766
|
// src/review/pr-comments.ts
|
|
30661
30767
|
init_fs();
|
|
30662
30768
|
import { readFile as readFile26 } from "fs/promises";
|
|
30663
|
-
import
|
|
30769
|
+
import path57 from "path";
|
|
30664
30770
|
var SEVERITY_SET = new Set(["blocker", "major", "minor", "info"]);
|
|
30665
30771
|
var KNOWN_REVIEW_STATES = new Set(["APPROVED", "CHANGES_REQUESTED", "COMMENTED", "DISMISSED", "PENDING"]);
|
|
30666
30772
|
var PR_COMMENT_STATE_VERSION = 1;
|
|
30667
30773
|
function prCommentsStatePath(cwd, repo, number) {
|
|
30668
|
-
return
|
|
30774
|
+
return path57.join(cwd, ".metaproject", "reviews", "pr-comments", `${repo.replace(/\//g, "__")}__${number}.json`);
|
|
30669
30775
|
}
|
|
30670
30776
|
function emptyPrCommentState(repo, number) {
|
|
30671
30777
|
return {
|
|
@@ -30724,7 +30830,7 @@ function safeFsErrorCode(error) {
|
|
|
30724
30830
|
return typeof code === "string" && /^[A-Z][A-Z0-9]{2,15}$/.test(code) ? code : undefined;
|
|
30725
30831
|
}
|
|
30726
30832
|
async function readReviewGateConfig(cwd) {
|
|
30727
|
-
const file =
|
|
30833
|
+
const file = path58.join(cwd, REVIEW_GATE_CONFIG_PATH);
|
|
30728
30834
|
if (!await pathExists(file)) {
|
|
30729
30835
|
return { ...DEFAULT_REVIEW_GATE_CONFIG, notes: [] };
|
|
30730
30836
|
}
|
|
@@ -30876,13 +30982,13 @@ function matchNumber(text, key) {
|
|
|
30876
30982
|
async function readReviewRounds(cwd, flowDir) {
|
|
30877
30983
|
const rounds = [];
|
|
30878
30984
|
for (const packageDir of await flowPackageDirs(cwd, flowDir)) {
|
|
30879
|
-
const name =
|
|
30985
|
+
const name = path58.basename(packageDir);
|
|
30880
30986
|
const problems = [];
|
|
30881
|
-
const manifest = readRecord(await readJson3(
|
|
30987
|
+
const manifest = readRecord(await readJson3(path58.join(packageDir, "manifest.json")));
|
|
30882
30988
|
if (manifest === null) {
|
|
30883
30989
|
problems.push("manifest.json is missing or unreadable");
|
|
30884
30990
|
}
|
|
30885
|
-
const rawFindings = await readJson3(
|
|
30991
|
+
const rawFindings = await readJson3(path58.join(packageDir, "findings.json"));
|
|
30886
30992
|
if (!Array.isArray(rawFindings)) {
|
|
30887
30993
|
problems.push("findings.json is missing, unreadable, or not an array");
|
|
30888
30994
|
}
|
|
@@ -30900,14 +31006,14 @@ async function readReviewRounds(cwd, flowDir) {
|
|
|
30900
31006
|
}
|
|
30901
31007
|
];
|
|
30902
31008
|
}) : [];
|
|
30903
|
-
const scopePath =
|
|
31009
|
+
const scopePath = path58.join(packageDir, "scope.md");
|
|
30904
31010
|
const scope = await pathExists(scopePath) ? await readScopeMarkdown(scopePath) : null;
|
|
30905
31011
|
const completeness = readString(manifest?.["status"]) !== "closed" && await packageRecordsCoverage(packageDir);
|
|
30906
31012
|
problems.push(...await packageResearchErrors(packageDir, manifest?.["artifacts"], rawFindings, { completeness }));
|
|
30907
31013
|
const findings = Array.isArray(rawFindings) ? await findingsWithHolds(rawFindings.map((item, index) => toGateFinding(item, reviewId, index)), packageDir, problems) : [];
|
|
30908
31014
|
rounds.push({
|
|
30909
31015
|
reviewId,
|
|
30910
|
-
dir:
|
|
31016
|
+
dir: path58.relative(cwd, packageDir),
|
|
30911
31017
|
sortKey: readString(manifest?.["createdAt"]) ?? name,
|
|
30912
31018
|
ingested: problems.length === 0,
|
|
30913
31019
|
problems,
|
|
@@ -31194,7 +31300,7 @@ var durableExternalCommentsGate = async (input) => {
|
|
|
31194
31300
|
return {
|
|
31195
31301
|
collected: false,
|
|
31196
31302
|
unanswered: [],
|
|
31197
|
-
detail: `nothing records whether anyone commented on ${ref.repo}#${ref.number} ` + `(\`${
|
|
31303
|
+
detail: `nothing records whether anyone commented on ${ref.repo}#${ref.number} ` + `(\`${path58.relative(input.cwd, file)}\` does not exist). Zero collected comments and no collection at ` + "all are different facts, and only one of them is clean. Run " + `\`keryx review comments collect --repo ${ref.repo} --pr ${ref.number} --sha <pr-head>\`, or inject ` + "`FlowServiceDeps.externalCommentsGate` with a collector of your own."
|
|
31198
31304
|
};
|
|
31199
31305
|
}
|
|
31200
31306
|
const state = await readPrCommentState(input.cwd, ref.repo, ref.number);
|
|
@@ -31783,14 +31889,14 @@ function describeIdentity(identity) {
|
|
|
31783
31889
|
// src/review/flow-move.ts
|
|
31784
31890
|
init_fs();
|
|
31785
31891
|
import { readdir as readdir14, readFile as readFile29, rename as rename3 } from "fs/promises";
|
|
31786
|
-
import
|
|
31892
|
+
import path62 from "path";
|
|
31787
31893
|
|
|
31788
31894
|
// src/review/review-notes.ts
|
|
31789
31895
|
init_fs();
|
|
31790
31896
|
init_store();
|
|
31791
|
-
import
|
|
31897
|
+
import path61 from "path";
|
|
31792
31898
|
function reviewNotesDir(cwd) {
|
|
31793
|
-
return
|
|
31899
|
+
return path61.join(memoryRoot(cwd), "review-notes");
|
|
31794
31900
|
}
|
|
31795
31901
|
|
|
31796
31902
|
// src/review/flow-move.ts
|
|
@@ -31798,16 +31904,16 @@ var RECORDED_FLOWS_ROOT = ".metaproject/flows";
|
|
|
31798
31904
|
var SCOPE_FLOW_LINE = /^flow: (\d{3})\b/m;
|
|
31799
31905
|
var NOTE_PATH_LINE = /^(- (?:Link|Location): `?)([^`\s]+)/gm;
|
|
31800
31906
|
async function moveFlowDirWithReviewRecords(move) {
|
|
31801
|
-
const fromRoot =
|
|
31802
|
-
const toRoot =
|
|
31803
|
-
const plan = await planReviewRecordRewrites(move, fromRoot, (file) =>
|
|
31907
|
+
const fromRoot = path62.join(flowsRoot(move.cwd), move.fromDir);
|
|
31908
|
+
const toRoot = path62.join(flowsRoot(move.cwd), move.toDir);
|
|
31909
|
+
const plan = await planReviewRecordRewrites(move, fromRoot, (file) => path62.relative(move.cwd, file.startsWith(`${fromRoot}${path62.sep}`) ? path62.join(toRoot, path62.relative(fromRoot, file)) : file));
|
|
31804
31910
|
const written = [];
|
|
31805
31911
|
try {
|
|
31806
31912
|
for (const rewrite of plan.rewrites) {
|
|
31807
31913
|
await writeFileAtomic(rewrite.current, rewrite.after);
|
|
31808
31914
|
written.push(rewrite);
|
|
31809
31915
|
}
|
|
31810
|
-
await rename3(
|
|
31916
|
+
await rename3(path62.join(flowsRoot(move.cwd), move.fromDir), path62.join(flowsRoot(move.cwd), move.toDir));
|
|
31811
31917
|
} catch (error) {
|
|
31812
31918
|
const unrestored = await restore(written);
|
|
31813
31919
|
if (unrestored.length > 0) {
|
|
@@ -31833,14 +31939,14 @@ async function planReviewRecordRewrites(move, packagesRoot, finalOf) {
|
|
|
31833
31939
|
const newPrefix = `${RECORDED_FLOWS_ROOT}/${move.toDir}`;
|
|
31834
31940
|
const movePath = (value) => value === oldPrefix || value.startsWith(`${oldPrefix}/`) ? `${newPrefix}${value.slice(oldPrefix.length)}` : value;
|
|
31835
31941
|
const plan = { rewrites: [], unreadable: [] };
|
|
31836
|
-
const reviewsDir =
|
|
31942
|
+
const reviewsDir = path62.join(packagesRoot, "reviews");
|
|
31837
31943
|
if (await pathExists(reviewsDir)) {
|
|
31838
31944
|
for (const entry of await readdir14(reviewsDir, { withFileTypes: true })) {
|
|
31839
31945
|
if (!entry.isDirectory()) {
|
|
31840
31946
|
continue;
|
|
31841
31947
|
}
|
|
31842
|
-
const packageDir =
|
|
31843
|
-
await planJson(plan,
|
|
31948
|
+
const packageDir = path62.join(reviewsDir, entry.name);
|
|
31949
|
+
await planJson(plan, path62.join(packageDir, "manifest.json"), finalOf, (value) => {
|
|
31844
31950
|
const moved = mapStrings(value, movePath);
|
|
31845
31951
|
const flow = asRecord3(asRecord3(moved)?.["flow"]);
|
|
31846
31952
|
if (flow !== null && flow["id"] === move.from) {
|
|
@@ -31848,8 +31954,8 @@ async function planReviewRecordRewrites(move, packagesRoot, finalOf) {
|
|
|
31848
31954
|
}
|
|
31849
31955
|
return moved;
|
|
31850
31956
|
});
|
|
31851
|
-
await planJson(plan,
|
|
31852
|
-
await planText(plan,
|
|
31957
|
+
await planJson(plan, path62.join(packageDir, "findings.json"), finalOf, (value) => mapStrings(value, movePath));
|
|
31958
|
+
await planText(plan, path62.join(packageDir, "scope.md"), finalOf, (text) => text.replace(SCOPE_FLOW_LINE, (line, id) => id === move.from ? `flow: ${move.to}` : line));
|
|
31853
31959
|
}
|
|
31854
31960
|
}
|
|
31855
31961
|
const notesDir = reviewNotesDir(move.cwd);
|
|
@@ -31858,7 +31964,7 @@ async function planReviewRecordRewrites(move, packagesRoot, finalOf) {
|
|
|
31858
31964
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
31859
31965
|
continue;
|
|
31860
31966
|
}
|
|
31861
|
-
await planText(plan,
|
|
31967
|
+
await planText(plan, path62.join(notesDir, entry.name), finalOf, (text) => text.replace(NOTE_PATH_LINE, (_line, lead, value) => `${lead}${movePath(value)}`));
|
|
31862
31968
|
}
|
|
31863
31969
|
}
|
|
31864
31970
|
return plan;
|
|
@@ -31909,7 +32015,7 @@ function asRecord3(value) {
|
|
|
31909
32015
|
}
|
|
31910
32016
|
|
|
31911
32017
|
// src/flow/ac-reseal.ts
|
|
31912
|
-
import
|
|
32018
|
+
import path63 from "path";
|
|
31913
32019
|
async function acFileUnchangedSinceHead(cwd, acRelativePath) {
|
|
31914
32020
|
if (!Bun.which("git")) {
|
|
31915
32021
|
return { clean: false, reason: "git is not on PATH, so the file's state cannot be established" };
|
|
@@ -31960,7 +32066,7 @@ async function acFileUnchangedSinceHead(cwd, acRelativePath) {
|
|
|
31960
32066
|
return { clean: true };
|
|
31961
32067
|
}
|
|
31962
32068
|
function acRelativePathFor(flowsRootRelative, dir) {
|
|
31963
|
-
return
|
|
32069
|
+
return path63.posix.join(flowsRootRelative, dir, "acceptance-criteria.md");
|
|
31964
32070
|
}
|
|
31965
32071
|
|
|
31966
32072
|
// src/flow/schema.ts
|
|
@@ -32306,7 +32412,7 @@ function flowStateSchema() {
|
|
|
32306
32412
|
init_fs();
|
|
32307
32413
|
init_config2();
|
|
32308
32414
|
import { readFile as readFile30 } from "fs/promises";
|
|
32309
|
-
import
|
|
32415
|
+
import path66 from "path";
|
|
32310
32416
|
|
|
32311
32417
|
// src/memory/inject.ts
|
|
32312
32418
|
init_config2();
|
|
@@ -32463,7 +32569,7 @@ ${renderMemorySearchReportMarkdown(report, { includeHeader: false }).trimEnd()}`
|
|
|
32463
32569
|
".metaproject/data/gdgraph/artifacts/summary.md",
|
|
32464
32570
|
".metaproject/data/gdgraph/artifacts/module-map.json"
|
|
32465
32571
|
]) {
|
|
32466
|
-
if (await pathExists(
|
|
32572
|
+
if (await pathExists(path66.join(cwd, rel))) {
|
|
32467
32573
|
graphRefs.push(`- \`${rel}\``);
|
|
32468
32574
|
}
|
|
32469
32575
|
}
|
|
@@ -32475,7 +32581,7 @@ ${graphRefs.join(`
|
|
|
32475
32581
|
|
|
32476
32582
|
Use \`keryx gdgraph affected <file>\` for blast radius.`);
|
|
32477
32583
|
}
|
|
32478
|
-
const healthLatest =
|
|
32584
|
+
const healthLatest = path66.join(cwd, ".metaproject", "data", "health", "artifacts", "latest.json");
|
|
32479
32585
|
if (await pathExists(healthLatest)) {
|
|
32480
32586
|
try {
|
|
32481
32587
|
const report = JSON.parse(await readFile30(healthLatest, "utf8"));
|
|
@@ -32485,7 +32591,7 @@ Use \`keryx gdgraph affected <file>\` for blast radius.`);
|
|
|
32485
32591
|
- refresh: \`keryx health run\``);
|
|
32486
32592
|
} catch {}
|
|
32487
32593
|
}
|
|
32488
|
-
const manifestPath =
|
|
32594
|
+
const manifestPath = path66.join(cwd, ".metaproject", "metaproject.json");
|
|
32489
32595
|
if (await pathExists(manifestPath)) {
|
|
32490
32596
|
try {
|
|
32491
32597
|
const manifest = JSON.parse(await readFile30(manifestPath, "utf8"));
|
|
@@ -32527,13 +32633,13 @@ var DEFAULT_TASKS = [
|
|
|
32527
32633
|
init_fs();
|
|
32528
32634
|
import { createHash as createHash10, randomBytes as randomBytes4 } from "crypto";
|
|
32529
32635
|
import { readFile as readFile31 } from "fs/promises";
|
|
32530
|
-
import
|
|
32636
|
+
import path67 from "path";
|
|
32531
32637
|
var CONFIRMATION_TOKEN_TTL_MS = 10 * 60000;
|
|
32532
32638
|
var CONFIRMATION_CAVEAT = "The token proves an interactive confirmation step ran outside the agent's tool roster, within its TTL, " + "for this criteria checksum \u2014 not that a human ran it, and not who.";
|
|
32533
32639
|
var CONFIRM_TOKEN_FILE = "confirm-token.json";
|
|
32534
32640
|
var TOKEN_REF_LENGTH = 12;
|
|
32535
32641
|
function confirmTokenPath(cwd, dir) {
|
|
32536
|
-
return
|
|
32642
|
+
return path67.join(flowsRoot(cwd), dir, CONFIRM_TOKEN_FILE);
|
|
32537
32643
|
}
|
|
32538
32644
|
function completionTarget(input) {
|
|
32539
32645
|
return input.merged ? "merged" : `pr:${input.prUrl ?? "none"}`;
|
|
@@ -32648,7 +32754,7 @@ function describeConfirmationFailure(reason, flowId) {
|
|
|
32648
32754
|
}
|
|
32649
32755
|
async function readRequireConfirmationDefault(cwd) {
|
|
32650
32756
|
try {
|
|
32651
|
-
const parsed = JSON.parse(await readFile31(
|
|
32757
|
+
const parsed = JSON.parse(await readFile31(path67.join(cwd, REVIEW_GATE_CONFIG_PATH), "utf8"));
|
|
32652
32758
|
if (typeof parsed !== "object" || parsed === null)
|
|
32653
32759
|
return false;
|
|
32654
32760
|
const completion = parsed["completion"];
|
|
@@ -32772,7 +32878,7 @@ async function githubPostReview(url, event, body) {
|
|
|
32772
32878
|
}
|
|
32773
32879
|
}
|
|
32774
32880
|
// src/flow/uncommitted-state.ts
|
|
32775
|
-
import
|
|
32881
|
+
import path68 from "path";
|
|
32776
32882
|
var UNCOMMITTED_LIST_CAP = 5;
|
|
32777
32883
|
var READ_ONLY_GIT_FLAGS = ["--no-optional-locks"];
|
|
32778
32884
|
var GIT_TIMEOUT_MS2 = 5000;
|
|
@@ -32849,7 +32955,7 @@ async function readGit(cwd, scope, git) {
|
|
|
32849
32955
|
}
|
|
32850
32956
|
async function uncommittedFlowStateNote(cwd, flowId, dir, git = spawnGit) {
|
|
32851
32957
|
try {
|
|
32852
|
-
const read = await readGit(cwd,
|
|
32958
|
+
const read = await readGit(cwd, path68.join(flowsRoot(cwd), dir), git);
|
|
32853
32959
|
return read === null ? null : noteFor(flowId, dir, read);
|
|
32854
32960
|
} catch {
|
|
32855
32961
|
return null;
|
|
@@ -33055,7 +33161,7 @@ function readAcKindRecords(value) {
|
|
|
33055
33161
|
}
|
|
33056
33162
|
// src/flow/description-intent.ts
|
|
33057
33163
|
import { readFile as readFile32 } from "fs/promises";
|
|
33058
|
-
import
|
|
33164
|
+
import path69 from "path";
|
|
33059
33165
|
var OUTCOME_HINT = 'State an outcome criterion: what you would look at afterwards to see this helped, as a bullet. Or write "- not measured \u2014 <reason>".';
|
|
33060
33166
|
var PLACEHOLDERS = ["Describe the problem precisely", "What must be true when this flow is done"];
|
|
33061
33167
|
var FLOW_STATEMENT_HEADINGS = [/^problem$/i, /^expected outcome$/i];
|
|
@@ -33168,7 +33274,7 @@ function initIntentNote(description) {
|
|
|
33168
33274
|
}
|
|
33169
33275
|
async function intentNoteForNewFlow(cwd, dir) {
|
|
33170
33276
|
try {
|
|
33171
|
-
return initIntentNote(await readFile32(
|
|
33277
|
+
return initIntentNote(await readFile32(path69.join(cwd, dir, "description.md"), "utf8"));
|
|
33172
33278
|
} catch {
|
|
33173
33279
|
return null;
|
|
33174
33280
|
}
|
|
@@ -33176,7 +33282,7 @@ async function intentNoteForNewFlow(cwd, dir) {
|
|
|
33176
33282
|
// src/flow/check-ac.ts
|
|
33177
33283
|
import { createHash as createHash11 } from "crypto";
|
|
33178
33284
|
import { chmod as chmod4, mkdir as mkdir11, readFile as readFile33 } from "fs/promises";
|
|
33179
|
-
import
|
|
33285
|
+
import path70 from "path";
|
|
33180
33286
|
init_service2();
|
|
33181
33287
|
init_fs();
|
|
33182
33288
|
var AC_LINE2 = /^\s*[-*]\s*(AC\d+)\s*:\s?(.*)$/i;
|
|
@@ -33357,7 +33463,7 @@ function factsOnlyVerdict(item) {
|
|
|
33357
33463
|
}
|
|
33358
33464
|
async function readAcCheckEnabled(cwd) {
|
|
33359
33465
|
try {
|
|
33360
|
-
const parsed = JSON.parse(await readFile33(
|
|
33466
|
+
const parsed = JSON.parse(await readFile33(path70.join(cwd, REVIEW_GATE_CONFIG_PATH), "utf8"));
|
|
33361
33467
|
if (typeof parsed !== "object" || parsed === null)
|
|
33362
33468
|
return false;
|
|
33363
33469
|
const review = parsed["review"];
|
|
@@ -33378,7 +33484,7 @@ function acCheckCacheKey(criteriaChecksum, diffText) {
|
|
|
33378
33484
|
return `${criteriaChecksum}:${hashDiff(diffText)}`;
|
|
33379
33485
|
}
|
|
33380
33486
|
function acCheckCachePath(cwd, flowDir) {
|
|
33381
|
-
return
|
|
33487
|
+
return path70.join(cwd, ".metaproject", "data", "ac-check", `${flowDir}.json`);
|
|
33382
33488
|
}
|
|
33383
33489
|
async function readAcCheckCache(cachePath) {
|
|
33384
33490
|
if (!await pathExists(cachePath))
|
|
@@ -33390,7 +33496,7 @@ async function readAcCheckCache(cachePath) {
|
|
|
33390
33496
|
}
|
|
33391
33497
|
}
|
|
33392
33498
|
async function writeAcCheckCache(cachePath, record) {
|
|
33393
|
-
await mkdir11(
|
|
33499
|
+
await mkdir11(path70.dirname(cachePath), { recursive: true });
|
|
33394
33500
|
await writeFileAtomic(cachePath, `${JSON.stringify(record, null, 2)}
|
|
33395
33501
|
`, { mode: 384 });
|
|
33396
33502
|
if (process.platform !== "win32") {
|
|
@@ -33448,10 +33554,10 @@ function renderAcCheckAdvisoryNotice(verdicts) {
|
|
|
33448
33554
|
}
|
|
33449
33555
|
// src/flow/allocation.ts
|
|
33450
33556
|
import { appendFile as appendFile3, mkdir as mkdir12, readFile as readFile34 } from "fs/promises";
|
|
33451
|
-
import
|
|
33557
|
+
import path72 from "path";
|
|
33452
33558
|
|
|
33453
33559
|
// src/lib/clone-scope.ts
|
|
33454
|
-
import
|
|
33560
|
+
import path71 from "path";
|
|
33455
33561
|
async function gitCommonDir(cwd) {
|
|
33456
33562
|
try {
|
|
33457
33563
|
const proc = Bun.spawn(["git", "rev-parse", "--git-common-dir"], {
|
|
@@ -33463,7 +33569,7 @@ async function gitCommonDir(cwd) {
|
|
|
33463
33569
|
if (await proc.exited !== 0 || output.length === 0) {
|
|
33464
33570
|
return null;
|
|
33465
33571
|
}
|
|
33466
|
-
return
|
|
33572
|
+
return path71.isAbsolute(output) ? output : path71.resolve(cwd, output);
|
|
33467
33573
|
} catch {
|
|
33468
33574
|
return null;
|
|
33469
33575
|
}
|
|
@@ -33488,23 +33594,23 @@ function projectKey(toplevel, cwd) {
|
|
|
33488
33594
|
if (!toplevel) {
|
|
33489
33595
|
return "root";
|
|
33490
33596
|
}
|
|
33491
|
-
const relative =
|
|
33597
|
+
const relative = path72.relative(toplevel, cwd);
|
|
33492
33598
|
return relative === "" ? "root" : slugify(relative);
|
|
33493
33599
|
}
|
|
33494
33600
|
async function resolveAllocationScope(cwd) {
|
|
33495
33601
|
const commonDir = await gitCommonDir(cwd);
|
|
33496
33602
|
if (!commonDir) {
|
|
33497
33603
|
return {
|
|
33498
|
-
lockPath:
|
|
33604
|
+
lockPath: path72.join(flowsRoot(cwd), ".flow-init.lock"),
|
|
33499
33605
|
ledgerPath: null,
|
|
33500
33606
|
project: "root"
|
|
33501
33607
|
};
|
|
33502
33608
|
}
|
|
33503
33609
|
const key = projectKey(await gitToplevel(cwd), cwd);
|
|
33504
|
-
const dir =
|
|
33610
|
+
const dir = path72.join(commonDir, "keryx", "flow-ids");
|
|
33505
33611
|
return {
|
|
33506
|
-
lockPath:
|
|
33507
|
-
ledgerPath:
|
|
33612
|
+
lockPath: path72.join(dir, `${key}.lock`),
|
|
33613
|
+
ledgerPath: path72.join(dir, `${key}.jsonl`),
|
|
33508
33614
|
project: key
|
|
33509
33615
|
};
|
|
33510
33616
|
}
|
|
@@ -33537,13 +33643,13 @@ async function recordAllocation(scope, record) {
|
|
|
33537
33643
|
if (!scope.ledgerPath) {
|
|
33538
33644
|
return;
|
|
33539
33645
|
}
|
|
33540
|
-
await mkdir12(
|
|
33646
|
+
await mkdir12(path72.dirname(scope.ledgerPath), { recursive: true });
|
|
33541
33647
|
await appendFile3(scope.ledgerPath, `${JSON.stringify(record)}
|
|
33542
33648
|
`, "utf8");
|
|
33543
33649
|
}
|
|
33544
33650
|
|
|
33545
33651
|
// src/flow/remote-flows.ts
|
|
33546
|
-
import
|
|
33652
|
+
import path73 from "path";
|
|
33547
33653
|
var MAX_REFS = 500;
|
|
33548
33654
|
var CONCURRENCY = 8;
|
|
33549
33655
|
async function runGit2(cwd, args) {
|
|
@@ -33624,7 +33730,7 @@ async function flowDirsOnRef(cwd, ref, flowsPath) {
|
|
|
33624
33730
|
if (result === undefined || result.code !== 0) {
|
|
33625
33731
|
return [];
|
|
33626
33732
|
}
|
|
33627
|
-
return nulSplit(result.stdout).map((entry) =>
|
|
33733
|
+
return nulSplit(result.stdout).map((entry) => path73.posix.basename(entry)).filter((name) => /^\d{3}-/.test(name));
|
|
33628
33734
|
}
|
|
33629
33735
|
async function knownRemoteFlowDirs(cwd) {
|
|
33630
33736
|
try {
|
|
@@ -33804,12 +33910,12 @@ function renderJournal(createdAt) {
|
|
|
33804
33910
|
|
|
33805
33911
|
// src/flow/task-project-context.ts
|
|
33806
33912
|
import { readFile as readFile38 } from "fs/promises";
|
|
33807
|
-
import
|
|
33913
|
+
import path77 from "path";
|
|
33808
33914
|
|
|
33809
33915
|
// src/gdskills/project-context-config.ts
|
|
33810
33916
|
init_fs();
|
|
33811
33917
|
import { readFile as readFile35 } from "fs/promises";
|
|
33812
|
-
import
|
|
33918
|
+
import path74 from "path";
|
|
33813
33919
|
var PROJECT_CONTEXT_CONFIG_PATH = ".metaproject/tasks.config.json";
|
|
33814
33920
|
var DEFAULT_MAX_BYTES = 131072;
|
|
33815
33921
|
var MAX_BYTES_FLOOR = 4096;
|
|
@@ -33819,7 +33925,7 @@ function isValidMaxBytes(value) {
|
|
|
33819
33925
|
}
|
|
33820
33926
|
async function readRoot(projectRoot) {
|
|
33821
33927
|
try {
|
|
33822
|
-
const parsed = JSON.parse(await readFile35(
|
|
33928
|
+
const parsed = JSON.parse(await readFile35(path74.join(projectRoot, PROJECT_CONTEXT_CONFIG_PATH), "utf8"));
|
|
33823
33929
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
|
|
33824
33930
|
} catch {
|
|
33825
33931
|
return;
|
|
@@ -33863,7 +33969,7 @@ async function readProjectContextConfig(projectRoot) {
|
|
|
33863
33969
|
|
|
33864
33970
|
// src/gdskills/resolved-context-check.ts
|
|
33865
33971
|
import { readFile as readFile37 } from "fs/promises";
|
|
33866
|
-
import
|
|
33972
|
+
import path76 from "path";
|
|
33867
33973
|
|
|
33868
33974
|
// src/gdskills/project-rules.ts
|
|
33869
33975
|
import { createHash as createHash12 } from "crypto";
|
|
@@ -33876,7 +33982,7 @@ function sha256Hex(content) {
|
|
|
33876
33982
|
|
|
33877
33983
|
// src/gdskills/reference-proof.ts
|
|
33878
33984
|
import { readFile as readFile36 } from "fs/promises";
|
|
33879
|
-
import
|
|
33985
|
+
import path75 from "path";
|
|
33880
33986
|
|
|
33881
33987
|
// src/gdskills/references-section.ts
|
|
33882
33988
|
var FENCE = /^ {0,3}(`{3,}|~{3,})/;
|
|
@@ -33982,7 +34088,7 @@ function pickProofItem(items, pinned) {
|
|
|
33982
34088
|
return items.find((item) => item.sha256 === pinned && item.how !== "n/a") ?? items.find((item) => item.how !== "n/a") ?? items[0];
|
|
33983
34089
|
}
|
|
33984
34090
|
async function currentSection(projectRoot, entry) {
|
|
33985
|
-
const file =
|
|
34091
|
+
const file = path75.isAbsolute(entry.path) ? entry.path : path75.join(projectRoot, entry.path);
|
|
33986
34092
|
let text;
|
|
33987
34093
|
try {
|
|
33988
34094
|
text = await readFile36(file, "utf8");
|
|
@@ -34074,7 +34180,7 @@ function appliedOf(result) {
|
|
|
34074
34180
|
}
|
|
34075
34181
|
async function currentSha(projectRoot, entryPath) {
|
|
34076
34182
|
try {
|
|
34077
|
-
return sha256Hex(await readFile37(
|
|
34183
|
+
return sha256Hex(await readFile37(path76.isAbsolute(entryPath) ? entryPath : path76.join(projectRoot, entryPath), "utf8"));
|
|
34078
34184
|
} catch {
|
|
34079
34185
|
return;
|
|
34080
34186
|
}
|
|
@@ -34153,8 +34259,8 @@ async function checkTaskProjectContext(input) {
|
|
|
34153
34259
|
}
|
|
34154
34260
|
const report = await checkResolvedContext({
|
|
34155
34261
|
projectRoot: input.projectRoot,
|
|
34156
|
-
resolved: await readResolvedFile(
|
|
34157
|
-
result: await readResultFile(
|
|
34262
|
+
resolved: await readResolvedFile(path77.resolve(input.projectRoot, input.resolvedFile)),
|
|
34263
|
+
result: await readResultFile(path77.resolve(input.projectRoot, input.resultFile))
|
|
34158
34264
|
});
|
|
34159
34265
|
const lines = formatResolvedReport(report);
|
|
34160
34266
|
if (lines.length === 0)
|
|
@@ -34468,7 +34574,7 @@ function createFlowService(deps) {
|
|
|
34468
34574
|
const date = now().slice(0, 10);
|
|
34469
34575
|
const slug = slugify(input.slug ?? title);
|
|
34470
34576
|
const dir = `${id}-${date}-${slug}`;
|
|
34471
|
-
const absolute =
|
|
34577
|
+
const absolute = path78.join(flowsRoot(input.cwd), dir);
|
|
34472
34578
|
if (await pathExists(absolute)) {
|
|
34473
34579
|
throw new Error(`Flow directory already exists: ${dir}`);
|
|
34474
34580
|
}
|
|
@@ -34504,17 +34610,17 @@ function createFlowService(deps) {
|
|
|
34504
34610
|
history: [{ at: createdAt, event: "created" }]
|
|
34505
34611
|
};
|
|
34506
34612
|
const sourceLabel = input.issue ?? "user description";
|
|
34507
|
-
await writeFileAtomic(
|
|
34508
|
-
await writeFileAtomic(
|
|
34509
|
-
await writeFileAtomic(
|
|
34510
|
-
await writeFileAtomic(
|
|
34511
|
-
await writeFileAtomic(
|
|
34512
|
-
await writeFileAtomic(
|
|
34613
|
+
await writeFileAtomic(path78.join(absolute, "description.md"), renderDescription(title, originDraft === undefined ? sourceLabel : `${sourceLabel} (origin: ${originDraft.kind})`, originDraft));
|
|
34614
|
+
await writeFileAtomic(path78.join(absolute, "context.md"), context.markdown);
|
|
34615
|
+
await writeFileAtomic(path78.join(absolute, "plan.md"), renderPlan());
|
|
34616
|
+
await writeFileAtomic(path78.join(absolute, "tasks.md"), renderTasksDoc());
|
|
34617
|
+
await writeFileAtomic(path78.join(absolute, "acceptance-criteria.md"), renderAcceptanceCriteria());
|
|
34618
|
+
await writeFileAtomic(path78.join(absolute, "journal.md"), renderJournal(createdAt));
|
|
34513
34619
|
await writeFlow(input.cwd, dir, flow);
|
|
34514
34620
|
await recordAllocation(scope, { id, dir, at: createdAt, project: scope.project });
|
|
34515
34621
|
return {
|
|
34516
34622
|
flow,
|
|
34517
|
-
dir:
|
|
34623
|
+
dir: path78.relative(input.cwd, absolute),
|
|
34518
34624
|
contextNotes: context.notes,
|
|
34519
34625
|
...originNote === undefined ? {} : { originNote }
|
|
34520
34626
|
};
|
|
@@ -35155,7 +35261,7 @@ function createFlowService(deps) {
|
|
|
35155
35261
|
issues.push({ flow: dir, kind: "schema", message: `unknown schemaVersion ${flow.schemaVersion}` });
|
|
35156
35262
|
}
|
|
35157
35263
|
try {
|
|
35158
|
-
const raw = JSON.parse(await Bun.file(
|
|
35264
|
+
const raw = JSON.parse(await Bun.file(path78.join(flowsRoot(cwd), dir, "flow.json")).text());
|
|
35159
35265
|
const validation = validateAgainstSchemaObject(flowStateSchema(), raw);
|
|
35160
35266
|
if (!validation.valid) {
|
|
35161
35267
|
issues.push({
|
|
@@ -35166,7 +35272,7 @@ function createFlowService(deps) {
|
|
|
35166
35272
|
}
|
|
35167
35273
|
} catch {}
|
|
35168
35274
|
for (const file of ["description.md", "context.md", "plan.md", "tasks.md", "acceptance-criteria.md", "journal.md"]) {
|
|
35169
|
-
if (!await pathExists(
|
|
35275
|
+
if (!await pathExists(path78.join(flowsRoot(cwd), dir, file))) {
|
|
35170
35276
|
issues.push({ flow: dir, kind: "structure", message: `missing ${file}` });
|
|
35171
35277
|
}
|
|
35172
35278
|
}
|
|
@@ -35205,7 +35311,7 @@ function createFlowService(deps) {
|
|
|
35205
35311
|
};
|
|
35206
35312
|
}
|
|
35207
35313
|
async function appendIdMap(cwd, entry) {
|
|
35208
|
-
const file =
|
|
35314
|
+
const file = path78.join(flowsRoot(cwd), "id-map.json");
|
|
35209
35315
|
let entries = [];
|
|
35210
35316
|
if (await pathExists(file)) {
|
|
35211
35317
|
const raw = await readFile39(file, "utf8");
|
|
@@ -35216,7 +35322,7 @@ async function appendIdMap(cwd, entry) {
|
|
|
35216
35322
|
}
|
|
35217
35323
|
entries = parsed;
|
|
35218
35324
|
} catch (error) {
|
|
35219
|
-
throw new Error(`Cannot read ${
|
|
35325
|
+
throw new Error(`Cannot read ${path78.relative(cwd, file)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
35220
35326
|
}
|
|
35221
35327
|
}
|
|
35222
35328
|
entries.push(entry);
|
|
@@ -35504,7 +35610,7 @@ async function acCriterionKnown(input) {
|
|
|
35504
35610
|
init_data_dir();
|
|
35505
35611
|
import { mkdir as mkdir23, readdir as readdir18, readFile as readFile51, writeFile as writeFile17 } from "fs/promises";
|
|
35506
35612
|
import { existsSync as existsSync11 } from "fs";
|
|
35507
|
-
import
|
|
35613
|
+
import path101 from "path";
|
|
35508
35614
|
|
|
35509
35615
|
// src/gdgraph/types.ts
|
|
35510
35616
|
var UNKNOWN_IMPORT_KIND = "unknown-static";
|
|
@@ -35571,7 +35677,7 @@ async function buildGraph(projectRoot, options = {}) {
|
|
|
35571
35677
|
const assetNodes = new Map;
|
|
35572
35678
|
const fileRecords = [];
|
|
35573
35679
|
for (const file of files) {
|
|
35574
|
-
const absolutePath =
|
|
35680
|
+
const absolutePath = path101.join(projectRoot, file);
|
|
35575
35681
|
const content = await readFile51(absolutePath, "utf8");
|
|
35576
35682
|
fileRecords.push({ path: file, content });
|
|
35577
35683
|
const language = getLanguage(file);
|
|
@@ -35624,24 +35730,24 @@ async function collectSourceFiles(projectRoot) {
|
|
|
35624
35730
|
const result = [];
|
|
35625
35731
|
const skippedDirectories = [];
|
|
35626
35732
|
async function walk(relativeDir) {
|
|
35627
|
-
const absoluteDir =
|
|
35733
|
+
const absoluteDir = path101.join(projectRoot, relativeDir);
|
|
35628
35734
|
const entries = await readdir18(absoluteDir, { withFileTypes: true });
|
|
35629
35735
|
for (const entry of entries) {
|
|
35630
35736
|
if (entry.isDirectory()) {
|
|
35631
35737
|
if (IGNORE_DIRS.has(entry.name)) {
|
|
35632
|
-
skippedDirectories.push(normalizePath3(
|
|
35738
|
+
skippedDirectories.push(normalizePath3(path101.posix.join(relativeDir, entry.name)).replace(/^\.\//, ""));
|
|
35633
35739
|
continue;
|
|
35634
35740
|
}
|
|
35635
|
-
await walk(normalizePath3(
|
|
35741
|
+
await walk(normalizePath3(path101.posix.join(relativeDir, entry.name)));
|
|
35636
35742
|
continue;
|
|
35637
35743
|
}
|
|
35638
35744
|
if (!entry.isFile()) {
|
|
35639
35745
|
continue;
|
|
35640
35746
|
}
|
|
35641
|
-
if (!SOURCE_EXTENSIONS.includes(
|
|
35747
|
+
if (!SOURCE_EXTENSIONS.includes(path101.extname(entry.name))) {
|
|
35642
35748
|
continue;
|
|
35643
35749
|
}
|
|
35644
|
-
result.push(normalizePath3(
|
|
35750
|
+
result.push(normalizePath3(path101.posix.join(relativeDir, entry.name)).replace(/^\.\//, ""));
|
|
35645
35751
|
}
|
|
35646
35752
|
}
|
|
35647
35753
|
await walk(".");
|
|
@@ -35723,8 +35829,8 @@ function resolveImport(projectRoot, fromFile, specifier, fileSet, resolver) {
|
|
|
35723
35829
|
}
|
|
35724
35830
|
function resolveAssetImport(projectRoot, fromFile, specifier, resolver) {
|
|
35725
35831
|
for (const candidate of importCandidateBases(projectRoot, fromFile, specifier, resolver)) {
|
|
35726
|
-
const extension =
|
|
35727
|
-
if (ASSET_EXTENSIONS.includes(extension) && existsSync11(
|
|
35832
|
+
const extension = path101.posix.extname(candidate);
|
|
35833
|
+
if (ASSET_EXTENSIONS.includes(extension) && existsSync11(path101.join(projectRoot, candidate))) {
|
|
35728
35834
|
return candidate;
|
|
35729
35835
|
}
|
|
35730
35836
|
}
|
|
@@ -35733,17 +35839,17 @@ function resolveAssetImport(projectRoot, fromFile, specifier, resolver) {
|
|
|
35733
35839
|
function importCandidateBases(projectRoot, fromFile, specifier, resolver) {
|
|
35734
35840
|
const normalizedSpecifier = stripImportSuffix(specifier);
|
|
35735
35841
|
if (normalizedSpecifier.startsWith(".") && getLanguage(fromFile) !== "python") {
|
|
35736
|
-
const fromDir =
|
|
35737
|
-
return [normalizePath3(
|
|
35842
|
+
const fromDir = path101.posix.dirname(fromFile);
|
|
35843
|
+
return [normalizePath3(path101.posix.normalize(path101.posix.join(fromDir, normalizedSpecifier)))];
|
|
35738
35844
|
}
|
|
35739
|
-
return resolver.candidateBases(normalizedSpecifier, fromFile).map((candidate) => normalizePath3(
|
|
35845
|
+
return resolver.candidateBases(normalizedSpecifier, fromFile).map((candidate) => normalizePath3(path101.relative(projectRoot, path101.join(projectRoot, candidate))));
|
|
35740
35846
|
}
|
|
35741
35847
|
function resolveSourceCandidate(base, fileSet) {
|
|
35742
35848
|
const candidates = [
|
|
35743
35849
|
base,
|
|
35744
35850
|
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) => `${base}${extension}`),
|
|
35745
|
-
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) =>
|
|
35746
|
-
|
|
35851
|
+
...SOURCE_RESOLUTION_EXTENSIONS.map((extension) => path101.posix.join(base, `index${extension}`)),
|
|
35852
|
+
path101.posix.join(base, "__init__.py")
|
|
35747
35853
|
];
|
|
35748
35854
|
return candidates.find((candidate) => fileSet.has(candidate)) ?? null;
|
|
35749
35855
|
}
|
|
@@ -35773,7 +35879,7 @@ async function loadJavaResolver(projectRoot) {
|
|
|
35773
35879
|
return createJavaResolver(roots);
|
|
35774
35880
|
}
|
|
35775
35881
|
async function discoverJavaSourceRoots(projectRoot) {
|
|
35776
|
-
const pomPath =
|
|
35882
|
+
const pomPath = path101.join(projectRoot, "pom.xml");
|
|
35777
35883
|
if (existsSync11(pomPath)) {
|
|
35778
35884
|
try {
|
|
35779
35885
|
const roots = parseMavenSourceRoots(await readFile51(pomPath, "utf8"));
|
|
@@ -35783,7 +35889,7 @@ async function discoverJavaSourceRoots(projectRoot) {
|
|
|
35783
35889
|
} catch {}
|
|
35784
35890
|
}
|
|
35785
35891
|
for (const gradleName of ["build.gradle", "build.gradle.kts"]) {
|
|
35786
|
-
const gradlePath =
|
|
35892
|
+
const gradlePath = path101.join(projectRoot, gradleName);
|
|
35787
35893
|
if (existsSync11(gradlePath)) {
|
|
35788
35894
|
try {
|
|
35789
35895
|
const roots = parseGradleSourceRoots(await readFile51(gradlePath, "utf8"));
|
|
@@ -35805,12 +35911,12 @@ function createJavaResolver(sourceRoots) {
|
|
|
35805
35911
|
return [];
|
|
35806
35912
|
}
|
|
35807
35913
|
const relative = specifier.split(".").join("/");
|
|
35808
|
-
return sourceRoots.map((root) => normalizePath3(
|
|
35914
|
+
return sourceRoots.map((root) => normalizePath3(path101.posix.join(root, relative)));
|
|
35809
35915
|
}
|
|
35810
35916
|
};
|
|
35811
35917
|
}
|
|
35812
35918
|
function loadPythonResolver(projectRoot) {
|
|
35813
|
-
const hasSrc = existsSync11(
|
|
35919
|
+
const hasSrc = existsSync11(path101.join(projectRoot, "src"));
|
|
35814
35920
|
return createPythonResolver(hasSrc);
|
|
35815
35921
|
}
|
|
35816
35922
|
function createPythonResolver(hasSrc) {
|
|
@@ -35823,16 +35929,16 @@ function createPythonResolver(hasSrc) {
|
|
|
35823
35929
|
if (specifier.startsWith(".")) {
|
|
35824
35930
|
const dots = /^\.+/.exec(specifier)?.[0].length ?? 0;
|
|
35825
35931
|
const rest = specifier.slice(dots);
|
|
35826
|
-
let baseDir =
|
|
35932
|
+
let baseDir = path101.posix.dirname(fromFile);
|
|
35827
35933
|
for (let level = 1;level < dots; level += 1) {
|
|
35828
|
-
baseDir =
|
|
35934
|
+
baseDir = path101.posix.dirname(baseDir);
|
|
35829
35935
|
}
|
|
35830
35936
|
const suffix = rest ? rest.split(".").join("/") : "";
|
|
35831
|
-
const base = suffix ?
|
|
35937
|
+
const base = suffix ? path101.posix.join(baseDir, suffix) : baseDir;
|
|
35832
35938
|
return [normalizePath3(base)];
|
|
35833
35939
|
}
|
|
35834
35940
|
const relative = specifier.split(".").join("/");
|
|
35835
|
-
return roots.map((root) => normalizePath3(root ?
|
|
35941
|
+
return roots.map((root) => normalizePath3(root ? path101.posix.join(root, relative) : relative));
|
|
35836
35942
|
}
|
|
35837
35943
|
};
|
|
35838
35944
|
}
|
|
@@ -35841,8 +35947,8 @@ function parseMavenSourceRoots(pomXml) {
|
|
|
35841
35947
|
for (const match of pomXml.matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
|
|
35842
35948
|
const moduleDir = match[1]?.trim();
|
|
35843
35949
|
if (moduleDir && !moduleDir.includes("${")) {
|
|
35844
|
-
roots.add(normalizePath3(
|
|
35845
|
-
roots.add(normalizePath3(
|
|
35950
|
+
roots.add(normalizePath3(path101.posix.join(moduleDir, "src/main/java")));
|
|
35951
|
+
roots.add(normalizePath3(path101.posix.join(moduleDir, "src/test/java")));
|
|
35846
35952
|
}
|
|
35847
35953
|
}
|
|
35848
35954
|
for (const tag of ["sourceDirectory", "testSourceDirectory"]) {
|
|
@@ -35875,7 +35981,7 @@ function parseGradleSourceRoots(buildGradle) {
|
|
|
35875
35981
|
var TSCONFIG_EXTENDS_MAX_DEPTH = 10;
|
|
35876
35982
|
async function loadTsconfigResolver(projectRoot) {
|
|
35877
35983
|
const empty = createTsconfigResolver(null, []);
|
|
35878
|
-
const tsconfigPath =
|
|
35984
|
+
const tsconfigPath = path101.join(projectRoot, "tsconfig.json");
|
|
35879
35985
|
if (!existsSync11(tsconfigPath)) {
|
|
35880
35986
|
return empty;
|
|
35881
35987
|
}
|
|
@@ -35888,7 +35994,7 @@ async function loadTsconfigResolver(projectRoot) {
|
|
|
35888
35994
|
return createTsconfigResolver(resolved.baseUrl, paths);
|
|
35889
35995
|
}
|
|
35890
35996
|
function toProjectRelativeDir(projectRoot, absoluteDir) {
|
|
35891
|
-
const relative = normalizePath3(
|
|
35997
|
+
const relative = normalizePath3(path101.relative(projectRoot, absoluteDir)).replace(/^\.\//, "");
|
|
35892
35998
|
return relative === "." ? "" : relative;
|
|
35893
35999
|
}
|
|
35894
36000
|
async function readTsconfigJson(configPath) {
|
|
@@ -35903,11 +36009,11 @@ function resolveTsconfigExtendsPath(configDir, extendsSpecifier) {
|
|
|
35903
36009
|
if (!extendsSpecifier.startsWith(".") && !extendsSpecifier.startsWith("/")) {
|
|
35904
36010
|
return null;
|
|
35905
36011
|
}
|
|
35906
|
-
const resolved =
|
|
36012
|
+
const resolved = path101.resolve(configDir, extendsSpecifier);
|
|
35907
36013
|
return resolved.endsWith(".json") ? resolved : `${resolved}.json`;
|
|
35908
36014
|
}
|
|
35909
36015
|
async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
35910
|
-
const resolvedPath =
|
|
36016
|
+
const resolvedPath = path101.resolve(configPath);
|
|
35911
36017
|
if (depth > TSCONFIG_EXTENDS_MAX_DEPTH || visited.has(resolvedPath)) {
|
|
35912
36018
|
return null;
|
|
35913
36019
|
}
|
|
@@ -35918,15 +36024,15 @@ async function resolveTsconfigOptions(configPath, visited, depth, projectRoot) {
|
|
|
35918
36024
|
}
|
|
35919
36025
|
let inherited = null;
|
|
35920
36026
|
if (typeof parsed.extends === "string") {
|
|
35921
|
-
const basePath = resolveTsconfigExtendsPath(
|
|
36027
|
+
const basePath = resolveTsconfigExtendsPath(path101.dirname(resolvedPath), parsed.extends);
|
|
35922
36028
|
if (basePath) {
|
|
35923
36029
|
inherited = await resolveTsconfigOptions(basePath, visited, depth + 1, projectRoot);
|
|
35924
36030
|
}
|
|
35925
36031
|
}
|
|
35926
|
-
const configDir =
|
|
36032
|
+
const configDir = path101.dirname(resolvedPath);
|
|
35927
36033
|
const declaringDir = toProjectRelativeDir(projectRoot, configDir);
|
|
35928
36034
|
const options = parsed.compilerOptions ?? {};
|
|
35929
|
-
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot,
|
|
36035
|
+
const ownBaseUrl = typeof options.baseUrl === "string" ? toProjectRelativeDir(projectRoot, path101.resolve(configDir, options.baseUrl)) : null;
|
|
35930
36036
|
const baseUrl = ownBaseUrl ?? inherited?.baseUrl ?? null;
|
|
35931
36037
|
const ownPaths = isRecord5(options.paths) ? Object.entries(options.paths).filter((entry) => Array.isArray(entry[1])).map(([pattern, targets]) => ({
|
|
35932
36038
|
pattern,
|
|
@@ -35955,7 +36061,7 @@ function createTsconfigResolver(baseUrl, mappings) {
|
|
|
35955
36061
|
}
|
|
35956
36062
|
}
|
|
35957
36063
|
if (baseUrl !== null) {
|
|
35958
|
-
candidates.push(normalizePath3(
|
|
36064
|
+
candidates.push(normalizePath3(path101.posix.join(baseUrl, normalizedSpecifier)));
|
|
35959
36065
|
}
|
|
35960
36066
|
return [...new Set(candidates.map((candidate) => candidate.replace(/^\.\//, "")))];
|
|
35961
36067
|
}
|
|
@@ -35975,7 +36081,7 @@ function matchPathPattern(pattern, specifier) {
|
|
|
35975
36081
|
}
|
|
35976
36082
|
function applyPathTarget(base, target, wildcard) {
|
|
35977
36083
|
const replaced = target.includes("*") ? target.replace("*", wildcard) : target;
|
|
35978
|
-
return normalizePath3(
|
|
36084
|
+
return normalizePath3(path101.posix.normalize(path101.posix.join(base, replaced)));
|
|
35979
36085
|
}
|
|
35980
36086
|
function stripJsonComments(source) {
|
|
35981
36087
|
let out = "";
|
|
@@ -36032,13 +36138,13 @@ async function writeGraph(projectRoot, graph, dataDir) {
|
|
|
36032
36138
|
const artifactsDir = gdgraphArtifactsDir(projectRoot, dataDir);
|
|
36033
36139
|
await mkdir23(storageDir, { recursive: true });
|
|
36034
36140
|
await mkdir23(artifactsDir, { recursive: true });
|
|
36035
|
-
await writeFile17(
|
|
36141
|
+
await writeFile17(path101.join(storageDir, "nodes.jsonl"), graph.nodes.map((node) => JSON.stringify(node)).join(`
|
|
36036
36142
|
`) + `
|
|
36037
36143
|
`, "utf8");
|
|
36038
|
-
await writeFile17(
|
|
36144
|
+
await writeFile17(path101.join(storageDir, "edges.jsonl"), graph.edges.map((edge) => JSON.stringify(edge)).join(`
|
|
36039
36145
|
`) + `
|
|
36040
36146
|
`, "utf8");
|
|
36041
|
-
await writeFile17(
|
|
36147
|
+
await writeFile17(path101.join(artifactsDir, "module-map.json"), JSON.stringify(buildModuleMap(graph), null, 2) + `
|
|
36042
36148
|
`, "utf8");
|
|
36043
36149
|
}
|
|
36044
36150
|
async function writeSummary(projectRoot, graph, collection, dataDir) {
|
|
@@ -36057,7 +36163,7 @@ async function writeSummary(projectRoot, graph, collection, dataDir) {
|
|
|
36057
36163
|
`);
|
|
36058
36164
|
const skippedList = collection.skippedDirectories.length > 0 ? collection.skippedDirectories.slice(0, 30).map((dir) => `- \`${dir}\``).join(`
|
|
36059
36165
|
`) : "- none";
|
|
36060
|
-
const summaryPath =
|
|
36166
|
+
const summaryPath = path101.join(artifactsDir, "summary.md");
|
|
36061
36167
|
const content = `# gdgraph Summary
|
|
36062
36168
|
|
|
36063
36169
|
## Stats
|
|
@@ -36138,7 +36244,7 @@ function groupBy(items, getKey) {
|
|
|
36138
36244
|
}
|
|
36139
36245
|
function unresolvedType(specifier) {
|
|
36140
36246
|
const stripped = stripImportSuffix(specifier);
|
|
36141
|
-
const extension =
|
|
36247
|
+
const extension = path101.posix.extname(stripped);
|
|
36142
36248
|
if (extension) {
|
|
36143
36249
|
return extension;
|
|
36144
36250
|
}
|
|
@@ -36148,7 +36254,7 @@ function unresolvedType(specifier) {
|
|
|
36148
36254
|
return "package";
|
|
36149
36255
|
}
|
|
36150
36256
|
function normalizePath3(value) {
|
|
36151
|
-
return value.split(
|
|
36257
|
+
return value.split(path101.sep).join("/");
|
|
36152
36258
|
}
|
|
36153
36259
|
|
|
36154
36260
|
// src/gdgraph/service.ts
|
|
@@ -36157,17 +36263,17 @@ init_config3();
|
|
|
36157
36263
|
|
|
36158
36264
|
// src/gdgraph/query.ts
|
|
36159
36265
|
import { readFile as readFile52 } from "fs/promises";
|
|
36160
|
-
import
|
|
36266
|
+
import path102 from "path";
|
|
36161
36267
|
init_data_dir();
|
|
36162
36268
|
init_target();
|
|
36163
36269
|
async function loadGraph(projectRoot, dataDir) {
|
|
36164
36270
|
const storageDir = gdgraphStorageDir(projectRoot, dataDir);
|
|
36165
|
-
const nodes = await readJsonl(
|
|
36166
|
-
const edges = await readJsonl(
|
|
36167
|
-
const symbols = await readJsonl(
|
|
36168
|
-
const calls = await readJsonl(
|
|
36169
|
-
const wikiPages = await readJsonl(
|
|
36170
|
-
const describes = await readJsonl(
|
|
36271
|
+
const nodes = await readJsonl(path102.join(storageDir, "nodes.jsonl"));
|
|
36272
|
+
const edges = await readJsonl(path102.join(storageDir, "edges.jsonl"));
|
|
36273
|
+
const symbols = await readJsonl(path102.join(storageDir, "symbols.jsonl"));
|
|
36274
|
+
const calls = await readJsonl(path102.join(storageDir, "calls.jsonl"));
|
|
36275
|
+
const wikiPages = await readJsonl(path102.join(storageDir, "wiki-pages.jsonl"));
|
|
36276
|
+
const describes = await readJsonl(path102.join(storageDir, "describes.jsonl"));
|
|
36171
36277
|
const graph = { nodes, edges };
|
|
36172
36278
|
if (symbols.length > 0) {
|
|
36173
36279
|
graph.symbols = symbols;
|
|
@@ -36227,7 +36333,7 @@ async function bunfigPreloadRoots(projectRoot) {
|
|
|
36227
36333
|
const roots = new Set;
|
|
36228
36334
|
let text;
|
|
36229
36335
|
try {
|
|
36230
|
-
text = await readFile52(
|
|
36336
|
+
text = await readFile52(path102.join(projectRoot, "bunfig.toml"), "utf8");
|
|
36231
36337
|
} catch {
|
|
36232
36338
|
return roots;
|
|
36233
36339
|
}
|
|
@@ -36241,8 +36347,8 @@ async function bunfigPreloadRoots(projectRoot) {
|
|
|
36241
36347
|
if (raw.length === 0) {
|
|
36242
36348
|
continue;
|
|
36243
36349
|
}
|
|
36244
|
-
const absolute =
|
|
36245
|
-
const relative =
|
|
36350
|
+
const absolute = path102.resolve(projectRoot, raw);
|
|
36351
|
+
const relative = path102.relative(projectRoot, absolute).split(path102.sep).join("/");
|
|
36246
36352
|
roots.add(relative);
|
|
36247
36353
|
}
|
|
36248
36354
|
}
|
|
@@ -36397,12 +36503,12 @@ init_section_tombstone();
|
|
|
36397
36503
|
// src/forgetting/journal.ts
|
|
36398
36504
|
init_fs();
|
|
36399
36505
|
import { appendFile as appendFile5, mkdir as mkdir25, readFile as readFile53, stat as stat15 } from "fs/promises";
|
|
36400
|
-
import
|
|
36506
|
+
import path104 from "path";
|
|
36401
36507
|
function forgettingDataDir(cwd) {
|
|
36402
|
-
return
|
|
36508
|
+
return path104.join(cwd, ".metaproject", "data", "forgetting");
|
|
36403
36509
|
}
|
|
36404
36510
|
function deletionJournalPath(cwd) {
|
|
36405
|
-
return
|
|
36511
|
+
return path104.join(forgettingDataDir(cwd), "journal.jsonl");
|
|
36406
36512
|
}
|
|
36407
36513
|
async function readDeletionJournal(cwd) {
|
|
36408
36514
|
const file = deletionJournalPath(cwd);
|
|
@@ -36817,12 +36923,12 @@ function createGdgraphService() {
|
|
|
36817
36923
|
// src/health/service.ts
|
|
36818
36924
|
init_fs();
|
|
36819
36925
|
init_json();
|
|
36820
|
-
import
|
|
36926
|
+
import path124 from "path";
|
|
36821
36927
|
|
|
36822
36928
|
// src/health/config.ts
|
|
36823
36929
|
init_fs();
|
|
36824
36930
|
init_json();
|
|
36825
|
-
import
|
|
36931
|
+
import path105 from "path";
|
|
36826
36932
|
var DEFAULT_HEALTH_CONFIG = {
|
|
36827
36933
|
schemaVersion: 2,
|
|
36828
36934
|
ignore: {
|
|
@@ -36879,7 +36985,7 @@ var DEFAULT_HEALTH_CONFIG = {
|
|
|
36879
36985
|
}
|
|
36880
36986
|
};
|
|
36881
36987
|
function configPath2(cwd) {
|
|
36882
|
-
return
|
|
36988
|
+
return path105.join(cwd, ".metaproject", "health.config.json");
|
|
36883
36989
|
}
|
|
36884
36990
|
var STRICTEST_GATE = {
|
|
36885
36991
|
failOnPriorities: ["P0", "P1", "P2", "P3"],
|
|
@@ -36930,7 +37036,7 @@ async function loadHealthConfig(cwd) {
|
|
|
36930
37036
|
// src/health/run.ts
|
|
36931
37037
|
init_fs();
|
|
36932
37038
|
import { mkdir as mkdir29, writeFile as writeFile22 } from "fs/promises";
|
|
36933
|
-
import
|
|
37039
|
+
import path123 from "path";
|
|
36934
37040
|
|
|
36935
37041
|
// src/metrics/provenance.ts
|
|
36936
37042
|
async function collectGitProvenance(cwd) {
|
|
@@ -37043,12 +37149,12 @@ function computeGate2(input) {
|
|
|
37043
37149
|
}
|
|
37044
37150
|
|
|
37045
37151
|
// src/health/scopes.ts
|
|
37046
|
-
import
|
|
37152
|
+
import path108 from "path";
|
|
37047
37153
|
|
|
37048
37154
|
// src/health/util.ts
|
|
37049
37155
|
init_fs();
|
|
37050
37156
|
import { mkdir as mkdir26, readFile as readFile54, readdir as readdir19, writeFile as writeFile19 } from "fs/promises";
|
|
37051
|
-
import
|
|
37157
|
+
import path106 from "path";
|
|
37052
37158
|
var IGNORED_DIRS = new Set([
|
|
37053
37159
|
"node_modules",
|
|
37054
37160
|
".git",
|
|
@@ -37092,14 +37198,14 @@ async function toolVersion(command, cwd) {
|
|
|
37092
37198
|
}
|
|
37093
37199
|
}
|
|
37094
37200
|
function dataRoot3(cwd) {
|
|
37095
|
-
return
|
|
37201
|
+
return path106.join(cwd, ".metaproject", "data", "health");
|
|
37096
37202
|
}
|
|
37097
37203
|
async function writeRaw(cwd, source, content, stamp) {
|
|
37098
|
-
const dir =
|
|
37204
|
+
const dir = path106.join(dataRoot3(cwd), "raw", source);
|
|
37099
37205
|
await mkdir26(dir, { recursive: true });
|
|
37100
|
-
const file =
|
|
37206
|
+
const file = path106.join(dir, `${stamp}.log`);
|
|
37101
37207
|
await writeFile19(file, content, "utf8");
|
|
37102
|
-
return
|
|
37208
|
+
return path106.relative(cwd, file);
|
|
37103
37209
|
}
|
|
37104
37210
|
async function listSourceFiles(cwd, ignorePaths = []) {
|
|
37105
37211
|
const { files } = await listSourceFilesWithReasons(cwd, ignorePaths);
|
|
@@ -37119,7 +37225,7 @@ async function walk2(root, dir, out, incompleteReasons) {
|
|
|
37119
37225
|
try {
|
|
37120
37226
|
entries = await readdir19(dir, { withFileTypes: true });
|
|
37121
37227
|
} catch (error) {
|
|
37122
|
-
incompleteReasons.push(`${
|
|
37228
|
+
incompleteReasons.push(`${path106.relative(root, dir) || "."}: ${error instanceof Error ? error.message : String(error)}`);
|
|
37123
37229
|
return;
|
|
37124
37230
|
}
|
|
37125
37231
|
for (const entry of entries) {
|
|
@@ -37127,14 +37233,14 @@ async function walk2(root, dir, out, incompleteReasons) {
|
|
|
37127
37233
|
if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {}
|
|
37128
37234
|
continue;
|
|
37129
37235
|
}
|
|
37130
|
-
const abs =
|
|
37236
|
+
const abs = path106.join(dir, entry.name);
|
|
37131
37237
|
if (entry.isDirectory()) {
|
|
37132
37238
|
if (IGNORED_DIRS.has(entry.name)) {
|
|
37133
37239
|
continue;
|
|
37134
37240
|
}
|
|
37135
37241
|
await walk2(root, abs, out, incompleteReasons);
|
|
37136
|
-
} else if (entry.isFile() && SOURCE_EXT.has(
|
|
37137
|
-
out.push(
|
|
37242
|
+
} else if (entry.isFile() && SOURCE_EXT.has(path106.extname(entry.name))) {
|
|
37243
|
+
out.push(path106.relative(root, abs));
|
|
37138
37244
|
}
|
|
37139
37245
|
}
|
|
37140
37246
|
}
|
|
@@ -37149,7 +37255,7 @@ function moduleOfFile(file) {
|
|
|
37149
37255
|
return null;
|
|
37150
37256
|
}
|
|
37151
37257
|
function isSourceFile(file) {
|
|
37152
|
-
return SOURCE_EXT.has(
|
|
37258
|
+
return SOURCE_EXT.has(path106.extname(file));
|
|
37153
37259
|
}
|
|
37154
37260
|
function matchesAnyPattern(file, patterns) {
|
|
37155
37261
|
const normalized = file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -37251,7 +37357,7 @@ function rankHotspots(files, churn, sourceAnalysis) {
|
|
|
37251
37357
|
// src/health/source-analysis.ts
|
|
37252
37358
|
init_fs();
|
|
37253
37359
|
import { readFile as readFile55 } from "fs/promises";
|
|
37254
|
-
import
|
|
37360
|
+
import path107 from "path";
|
|
37255
37361
|
|
|
37256
37362
|
// src/health/metrics/complexity.ts
|
|
37257
37363
|
var CONTROL_KEYWORDS = new Set([
|
|
@@ -37485,7 +37591,7 @@ async function analyzeSourceFiles(cwd, sourceFiles) {
|
|
|
37485
37591
|
return new Map(entries.filter((entry) => entry !== null));
|
|
37486
37592
|
}
|
|
37487
37593
|
async function analyzeSourceFile(cwd, file) {
|
|
37488
|
-
const abs =
|
|
37594
|
+
const abs = path107.join(cwd, file);
|
|
37489
37595
|
if (!await pathExists(abs)) {
|
|
37490
37596
|
return null;
|
|
37491
37597
|
}
|
|
@@ -37562,14 +37668,14 @@ async function computeMetrics(input) {
|
|
|
37562
37668
|
}
|
|
37563
37669
|
const components = new Map;
|
|
37564
37670
|
for (const file of sourceFiles) {
|
|
37565
|
-
const dir =
|
|
37671
|
+
const dir = path108.posix.dirname(file);
|
|
37566
37672
|
if (dir === "." || dir === moduleOfFile(file)) {
|
|
37567
37673
|
continue;
|
|
37568
37674
|
}
|
|
37569
37675
|
components.set(dir, [...components.get(dir) ?? [], file]);
|
|
37570
37676
|
}
|
|
37571
37677
|
for (const [name, files] of [...components.entries()].sort()) {
|
|
37572
|
-
const compFindings = findings.filter((f) => f.file &&
|
|
37678
|
+
const compFindings = findings.filter((f) => f.file && path108.posix.dirname(f.file) === name);
|
|
37573
37679
|
scopes.push(build("component", `component:${name}`, name, files, compFindings, averageCoverage(files, coverage)));
|
|
37574
37680
|
}
|
|
37575
37681
|
const findingFiles = findings.map((f) => f.file).filter((f) => Boolean(f));
|
|
@@ -37614,7 +37720,7 @@ function tally2(values) {
|
|
|
37614
37720
|
// src/health/baseline.ts
|
|
37615
37721
|
init_fs();
|
|
37616
37722
|
import { mkdir as mkdir27, readFile as readFile56, writeFile as writeFile20 } from "fs/promises";
|
|
37617
|
-
import
|
|
37723
|
+
import path109 from "path";
|
|
37618
37724
|
var LEGACY_BASELINE_SOURCES = Object.freeze([
|
|
37619
37725
|
"complexity",
|
|
37620
37726
|
"coverage",
|
|
@@ -37639,7 +37745,7 @@ function measuredSources(sources) {
|
|
|
37639
37745
|
return sources.filter((s) => s !== null && typeof s === "object").filter((s) => !didNotProduceResult(s)).map((s) => s.source).sort();
|
|
37640
37746
|
}
|
|
37641
37747
|
function baselinePath(cwd) {
|
|
37642
|
-
return
|
|
37748
|
+
return path109.join(cwd, ".metaproject", "health", "baselines", "scores.json");
|
|
37643
37749
|
}
|
|
37644
37750
|
async function readBaselineFile(cwd) {
|
|
37645
37751
|
const file = baselinePath(cwd);
|
|
@@ -37660,7 +37766,7 @@ async function loadBaselineSources(cwd) {
|
|
|
37660
37766
|
}
|
|
37661
37767
|
async function writeBaselineFile(cwd, data) {
|
|
37662
37768
|
const file = baselinePath(cwd);
|
|
37663
|
-
await mkdir27(
|
|
37769
|
+
await mkdir27(path109.dirname(file), { recursive: true });
|
|
37664
37770
|
await writeFile20(file, `${JSON.stringify(data, null, 2)}
|
|
37665
37771
|
`, "utf8");
|
|
37666
37772
|
}
|
|
@@ -37742,10 +37848,10 @@ async function getChurn(cwd, windowDays) {
|
|
|
37742
37848
|
|
|
37743
37849
|
// src/health/metrics/wiki-freshness.ts
|
|
37744
37850
|
import { readFile as readFile57, stat as stat16 } from "fs/promises";
|
|
37745
|
-
import
|
|
37851
|
+
import path110 from "path";
|
|
37746
37852
|
var DEFAULT_STALE_EVIDENCE_DAYS = 7;
|
|
37747
37853
|
function freshnessReportPath(cwd) {
|
|
37748
|
-
return
|
|
37854
|
+
return path110.join(cwd, ".metaproject", "data", "wiki", "freshness", "latest.json");
|
|
37749
37855
|
}
|
|
37750
37856
|
async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
37751
37857
|
const file = freshnessReportPath(cwd);
|
|
@@ -37766,7 +37872,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
37766
37872
|
} catch {
|
|
37767
37873
|
return {
|
|
37768
37874
|
status: "unreadable-report",
|
|
37769
|
-
reason: `${
|
|
37875
|
+
reason: `${path110.relative(cwd, file)} is not valid JSON; re-run \`keryx wiki freshness\`.`
|
|
37770
37876
|
};
|
|
37771
37877
|
}
|
|
37772
37878
|
const report = parsed;
|
|
@@ -37776,7 +37882,7 @@ async function readWikiFreshnessMetric(cwd, options = {}) {
|
|
|
37776
37882
|
if (pagesTotal === null || pagesFresh === null) {
|
|
37777
37883
|
return {
|
|
37778
37884
|
status: "unreadable-report",
|
|
37779
|
-
reason: `${
|
|
37885
|
+
reason: `${path110.relative(cwd, file)} is missing its totals; re-run \`keryx wiki freshness\`.`
|
|
37780
37886
|
};
|
|
37781
37887
|
}
|
|
37782
37888
|
const pagesUndecidable = numberOr3(totals.pagesUndecidable) ?? 0;
|
|
@@ -37830,12 +37936,12 @@ function renderWikiFreshnessLine(metric) {
|
|
|
37830
37936
|
}
|
|
37831
37937
|
|
|
37832
37938
|
// src/health/sources/helpers.ts
|
|
37833
|
-
import
|
|
37939
|
+
import path111 from "path";
|
|
37834
37940
|
import { existsSync as existsSync12 } from "fs";
|
|
37835
37941
|
class NoImportError extends Error {
|
|
37836
37942
|
}
|
|
37837
37943
|
function resolveBin(cwd, name) {
|
|
37838
|
-
const local =
|
|
37944
|
+
const local = path111.join(cwd, "node_modules", ".bin", name);
|
|
37839
37945
|
if (existsSync12(local)) {
|
|
37840
37946
|
return local;
|
|
37841
37947
|
}
|
|
@@ -37916,13 +38022,13 @@ async function getComplexityFindings(cwd, sourceFiles, config, sourceAnalysis) {
|
|
|
37916
38022
|
|
|
37917
38023
|
// src/health/metrics/coverage.ts
|
|
37918
38024
|
import { existsSync as existsSync13 } from "fs";
|
|
37919
|
-
import
|
|
38025
|
+
import path112 from "path";
|
|
37920
38026
|
var SUMMARY_PATHS = [
|
|
37921
38027
|
"coverage/coverage-summary.json",
|
|
37922
38028
|
"coverage/coverage-final.json"
|
|
37923
38029
|
];
|
|
37924
38030
|
async function getCoverage(cwd) {
|
|
37925
|
-
const summary = SUMMARY_PATHS.map((rel) =>
|
|
38031
|
+
const summary = SUMMARY_PATHS.map((rel) => path112.join(cwd, rel)).find((abs) => existsSync13(abs));
|
|
37926
38032
|
if (!summary) {
|
|
37927
38033
|
return { status: "missing", total: null, byFile: new Map };
|
|
37928
38034
|
}
|
|
@@ -37943,7 +38049,7 @@ async function getCoverage(cwd) {
|
|
|
37943
38049
|
total = pct;
|
|
37944
38050
|
continue;
|
|
37945
38051
|
}
|
|
37946
|
-
const relative =
|
|
38052
|
+
const relative = path112.isAbsolute(key) ? path112.relative(cwd, key) : key;
|
|
37947
38053
|
byFile.set(relative.replace(/\\/g, "/"), pct);
|
|
37948
38054
|
}
|
|
37949
38055
|
return { status: "available", total, byFile };
|
|
@@ -38076,10 +38182,10 @@ function renderWikiFreshness(metric) {
|
|
|
38076
38182
|
// src/health/skills.ts
|
|
38077
38183
|
init_fs();
|
|
38078
38184
|
import { readFile as readFile58 } from "fs/promises";
|
|
38079
|
-
import
|
|
38185
|
+
import path113 from "path";
|
|
38080
38186
|
var EMPTY = { skills: [], skillForFile: () => null };
|
|
38081
38187
|
async function loadSkillOwnership(cwd) {
|
|
38082
|
-
const manifestPath =
|
|
38188
|
+
const manifestPath = path113.join(cwd, ".metaproject", "metaproject.json");
|
|
38083
38189
|
if (!await pathExists(manifestPath)) {
|
|
38084
38190
|
return EMPTY;
|
|
38085
38191
|
}
|
|
@@ -38117,7 +38223,7 @@ function normalize(value) {
|
|
|
38117
38223
|
|
|
38118
38224
|
// src/health/sources/eslint.ts
|
|
38119
38225
|
import { existsSync as existsSync14 } from "fs";
|
|
38120
|
-
import
|
|
38226
|
+
import path114 from "path";
|
|
38121
38227
|
var CONFIG_FILES = [
|
|
38122
38228
|
".eslintrc",
|
|
38123
38229
|
".eslintrc.js",
|
|
@@ -38131,7 +38237,7 @@ var CONFIG_FILES = [
|
|
|
38131
38237
|
"eslint.config.ts"
|
|
38132
38238
|
];
|
|
38133
38239
|
function hasConfig(cwd) {
|
|
38134
|
-
return CONFIG_FILES.some((file) => existsSync14(
|
|
38240
|
+
return CONFIG_FILES.some((file) => existsSync14(path114.join(cwd, file)));
|
|
38135
38241
|
}
|
|
38136
38242
|
var eslintAdapter = {
|
|
38137
38243
|
id: "eslint",
|
|
@@ -38157,7 +38263,7 @@ var eslintAdapter = {
|
|
|
38157
38263
|
};
|
|
38158
38264
|
},
|
|
38159
38265
|
async import(ctx) {
|
|
38160
|
-
const report =
|
|
38266
|
+
const report = path114.join(ctx.cwd, "eslint-report.json");
|
|
38161
38267
|
if (!existsSync14(report)) {
|
|
38162
38268
|
throw new NoImportError("no eslint-report.json");
|
|
38163
38269
|
}
|
|
@@ -38184,7 +38290,7 @@ var eslintAdapter = {
|
|
|
38184
38290
|
}
|
|
38185
38291
|
const findings = [];
|
|
38186
38292
|
for (const file of data) {
|
|
38187
|
-
const relative =
|
|
38293
|
+
const relative = path114.isAbsolute(file.filePath) ? path114.relative(ctx.cwd, file.filePath) : file.filePath;
|
|
38188
38294
|
for (const message of file.messages ?? []) {
|
|
38189
38295
|
const isError = message.severity === 2;
|
|
38190
38296
|
findings.push(makeFinding({
|
|
@@ -38216,19 +38322,19 @@ var eslintAdapter = {
|
|
|
38216
38322
|
|
|
38217
38323
|
// src/health/sources/oxlint.ts
|
|
38218
38324
|
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
38219
|
-
import
|
|
38325
|
+
import path115 from "path";
|
|
38220
38326
|
var CONFIG_FILES2 = [".oxlintrc.json", ".oxlintrc.jsonc"];
|
|
38221
38327
|
var OXLINT_FORMAT = "json";
|
|
38222
38328
|
var OXLINT_PARSE_ERROR = "oxlint JSON format was not recognized";
|
|
38223
38329
|
var OXLINT_NO_FILES = "oxlint found no files to lint (check its paths and ignore patterns)";
|
|
38224
38330
|
var NO_FILES_PREAMBLE = "No files found to lint";
|
|
38225
38331
|
function hasConfigFile(cwd) {
|
|
38226
|
-
if (CONFIG_FILES2.some((file) => existsSync15(
|
|
38332
|
+
if (CONFIG_FILES2.some((file) => existsSync15(path115.join(cwd, file))))
|
|
38227
38333
|
return true;
|
|
38228
|
-
return [".oxlintrc.js", ".oxlintrc.cjs", ".oxlintrc.mjs", ".oxlintrc.ts"].some((file) => existsSync15(
|
|
38334
|
+
return [".oxlintrc.js", ".oxlintrc.cjs", ".oxlintrc.mjs", ".oxlintrc.ts"].some((file) => existsSync15(path115.join(cwd, file)));
|
|
38229
38335
|
}
|
|
38230
38336
|
function packageNamesOxlint(cwd) {
|
|
38231
|
-
const file =
|
|
38337
|
+
const file = path115.join(cwd, "package.json");
|
|
38232
38338
|
if (!existsSync15(file))
|
|
38233
38339
|
return false;
|
|
38234
38340
|
try {
|
|
@@ -38286,7 +38392,7 @@ var oxlintAdapter = {
|
|
|
38286
38392
|
return [];
|
|
38287
38393
|
return data.map((diagnostic) => {
|
|
38288
38394
|
const filePath = diagnostic.filename ?? "";
|
|
38289
|
-
const relative = filePath &&
|
|
38395
|
+
const relative = filePath && path115.isAbsolute(filePath) ? path115.relative(ctx.cwd, filePath) : filePath;
|
|
38290
38396
|
const isError = diagnostic.severity === "error";
|
|
38291
38397
|
return makeFinding({
|
|
38292
38398
|
source: "oxlint",
|
|
@@ -38312,9 +38418,9 @@ var oxlintAdapter = {
|
|
|
38312
38418
|
|
|
38313
38419
|
// src/health/sources/typescript.ts
|
|
38314
38420
|
import { existsSync as existsSync16 } from "fs";
|
|
38315
|
-
import
|
|
38421
|
+
import path116 from "path";
|
|
38316
38422
|
function hasTsconfig(cwd) {
|
|
38317
|
-
return existsSync16(
|
|
38423
|
+
return existsSync16(path116.join(cwd, "tsconfig.json"));
|
|
38318
38424
|
}
|
|
38319
38425
|
var typescriptAdapter = {
|
|
38320
38426
|
id: "typescript",
|
|
@@ -38375,12 +38481,12 @@ init_fs();
|
|
|
38375
38481
|
init_contained_write();
|
|
38376
38482
|
import { existsSync as existsSync17 } from "fs";
|
|
38377
38483
|
import { readFile as readFile61, readdir as readdir20 } from "fs/promises";
|
|
38378
|
-
import
|
|
38484
|
+
import path120 from "path";
|
|
38379
38485
|
// src/metrics/lifecycle.ts
|
|
38380
38486
|
init_fs();
|
|
38381
38487
|
init_guard();
|
|
38382
38488
|
import { readFile as readFile59 } from "fs/promises";
|
|
38383
|
-
import
|
|
38489
|
+
import path117 from "path";
|
|
38384
38490
|
|
|
38385
38491
|
// src/metrics/record.ts
|
|
38386
38492
|
var RELIABILITIES = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -38395,7 +38501,7 @@ var RETRY_TYPES = new Set([
|
|
|
38395
38501
|
|
|
38396
38502
|
// src/metrics/lifecycle.ts
|
|
38397
38503
|
async function readArtifactPointer(root, expected) {
|
|
38398
|
-
const pointerFile =
|
|
38504
|
+
const pointerFile = path117.join(root, "latest.json");
|
|
38399
38505
|
if (!await pathExists(pointerFile))
|
|
38400
38506
|
return { status: "missing", pointer: null };
|
|
38401
38507
|
let pointer;
|
|
@@ -38404,7 +38510,7 @@ async function readArtifactPointer(root, expected) {
|
|
|
38404
38510
|
} catch {
|
|
38405
38511
|
return { status: "stale", pointer: null, reason: "latest pointer is invalid JSON" };
|
|
38406
38512
|
}
|
|
38407
|
-
const recordFile = pointer.record ?
|
|
38513
|
+
const recordFile = pointer.record ? path117.join(root, pointer.record) : null;
|
|
38408
38514
|
if (!recordFile || !isPathInside(root, recordFile) || !await pathExists(recordFile)) {
|
|
38409
38515
|
return { status: "stale", pointer, reason: "pointer record is missing" };
|
|
38410
38516
|
}
|
|
@@ -38420,9 +38526,9 @@ init_guard();
|
|
|
38420
38526
|
// src/testing/capability.ts
|
|
38421
38527
|
init_fs();
|
|
38422
38528
|
init_json();
|
|
38423
|
-
import
|
|
38529
|
+
import path118 from "path";
|
|
38424
38530
|
async function isTestingCapabilityEnabled(cwd, capability) {
|
|
38425
|
-
const manifestPath =
|
|
38531
|
+
const manifestPath = path118.join(cwd, ".metaproject", "metaproject.json");
|
|
38426
38532
|
if (!await pathExists(manifestPath)) {
|
|
38427
38533
|
return false;
|
|
38428
38534
|
}
|
|
@@ -38448,10 +38554,10 @@ async function isTestingCapabilityEnabled(cwd, capability) {
|
|
|
38448
38554
|
init_fs();
|
|
38449
38555
|
init_guard();
|
|
38450
38556
|
import { readFile as readFile60, mkdir as mkdir28, writeFile as writeFile21 } from "fs/promises";
|
|
38451
|
-
import
|
|
38557
|
+
import path119 from "path";
|
|
38452
38558
|
var COVERAGE_MAP_SCHEMA_VERSION = 1;
|
|
38453
38559
|
function coverageMapPath(cwd, cfg) {
|
|
38454
|
-
return
|
|
38560
|
+
return path119.isAbsolute(cfg.coverageMap.artifact) ? cfg.coverageMap.artifact : path119.join(cwd, cfg.coverageMap.artifact);
|
|
38455
38561
|
}
|
|
38456
38562
|
async function loadCoverageMap(cwd, cfg) {
|
|
38457
38563
|
const file = coverageMapPath(cwd, cfg);
|
|
@@ -38586,7 +38692,7 @@ var DEFAULT_TESTING_CONFIG = {
|
|
|
38586
38692
|
}
|
|
38587
38693
|
};
|
|
38588
38694
|
function testingDataRoot(cwd) {
|
|
38589
|
-
return
|
|
38695
|
+
return path120.join(cwd, ".metaproject", "data", "testing");
|
|
38590
38696
|
}
|
|
38591
38697
|
async function computeTestingContext(cwd) {
|
|
38592
38698
|
const { files, incompleteReasons } = await listProjectFiles(cwd);
|
|
@@ -38761,7 +38867,7 @@ async function runTesting(input) {
|
|
|
38761
38867
|
return runResult;
|
|
38762
38868
|
}
|
|
38763
38869
|
async function loadTestingContext(cwd) {
|
|
38764
|
-
const file =
|
|
38870
|
+
const file = path120.join(testingDataRoot(cwd), "context.json");
|
|
38765
38871
|
if (!await pathExists(file)) {
|
|
38766
38872
|
return null;
|
|
38767
38873
|
}
|
|
@@ -38772,18 +38878,18 @@ async function loadTestingContext(cwd) {
|
|
|
38772
38878
|
}
|
|
38773
38879
|
}
|
|
38774
38880
|
async function loadTestingReport(cwd) {
|
|
38775
|
-
const file =
|
|
38881
|
+
const file = path120.join(testingDataRoot(cwd), "artifacts", "latest.json");
|
|
38776
38882
|
if (!await pathExists(file)) {
|
|
38777
38883
|
return null;
|
|
38778
38884
|
}
|
|
38779
38885
|
try {
|
|
38780
38886
|
const latest = JSON.parse(await readFile61(file, "utf8"));
|
|
38781
38887
|
if (typeof latest.record === "string") {
|
|
38782
|
-
const pointer = await readArtifactPointer(
|
|
38888
|
+
const pointer = await readArtifactPointer(path120.join(testingDataRoot(cwd), "artifacts"), await collectGitProvenance(cwd));
|
|
38783
38889
|
if (pointer.status !== "fresh")
|
|
38784
38890
|
return null;
|
|
38785
|
-
const recordFile =
|
|
38786
|
-
if (!isPathInside(
|
|
38891
|
+
const recordFile = path120.join(testingDataRoot(cwd), "artifacts", latest.record);
|
|
38892
|
+
if (!isPathInside(path120.join(testingDataRoot(cwd), "artifacts"), recordFile))
|
|
38787
38893
|
return null;
|
|
38788
38894
|
if (!await pathExists(recordFile))
|
|
38789
38895
|
return null;
|
|
@@ -38830,28 +38936,28 @@ async function ensureContext(cwd) {
|
|
|
38830
38936
|
}
|
|
38831
38937
|
async function writeContext(cwd, context) {
|
|
38832
38938
|
const root = testingDataRoot(cwd);
|
|
38833
|
-
const rel = (p) =>
|
|
38939
|
+
const rel = (p) => path120.relative(cwd, p);
|
|
38834
38940
|
await mkdirContained(cwd, rel(root));
|
|
38835
|
-
await writeContained(cwd, rel(
|
|
38941
|
+
await writeContained(cwd, rel(path120.join(root, "context.json")), `${JSON.stringify(context, null, 2)}
|
|
38836
38942
|
`);
|
|
38837
|
-
await writeContained(cwd, rel(
|
|
38838
|
-
await writeContained(cwd, rel(
|
|
38943
|
+
await writeContained(cwd, rel(path120.join(root, "context.md")), renderContextMarkdown(context));
|
|
38944
|
+
await writeContained(cwd, rel(path120.join(root, "recommendations.md")), renderRecommendationsMarkdown(context));
|
|
38839
38945
|
}
|
|
38840
38946
|
async function writeReport(cwd, report) {
|
|
38841
38947
|
const root = testingDataRoot(cwd);
|
|
38842
|
-
const artifacts =
|
|
38843
|
-
const history =
|
|
38844
|
-
const rel = (p) =>
|
|
38948
|
+
const artifacts = path120.join(root, "artifacts");
|
|
38949
|
+
const history = path120.join(root, "history");
|
|
38950
|
+
const rel = (p) => path120.relative(cwd, p);
|
|
38845
38951
|
await mkdirContained(cwd, rel(artifacts));
|
|
38846
38952
|
await mkdirContained(cwd, rel(history));
|
|
38847
38953
|
const stamp = report.generatedAt.replace(/[:.]/g, "-");
|
|
38848
|
-
const json =
|
|
38849
|
-
const markdown =
|
|
38954
|
+
const json = path120.join(artifacts, "latest.json");
|
|
38955
|
+
const markdown = path120.join(artifacts, "latest.md");
|
|
38850
38956
|
const serialized = `${JSON.stringify(report, null, 2)}
|
|
38851
38957
|
`;
|
|
38852
38958
|
if (report.runId) {
|
|
38853
|
-
const runJson =
|
|
38854
|
-
const runMarkdown =
|
|
38959
|
+
const runJson = path120.join(artifacts, "runs", `${report.runId}.json`);
|
|
38960
|
+
const runMarkdown = path120.join(artifacts, "runs", `${report.runId}.md`);
|
|
38855
38961
|
if (await pathExists(runJson))
|
|
38856
38962
|
throw new Error(`immutable testing run already exists: ${report.runId}`);
|
|
38857
38963
|
await writeContained(cwd, rel(runJson), serialized);
|
|
@@ -38862,26 +38968,26 @@ async function writeReport(cwd, report) {
|
|
|
38862
38968
|
branch: report.provenance?.branch ?? null,
|
|
38863
38969
|
worktree: report.provenance?.worktree ?? null,
|
|
38864
38970
|
generated_at: report.generatedAt,
|
|
38865
|
-
record:
|
|
38971
|
+
record: path120.posix.join("runs", `${report.runId}.json`)
|
|
38866
38972
|
}, null, 2)}
|
|
38867
38973
|
`);
|
|
38868
38974
|
} else {
|
|
38869
38975
|
await writeContained(cwd, rel(json), serialized);
|
|
38870
38976
|
}
|
|
38871
38977
|
await writeContained(cwd, rel(markdown), renderReportMarkdown3(report));
|
|
38872
|
-
await writeContained(cwd, rel(
|
|
38978
|
+
await writeContained(cwd, rel(path120.join(history, `${stamp}.json`)), serialized);
|
|
38873
38979
|
return {
|
|
38874
|
-
markdownPath:
|
|
38875
|
-
jsonPath:
|
|
38980
|
+
markdownPath: path120.relative(cwd, markdown),
|
|
38981
|
+
jsonPath: path120.relative(cwd, json)
|
|
38876
38982
|
};
|
|
38877
38983
|
}
|
|
38878
38984
|
async function writeRawLog(cwd, raw) {
|
|
38879
|
-
const logs =
|
|
38880
|
-
const rel = (p) =>
|
|
38985
|
+
const logs = path120.join(testingDataRoot(cwd), "logs");
|
|
38986
|
+
const rel = (p) => path120.relative(cwd, p);
|
|
38881
38987
|
await mkdirContained(cwd, rel(logs));
|
|
38882
|
-
const latest =
|
|
38988
|
+
const latest = path120.join(logs, "latest.raw.log");
|
|
38883
38989
|
await writeContained(cwd, rel(latest), raw);
|
|
38884
|
-
return
|
|
38990
|
+
return path120.relative(cwd, latest);
|
|
38885
38991
|
}
|
|
38886
38992
|
async function listProjectFiles(cwd) {
|
|
38887
38993
|
const out = [];
|
|
@@ -38894,11 +39000,11 @@ async function walk3(root, dir, out, incompleteReasons) {
|
|
|
38894
39000
|
try {
|
|
38895
39001
|
entries = await readdir20(dir, { withFileTypes: true });
|
|
38896
39002
|
} catch (error) {
|
|
38897
|
-
incompleteReasons.push(`${normalizePath6(
|
|
39003
|
+
incompleteReasons.push(`${normalizePath6(path120.relative(root, dir)) || "."}: ${error instanceof Error ? error.message : String(error)}`);
|
|
38898
39004
|
return;
|
|
38899
39005
|
}
|
|
38900
39006
|
for (const entry of entries) {
|
|
38901
|
-
const abs =
|
|
39007
|
+
const abs = path120.join(dir, entry.name);
|
|
38902
39008
|
if (entry.isDirectory()) {
|
|
38903
39009
|
if (IGNORED_DIRS2.has(entry.name) || entry.name.startsWith(".tmp-")) {
|
|
38904
39010
|
continue;
|
|
@@ -38907,12 +39013,12 @@ async function walk3(root, dir, out, incompleteReasons) {
|
|
|
38907
39013
|
continue;
|
|
38908
39014
|
}
|
|
38909
39015
|
if (entry.isFile()) {
|
|
38910
|
-
out.push(normalizePath6(
|
|
39016
|
+
out.push(normalizePath6(path120.relative(root, abs)));
|
|
38911
39017
|
}
|
|
38912
39018
|
}
|
|
38913
39019
|
}
|
|
38914
39020
|
async function readPackageJson(cwd) {
|
|
38915
|
-
const file =
|
|
39021
|
+
const file = path120.join(cwd, "package.json");
|
|
38916
39022
|
if (!await pathExists(file)) {
|
|
38917
39023
|
return null;
|
|
38918
39024
|
}
|
|
@@ -38923,7 +39029,7 @@ async function readPackageJson(cwd) {
|
|
|
38923
39029
|
}
|
|
38924
39030
|
}
|
|
38925
39031
|
async function loadTestingConfig(cwd) {
|
|
38926
|
-
const file =
|
|
39032
|
+
const file = path120.join(cwd, ".metaproject", "testing.config.json");
|
|
38927
39033
|
if (!await pathExists(file)) {
|
|
38928
39034
|
return DEFAULT_TESTING_CONFIG;
|
|
38929
39035
|
}
|
|
@@ -38988,7 +39094,7 @@ function detectFrameworks(input) {
|
|
|
38988
39094
|
async function extractConventions(cwd, files) {
|
|
38989
39095
|
const conventions = new Set;
|
|
38990
39096
|
for (const file of files.slice(0, 40)) {
|
|
38991
|
-
const abs =
|
|
39097
|
+
const abs = path120.join(cwd, file);
|
|
38992
39098
|
if (!await pathExists(abs)) {
|
|
38993
39099
|
continue;
|
|
38994
39100
|
}
|
|
@@ -39118,7 +39224,7 @@ async function findTestsByImportedTargets(cwd, targets, testFiles) {
|
|
|
39118
39224
|
const targetKeys = new Set(targets.map((target) => modulePathKey(normalizePath6(target))));
|
|
39119
39225
|
const selected = new Set;
|
|
39120
39226
|
for (const testFile of testFiles) {
|
|
39121
|
-
const absoluteTest =
|
|
39227
|
+
const absoluteTest = path120.join(cwd, testFile);
|
|
39122
39228
|
if (!await pathExists(absoluteTest)) {
|
|
39123
39229
|
continue;
|
|
39124
39230
|
}
|
|
@@ -39152,10 +39258,10 @@ function resolveImportPath(cwd, testFile, importPath) {
|
|
|
39152
39258
|
if (!importPath.startsWith(".")) {
|
|
39153
39259
|
return null;
|
|
39154
39260
|
}
|
|
39155
|
-
const importerDir =
|
|
39156
|
-
const absoluteResolved =
|
|
39157
|
-
const relative =
|
|
39158
|
-
if (relative.startsWith(`..${
|
|
39261
|
+
const importerDir = path120.dirname(testFile);
|
|
39262
|
+
const absoluteResolved = path120.resolve(path120.join(cwd, importerDir), importPath);
|
|
39263
|
+
const relative = path120.relative(cwd, absoluteResolved);
|
|
39264
|
+
if (relative.startsWith(`..${path120.sep}`) || relative === "..") {
|
|
39159
39265
|
return null;
|
|
39160
39266
|
}
|
|
39161
39267
|
const relativeNormalized = normalizePath6(relative);
|
|
@@ -39207,13 +39313,13 @@ function resolveTestCommand(cwd, context, input) {
|
|
|
39207
39313
|
}
|
|
39208
39314
|
var BUN_LOCKFILES = ["bun.lock", "bun.lockb"];
|
|
39209
39315
|
function detectPackageManager(cwd) {
|
|
39210
|
-
if (resolveBunBin() && BUN_LOCKFILES.some((name) => pathExistsSync(
|
|
39316
|
+
if (resolveBunBin() && BUN_LOCKFILES.some((name) => pathExistsSync(path120.join(cwd, name)))) {
|
|
39211
39317
|
return "bun";
|
|
39212
39318
|
}
|
|
39213
|
-
if (Bun.which("pnpm") && pathExistsSync(
|
|
39319
|
+
if (Bun.which("pnpm") && pathExistsSync(path120.join(cwd, "pnpm-lock.yaml"))) {
|
|
39214
39320
|
return "pnpm";
|
|
39215
39321
|
}
|
|
39216
|
-
if (Bun.which("yarn") && pathExistsSync(
|
|
39322
|
+
if (Bun.which("yarn") && pathExistsSync(path120.join(cwd, "yarn.lock"))) {
|
|
39217
39323
|
return "yarn";
|
|
39218
39324
|
}
|
|
39219
39325
|
return "npm";
|
|
@@ -39553,7 +39659,7 @@ async function compatibleReportForHealth(ctx) {
|
|
|
39553
39659
|
|
|
39554
39660
|
// src/health/sources/dependency-audit.ts
|
|
39555
39661
|
import { existsSync as existsSync18 } from "fs";
|
|
39556
|
-
import
|
|
39662
|
+
import path121 from "path";
|
|
39557
39663
|
function auditSeverityToPriority(severity) {
|
|
39558
39664
|
if (severity === "critical" || severity === "high")
|
|
39559
39665
|
return "P0";
|
|
@@ -39672,7 +39778,7 @@ var LOCKFILE_COMMANDS = [
|
|
|
39672
39778
|
];
|
|
39673
39779
|
function auditCommand(cwd) {
|
|
39674
39780
|
for (const entry of LOCKFILE_COMMANDS) {
|
|
39675
|
-
if (!entry.lockfiles.some((name) => existsSync18(
|
|
39781
|
+
if (!entry.lockfiles.some((name) => existsSync18(path121.join(cwd, name))))
|
|
39676
39782
|
continue;
|
|
39677
39783
|
const bin = resolveBin(cwd, entry.bin);
|
|
39678
39784
|
if (bin)
|
|
@@ -39731,13 +39837,13 @@ var dependencyAuditAdapter = {
|
|
|
39731
39837
|
|
|
39732
39838
|
// src/health/sources/sonarqube.ts
|
|
39733
39839
|
import { existsSync as existsSync19 } from "fs";
|
|
39734
|
-
import
|
|
39840
|
+
import path122 from "path";
|
|
39735
39841
|
var REPORT_FILES = ["sonar-issues.json", ".metaproject/data/health/sonar-issues.json"];
|
|
39736
39842
|
function reportPath(cwd) {
|
|
39737
39843
|
const fromEnv = process.env.SONAR_ISSUES_FILE;
|
|
39738
39844
|
const candidates = fromEnv ? [fromEnv, ...REPORT_FILES] : REPORT_FILES;
|
|
39739
39845
|
for (const rel of candidates) {
|
|
39740
|
-
const abs =
|
|
39846
|
+
const abs = path122.isAbsolute(rel) ? rel : path122.join(cwd, rel);
|
|
39741
39847
|
if (existsSync19(abs)) {
|
|
39742
39848
|
return abs;
|
|
39743
39849
|
}
|
|
@@ -39821,7 +39927,7 @@ var sonarqubeAdapter = {
|
|
|
39821
39927
|
function relativizeComponent(component, cwd) {
|
|
39822
39928
|
const colon = component.indexOf(":");
|
|
39823
39929
|
const value = colon >= 0 ? component.slice(colon + 1) : component;
|
|
39824
|
-
return
|
|
39930
|
+
return path122.isAbsolute(value) ? path122.relative(cwd, value) : value;
|
|
39825
39931
|
}
|
|
39826
39932
|
|
|
39827
39933
|
// src/health/sources/index.ts
|
|
@@ -40182,19 +40288,19 @@ function filteredInfo(source, cfg) {
|
|
|
40182
40288
|
};
|
|
40183
40289
|
}
|
|
40184
40290
|
async function writeOutputs(cwd, report, config, stamp) {
|
|
40185
|
-
const artifacts =
|
|
40186
|
-
const history =
|
|
40291
|
+
const artifacts = path123.join(dataRoot3(cwd), "artifacts");
|
|
40292
|
+
const history = path123.join(dataRoot3(cwd), "history");
|
|
40187
40293
|
await mkdir29(artifacts, { recursive: true });
|
|
40188
40294
|
await mkdir29(history, { recursive: true });
|
|
40189
|
-
const markdown =
|
|
40190
|
-
const json =
|
|
40191
|
-
const historyJson =
|
|
40295
|
+
const markdown = path123.join(artifacts, "latest.md");
|
|
40296
|
+
const json = path123.join(artifacts, "latest.json");
|
|
40297
|
+
const historyJson = path123.join(history, `${stamp}.json`);
|
|
40192
40298
|
const serialized = `${JSON.stringify(report, null, 2)}
|
|
40193
40299
|
`;
|
|
40194
40300
|
await writeFile22(markdown, renderReportMarkdown2(report, config), "utf8");
|
|
40195
40301
|
if (report.runId) {
|
|
40196
|
-
const runJson =
|
|
40197
|
-
const runMarkdown =
|
|
40302
|
+
const runJson = path123.join(artifacts, "runs", `${report.runId}.json`);
|
|
40303
|
+
const runMarkdown = path123.join(artifacts, "runs", `${report.runId}.md`);
|
|
40198
40304
|
if (await pathExists(runJson))
|
|
40199
40305
|
throw new Error(`immutable health run already exists: ${report.runId}`);
|
|
40200
40306
|
await writeFileAtomic(runJson, serialized);
|
|
@@ -40205,7 +40311,7 @@ async function writeOutputs(cwd, report, config, stamp) {
|
|
|
40205
40311
|
branch: report.provenance?.branch ?? null,
|
|
40206
40312
|
worktree: report.provenance?.worktree ?? null,
|
|
40207
40313
|
generated_at: report.generatedAt,
|
|
40208
|
-
record:
|
|
40314
|
+
record: path123.posix.join("runs", `${report.runId}.json`)
|
|
40209
40315
|
}, null, 2)}
|
|
40210
40316
|
`);
|
|
40211
40317
|
} else {
|
|
@@ -40213,8 +40319,8 @@ async function writeOutputs(cwd, report, config, stamp) {
|
|
|
40213
40319
|
}
|
|
40214
40320
|
await writeFile22(historyJson, serialized, "utf8");
|
|
40215
40321
|
return {
|
|
40216
|
-
markdownPath:
|
|
40217
|
-
jsonPath:
|
|
40322
|
+
markdownPath: path123.relative(cwd, markdown),
|
|
40323
|
+
jsonPath: path123.relative(cwd, json)
|
|
40218
40324
|
};
|
|
40219
40325
|
}
|
|
40220
40326
|
async function resolveChanged(cwd, selector) {
|
|
@@ -40267,7 +40373,7 @@ function hasGateShape(value) {
|
|
|
40267
40373
|
return Array.isArray(report.metrics) && Array.isArray(report.sources) && Array.isArray(report.findings);
|
|
40268
40374
|
}
|
|
40269
40375
|
async function readLatest(cwd) {
|
|
40270
|
-
const file =
|
|
40376
|
+
const file = path124.join(dataRoot3(cwd), "artifacts", "latest.json");
|
|
40271
40377
|
if (!await pathExists(file)) {
|
|
40272
40378
|
return null;
|
|
40273
40379
|
}
|
|
@@ -40278,11 +40384,11 @@ async function readLatest(cwd) {
|
|
|
40278
40384
|
const latest = read.value;
|
|
40279
40385
|
const record = latest.record;
|
|
40280
40386
|
if (typeof record === "string") {
|
|
40281
|
-
const pointer = await readArtifactPointer(
|
|
40387
|
+
const pointer = await readArtifactPointer(path124.join(dataRoot3(cwd), "artifacts"), await collectGitProvenance(cwd));
|
|
40282
40388
|
if (pointer.status !== "fresh")
|
|
40283
40389
|
return null;
|
|
40284
|
-
const recordFile =
|
|
40285
|
-
if (!isPathInside(
|
|
40390
|
+
const recordFile = path124.join(dataRoot3(cwd), "artifacts", record);
|
|
40391
|
+
if (!isPathInside(path124.join(dataRoot3(cwd), "artifacts"), recordFile))
|
|
40286
40392
|
return null;
|
|
40287
40393
|
if (!await pathExists(recordFile))
|
|
40288
40394
|
return null;
|
|
@@ -40355,7 +40461,7 @@ function createCodeHealthService() {
|
|
|
40355
40461
|
return {
|
|
40356
40462
|
run: (input) => runHealth(input),
|
|
40357
40463
|
async status(input) {
|
|
40358
|
-
const enabled = await pathExists(
|
|
40464
|
+
const enabled = await pathExists(path124.join(input.cwd, ".metaproject", "health.config.json"));
|
|
40359
40465
|
const latest = await readLatest(input.cwd);
|
|
40360
40466
|
const project = latest?.metrics.find((m) => m.key === "project") ?? null;
|
|
40361
40467
|
const decliningScopes = latest ? latest.metrics.filter((m) => m.regression_score > 0).length : 0;
|
|
@@ -40512,10 +40618,10 @@ function renderEvidenceBlock(evidences, siblings) {
|
|
|
40512
40618
|
init_fs();
|
|
40513
40619
|
init_contained_write();
|
|
40514
40620
|
init_json();
|
|
40515
|
-
import
|
|
40621
|
+
import path125 from "path";
|
|
40516
40622
|
import { createHash as createHash21 } from "crypto";
|
|
40517
40623
|
function impactEvidenceDataRoot(root) {
|
|
40518
|
-
return
|
|
40624
|
+
return path125.join(root, ".metaproject", "data", "security", "impact-evidence");
|
|
40519
40625
|
}
|
|
40520
40626
|
function sanitizeSessionId(sessionId) {
|
|
40521
40627
|
const cleaned = sessionId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -40527,7 +40633,7 @@ function sessionStateFilename(sessionId) {
|
|
|
40527
40633
|
return `${sanitized}-${suffix}.json`;
|
|
40528
40634
|
}
|
|
40529
40635
|
function sessionStatePath(root, sessionId) {
|
|
40530
|
-
return
|
|
40636
|
+
return path125.join(impactEvidenceDataRoot(root), "sessions", sessionStateFilename(sessionId));
|
|
40531
40637
|
}
|
|
40532
40638
|
async function loadSessionState(root, sessionId) {
|
|
40533
40639
|
const file = sessionStatePath(root, sessionId);
|
|
@@ -40546,11 +40652,11 @@ async function loadSessionState(root, sessionId) {
|
|
|
40546
40652
|
}
|
|
40547
40653
|
async function saveSessionState(root, sessionId, state) {
|
|
40548
40654
|
const file = sessionStatePath(root, sessionId);
|
|
40549
|
-
await writeContained(root,
|
|
40655
|
+
await writeContained(root, path125.relative(root, file), `${JSON.stringify(state, null, 2)}
|
|
40550
40656
|
`);
|
|
40551
40657
|
}
|
|
40552
40658
|
function logPath(root) {
|
|
40553
|
-
return
|
|
40659
|
+
return path125.join(impactEvidenceDataRoot(root), "log.jsonl");
|
|
40554
40660
|
}
|
|
40555
40661
|
async function appendLogRecord(root, record) {
|
|
40556
40662
|
const full = {
|
|
@@ -40561,7 +40667,7 @@ async function appendLogRecord(root, record) {
|
|
|
40561
40667
|
...record.detail !== undefined ? { detail: record.detail } : {}
|
|
40562
40668
|
};
|
|
40563
40669
|
const file = logPath(root);
|
|
40564
|
-
await appendContained(root,
|
|
40670
|
+
await appendContained(root, path125.relative(root, file), `${JSON.stringify(full)}
|
|
40565
40671
|
`);
|
|
40566
40672
|
return full;
|
|
40567
40673
|
}
|
|
@@ -40588,7 +40694,7 @@ init_command_risk();
|
|
|
40588
40694
|
init_shell_syntax();
|
|
40589
40695
|
init_fs();
|
|
40590
40696
|
init_service2();
|
|
40591
|
-
import
|
|
40697
|
+
import path126 from "path";
|
|
40592
40698
|
import { createHash as createHash22 } from "crypto";
|
|
40593
40699
|
import { realpathSync as realpathSync2 } from "fs";
|
|
40594
40700
|
function impactEvidenceHookClass(strict) {
|
|
@@ -40604,29 +40710,29 @@ function realpathNearestExisting(p) {
|
|
|
40604
40710
|
for (;; ) {
|
|
40605
40711
|
try {
|
|
40606
40712
|
const real = realpathSync2(current);
|
|
40607
|
-
return tail.length > 0 ?
|
|
40713
|
+
return tail.length > 0 ? path126.join(real, ...tail) : real;
|
|
40608
40714
|
} catch {
|
|
40609
|
-
const parent =
|
|
40715
|
+
const parent = path126.dirname(current);
|
|
40610
40716
|
if (parent === current) {
|
|
40611
|
-
return tail.length > 0 ?
|
|
40717
|
+
return tail.length > 0 ? path126.join(current, ...tail) : current;
|
|
40612
40718
|
}
|
|
40613
|
-
tail.unshift(
|
|
40719
|
+
tail.unshift(path126.basename(current));
|
|
40614
40720
|
current = parent;
|
|
40615
40721
|
}
|
|
40616
40722
|
}
|
|
40617
40723
|
}
|
|
40618
40724
|
function normalizeRequestFiles(root, files) {
|
|
40619
|
-
const rootReal = realpathNearestExisting(
|
|
40725
|
+
const rootReal = realpathNearestExisting(path126.resolve(root));
|
|
40620
40726
|
const normalized = [];
|
|
40621
40727
|
const rejected = [];
|
|
40622
40728
|
for (const file of files) {
|
|
40623
|
-
const lexicalAbsolute =
|
|
40729
|
+
const lexicalAbsolute = path126.isAbsolute(file) ? path126.resolve(file) : path126.resolve(rootReal, toPosix(file).replace(/^\.\//, ""));
|
|
40624
40730
|
const effective = realpathNearestExisting(lexicalAbsolute);
|
|
40625
40731
|
if (!isPathInside(rootReal, effective)) {
|
|
40626
40732
|
rejected.push(file);
|
|
40627
40733
|
continue;
|
|
40628
40734
|
}
|
|
40629
|
-
normalized.push(toPosix(
|
|
40735
|
+
normalized.push(toPosix(path126.relative(rootReal, effective)));
|
|
40630
40736
|
}
|
|
40631
40737
|
return { files: [...new Set(normalized)], rejected };
|
|
40632
40738
|
}
|
|
@@ -40908,7 +41014,7 @@ function hostDeliveryStatus() {
|
|
|
40908
41014
|
// src/job/service.ts
|
|
40909
41015
|
init_fs();
|
|
40910
41016
|
import { copyFile, mkdir as mkdir31 } from "fs/promises";
|
|
40911
|
-
import
|
|
41017
|
+
import path129 from "path";
|
|
40912
41018
|
|
|
40913
41019
|
// src/job/types.ts
|
|
40914
41020
|
var JOB_INTENTS = ["implement", "analyze", "review", "custom"];
|
|
@@ -41049,15 +41155,15 @@ init_validator();
|
|
|
41049
41155
|
init_contracts();
|
|
41050
41156
|
init_fs();
|
|
41051
41157
|
import { appendFile as appendFile6, mkdir as mkdir30, readFile as readFile63, readdir as readdir21 } from "fs/promises";
|
|
41052
|
-
import
|
|
41158
|
+
import path128 from "path";
|
|
41053
41159
|
function jobsRoot(cwd) {
|
|
41054
|
-
return
|
|
41160
|
+
return path128.join(cwd, ".metaproject", "jobs");
|
|
41055
41161
|
}
|
|
41056
41162
|
function jobDir(cwd, name) {
|
|
41057
|
-
return
|
|
41163
|
+
return path128.join(jobsRoot(cwd), name);
|
|
41058
41164
|
}
|
|
41059
41165
|
function jobStatePath(cwd, name) {
|
|
41060
|
-
return
|
|
41166
|
+
return path128.join(jobDir(cwd, name), "state.json");
|
|
41061
41167
|
}
|
|
41062
41168
|
var JOB_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
41063
41169
|
function assertJobName(name) {
|
|
@@ -41094,12 +41200,12 @@ async function writeJob(cwd, name, state) {
|
|
|
41094
41200
|
`));
|
|
41095
41201
|
}
|
|
41096
41202
|
const file = jobStatePath(cwd, name);
|
|
41097
|
-
await mkdir30(
|
|
41203
|
+
await mkdir30(path128.dirname(file), { recursive: true });
|
|
41098
41204
|
await writeFileAtomic(file, `${JSON.stringify(state, null, 2)}
|
|
41099
41205
|
`);
|
|
41100
41206
|
}
|
|
41101
41207
|
async function appendJournal2(cwd, name, at, line) {
|
|
41102
|
-
await appendFile6(
|
|
41208
|
+
await appendFile6(path128.join(jobDir(cwd, name), "journal.md"), `- ${at} - ${line}
|
|
41103
41209
|
`, "utf8");
|
|
41104
41210
|
}
|
|
41105
41211
|
function renderJournal2(name, at) {
|
|
@@ -41120,7 +41226,7 @@ function assertDocumentType(raw) {
|
|
|
41120
41226
|
return raw;
|
|
41121
41227
|
}
|
|
41122
41228
|
function documentFileName(type, sourcePath) {
|
|
41123
|
-
const extension =
|
|
41229
|
+
const extension = path128.extname(sourcePath) || ".md";
|
|
41124
41230
|
return `${type}${extension}`;
|
|
41125
41231
|
}
|
|
41126
41232
|
|
|
@@ -41128,7 +41234,7 @@ function documentFileName(type, sourcePath) {
|
|
|
41128
41234
|
function createJobService(deps) {
|
|
41129
41235
|
const now = () => deps.now().toISOString();
|
|
41130
41236
|
function lockPath(cwd, name) {
|
|
41131
|
-
return
|
|
41237
|
+
return path129.join(jobsRoot(cwd), `.job-lock-${name}`);
|
|
41132
41238
|
}
|
|
41133
41239
|
async function mutate(cwd, name, run) {
|
|
41134
41240
|
assertJobName(name);
|
|
@@ -41177,20 +41283,20 @@ function createJobService(deps) {
|
|
|
41177
41283
|
},
|
|
41178
41284
|
plan: { steps: defaultPlan(intent) },
|
|
41179
41285
|
documentation: {
|
|
41180
|
-
job_path:
|
|
41286
|
+
job_path: path129.join(".metaproject", "jobs", name),
|
|
41181
41287
|
documents_created: []
|
|
41182
41288
|
},
|
|
41183
41289
|
metrics: { steps: [] },
|
|
41184
|
-
jobs_root:
|
|
41290
|
+
jobs_root: path129.join(".metaproject", "jobs"),
|
|
41185
41291
|
updated_at: createdAt
|
|
41186
41292
|
};
|
|
41187
41293
|
const first = firstOpenStep(state.plan.steps);
|
|
41188
41294
|
if (first) {
|
|
41189
41295
|
state.plan.current_step = first.id;
|
|
41190
41296
|
}
|
|
41191
|
-
await writeFileAtomic(
|
|
41297
|
+
await writeFileAtomic(path129.join(absolute, "journal.md"), renderJournal2(name, createdAt));
|
|
41192
41298
|
await writeJob(input.cwd, name, state);
|
|
41193
|
-
return { state, dir:
|
|
41299
|
+
return { state, dir: path129.relative(input.cwd, absolute) };
|
|
41194
41300
|
},
|
|
41195
41301
|
async status({ cwd, name }) {
|
|
41196
41302
|
assertJobName(name);
|
|
@@ -41202,7 +41308,7 @@ function createJobService(deps) {
|
|
|
41202
41308
|
}
|
|
41203
41309
|
return {
|
|
41204
41310
|
state,
|
|
41205
|
-
dir:
|
|
41311
|
+
dir: path129.join(".metaproject", "jobs", name),
|
|
41206
41312
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
41207
41313
|
stepsTotal: state.plan.steps.length,
|
|
41208
41314
|
nextStep: firstOpenStep(state.plan.steps),
|
|
@@ -41259,23 +41365,23 @@ function createJobService(deps) {
|
|
|
41259
41365
|
if (!input.file) {
|
|
41260
41366
|
throw new Error("Missing --file. Usage: keryx job document <name> --type <type> --file <path>");
|
|
41261
41367
|
}
|
|
41262
|
-
const source =
|
|
41368
|
+
const source = path129.isAbsolute(input.file) ? input.file : path129.join(input.cwd, input.file);
|
|
41263
41369
|
if (!await pathExists(source)) {
|
|
41264
41370
|
throw new Error(`--file not found: ${input.file}. Write the document first, then record it.`);
|
|
41265
41371
|
}
|
|
41266
41372
|
let recorded;
|
|
41267
41373
|
await mutate(input.cwd, input.name, async (state) => {
|
|
41268
41374
|
const fileName = documentFileName(type, source);
|
|
41269
|
-
const target =
|
|
41375
|
+
const target = path129.join(jobDir(input.cwd, state.job_name), fileName);
|
|
41270
41376
|
await copyFile(source, target);
|
|
41271
41377
|
state.documentation ??= {};
|
|
41272
|
-
state.documentation.job_path ??=
|
|
41378
|
+
state.documentation.job_path ??= path129.join(".metaproject", "jobs", state.job_name);
|
|
41273
41379
|
const created = state.documentation.documents_created ?? [];
|
|
41274
41380
|
state.documentation.documents_created = created.includes(fileName) ? created : [...created, fileName];
|
|
41275
41381
|
recorded = {
|
|
41276
41382
|
state,
|
|
41277
41383
|
document: fileName,
|
|
41278
|
-
path:
|
|
41384
|
+
path: path129.join(".metaproject", "jobs", state.job_name, fileName)
|
|
41279
41385
|
};
|
|
41280
41386
|
return { state, event: "document", detail: `${type} -> ${fileName}` };
|
|
41281
41387
|
});
|
|
@@ -41310,7 +41416,7 @@ function createJobService(deps) {
|
|
|
41310
41416
|
name: state.job_name,
|
|
41311
41417
|
phase: state.phase,
|
|
41312
41418
|
intent: state.intent,
|
|
41313
|
-
dir:
|
|
41419
|
+
dir: path129.join(".metaproject", "jobs", name),
|
|
41314
41420
|
stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
|
|
41315
41421
|
stepsTotal: state.plan.steps.length,
|
|
41316
41422
|
nextStep: firstOpenStep(state.plan.steps)?.id ?? null
|
|
@@ -41331,12 +41437,12 @@ init_fs();
|
|
|
41331
41437
|
init_preservation();
|
|
41332
41438
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
41333
41439
|
import { lstat as lstat9, readdir as readdir26 } from "fs/promises";
|
|
41334
|
-
import
|
|
41440
|
+
import path143 from "path";
|
|
41335
41441
|
|
|
41336
41442
|
// src/session/paths.ts
|
|
41337
41443
|
import { existsSync as existsSync21 } from "fs";
|
|
41338
41444
|
import { homedir as homedir2 } from "os";
|
|
41339
|
-
import
|
|
41445
|
+
import path131 from "path";
|
|
41340
41446
|
function keryxDataDir(override) {
|
|
41341
41447
|
if (override !== undefined && override.length > 0) {
|
|
41342
41448
|
return override;
|
|
@@ -41348,22 +41454,22 @@ function keryxDataDir(override) {
|
|
|
41348
41454
|
const home = homedir2();
|
|
41349
41455
|
if (process.platform === "win32") {
|
|
41350
41456
|
const appData = process.env.APPDATA;
|
|
41351
|
-
const base = appData !== undefined && appData.length > 0 ? appData :
|
|
41352
|
-
return
|
|
41457
|
+
const base = appData !== undefined && appData.length > 0 ? appData : path131.join(home, "AppData", "Roaming");
|
|
41458
|
+
return path131.join(base, "keryx");
|
|
41353
41459
|
}
|
|
41354
41460
|
const xdg = process.env.XDG_DATA_HOME;
|
|
41355
|
-
const base = xdg !== undefined && xdg.length > 0 ? xdg :
|
|
41356
|
-
return
|
|
41461
|
+
const base = xdg !== undefined && xdg.length > 0 ? xdg : path131.join(home, ".local", "share");
|
|
41462
|
+
return path131.join(base, "keryx");
|
|
41357
41463
|
}
|
|
41358
41464
|
function resolveProjectRoot2(cwd) {
|
|
41359
|
-
const abs =
|
|
41465
|
+
const abs = path131.resolve(cwd);
|
|
41360
41466
|
let dir = abs;
|
|
41361
41467
|
for (;; ) {
|
|
41362
|
-
const gitPath =
|
|
41468
|
+
const gitPath = path131.join(dir, ".git");
|
|
41363
41469
|
if (existsSync21(gitPath)) {
|
|
41364
41470
|
return dir;
|
|
41365
41471
|
}
|
|
41366
|
-
const parent =
|
|
41472
|
+
const parent = path131.dirname(dir);
|
|
41367
41473
|
if (parent === dir) {
|
|
41368
41474
|
break;
|
|
41369
41475
|
}
|
|
@@ -41372,12 +41478,12 @@ function resolveProjectRoot2(cwd) {
|
|
|
41372
41478
|
return abs;
|
|
41373
41479
|
}
|
|
41374
41480
|
function projectKeyFromPath(projectPath) {
|
|
41375
|
-
const abs =
|
|
41481
|
+
const abs = path131.resolve(projectPath);
|
|
41376
41482
|
const encoded = encodeURIComponent(abs);
|
|
41377
41483
|
if (encoded.length <= 200) {
|
|
41378
41484
|
return encoded;
|
|
41379
41485
|
}
|
|
41380
|
-
const base =
|
|
41486
|
+
const base = path131.basename(abs).replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40);
|
|
41381
41487
|
let h = 0;
|
|
41382
41488
|
for (let i = 0;i < abs.length; i++) {
|
|
41383
41489
|
h = Math.imul(31, h) + abs.charCodeAt(i) | 0;
|
|
@@ -41387,22 +41493,22 @@ function projectKeyFromPath(projectPath) {
|
|
|
41387
41493
|
}
|
|
41388
41494
|
function projectSessionsDir(projectPath, dataDir) {
|
|
41389
41495
|
const key = projectKeyFromPath(resolveProjectRoot2(projectPath));
|
|
41390
|
-
return
|
|
41496
|
+
return path131.join(keryxDataDir(dataDir), "sessions", key);
|
|
41391
41497
|
}
|
|
41392
41498
|
function sessionDir(projectPath, sessionId, dataDir) {
|
|
41393
|
-
return
|
|
41499
|
+
return path131.join(projectSessionsDir(projectPath, dataDir), sessionId);
|
|
41394
41500
|
}
|
|
41395
41501
|
|
|
41396
41502
|
// src/sac/proposal-lifecycle.ts
|
|
41397
41503
|
init_fs();
|
|
41398
41504
|
import { createHash as createHash32, randomUUID as randomUUID7 } from "crypto";
|
|
41399
41505
|
import { appendFile as appendFile7, mkdir as mkdir35, readdir as readdir25, readFile as readFile73 } from "fs/promises";
|
|
41400
|
-
import
|
|
41506
|
+
import path142 from "path";
|
|
41401
41507
|
|
|
41402
41508
|
// src/sac/index.ts
|
|
41403
41509
|
import { access as access3, readFile as readFile64, realpath as realpath14 } from "fs/promises";
|
|
41404
41510
|
import { createHash as createHash24 } from "crypto";
|
|
41405
|
-
import
|
|
41511
|
+
import path132 from "path";
|
|
41406
41512
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
41407
41513
|
|
|
41408
41514
|
// src/sac/workspace-reference.ts
|
|
@@ -41462,21 +41568,21 @@ var normativeSchemaFiles = {
|
|
|
41462
41568
|
"review-decision": "review-decision.schema.json"
|
|
41463
41569
|
};
|
|
41464
41570
|
var normativeSchemas = new Map;
|
|
41465
|
-
var NORMATIVE_SCHEMA_DIR =
|
|
41571
|
+
var NORMATIVE_SCHEMA_DIR = path132.join("docs", "requirements", "shared-agent-context", "schemas");
|
|
41466
41572
|
async function resolveSacNormativeSchemaPath(fileName, searchFrom = [fileURLToPath3(new URL(".", import.meta.url)), process.cwd()]) {
|
|
41467
41573
|
const seen = new Set;
|
|
41468
41574
|
for (const start of searchFrom) {
|
|
41469
|
-
let dir =
|
|
41575
|
+
let dir = path132.resolve(start);
|
|
41470
41576
|
for (let i = 0;i < 10; i++) {
|
|
41471
41577
|
if (seen.has(dir))
|
|
41472
41578
|
break;
|
|
41473
41579
|
seen.add(dir);
|
|
41474
|
-
const candidate =
|
|
41580
|
+
const candidate = path132.join(dir, NORMATIVE_SCHEMA_DIR, fileName);
|
|
41475
41581
|
try {
|
|
41476
41582
|
await access3(candidate);
|
|
41477
41583
|
return candidate;
|
|
41478
41584
|
} catch {}
|
|
41479
|
-
const parent =
|
|
41585
|
+
const parent = path132.dirname(dir);
|
|
41480
41586
|
if (parent === dir)
|
|
41481
41587
|
break;
|
|
41482
41588
|
dir = parent;
|
|
@@ -42145,12 +42251,12 @@ class SacReferenceError extends Error {
|
|
|
42145
42251
|
code = "unsafe_workspace_reference";
|
|
42146
42252
|
}
|
|
42147
42253
|
async function resolveWorkspaceReference(input) {
|
|
42148
|
-
if (!workspacePathPattern.test(input.uri) ||
|
|
42254
|
+
if (!workspacePathPattern.test(input.uri) || path132.isAbsolute(input.uri) || /^[a-z][a-z0-9+.-]*:/i.test(input.uri) || input.uri.includes("\\"))
|
|
42149
42255
|
throw new SacReferenceError("unsafe workspace reference");
|
|
42150
|
-
const lexicalRoot =
|
|
42256
|
+
const lexicalRoot = path132.resolve(input.workspaceRoot);
|
|
42151
42257
|
const root = await realpath14(lexicalRoot);
|
|
42152
|
-
const candidate =
|
|
42153
|
-
if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${
|
|
42258
|
+
const candidate = path132.resolve(lexicalRoot, input.uri.slice(2));
|
|
42259
|
+
if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${path132.sep}`))
|
|
42154
42260
|
throw new SacReferenceError("workspace reference escapes root");
|
|
42155
42261
|
let resolved;
|
|
42156
42262
|
try {
|
|
@@ -42158,7 +42264,7 @@ async function resolveWorkspaceReference(input) {
|
|
|
42158
42264
|
} catch {
|
|
42159
42265
|
throw new SacReferenceError("workspace reference is not resolvable");
|
|
42160
42266
|
}
|
|
42161
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
42267
|
+
if (resolved !== root && !resolved.startsWith(`${root}${path132.sep}`))
|
|
42162
42268
|
throw new SacReferenceError("workspace reference escapes root after realpath");
|
|
42163
42269
|
return candidate;
|
|
42164
42270
|
}
|
|
@@ -42214,16 +42320,16 @@ async function evaluateStrictSacGuard(input) {
|
|
|
42214
42320
|
init_fs();
|
|
42215
42321
|
import { mkdir as mkdir32, readdir as readdir22, readFile as readFile65 } from "fs/promises";
|
|
42216
42322
|
import { createHash as createHash25, randomUUID as randomUUID4 } from "crypto";
|
|
42217
|
-
import
|
|
42323
|
+
import path134 from "path";
|
|
42218
42324
|
|
|
42219
42325
|
// src/sac/secure-resource-read.ts
|
|
42220
42326
|
init_contained_read();
|
|
42221
42327
|
init_descriptor_read();
|
|
42222
|
-
import
|
|
42328
|
+
import path133 from "path";
|
|
42223
42329
|
function readWorkspaceFileNoFollow(workspaceRoot, absolutePath) {
|
|
42224
|
-
const relativePath =
|
|
42225
|
-
const components = relativePath.split(
|
|
42226
|
-
if (!relativePath ||
|
|
42330
|
+
const relativePath = path133.relative(workspaceRoot, absolutePath);
|
|
42331
|
+
const components = relativePath.split(path133.sep);
|
|
42332
|
+
if (!relativePath || path133.isAbsolute(relativePath) || components.some((component) => !component || component === "." || component === "..")) {
|
|
42227
42333
|
throw new Error("safe source path is not workspace-relative");
|
|
42228
42334
|
}
|
|
42229
42335
|
return readDescriptorChain(workspaceRoot, absolutePath, {
|
|
@@ -42250,8 +42356,8 @@ class WorkspaceService {
|
|
|
42250
42356
|
now;
|
|
42251
42357
|
constructor(options) {
|
|
42252
42358
|
this.options = options;
|
|
42253
|
-
this.root =
|
|
42254
|
-
this.storageRoot =
|
|
42359
|
+
this.root = path134.resolve(options.workspaceRoot);
|
|
42360
|
+
this.storageRoot = path134.resolve(options.storageRoot ?? path134.join(this.root, ".metaproject", "workspaces"));
|
|
42255
42361
|
this.now = options.now ?? (() => new Date);
|
|
42256
42362
|
}
|
|
42257
42363
|
async create(input) {
|
|
@@ -42577,13 +42683,13 @@ class WorkspaceService {
|
|
|
42577
42683
|
return parsed;
|
|
42578
42684
|
}
|
|
42579
42685
|
workspaceDir(id) {
|
|
42580
|
-
return
|
|
42686
|
+
return path134.join(this.storageRoot, id);
|
|
42581
42687
|
}
|
|
42582
42688
|
manifestPath(id) {
|
|
42583
|
-
return
|
|
42689
|
+
return path134.join(this.workspaceDir(id), "workspace.json");
|
|
42584
42690
|
}
|
|
42585
42691
|
lockPath(id) {
|
|
42586
|
-
return
|
|
42692
|
+
return path134.join(this.storageRoot, `.${id}.lock`);
|
|
42587
42693
|
}
|
|
42588
42694
|
timestamp() {
|
|
42589
42695
|
return this.now().toISOString();
|
|
@@ -42699,7 +42805,7 @@ function bindingHash(owner, intent) {
|
|
|
42699
42805
|
// src/sac/session-wrap-up.ts
|
|
42700
42806
|
import { createHash as createHash28 } from "crypto";
|
|
42701
42807
|
import { mkdir as mkdir34, writeFile as writeFile23 } from "fs/promises";
|
|
42702
|
-
import
|
|
42808
|
+
import path137 from "path";
|
|
42703
42809
|
|
|
42704
42810
|
// src/session/store.ts
|
|
42705
42811
|
init_preservation();
|
|
@@ -42712,7 +42818,7 @@ import {
|
|
|
42712
42818
|
renameSync as renameSync5,
|
|
42713
42819
|
writeFileSync as writeFileSync4
|
|
42714
42820
|
} from "fs";
|
|
42715
|
-
import
|
|
42821
|
+
import path136 from "path";
|
|
42716
42822
|
init_slate();
|
|
42717
42823
|
init_redact();
|
|
42718
42824
|
var SESSION_SCHEMA_VERSION = 1;
|
|
@@ -42805,7 +42911,7 @@ function parseRemote(value) {
|
|
|
42805
42911
|
function readSummaryFile(file) {
|
|
42806
42912
|
let pending;
|
|
42807
42913
|
try {
|
|
42808
|
-
pending = readPendingHistory(
|
|
42914
|
+
pending = readPendingHistory(path136.dirname(file));
|
|
42809
42915
|
} catch {
|
|
42810
42916
|
pending = undefined;
|
|
42811
42917
|
}
|
|
@@ -42857,11 +42963,11 @@ class TranscriptUnreadableError extends Error {
|
|
|
42857
42963
|
}
|
|
42858
42964
|
}
|
|
42859
42965
|
function readJsonl2(file) {
|
|
42860
|
-
const pending = readPendingHistory(
|
|
42966
|
+
const pending = readPendingHistory(path136.dirname(file));
|
|
42861
42967
|
if (pending === undefined && !existsSync22(file)) {
|
|
42862
42968
|
return [];
|
|
42863
42969
|
}
|
|
42864
|
-
const read = pending !== undefined ? { ok: true, text: pending.files[
|
|
42970
|
+
const read = pending !== undefined ? { ok: true, text: pending.files[path136.basename(file)] } : readTranscriptFile(file);
|
|
42865
42971
|
if (!read.ok) {
|
|
42866
42972
|
throw new TranscriptUnreadableError(file, read.reason);
|
|
42867
42973
|
}
|
|
@@ -42912,11 +43018,11 @@ function listSessions(cwd, dataDir) {
|
|
|
42912
43018
|
if (name.startsWith(".")) {
|
|
42913
43019
|
continue;
|
|
42914
43020
|
}
|
|
42915
|
-
const summary = readSummaryFile(
|
|
43021
|
+
const summary = readSummaryFile(path136.join(root, name, "summary.json"));
|
|
42916
43022
|
if (summary === undefined) {
|
|
42917
43023
|
continue;
|
|
42918
43024
|
}
|
|
42919
|
-
if (
|
|
43025
|
+
if (path136.resolve(summary.projectPath) !== path136.resolve(projectPath)) {
|
|
42920
43026
|
continue;
|
|
42921
43027
|
}
|
|
42922
43028
|
out.push(summary);
|
|
@@ -42949,15 +43055,15 @@ async function readSessionSlate(cwd, sessionId, dataDir) {
|
|
|
42949
43055
|
}
|
|
42950
43056
|
function loadContext(cwd, sessionId, dataDir) {
|
|
42951
43057
|
const dir = sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir);
|
|
42952
|
-
const contextPath =
|
|
43058
|
+
const contextPath = path136.join(dir, "context.jsonl");
|
|
42953
43059
|
if (readPendingHistory(dir) !== undefined || existsSync22(contextPath)) {
|
|
42954
43060
|
return readJsonl2(contextPath);
|
|
42955
43061
|
}
|
|
42956
|
-
return readJsonl2(
|
|
43062
|
+
return readJsonl2(path136.join(dir, "transcript.jsonl"));
|
|
42957
43063
|
}
|
|
42958
43064
|
function loadArchive(cwd, sessionId, dataDir, onDegraded) {
|
|
42959
43065
|
const dir = sessionDir(resolveProjectRoot2(cwd), sessionId, dataDir);
|
|
42960
|
-
const archivePath =
|
|
43066
|
+
const archivePath = path136.join(dir, "archive.jsonl");
|
|
42961
43067
|
if (readPendingHistory(dir) !== undefined || existsSync22(archivePath)) {
|
|
42962
43068
|
try {
|
|
42963
43069
|
const archive = readJsonl2(archivePath);
|
|
@@ -43032,7 +43138,7 @@ function sessionEvidenceRef(workspaceId, sessionId) {
|
|
|
43032
43138
|
}
|
|
43033
43139
|
async function resolveSessionWrapUp(input) {
|
|
43034
43140
|
const now = input.now ?? (() => new Date);
|
|
43035
|
-
const sessionId =
|
|
43141
|
+
const sessionId = path137.posix.basename(input.sourceRef, ".md");
|
|
43036
43142
|
const summary = findSession(input.cwd, sessionId);
|
|
43037
43143
|
if (summary === undefined || input.sourceRef !== sessionEvidenceRef(input.workspaceId, summary.id)) {
|
|
43038
43144
|
throw new SessionWrapUpError("session_not_found", `no session matching "${input.sourceRef}" in this project \u2014 use \`keryx sessions list\``);
|
|
@@ -43049,7 +43155,7 @@ async function resolveSessionWrapUp(input) {
|
|
|
43049
43155
|
throw new SessionWrapUpError("session_unreadable", `session "${summary.id}" could not be read: ${cause.message}`);
|
|
43050
43156
|
}
|
|
43051
43157
|
const relPath = sessionEvidenceRef(input.workspaceId, summary.id).slice(2);
|
|
43052
|
-
const evidenceDir =
|
|
43158
|
+
const evidenceDir = path137.dirname(relPath);
|
|
43053
43159
|
const slate = await readSessionSlate(input.cwd, summary.id);
|
|
43054
43160
|
const diff = await gitDiff(input.cwd);
|
|
43055
43161
|
const diffBody = diff.kind === "measured" ? diff.text : unmeasurableDiffBody(diff.detail);
|
|
@@ -43071,23 +43177,23 @@ async function resolveSessionWrapUp(input) {
|
|
|
43071
43177
|
""
|
|
43072
43178
|
].join(`
|
|
43073
43179
|
`);
|
|
43074
|
-
const wrapUpRelPath =
|
|
43075
|
-
const diffRelPath =
|
|
43076
|
-
const noticeRelPath =
|
|
43180
|
+
const wrapUpRelPath = path137.join(evidenceDir, `${summary.id}.wrap-up.md`);
|
|
43181
|
+
const diffRelPath = path137.join(evidenceDir, `${summary.id}.diff.txt`);
|
|
43182
|
+
const noticeRelPath = path137.join(evidenceDir, `${summary.id}.redaction.md`);
|
|
43077
43183
|
const floor = await applyEvidenceRedactionFloor(input.cwd, [
|
|
43078
|
-
{ name:
|
|
43079
|
-
{ name:
|
|
43080
|
-
{ name:
|
|
43184
|
+
{ name: path137.basename(relPath), content: markdown, path: relPath, source: "generated" },
|
|
43185
|
+
{ name: path137.basename(wrapUpRelPath), content: wrapUpMarkdown, path: wrapUpRelPath, source: "generated" },
|
|
43186
|
+
{ name: path137.basename(diffRelPath), content: diffBody, path: diffRelPath, source: "tool-output" }
|
|
43081
43187
|
]);
|
|
43082
43188
|
if (!floor.ok)
|
|
43083
43189
|
throw new SessionWrapUpError("security_denied", floor.reason);
|
|
43084
43190
|
const [safeSession, safeWrapUp, safeDiff] = floor.bodies;
|
|
43085
|
-
await mkdir34(
|
|
43086
|
-
await writeFile23(
|
|
43087
|
-
await writeFile23(
|
|
43088
|
-
await writeFile23(
|
|
43191
|
+
await mkdir34(path137.join(input.cwd, evidenceDir), { recursive: true });
|
|
43192
|
+
await writeFile23(path137.join(input.cwd, relPath), safeSession.content, "utf8");
|
|
43193
|
+
await writeFile23(path137.join(input.cwd, wrapUpRelPath), safeWrapUp.content, "utf8");
|
|
43194
|
+
await writeFile23(path137.join(input.cwd, diffRelPath), safeDiff.content, "utf8");
|
|
43089
43195
|
if (floor.notice !== undefined)
|
|
43090
|
-
await writeFile23(
|
|
43196
|
+
await writeFile23(path137.join(input.cwd, noticeRelPath), floor.notice, "utf8");
|
|
43091
43197
|
const observedAt = now().toISOString();
|
|
43092
43198
|
const evidence = [
|
|
43093
43199
|
{ kind: "wrap-up", uri: `./${wrapUpRelPath}`, revision: createHash28("sha256").update(safeWrapUp.content).digest("hex"), observedAt },
|
|
@@ -43110,13 +43216,13 @@ async function resolveSessionWrapUp(input) {
|
|
|
43110
43216
|
init_fs();
|
|
43111
43217
|
import { createHash as createHash29, randomBytes as randomBytes6 } from "crypto";
|
|
43112
43218
|
import { readFile as readFile67 } from "fs/promises";
|
|
43113
|
-
import
|
|
43219
|
+
import path138 from "path";
|
|
43114
43220
|
var CONFIRM_TOKEN_TTL_MS = 2 * 60000;
|
|
43115
43221
|
function confirmTokenPath2(cwd, workspaceId, proposalId) {
|
|
43116
|
-
return
|
|
43222
|
+
return path138.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.confirm-token.json`);
|
|
43117
43223
|
}
|
|
43118
43224
|
function confirmReceiptPath(cwd, workspaceId, proposalId, idempotencyKey) {
|
|
43119
|
-
return
|
|
43225
|
+
return path138.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${sha2563(idempotencyKey)}.confirm-receipt.json`);
|
|
43120
43226
|
}
|
|
43121
43227
|
function sha2563(text) {
|
|
43122
43228
|
return createHash29("sha256").update(text).digest("hex");
|
|
@@ -43157,21 +43263,21 @@ init_store();
|
|
|
43157
43263
|
init_config2();
|
|
43158
43264
|
import { readFile as readFile69, readdir as readdir24 } from "fs/promises";
|
|
43159
43265
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
43160
|
-
import
|
|
43266
|
+
import path140 from "path";
|
|
43161
43267
|
|
|
43162
43268
|
// src/sac/proposal-evidence.ts
|
|
43163
43269
|
init_fs();
|
|
43164
43270
|
import { createHash as createHash30 } from "crypto";
|
|
43165
43271
|
import { readFile as readFile68, rm as rm7 } from "fs/promises";
|
|
43166
|
-
import
|
|
43272
|
+
import path139 from "path";
|
|
43167
43273
|
function proposalPath(cwd, workspaceId, proposalId) {
|
|
43168
|
-
return
|
|
43274
|
+
return path139.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
|
|
43169
43275
|
}
|
|
43170
43276
|
function ownerReceiptPath(cwd, owner, workspaceId, idempotencyKey) {
|
|
43171
|
-
return
|
|
43277
|
+
return path139.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-receipts`, `${idempotencyKey}.json`);
|
|
43172
43278
|
}
|
|
43173
43279
|
function proposalNotePath(cwd, workspaceId, proposalId) {
|
|
43174
|
-
return
|
|
43280
|
+
return path139.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.note.txt`);
|
|
43175
43281
|
}
|
|
43176
43282
|
async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
43177
43283
|
let proposal;
|
|
@@ -43185,7 +43291,7 @@ async function readVerifiedProposalEvidence(cwd, workspaceId, proposalId) {
|
|
|
43185
43291
|
return { ok: false, code: "no_evidence_to_write" };
|
|
43186
43292
|
let content;
|
|
43187
43293
|
try {
|
|
43188
|
-
content = await readFile68(
|
|
43294
|
+
content = await readFile68(path139.join(cwd, evidence.uri), "utf8");
|
|
43189
43295
|
} catch {
|
|
43190
43296
|
return { ok: false, code: "evidence_file_unreadable" };
|
|
43191
43297
|
}
|
|
@@ -43215,22 +43321,22 @@ function targetRelPath(targetRef) {
|
|
|
43215
43321
|
return targetRef.replace(/^\.\//, "");
|
|
43216
43322
|
}
|
|
43217
43323
|
function targetAbsolutePath(cwd, targetRef) {
|
|
43218
|
-
return
|
|
43324
|
+
return path139.join(cwd, ".metaproject", targetRelPath(targetRef));
|
|
43219
43325
|
}
|
|
43220
43326
|
function targetKey(targetRef) {
|
|
43221
43327
|
return digestOf2(targetRelPath(targetRef)).slice(0, 32);
|
|
43222
43328
|
}
|
|
43223
43329
|
function ownerTargetVersionPath(cwd, owner, workspaceId, targetRef) {
|
|
43224
|
-
return
|
|
43330
|
+
return path139.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-target-versions`, `${targetKey(targetRef)}.json`);
|
|
43225
43331
|
}
|
|
43226
43332
|
function ownerTargetLockPath(cwd, owner, targetRef) {
|
|
43227
|
-
return
|
|
43333
|
+
return path139.join(cwd, ".metaproject", "data", "sac", "target-locks", owner, `${targetKey(targetRef)}.lock`);
|
|
43228
43334
|
}
|
|
43229
43335
|
function ownerStagingDir(cwd, owner, workspaceId, idempotencyKey) {
|
|
43230
|
-
return
|
|
43336
|
+
return path139.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-staging`, digestOf2(idempotencyKey).slice(0, 32));
|
|
43231
43337
|
}
|
|
43232
43338
|
function ownerConflictDir(cwd, owner, workspaceId, idempotencyKey) {
|
|
43233
|
-
return
|
|
43339
|
+
return path139.join(cwd, ".metaproject", "workspaces", workspaceId, `${owner}-write-conflicts`, digestOf2(idempotencyKey).slice(0, 32));
|
|
43234
43340
|
}
|
|
43235
43341
|
async function readJson4(file) {
|
|
43236
43342
|
try {
|
|
@@ -43301,7 +43407,7 @@ async function applyGuardedTargetWrite(input) {
|
|
|
43301
43407
|
staged: input.staged,
|
|
43302
43408
|
stagedAt: receipt.completedAt
|
|
43303
43409
|
};
|
|
43304
|
-
await writeFileAtomic(
|
|
43410
|
+
await writeFileAtomic(path139.join(ownerStagingDir(cwd, owner, intent.workspaceId, intent.idempotencyKey), "plan.json"), `${JSON.stringify(staged, null, 2)}
|
|
43305
43411
|
`);
|
|
43306
43412
|
const applied = await input.apply(input.staged);
|
|
43307
43413
|
if (!applied.ok) {
|
|
@@ -43327,15 +43433,15 @@ async function recoverStagedOwnerWrite(input) {
|
|
|
43327
43433
|
});
|
|
43328
43434
|
}
|
|
43329
43435
|
async function readStagedOwnerWrite(cwd, owner, workspaceId, idempotencyKey) {
|
|
43330
|
-
return readJson4(
|
|
43436
|
+
return readJson4(path139.join(ownerStagingDir(cwd, owner, workspaceId, idempotencyKey), "plan.json"));
|
|
43331
43437
|
}
|
|
43332
43438
|
async function recordConflict(input) {
|
|
43333
43439
|
const base = { targetRef: input.targetRef, expectedVersion: input.expectedVersion, currentVersion: input.currentVersion };
|
|
43334
43440
|
if (input.proposedContent === undefined)
|
|
43335
43441
|
return base;
|
|
43336
43442
|
const dir = ownerConflictDir(input.cwd, input.owner, input.intent.workspaceId, input.intent.idempotencyKey);
|
|
43337
|
-
await writeFileAtomic(
|
|
43338
|
-
const diffRef = `./workspaces/${input.intent.workspaceId}/${input.owner}-write-conflicts/${
|
|
43443
|
+
await writeFileAtomic(path139.join(dir, "proposed"), input.proposedContent);
|
|
43444
|
+
const diffRef = `./workspaces/${input.intent.workspaceId}/${input.owner}-write-conflicts/${path139.basename(dir)}/proposed`;
|
|
43339
43445
|
return { ...base, diffRef };
|
|
43340
43446
|
}
|
|
43341
43447
|
|
|
@@ -43385,7 +43491,7 @@ async function resolveWorkspaceModule(cwd, workspaceId) {
|
|
|
43385
43491
|
}
|
|
43386
43492
|
}
|
|
43387
43493
|
async function collectWikiDecisionEntries(cwd) {
|
|
43388
|
-
const dir =
|
|
43494
|
+
const dir = path140.join(cwd, ".metaproject", "wiki", "decisions");
|
|
43389
43495
|
let files;
|
|
43390
43496
|
try {
|
|
43391
43497
|
files = (await readdir24(dir)).filter((name) => name.endsWith(".md"));
|
|
@@ -43394,12 +43500,12 @@ async function collectWikiDecisionEntries(cwd) {
|
|
|
43394
43500
|
}
|
|
43395
43501
|
const entries = [];
|
|
43396
43502
|
for (const file of files) {
|
|
43397
|
-
const absolutePath =
|
|
43503
|
+
const absolutePath = path140.join(dir, file);
|
|
43398
43504
|
try {
|
|
43399
43505
|
const content = await readFile69(absolutePath, "utf8");
|
|
43400
43506
|
entries.push({
|
|
43401
43507
|
absolutePath,
|
|
43402
|
-
relativePath:
|
|
43508
|
+
relativePath: path140.posix.join("decisions", file),
|
|
43403
43509
|
type: extractHeaderField(content, "Type") ?? "decision",
|
|
43404
43510
|
title: extractTitle(content),
|
|
43405
43511
|
version: extractHeaderField(content, "Version") ?? null,
|
|
@@ -43621,7 +43727,7 @@ function createRealMemoryOwnerWriter(cwd, opts) {
|
|
|
43621
43727
|
init_service4();
|
|
43622
43728
|
init_guard();
|
|
43623
43729
|
import { readFile as readFile71 } from "fs/promises";
|
|
43624
|
-
import
|
|
43730
|
+
import path141 from "path";
|
|
43625
43731
|
function renderWrapUpDecisionPage(input) {
|
|
43626
43732
|
const note = input.note?.trim();
|
|
43627
43733
|
return `# ${input.title}
|
|
@@ -43662,7 +43768,7 @@ export is the source of truth for what actually happened.
|
|
|
43662
43768
|
`;
|
|
43663
43769
|
}
|
|
43664
43770
|
function wikiPageRelativePath(proposalId) {
|
|
43665
|
-
return
|
|
43771
|
+
return path141.posix.join("decisions", `sac-${proposalId}.md`);
|
|
43666
43772
|
}
|
|
43667
43773
|
function createRealWikiOwnerWriter(cwd, opts) {
|
|
43668
43774
|
const now = opts?.now ?? (() => new Date);
|
|
@@ -43670,7 +43776,7 @@ function createRealWikiOwnerWriter(cwd, opts) {
|
|
|
43670
43776
|
const relativePath = staged.relativePath;
|
|
43671
43777
|
const content = staged.content;
|
|
43672
43778
|
const history = await openWikiWriteContext(cwd, "sac proposal accept");
|
|
43673
|
-
await writeWikiPage(history,
|
|
43779
|
+
await writeWikiPage(history, path141.join(cwd, ".metaproject", "wiki", relativePath), content);
|
|
43674
43780
|
return { ok: true };
|
|
43675
43781
|
};
|
|
43676
43782
|
return {
|
|
@@ -43819,7 +43925,7 @@ class ProposalLifecycleService {
|
|
|
43819
43925
|
now;
|
|
43820
43926
|
constructor(options) {
|
|
43821
43927
|
this.options = options;
|
|
43822
|
-
this.root =
|
|
43928
|
+
this.root = path142.resolve(options.workspaceRoot);
|
|
43823
43929
|
this.now = options.now ?? (() => new Date);
|
|
43824
43930
|
}
|
|
43825
43931
|
async create(input) {
|
|
@@ -43839,7 +43945,7 @@ class ProposalLifecycleService {
|
|
|
43839
43945
|
if (manifest.status === "archived")
|
|
43840
43946
|
throw new ProposalLifecycleError("guard_denied", "workspace is archived");
|
|
43841
43947
|
const file = this.proposalPath(workspaceId, proposal.id);
|
|
43842
|
-
await mkdir35(
|
|
43948
|
+
await mkdir35(path142.dirname(file), { recursive: true, mode: 448 });
|
|
43843
43949
|
return withFileLock(`${file}.lock`, async () => {
|
|
43844
43950
|
const consume = this.options.wrapUpAuthority.consume(input.wrapUp, { actor, workspaceId });
|
|
43845
43951
|
if (consume !== "ok")
|
|
@@ -43867,7 +43973,7 @@ class ProposalLifecycleService {
|
|
|
43867
43973
|
return this.options.workspaces.withAuthorizedActor({ actorContext: actor, workspaceId: input.workspaceId, action: "review", execute: async (manifest) => {
|
|
43868
43974
|
const proposal = await this.loadProposal(input.workspaceId, input.proposalId);
|
|
43869
43975
|
const ledger = this.ledgerPath(input.workspaceId);
|
|
43870
|
-
await mkdir35(
|
|
43976
|
+
await mkdir35(path142.dirname(ledger), { recursive: true, mode: 448 });
|
|
43871
43977
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
43872
43978
|
const records = (await this.records(ledger)).filter((record) => record.proposalId === proposal.id);
|
|
43873
43979
|
const events = records.filter((record) => record.recordType === "proposal-transition");
|
|
@@ -43909,7 +44015,7 @@ class ProposalLifecycleService {
|
|
|
43909
44015
|
} });
|
|
43910
44016
|
}
|
|
43911
44017
|
async listProposedProposals(workspaceId) {
|
|
43912
|
-
const proposalsDir =
|
|
44018
|
+
const proposalsDir = path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals");
|
|
43913
44019
|
let entries;
|
|
43914
44020
|
try {
|
|
43915
44021
|
entries = (await readdir25(proposalsDir)).filter((name) => name.endsWith(".json"));
|
|
@@ -43921,7 +44027,7 @@ class ProposalLifecycleService {
|
|
|
43921
44027
|
const proposals = [];
|
|
43922
44028
|
for (const entry of entries) {
|
|
43923
44029
|
try {
|
|
43924
|
-
const parsed = JSON.parse(await readFile73(
|
|
44030
|
+
const parsed = JSON.parse(await readFile73(path142.join(proposalsDir, entry), "utf8"));
|
|
43925
44031
|
if (parsed.recordType === "proposal-created")
|
|
43926
44032
|
proposals.push(parsed);
|
|
43927
44033
|
} catch {}
|
|
@@ -44187,31 +44293,31 @@ class ProposalLifecycleService {
|
|
|
44187
44293
|
}
|
|
44188
44294
|
}
|
|
44189
44295
|
proposalPath(workspaceId, proposalId) {
|
|
44190
|
-
return
|
|
44296
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
|
|
44191
44297
|
}
|
|
44192
44298
|
decisionRef(proposalId, key) {
|
|
44193
44299
|
return `./proposals/${proposalId}.${hash2(key)}.decision.json`;
|
|
44194
44300
|
}
|
|
44195
44301
|
decisionPath(workspaceId, proposalId, key) {
|
|
44196
|
-
return
|
|
44302
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash2(key)}.decision.json`);
|
|
44197
44303
|
}
|
|
44198
44304
|
approvalRef(proposalId, key) {
|
|
44199
44305
|
return `./proposals/${proposalId}.${hash2(key)}.approval.json`;
|
|
44200
44306
|
}
|
|
44201
44307
|
approvalPath(workspaceId, proposalId, key) {
|
|
44202
|
-
return
|
|
44308
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash2(key)}.approval.json`);
|
|
44203
44309
|
}
|
|
44204
44310
|
writeResultPath(workspaceId, proposalId, key) {
|
|
44205
|
-
return
|
|
44311
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash2(key)}.write-result.json`);
|
|
44206
44312
|
}
|
|
44207
44313
|
intentRef(proposalId, key) {
|
|
44208
44314
|
return `./proposals/${proposalId}.${hash2(key)}.write-intent.json`;
|
|
44209
44315
|
}
|
|
44210
44316
|
intentPath(workspaceId, proposalId, key) {
|
|
44211
|
-
return
|
|
44317
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash2(key)}.write-intent.json`);
|
|
44212
44318
|
}
|
|
44213
44319
|
attemptPath(workspaceId, proposalId, key) {
|
|
44214
|
-
return
|
|
44320
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.${hash2(key)}.owner-write-attempt.json`);
|
|
44215
44321
|
}
|
|
44216
44322
|
async loadWriteResult(workspaceId, proposalId, key) {
|
|
44217
44323
|
try {
|
|
@@ -44238,7 +44344,7 @@ class ProposalLifecycleService {
|
|
|
44238
44344
|
}
|
|
44239
44345
|
}
|
|
44240
44346
|
ledgerPath(workspaceId) {
|
|
44241
|
-
return
|
|
44347
|
+
return path142.join(this.root, ".metaproject", "workspaces", workspaceId, "activity.jsonl");
|
|
44242
44348
|
}
|
|
44243
44349
|
timestamp() {
|
|
44244
44350
|
return this.now().toISOString();
|
|
@@ -44357,11 +44463,11 @@ async function replayPreservation(input) {
|
|
|
44357
44463
|
if (typeof slate.workspaceId !== "string" || !/^[a-z][a-z0-9-]{2,63}$/.test(slate.workspaceId))
|
|
44358
44464
|
throw new Error("invalid workspace identity");
|
|
44359
44465
|
const workspaceId = slate.workspaceId;
|
|
44360
|
-
const outbox =
|
|
44361
|
-
const receipt = await readReplayJson(
|
|
44466
|
+
const outbox = path143.join(input.dir, "preservation", "outbox");
|
|
44467
|
+
const receipt = await readReplayJson(path143.join(outbox, `${input.snapshot}.json`));
|
|
44362
44468
|
if (receipt.version !== 1 || receipt.snapshot !== input.snapshot || receipt.state !== "pending-review")
|
|
44363
44469
|
throw new Error("invalid outbox receipt");
|
|
44364
|
-
return withFileLock(
|
|
44470
|
+
return withFileLock(path143.join(outbox, `${input.snapshot}.delivery.lock`), async () => {
|
|
44365
44471
|
input.signal?.throwIfAborted();
|
|
44366
44472
|
const { service, wrapUpAuthority, authorizationServer } = createHarnessProposalLifecycleService(input.cwd, {
|
|
44367
44473
|
workspaceId,
|
|
@@ -44388,12 +44494,12 @@ async function replayPreservation(input) {
|
|
|
44388
44494
|
} catch (error) {
|
|
44389
44495
|
if (!(error instanceof ProposalLifecycleError) || error.code !== "conflict")
|
|
44390
44496
|
throw error;
|
|
44391
|
-
const existing = await readReplayJson(
|
|
44497
|
+
const existing = await readReplayJson(path143.join(input.cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${id}.json`));
|
|
44392
44498
|
const refs = (evidence) => evidence.map((e) => [e.uri, e.revision]);
|
|
44393
44499
|
if (existing.workspaceId !== workspaceId || existing.author !== actor.subject || existing.kind !== "follow-up" || existing.proposalRevision !== input.snapshot || existing.wrapUp?.sourceRef !== sourceRef || existing.wrapUp?.sourceRevision !== wrapUp.sourceRevision || JSON.stringify(refs(existing.evidence)) !== JSON.stringify(refs(wrapUp.evidence)))
|
|
44394
44500
|
throw new Error("delivery conflict mismatch", { cause: error });
|
|
44395
44501
|
}
|
|
44396
|
-
const delivered =
|
|
44502
|
+
const delivered = path143.join(outbox, `${input.snapshot}.delivered`);
|
|
44397
44503
|
try {
|
|
44398
44504
|
const marker = await readReplayJson(delivered);
|
|
44399
44505
|
if (marker.version !== 1 || marker.workspaceId !== workspaceId || marker.proposalId !== id || marker.snapshot !== input.snapshot) {
|
|
@@ -44406,7 +44512,7 @@ async function replayPreservation(input) {
|
|
|
44406
44512
|
}
|
|
44407
44513
|
input.afterCreate?.();
|
|
44408
44514
|
input.signal?.throwIfAborted();
|
|
44409
|
-
writePreservationFile(
|
|
44515
|
+
writePreservationFile(path143.join(outbox, `${input.snapshot}.delivered`), JSON.stringify({ version: 1, workspaceId, proposalId: id, snapshot: input.snapshot }));
|
|
44410
44516
|
return "proposed";
|
|
44411
44517
|
}, input.signal ? { signal: input.signal } : {});
|
|
44412
44518
|
}
|
|
@@ -44429,14 +44535,14 @@ async function scanPreservationOutbox(input) {
|
|
|
44429
44535
|
const result = { proposed: 0, deferred: 0, unbound: 0 };
|
|
44430
44536
|
let entries;
|
|
44431
44537
|
try {
|
|
44432
|
-
entries = await readdir26(
|
|
44538
|
+
entries = await readdir26(path143.join(input.dir, "preservation", "outbox"));
|
|
44433
44539
|
} catch (error) {
|
|
44434
44540
|
if (error.code === "ENOENT")
|
|
44435
44541
|
return result;
|
|
44436
44542
|
throw error;
|
|
44437
44543
|
}
|
|
44438
44544
|
let attempted = 0;
|
|
44439
|
-
const cursorFile =
|
|
44545
|
+
const cursorFile = path143.join(input.dir, "preservation", "outbox", "SCAN_CURSOR");
|
|
44440
44546
|
for (const entry of resumeScan(entries.filter((name) => /^[a-f0-9]{64}\.json$/.test(name)), cursorFile)) {
|
|
44441
44547
|
if (input.signal?.aborted || Date.now() >= (input.deadline ?? Infinity) || attempted >= (input.maxItems ?? 8))
|
|
44442
44548
|
break;
|
|
@@ -44461,11 +44567,11 @@ async function replayProjectPreservationOutboxes(input) {
|
|
|
44461
44567
|
return boundedReplay(input, scanProjectPreservationOutboxes);
|
|
44462
44568
|
}
|
|
44463
44569
|
async function retryProjectPreservation(cwd) {
|
|
44464
|
-
return replayProjectPreservationOutboxes({ cwd, dir:
|
|
44570
|
+
return replayProjectPreservationOutboxes({ cwd, dir: path143.join(projectSessionsDir(cwd), ".explicit-retry") });
|
|
44465
44571
|
}
|
|
44466
44572
|
async function scanProjectPreservationOutboxes(input) {
|
|
44467
44573
|
const result = { proposed: 0, deferred: 0, unbound: 0 };
|
|
44468
|
-
const root =
|
|
44574
|
+
const root = path143.dirname(input.dir);
|
|
44469
44575
|
let entries;
|
|
44470
44576
|
try {
|
|
44471
44577
|
entries = await readdir26(root, { withFileTypes: true });
|
|
@@ -44476,7 +44582,7 @@ async function scanProjectPreservationOutboxes(input) {
|
|
|
44476
44582
|
}
|
|
44477
44583
|
const deadline = input.deadline ?? Date.now() + 2000;
|
|
44478
44584
|
let scanned = 0;
|
|
44479
|
-
const cursorFile =
|
|
44585
|
+
const cursorFile = path143.join(root, ".preservation-replay-cursor");
|
|
44480
44586
|
const candidates = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
|
|
44481
44587
|
for (const name of resumeScan(candidates, cursorFile)) {
|
|
44482
44588
|
if (input.signal?.aborted || Date.now() >= deadline || scanned >= 32)
|
|
@@ -44485,7 +44591,7 @@ async function scanProjectPreservationOutboxes(input) {
|
|
|
44485
44591
|
recordScanCursor(cursorFile, name);
|
|
44486
44592
|
scanned++;
|
|
44487
44593
|
try {
|
|
44488
|
-
const replay = await replayPreservationOutbox({ ...input, deadline, maxItems: 1, dir:
|
|
44594
|
+
const replay = await replayPreservationOutbox({ ...input, deadline, maxItems: 1, dir: path143.join(root, name) });
|
|
44489
44595
|
result.proposed += replay.proposed;
|
|
44490
44596
|
result.deferred += replay.deferred;
|
|
44491
44597
|
result.unbound += replay.unbound;
|
|
@@ -44524,7 +44630,7 @@ async function boundedReplay(input, scan) {
|
|
|
44524
44630
|
import { createHash as createHash35, randomUUID as randomUUID9 } from "crypto";
|
|
44525
44631
|
init_assembly();
|
|
44526
44632
|
import { appendFile as appendFile8, mkdir as mkdir36, open as open4, readFile as readFile74, rename as rename7, rm as rm8, stat as stat18, writeFile as writeFile24 } from "fs/promises";
|
|
44527
|
-
import
|
|
44633
|
+
import path144 from "path";
|
|
44528
44634
|
init_fs();
|
|
44529
44635
|
|
|
44530
44636
|
// src/sac/policy-experiment.ts
|
|
@@ -45267,7 +45373,7 @@ function hasReportIntegrity(report) {
|
|
|
45267
45373
|
return reportHashPattern.test(reportDigest) && sha2566(body) === reportDigest;
|
|
45268
45374
|
}
|
|
45269
45375
|
async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
|
|
45270
|
-
const configPath =
|
|
45376
|
+
const configPath = path144.join(workspaceRoot, ...policyExperimentConfigPath);
|
|
45271
45377
|
let configJson;
|
|
45272
45378
|
try {
|
|
45273
45379
|
const raw = await readFile74(configPath, "utf8").catch((error) => {
|
|
@@ -45465,9 +45571,9 @@ class FwkReadService {
|
|
|
45465
45571
|
const recordedAt = nowIso2(now);
|
|
45466
45572
|
const id = `receipt-${randomUUID9().replace(/-/g, "").slice(0, 16)}`;
|
|
45467
45573
|
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"}` } : {} };
|
|
45468
|
-
const ledger =
|
|
45469
|
-
const checkpointPath =
|
|
45470
|
-
await mkdir36(
|
|
45574
|
+
const ledger = path144.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
|
|
45575
|
+
const checkpointPath = path144.join(path144.dirname(ledger), "access-receipts.checkpoint.json");
|
|
45576
|
+
await mkdir36(path144.dirname(ledger), { recursive: true, mode: 448 });
|
|
45471
45577
|
return withFileLock(`${ledger}.lock`, async () => {
|
|
45472
45578
|
const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
|
|
45473
45579
|
const receipt = sealAccessReceipt(base, state.headHash);
|
|
@@ -45570,7 +45676,7 @@ function normalizeFwkResult(result) {
|
|
|
45570
45676
|
// src/sac/collaboration-service.ts
|
|
45571
45677
|
import { appendFile as appendFile9, mkdir as mkdir37, readFile as readFile75 } from "fs/promises";
|
|
45572
45678
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
45573
|
-
import
|
|
45679
|
+
import path145 from "path";
|
|
45574
45680
|
class CollaborationServiceError extends Error {
|
|
45575
45681
|
code;
|
|
45576
45682
|
constructor(code, message) {
|
|
@@ -45585,7 +45691,7 @@ class CollaborationService {
|
|
|
45585
45691
|
this.input = input;
|
|
45586
45692
|
}
|
|
45587
45693
|
file(id) {
|
|
45588
|
-
return
|
|
45694
|
+
return path145.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
|
|
45589
45695
|
}
|
|
45590
45696
|
async overview(input) {
|
|
45591
45697
|
const manifest = await this.input.workspaces.show(input);
|
|
@@ -45622,7 +45728,7 @@ class CollaborationService {
|
|
|
45622
45728
|
occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
|
|
45623
45729
|
...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
|
|
45624
45730
|
});
|
|
45625
|
-
await mkdir37(
|
|
45731
|
+
await mkdir37(path145.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
|
|
45626
45732
|
await appendFile9(this.file(manifest.id), `${JSON.stringify(activity)}
|
|
45627
45733
|
`, { mode: 384 });
|
|
45628
45734
|
return normalizeCollaborationResult(activity);
|
|
@@ -45672,14 +45778,14 @@ function normalizeCollaborationResult(value) {
|
|
|
45672
45778
|
// src/session/external-slate.ts
|
|
45673
45779
|
init_fs();
|
|
45674
45780
|
import { mkdir as mkdir39, readdir as readdir27, readFile as readFile76 } from "fs/promises";
|
|
45675
|
-
import
|
|
45781
|
+
import path147 from "path";
|
|
45676
45782
|
|
|
45677
45783
|
// src/sac/machine-wrap-up.ts
|
|
45678
45784
|
init_fs();
|
|
45679
45785
|
init_slate();
|
|
45680
45786
|
import { createHash as createHash36, randomUUID as randomUUID12 } from "crypto";
|
|
45681
45787
|
import { mkdir as mkdir38 } from "fs/promises";
|
|
45682
|
-
import
|
|
45788
|
+
import path146 from "path";
|
|
45683
45789
|
|
|
45684
45790
|
// src/sac/workspace-resolve.ts
|
|
45685
45791
|
init_redact();
|
|
@@ -45822,11 +45928,11 @@ async function resolveMachineWrapUp(input) {
|
|
|
45822
45928
|
`;
|
|
45823
45929
|
const seedsJson = `${JSON.stringify(seedsForKind.map((seed) => ({ text: seed.text, source: describeSource(seed.source) })), null, 2)}
|
|
45824
45930
|
`;
|
|
45825
|
-
const evidenceRelBase =
|
|
45931
|
+
const evidenceRelBase = path146.posix.join(".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
45826
45932
|
const floor = await applyEvidenceRedactionFloor(input.cwd, [
|
|
45827
|
-
{ name: `${input.kind}.diff.txt`, content: diffBody, path:
|
|
45828
|
-
{ name: `${input.kind}.flow.json`, content: flowSnapshotJson, path:
|
|
45829
|
-
{ name: `${input.kind}.seeds.json`, content: seedsJson, path:
|
|
45933
|
+
{ name: `${input.kind}.diff.txt`, content: diffBody, path: path146.posix.join(evidenceRelBase, `${input.kind}.diff.txt`), source: "tool-output" },
|
|
45934
|
+
{ name: `${input.kind}.flow.json`, content: flowSnapshotJson, path: path146.posix.join(evidenceRelBase, `${input.kind}.flow.json`), source: "generated" },
|
|
45935
|
+
{ name: `${input.kind}.seeds.json`, content: seedsJson, path: path146.posix.join(evidenceRelBase, `${input.kind}.seeds.json`), source: "generated" }
|
|
45830
45936
|
]);
|
|
45831
45937
|
if (!floor.ok)
|
|
45832
45938
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
@@ -45880,14 +45986,14 @@ ${safeSeeds.content}`;
|
|
|
45880
45986
|
}
|
|
45881
45987
|
summary = result.text.trim().length > 0 ? result.text.trim() : mechanicalSummary(summaryDiff, course);
|
|
45882
45988
|
}
|
|
45883
|
-
const evidenceDir =
|
|
45989
|
+
const evidenceDir = path146.join(input.cwd, ".metaproject", "workspaces", input.workspaceId, "machine-evidence");
|
|
45884
45990
|
await mkdir38(evidenceDir, { recursive: true });
|
|
45885
45991
|
const diffFile = `${input.kind}.${shortHash}.diff.txt`;
|
|
45886
45992
|
const flowFile = `${input.kind}.${shortHash}.flow.json`;
|
|
45887
45993
|
const seedsFile = `${input.kind}.${shortHash}.seeds.json`;
|
|
45888
|
-
await writeFileAtomic(
|
|
45889
|
-
await writeFileAtomic(
|
|
45890
|
-
await writeFileAtomic(
|
|
45994
|
+
await writeFileAtomic(path146.join(evidenceDir, diffFile), safeDiff.content);
|
|
45995
|
+
await writeFileAtomic(path146.join(evidenceDir, flowFile), safeFlow.content);
|
|
45996
|
+
await writeFileAtomic(path146.join(evidenceDir, seedsFile), safeSeeds.content);
|
|
45891
45997
|
const observedAt = now().toISOString();
|
|
45892
45998
|
const relBase = `./.metaproject/workspaces/${input.workspaceId}/machine-evidence`;
|
|
45893
45999
|
const evidence = [
|
|
@@ -45897,7 +46003,7 @@ ${safeSeeds.content}`;
|
|
|
45897
46003
|
];
|
|
45898
46004
|
if (floor.notice !== undefined) {
|
|
45899
46005
|
const noticeFile = `${input.kind}.${shortHash}.redaction.md`;
|
|
45900
|
-
await writeFileAtomic(
|
|
46006
|
+
await writeFileAtomic(path146.join(evidenceDir, noticeFile), floor.notice);
|
|
45901
46007
|
evidence.push({ kind: REDACTION_NOTICE_KIND, uri: `${relBase}/${noticeFile}`, revision: sha2567(floor.notice), observedAt });
|
|
45902
46008
|
}
|
|
45903
46009
|
return {
|
|
@@ -45916,18 +46022,18 @@ async function writeFlooredArchiveArtifact(params) {
|
|
|
45916
46022
|
{
|
|
45917
46023
|
name: params.filename,
|
|
45918
46024
|
content: params.content,
|
|
45919
|
-
path:
|
|
46025
|
+
path: path146.posix.join("slate-archive", params.filename),
|
|
45920
46026
|
source: "generated"
|
|
45921
46027
|
}
|
|
45922
46028
|
]);
|
|
45923
46029
|
if (!floor.ok)
|
|
45924
46030
|
return { ok: false, code: "security_denied", detail: floor.reason };
|
|
45925
46031
|
const body = floor.bodies[0];
|
|
45926
|
-
const archiveDir =
|
|
46032
|
+
const archiveDir = path146.join(params.dir, "slate-archive");
|
|
45927
46033
|
await mkdir38(archiveDir, { recursive: true });
|
|
45928
|
-
await writeFileAtomic(
|
|
46034
|
+
await writeFileAtomic(path146.join(archiveDir, params.filename), body.content);
|
|
45929
46035
|
if (floor.notice !== undefined) {
|
|
45930
|
-
await writeFileAtomic(
|
|
46036
|
+
await writeFileAtomic(path146.join(archiveDir, `${params.filename}${ARCHIVE_REDACTION_NOTICE_SUFFIX}`), floor.notice);
|
|
45931
46037
|
}
|
|
45932
46038
|
return { ok: true, altered: body.altered };
|
|
45933
46039
|
}
|
|
@@ -45963,9 +46069,9 @@ async function writeWrapUpOutcomeArtifact(cwd, dir, trigger, now, groups) {
|
|
|
45963
46069
|
return;
|
|
45964
46070
|
const refusalIso = now().toISOString();
|
|
45965
46071
|
const refusalFilename = `${refusalIso.replace(/[:.]/g, "-")}-wrap-up-outcome.json`;
|
|
45966
|
-
const archiveDir =
|
|
46072
|
+
const archiveDir = path146.join(dir, "slate-archive");
|
|
45967
46073
|
await mkdir38(archiveDir, { recursive: true });
|
|
45968
|
-
await writeFileAtomic(
|
|
46074
|
+
await writeFileAtomic(path146.join(archiveDir, refusalFilename), `${JSON.stringify({
|
|
45969
46075
|
recordType: "wrap-up-outcome",
|
|
45970
46076
|
trigger,
|
|
45971
46077
|
generatedAt: refusalIso,
|
|
@@ -46102,7 +46208,7 @@ async function runWrapUp(input) {
|
|
|
46102
46208
|
|
|
46103
46209
|
// src/session/external-slate.ts
|
|
46104
46210
|
function externalSlatesDir(cwd) {
|
|
46105
|
-
return
|
|
46211
|
+
return path147.join(cwd, ".keryx", "external-slates");
|
|
46106
46212
|
}
|
|
46107
46213
|
var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
46108
46214
|
function assertValidExternalSessionId(externalSessionId) {
|
|
@@ -46112,14 +46218,14 @@ function assertValidExternalSessionId(externalSessionId) {
|
|
|
46112
46218
|
}
|
|
46113
46219
|
function externalSlatePath(cwd, externalSessionId) {
|
|
46114
46220
|
assertValidExternalSessionId(externalSessionId);
|
|
46115
|
-
return
|
|
46221
|
+
return path147.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
|
|
46116
46222
|
}
|
|
46117
46223
|
function externalSlateLockPath(cwd, externalSessionId) {
|
|
46118
46224
|
return `${externalSlatePath(cwd, externalSessionId)}.lock`;
|
|
46119
46225
|
}
|
|
46120
46226
|
function externalSlateEvidenceDir(cwd, externalSessionId) {
|
|
46121
46227
|
assertValidExternalSessionId(externalSessionId);
|
|
46122
|
-
return
|
|
46228
|
+
return path147.join(externalSlatesDir(cwd), externalSessionId);
|
|
46123
46229
|
}
|
|
46124
46230
|
async function readExternalSlate(cwd, externalSessionId) {
|
|
46125
46231
|
try {
|
|
@@ -46203,12 +46309,12 @@ init_service2();
|
|
|
46203
46309
|
|
|
46204
46310
|
// src/standard/service.ts
|
|
46205
46311
|
init_fs();
|
|
46206
|
-
import
|
|
46312
|
+
import path150 from "path";
|
|
46207
46313
|
import { readFile as readFile79 } from "fs/promises";
|
|
46208
46314
|
|
|
46209
46315
|
// src/standard/validate.ts
|
|
46210
46316
|
init_fs();
|
|
46211
|
-
import
|
|
46317
|
+
import path149 from "path";
|
|
46212
46318
|
import { readFile as readFile78 } from "fs/promises";
|
|
46213
46319
|
|
|
46214
46320
|
// src/standard/schemas.ts
|
|
@@ -46338,7 +46444,7 @@ var SCHEMA_REGISTRY2 = {
|
|
|
46338
46444
|
// src/standard/profiles.ts
|
|
46339
46445
|
init_fs();
|
|
46340
46446
|
init_entrypoint_targets();
|
|
46341
|
-
import
|
|
46447
|
+
import path148 from "path";
|
|
46342
46448
|
import { readFile as readFile77 } from "fs/promises";
|
|
46343
46449
|
var AGENT_MODULES = ["gdgraph", "gdctx", "gdskills", "gdwiki", "memory"];
|
|
46344
46450
|
var CI_MODULES = ["health", "testing"];
|
|
@@ -46363,7 +46469,7 @@ function enabledModuleKeys(manifest) {
|
|
|
46363
46469
|
}
|
|
46364
46470
|
async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
46365
46471
|
for (const { path: name } of namedEntrypoints(manifest)) {
|
|
46366
|
-
const filePath =
|
|
46472
|
+
const filePath = path148.join(cwd, name);
|
|
46367
46473
|
if (!await pathExists(filePath)) {
|
|
46368
46474
|
continue;
|
|
46369
46475
|
}
|
|
@@ -46375,16 +46481,16 @@ async function rootEntrypointLinksIndex(cwd, manifest) {
|
|
|
46375
46481
|
return false;
|
|
46376
46482
|
}
|
|
46377
46483
|
async function minimalSatisfied(cwd) {
|
|
46378
|
-
const metaprojectRoot =
|
|
46484
|
+
const metaprojectRoot = path148.join(cwd, ".metaproject");
|
|
46379
46485
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
46380
46486
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
46381
46487
|
for (const file of requiredFiles) {
|
|
46382
|
-
if (!await pathExists(
|
|
46488
|
+
if (!await pathExists(path148.join(metaprojectRoot, file))) {
|
|
46383
46489
|
return false;
|
|
46384
46490
|
}
|
|
46385
46491
|
}
|
|
46386
46492
|
for (const dir of requiredDirs) {
|
|
46387
|
-
if (!await pathExists(
|
|
46493
|
+
if (!await pathExists(path148.join(metaprojectRoot, dir))) {
|
|
46388
46494
|
return false;
|
|
46389
46495
|
}
|
|
46390
46496
|
}
|
|
@@ -46397,7 +46503,7 @@ async function ciSatisfied(cwd, manifest) {
|
|
|
46397
46503
|
return false;
|
|
46398
46504
|
}
|
|
46399
46505
|
for (const key of ciModules) {
|
|
46400
|
-
if (await pathExists(
|
|
46506
|
+
if (await pathExists(path148.join(cwd, ".metaproject", "data", key, "artifacts"))) {
|
|
46401
46507
|
return true;
|
|
46402
46508
|
}
|
|
46403
46509
|
}
|
|
@@ -46410,7 +46516,7 @@ async function evaluateProfiles(cwd, manifest) {
|
|
|
46410
46516
|
satisfied.push("minimal");
|
|
46411
46517
|
}
|
|
46412
46518
|
const hasAgentModule = AGENT_MODULES.some((key) => new Set(enabledModuleKeys(manifest)).has(key));
|
|
46413
|
-
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(
|
|
46519
|
+
const agent = minimal && hasAgentModule && await rootEntrypointLinksIndex(cwd, manifest) && await pathExists(path148.join(cwd, ".metaproject", "rules"));
|
|
46414
46520
|
if (agent) {
|
|
46415
46521
|
satisfied.push("agent");
|
|
46416
46522
|
}
|
|
@@ -46579,20 +46685,20 @@ var MANIFEST_TOP_LEVEL_SCHEMA = {
|
|
|
46579
46685
|
async function validateWorkspace2(cwd) {
|
|
46580
46686
|
const errors = [];
|
|
46581
46687
|
const warnings = [];
|
|
46582
|
-
const metaprojectRoot =
|
|
46688
|
+
const metaprojectRoot = path149.join(cwd, ".metaproject");
|
|
46583
46689
|
const requiredFiles = ["index.md", "README.md", "metaproject.json"];
|
|
46584
46690
|
for (const file of requiredFiles) {
|
|
46585
|
-
if (!await pathExists(
|
|
46691
|
+
if (!await pathExists(path149.join(metaprojectRoot, file))) {
|
|
46586
46692
|
errors.push(issue("missing-required-file", `Required file .metaproject/${file} is missing`, `Run \`keryx init\` to scaffold .metaproject/${file}.`));
|
|
46587
46693
|
}
|
|
46588
46694
|
}
|
|
46589
46695
|
const requiredDirs = ["modules", "rules", "skills", "data"];
|
|
46590
46696
|
for (const dir of requiredDirs) {
|
|
46591
|
-
if (!await pathExists(
|
|
46697
|
+
if (!await pathExists(path149.join(metaprojectRoot, dir))) {
|
|
46592
46698
|
errors.push(issue("missing-required-dir", `Required directory .metaproject/${dir}/ is missing`, `Run \`keryx init\` to create .metaproject/${dir}/.`));
|
|
46593
46699
|
}
|
|
46594
46700
|
}
|
|
46595
|
-
const manifestPath =
|
|
46701
|
+
const manifestPath = path149.join(metaprojectRoot, "metaproject.json");
|
|
46596
46702
|
if (!await pathExists(manifestPath)) {
|
|
46597
46703
|
return { ok: errors.length === 0, errors, warnings };
|
|
46598
46704
|
}
|
|
@@ -46624,7 +46730,7 @@ async function validateWorkspace2(cwd) {
|
|
|
46624
46730
|
if (typeof value !== "string") {
|
|
46625
46731
|
continue;
|
|
46626
46732
|
}
|
|
46627
|
-
if (!await pathExists(
|
|
46733
|
+
if (!await pathExists(path149.join(cwd, value))) {
|
|
46628
46734
|
errors.push(issue("missing-declared-path", `Declared path paths.${key} -> ${value} does not exist`, `Create ${value} or remove paths.${key} from metaproject.json.`));
|
|
46629
46735
|
}
|
|
46630
46736
|
}
|
|
@@ -46634,7 +46740,7 @@ async function validateWorkspace2(cwd) {
|
|
|
46634
46740
|
continue;
|
|
46635
46741
|
}
|
|
46636
46742
|
if (typeof entry.manifest === "string") {
|
|
46637
|
-
if (!await pathExists(
|
|
46743
|
+
if (!await pathExists(path149.join(cwd, entry.manifest))) {
|
|
46638
46744
|
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.`));
|
|
46639
46745
|
}
|
|
46640
46746
|
}
|
|
@@ -46643,14 +46749,14 @@ async function validateWorkspace2(cwd) {
|
|
|
46643
46749
|
if (typeof value !== "string") {
|
|
46644
46750
|
continue;
|
|
46645
46751
|
}
|
|
46646
|
-
if (!await pathExists(
|
|
46752
|
+
if (!await pathExists(path149.join(cwd, value))) {
|
|
46647
46753
|
const sink = field === "data" ? warnings : errors;
|
|
46648
46754
|
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.`));
|
|
46649
46755
|
}
|
|
46650
46756
|
}
|
|
46651
46757
|
}
|
|
46652
46758
|
for (const { path: name, scope } of namedEntrypoints(manifest)) {
|
|
46653
|
-
const filePath =
|
|
46759
|
+
const filePath = path149.join(cwd, name);
|
|
46654
46760
|
if (!await pathExists(filePath)) {
|
|
46655
46761
|
continue;
|
|
46656
46762
|
}
|
|
@@ -47656,7 +47762,7 @@ async function runDoctor(cwd) {
|
|
|
47656
47762
|
return validateWorkspace2(cwd);
|
|
47657
47763
|
}
|
|
47658
47764
|
async function readManifest2(cwd) {
|
|
47659
|
-
const manifestPath =
|
|
47765
|
+
const manifestPath = path150.join(cwd, ".metaproject", "metaproject.json");
|
|
47660
47766
|
if (!await pathExists(manifestPath)) {
|
|
47661
47767
|
throw new Error("Metaproject is not initialized (missing .metaproject/metaproject.json). Run `keryx init` first.");
|
|
47662
47768
|
}
|