@fenglimg/fabric-server 2.0.0-rc.27 → 2.0.0-rc.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-NZSGNQKE.js → chunk-CTQ4UMO4.js} +323 -161
- package/dist/{http-3WADEK3O.js → http-TAI5X7U5.js} +26 -2
- package/dist/index.d.ts +29 -9
- package/dist/index.js +65 -89
- package/package.json +2 -2
|
@@ -84,7 +84,7 @@ import { readFile } from "fs/promises";
|
|
|
84
84
|
import { join } from "path";
|
|
85
85
|
import { agentsMetaSchema } from "@fenglimg/fabric-shared";
|
|
86
86
|
import { IOFabricError } from "@fenglimg/fabric-shared/errors";
|
|
87
|
-
import {
|
|
87
|
+
import { agentsMetaSchema as agentsMetaSchema2 } from "@fenglimg/fabric-shared";
|
|
88
88
|
var AgentsMetaFileMissingError = class extends IOFabricError {
|
|
89
89
|
constructor(metaPath, opts) {
|
|
90
90
|
super(`Fabric agents metadata file is missing: ${metaPath}`, {
|
|
@@ -1078,9 +1078,17 @@ function extractKnowledgeFieldsFromFrontmatter(frontmatter) {
|
|
|
1078
1078
|
`);
|
|
1079
1079
|
}
|
|
1080
1080
|
}
|
|
1081
|
+
const SINGULAR_TO_PLURAL = {
|
|
1082
|
+
model: "models",
|
|
1083
|
+
decision: "decisions",
|
|
1084
|
+
guideline: "guidelines",
|
|
1085
|
+
pitfall: "pitfalls",
|
|
1086
|
+
process: "processes"
|
|
1087
|
+
};
|
|
1081
1088
|
let knowledge_type;
|
|
1082
1089
|
if (rawType !== void 0) {
|
|
1083
|
-
const
|
|
1090
|
+
const normalized = SINGULAR_TO_PLURAL[rawType] ?? rawType;
|
|
1091
|
+
const parsed = KnowledgeTypeSchema.safeParse(normalized);
|
|
1084
1092
|
if (parsed.success) {
|
|
1085
1093
|
knowledge_type = parsed.data;
|
|
1086
1094
|
} else {
|
|
@@ -1429,6 +1437,12 @@ async function ensureKnowledgeFresh(projectRoot, opts) {
|
|
|
1429
1437
|
contextCache.invalidate("file_watch", projectRoot);
|
|
1430
1438
|
}
|
|
1431
1439
|
freshSyncCooldown.delete(projectRoot);
|
|
1440
|
+
if (opts?.autoHealOnDrift === true && events.length > 0) {
|
|
1441
|
+
try {
|
|
1442
|
+
await reconcileKnowledge(projectRoot, { trigger: "auto-heal-after-drift" });
|
|
1443
|
+
} catch {
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1432
1446
|
const status = warnings.length > 0 ? "errors" : "reconciled";
|
|
1433
1447
|
return {
|
|
1434
1448
|
status,
|
|
@@ -1620,11 +1634,11 @@ function checkLockOrThrow(projectRoot, opts) {
|
|
|
1620
1634
|
|
|
1621
1635
|
// src/services/doctor.ts
|
|
1622
1636
|
import { execFileSync } from "child_process";
|
|
1623
|
-
import { existsSync as
|
|
1637
|
+
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
|
|
1624
1638
|
import { access, mkdir as mkdir4, readFile as readFile5, rename, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
1625
1639
|
import { constants } from "fs";
|
|
1626
1640
|
import { homedir as homedir3 } from "os";
|
|
1627
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1641
|
+
import { isAbsolute as isAbsolute2, join as join7, posix, relative as nodeRelative, resolve as resolve3, sep as sep3 } from "path";
|
|
1628
1642
|
import { minimatch } from "minimatch";
|
|
1629
1643
|
import {
|
|
1630
1644
|
agentsMetaSchema as agentsMetaSchema4,
|
|
@@ -1643,6 +1657,41 @@ import {
|
|
|
1643
1657
|
resolveFabricLocale as resolveFabricLocale2
|
|
1644
1658
|
} from "@fenglimg/fabric-shared";
|
|
1645
1659
|
import { detectFramework } from "@fenglimg/fabric-shared/node";
|
|
1660
|
+
import {
|
|
1661
|
+
PAYLOAD_LIMIT_DEFAULT_HARD_BYTES,
|
|
1662
|
+
PAYLOAD_LIMIT_DEFAULT_WARN_BYTES
|
|
1663
|
+
} from "@fenglimg/fabric-shared/node/mcp-payload-guard";
|
|
1664
|
+
|
|
1665
|
+
// src/config-loader.ts
|
|
1666
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
1667
|
+
import { join as join6 } from "path";
|
|
1668
|
+
import { selectionTokenTtlMsSchema } from "@fenglimg/fabric-shared";
|
|
1669
|
+
function readFabricConfig(projectRoot) {
|
|
1670
|
+
const configPath = join6(projectRoot, "fabric.config.json");
|
|
1671
|
+
if (!existsSync4(configPath)) {
|
|
1672
|
+
return {};
|
|
1673
|
+
}
|
|
1674
|
+
const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
|
|
1675
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1676
|
+
throw new Error(`Expected object in ${configPath}`);
|
|
1677
|
+
}
|
|
1678
|
+
return parsed;
|
|
1679
|
+
}
|
|
1680
|
+
function readPayloadLimits(projectRoot) {
|
|
1681
|
+
return readFabricConfig(projectRoot).mcpPayloadLimits;
|
|
1682
|
+
}
|
|
1683
|
+
function readSelectionTokenTtlMs(projectRoot) {
|
|
1684
|
+
try {
|
|
1685
|
+
const raw = readFabricConfig(projectRoot).selection_token_ttl_ms;
|
|
1686
|
+
if (raw === void 0) return void 0;
|
|
1687
|
+
const parsed = selectionTokenTtlMsSchema.safeParse(raw);
|
|
1688
|
+
return parsed.success ? parsed.data : void 0;
|
|
1689
|
+
} catch {
|
|
1690
|
+
return void 0;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/services/doctor.ts
|
|
1646
1695
|
import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
1647
1696
|
var ORPHAN_DEMOTE_THRESHOLD_DAYS = {
|
|
1648
1697
|
stable: 90,
|
|
@@ -1753,6 +1802,7 @@ async function runDoctorReport(target) {
|
|
|
1753
1802
|
inspectL2ManagedBlockDrift(projectRoot)
|
|
1754
1803
|
]);
|
|
1755
1804
|
const mcpConfigInWrongFile = inspectMcpConfigInWrongFile(projectRoot);
|
|
1805
|
+
const skillRefMirror = inspectSkillRefMirror(projectRoot);
|
|
1756
1806
|
const metaManuallyDiverged = await inspectMetaManuallyDiverged(projectRoot);
|
|
1757
1807
|
const knowledgeDirUnindexed = inspectKnowledgeDirUnindexed(projectRoot, meta);
|
|
1758
1808
|
const knowledgeDirMissing = inspectKnowledgeDirMissing(projectRoot);
|
|
@@ -1815,6 +1865,11 @@ async function runDoctorReport(target) {
|
|
|
1815
1865
|
// events.jsonl rows that fail Zod validation because of unknown
|
|
1816
1866
|
// schema_version or event_type tokens. Previously silently dropped.
|
|
1817
1867
|
createEventLedgerSchemaCompatCheck(t, eventLedger),
|
|
1868
|
+
// v2.0.0-rc.28 TASK-04 (audit §3.1 follow-up): SKILL ref/ mirror parity.
|
|
1869
|
+
// Detects hand-edits or partial install that breaks the byte-identical
|
|
1870
|
+
// contract between .claude/skills/<slug>/ref/ and .codex/skills/<slug>/
|
|
1871
|
+
// ref/. warning severity — fab install restores parity.
|
|
1872
|
+
createSkillRefMirrorCheck(t, skillRefMirror),
|
|
1818
1873
|
createMcpConfigInWrongFileCheck(t, mcpConfigInWrongFile),
|
|
1819
1874
|
createMetaManuallyDivergedCheck(t, metaManuallyDiverged),
|
|
1820
1875
|
createKnowledgeDirUnindexedCheck(t, knowledgeDirUnindexed),
|
|
@@ -1908,11 +1963,27 @@ async function runDoctorReport(target) {
|
|
|
1908
1963
|
warningCount: warnings.length,
|
|
1909
1964
|
infoCount: infos.length,
|
|
1910
1965
|
targetFiles: Object.fromEntries(
|
|
1911
|
-
TARGET_FILE_PATHS.map((path2) => [path2,
|
|
1912
|
-
)
|
|
1966
|
+
TARGET_FILE_PATHS.map((path2) => [path2, existsSync5(join7(projectRoot, path2))])
|
|
1967
|
+
),
|
|
1968
|
+
// v2.0.0-rc.29 TASK-008 (BUG-F2): resolve and surface payload thresholds.
|
|
1969
|
+
// Best-effort: a corrupt fabric.config.json should not fail doctor; on
|
|
1970
|
+
// any read/parse error fall back to library defaults with source="default".
|
|
1971
|
+
payload_limits: resolvePayloadLimits(projectRoot)
|
|
1913
1972
|
}
|
|
1914
1973
|
};
|
|
1915
1974
|
}
|
|
1975
|
+
function resolvePayloadLimits(projectRoot) {
|
|
1976
|
+
let override;
|
|
1977
|
+
try {
|
|
1978
|
+
override = readPayloadLimits(projectRoot);
|
|
1979
|
+
} catch {
|
|
1980
|
+
override = void 0;
|
|
1981
|
+
}
|
|
1982
|
+
const warn = override?.warnBytes ?? PAYLOAD_LIMIT_DEFAULT_WARN_BYTES;
|
|
1983
|
+
const hard = override?.hardBytes ?? PAYLOAD_LIMIT_DEFAULT_HARD_BYTES;
|
|
1984
|
+
const source = override?.warnBytes !== void 0 || override?.hardBytes !== void 0 ? "config" : "default";
|
|
1985
|
+
return { warn_bytes: warn, hard_bytes: hard, source };
|
|
1986
|
+
}
|
|
1916
1987
|
async function runDoctorFix(target) {
|
|
1917
1988
|
const projectRoot = normalizeTarget(target);
|
|
1918
1989
|
const before = await runDoctorReport(projectRoot);
|
|
@@ -1935,7 +2006,7 @@ async function runDoctorFix(target) {
|
|
|
1935
2006
|
}
|
|
1936
2007
|
}
|
|
1937
2008
|
if (before.fixable_errors.some((issue) => issue.code === "bootstrap_snapshot_drift")) {
|
|
1938
|
-
const snapshotPath =
|
|
2009
|
+
const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
|
|
1939
2010
|
await ensureParentDirectory(snapshotPath);
|
|
1940
2011
|
await atomicWriteText4(snapshotPath, BOOTSTRAP_CANONICAL);
|
|
1941
2012
|
fixed.push(findIssue(before.fixable_errors, "bootstrap_snapshot_drift"));
|
|
@@ -2009,7 +2080,7 @@ async function runDoctorFix(target) {
|
|
|
2009
2080
|
if (before.infos.some((issue) => issue.code === "stale_serve_lock")) {
|
|
2010
2081
|
const lockInspection = inspectStaleServeLock(projectRoot, Date.now());
|
|
2011
2082
|
if (lockInspection.present && !lockInspection.pidAlive) {
|
|
2012
|
-
const lockFilePath =
|
|
2083
|
+
const lockFilePath = join7(projectRoot, ".fabric", ".serve.lock");
|
|
2013
2084
|
try {
|
|
2014
2085
|
await unlink(lockFilePath);
|
|
2015
2086
|
} catch (err) {
|
|
@@ -2166,7 +2237,7 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
|
|
|
2166
2237
|
};
|
|
2167
2238
|
}
|
|
2168
2239
|
const detail = `${candidate.maturity} -> ${next}`;
|
|
2169
|
-
const absPath =
|
|
2240
|
+
const absPath = join7(projectRoot, candidate.path);
|
|
2170
2241
|
try {
|
|
2171
2242
|
const source = await readFile5(absPath, "utf8");
|
|
2172
2243
|
const rewritten = rewriteFrontmatterMaturity(source, next);
|
|
@@ -2233,11 +2304,11 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
|
|
|
2233
2304
|
}
|
|
2234
2305
|
}
|
|
2235
2306
|
async function applyStaleArchive(projectRoot, candidate, now) {
|
|
2236
|
-
const sourceAbs =
|
|
2237
|
-
const destAbs =
|
|
2307
|
+
const sourceAbs = join7(projectRoot, candidate.path);
|
|
2308
|
+
const destAbs = join7(projectRoot, candidate.archive_path);
|
|
2238
2309
|
const detail = `${candidate.path} -> ${candidate.archive_path}`;
|
|
2239
2310
|
try {
|
|
2240
|
-
await mkdir4(
|
|
2311
|
+
await mkdir4(join7(destAbs, ".."), { recursive: true });
|
|
2241
2312
|
try {
|
|
2242
2313
|
await rename(sourceAbs, destAbs);
|
|
2243
2314
|
} catch (renameError) {
|
|
@@ -2296,7 +2367,7 @@ async function applyStaleArchive(projectRoot, candidate, now) {
|
|
|
2296
2367
|
async function applyPendingAutoArchive(projectRoot, candidate, now) {
|
|
2297
2368
|
const detail = `${candidate.pending_path} -> ${candidate.archived_to}`;
|
|
2298
2369
|
try {
|
|
2299
|
-
await mkdir4(
|
|
2370
|
+
await mkdir4(join7(candidate.archived_to_abs, ".."), { recursive: true });
|
|
2300
2371
|
let moved = false;
|
|
2301
2372
|
if (candidate.layer === "team") {
|
|
2302
2373
|
try {
|
|
@@ -2373,7 +2444,7 @@ function relativePosix(projectRoot, absolutePath) {
|
|
|
2373
2444
|
}
|
|
2374
2445
|
async function applySessionHintsStaleCleanup(projectRoot, candidate) {
|
|
2375
2446
|
const detail = `deleted (${candidate.age_days}d old)`;
|
|
2376
|
-
const absPath =
|
|
2447
|
+
const absPath = join7(projectRoot, candidate.path);
|
|
2377
2448
|
try {
|
|
2378
2449
|
const { unlink: unlink2 } = await import("fs/promises");
|
|
2379
2450
|
await unlink2(absPath);
|
|
@@ -2394,7 +2465,7 @@ async function applySessionHintsStaleCleanup(projectRoot, candidate) {
|
|
|
2394
2465
|
}
|
|
2395
2466
|
}
|
|
2396
2467
|
async function applyIndexDriftFix(projectRoot, inspection) {
|
|
2397
|
-
const metaPath =
|
|
2468
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
2398
2469
|
const detailParts = [];
|
|
2399
2470
|
try {
|
|
2400
2471
|
const meta = agentsMetaSchema4.parse(JSON.parse(await readFile5(metaPath, "utf8")));
|
|
@@ -2430,7 +2501,7 @@ function truncateErrorMessage(error) {
|
|
|
2430
2501
|
return raw.length > 240 ? `${raw.slice(0, 237)}...` : raw;
|
|
2431
2502
|
}
|
|
2432
2503
|
async function inspectForensic(projectRoot) {
|
|
2433
|
-
const path2 =
|
|
2504
|
+
const path2 = join7(projectRoot, ".fabric", "forensic.json");
|
|
2434
2505
|
try {
|
|
2435
2506
|
const parsed = forensicReportSchema.parse(JSON.parse(await readFile5(path2, "utf8")));
|
|
2436
2507
|
return { present: true, valid: true, report: parsed };
|
|
@@ -2442,12 +2513,12 @@ async function inspectForensic(projectRoot) {
|
|
|
2442
2513
|
}
|
|
2443
2514
|
}
|
|
2444
2515
|
function inspectMcpConfigInWrongFile(projectRoot) {
|
|
2445
|
-
const settingsPath =
|
|
2446
|
-
if (!
|
|
2516
|
+
const settingsPath = join7(projectRoot, ".claude", "settings.json");
|
|
2517
|
+
if (!existsSync5(settingsPath)) {
|
|
2447
2518
|
return { hasWrongEntry: false, settingsPath };
|
|
2448
2519
|
}
|
|
2449
2520
|
try {
|
|
2450
|
-
const parsed = JSON.parse(
|
|
2521
|
+
const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
2451
2522
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2452
2523
|
return { hasWrongEntry: false, settingsPath };
|
|
2453
2524
|
}
|
|
@@ -2463,7 +2534,7 @@ function inspectMcpConfigInWrongFile(projectRoot) {
|
|
|
2463
2534
|
}
|
|
2464
2535
|
}
|
|
2465
2536
|
async function inspectMeta(projectRoot) {
|
|
2466
|
-
const metaPath =
|
|
2537
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
2467
2538
|
const built = await tryBuildRuleMeta(projectRoot);
|
|
2468
2539
|
try {
|
|
2469
2540
|
const raw = await readFile5(metaPath, "utf8");
|
|
@@ -2536,7 +2607,7 @@ function inspectContentRefs(projectRoot, meta) {
|
|
|
2536
2607
|
if (isPersonalKnowledge) {
|
|
2537
2608
|
continue;
|
|
2538
2609
|
}
|
|
2539
|
-
if (!
|
|
2610
|
+
if (!existsSync5(join7(projectRoot, contentRef))) {
|
|
2540
2611
|
missing.push(contentRef);
|
|
2541
2612
|
}
|
|
2542
2613
|
}
|
|
@@ -2544,7 +2615,7 @@ function inspectContentRefs(projectRoot, meta) {
|
|
|
2544
2615
|
}
|
|
2545
2616
|
async function inspectEventLedger(projectRoot) {
|
|
2546
2617
|
const path2 = getEventLedgerPath(projectRoot);
|
|
2547
|
-
const exists =
|
|
2618
|
+
const exists = existsSync5(path2);
|
|
2548
2619
|
if (!exists) {
|
|
2549
2620
|
return {
|
|
2550
2621
|
exists: false,
|
|
@@ -2616,8 +2687,55 @@ async function inspectEventLedger(projectRoot) {
|
|
|
2616
2687
|
};
|
|
2617
2688
|
}
|
|
2618
2689
|
}
|
|
2690
|
+
function inspectSkillRefMirror(projectRoot) {
|
|
2691
|
+
const skillSlugs = ["fabric-archive", "fabric-review", "fabric-import"];
|
|
2692
|
+
const driftedPaths = [];
|
|
2693
|
+
for (const slug of skillSlugs) {
|
|
2694
|
+
const claudeRef = join7(projectRoot, ".claude", "skills", slug, "ref");
|
|
2695
|
+
const codexRef = join7(projectRoot, ".codex", "skills", slug, "ref");
|
|
2696
|
+
let claudeFiles = null;
|
|
2697
|
+
let codexFiles = null;
|
|
2698
|
+
try {
|
|
2699
|
+
claudeFiles = readdirSync(claudeRef).filter((n) => n.endsWith(".md"));
|
|
2700
|
+
} catch {
|
|
2701
|
+
}
|
|
2702
|
+
try {
|
|
2703
|
+
codexFiles = readdirSync(codexRef).filter((n) => n.endsWith(".md"));
|
|
2704
|
+
} catch {
|
|
2705
|
+
}
|
|
2706
|
+
if (claudeFiles === null || codexFiles === null) continue;
|
|
2707
|
+
const claudeSet = new Set(claudeFiles);
|
|
2708
|
+
const codexSet = new Set(codexFiles);
|
|
2709
|
+
const union = /* @__PURE__ */ new Set([...claudeFiles, ...codexFiles]);
|
|
2710
|
+
for (const fname of union) {
|
|
2711
|
+
const inClaude = claudeSet.has(fname);
|
|
2712
|
+
const inCodex = codexSet.has(fname);
|
|
2713
|
+
if (!inClaude || !inCodex) {
|
|
2714
|
+
driftedPaths.push(`skills/${slug}/ref/${fname}`);
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
let claudeBody;
|
|
2718
|
+
let codexBody;
|
|
2719
|
+
try {
|
|
2720
|
+
claudeBody = readFileSync3(join7(claudeRef, fname), "utf8");
|
|
2721
|
+
} catch {
|
|
2722
|
+
continue;
|
|
2723
|
+
}
|
|
2724
|
+
try {
|
|
2725
|
+
codexBody = readFileSync3(join7(codexRef, fname), "utf8");
|
|
2726
|
+
} catch {
|
|
2727
|
+
continue;
|
|
2728
|
+
}
|
|
2729
|
+
if (claudeBody !== codexBody) {
|
|
2730
|
+
driftedPaths.push(`skills/${slug}/ref/${fname}`);
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
if (driftedPaths.length === 0) return { status: "ok" };
|
|
2735
|
+
return { status: "drift", driftedPaths };
|
|
2736
|
+
}
|
|
2619
2737
|
async function inspectKnowledgeTestIndex(projectRoot) {
|
|
2620
|
-
const path2 =
|
|
2738
|
+
const path2 = join7(projectRoot, ".fabric", ".cache", "knowledge-test.index.json");
|
|
2621
2739
|
const built = await tryBuildRuleMeta(projectRoot);
|
|
2622
2740
|
try {
|
|
2623
2741
|
const index = knowledgeTestIndexSchema2.parse(JSON.parse(await readFile5(path2, "utf8")));
|
|
@@ -2641,8 +2759,8 @@ async function inspectKnowledgeTestIndex(projectRoot) {
|
|
|
2641
2759
|
}
|
|
2642
2760
|
function inspectBootstrapAnchor(projectRoot) {
|
|
2643
2761
|
return {
|
|
2644
|
-
hasAgentsMd:
|
|
2645
|
-
hasClaudeMd:
|
|
2762
|
+
hasAgentsMd: existsSync5(join7(projectRoot, "AGENTS.md")),
|
|
2763
|
+
hasClaudeMd: existsSync5(join7(projectRoot, "CLAUDE.md"))
|
|
2646
2764
|
};
|
|
2647
2765
|
}
|
|
2648
2766
|
var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
|
|
@@ -2654,8 +2772,8 @@ var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
|
|
|
2654
2772
|
async function inspectBootstrapMarkerMigration(target) {
|
|
2655
2773
|
const filesNeedingMigration = [];
|
|
2656
2774
|
for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
|
|
2657
|
-
const abs =
|
|
2658
|
-
if (!
|
|
2775
|
+
const abs = join7(target, rel);
|
|
2776
|
+
if (!existsSync5(abs)) {
|
|
2659
2777
|
continue;
|
|
2660
2778
|
}
|
|
2661
2779
|
let content;
|
|
@@ -2692,8 +2810,8 @@ function createBootstrapMarkerMigrationCheck(t, inspection) {
|
|
|
2692
2810
|
);
|
|
2693
2811
|
}
|
|
2694
2812
|
async function inspectL1BootstrapSnapshotDrift(target) {
|
|
2695
|
-
const abs =
|
|
2696
|
-
if (!
|
|
2813
|
+
const abs = join7(target, ".fabric", "AGENTS.md");
|
|
2814
|
+
if (!existsSync5(abs)) {
|
|
2697
2815
|
return { status: "missing", canonical: BOOTSTRAP_CANONICAL, onDisk: null };
|
|
2698
2816
|
}
|
|
2699
2817
|
let onDisk;
|
|
@@ -2724,8 +2842,8 @@ function createL1BootstrapSnapshotDriftCheck(t, inspection) {
|
|
|
2724
2842
|
);
|
|
2725
2843
|
}
|
|
2726
2844
|
async function inspectL2ManagedBlockDrift(target) {
|
|
2727
|
-
const snapshotPath =
|
|
2728
|
-
if (!
|
|
2845
|
+
const snapshotPath = join7(target, ".fabric", "AGENTS.md");
|
|
2846
|
+
if (!existsSync5(snapshotPath)) {
|
|
2729
2847
|
return { status: "ok", drifted: [] };
|
|
2730
2848
|
}
|
|
2731
2849
|
let snapshot;
|
|
@@ -2734,9 +2852,9 @@ async function inspectL2ManagedBlockDrift(target) {
|
|
|
2734
2852
|
} catch {
|
|
2735
2853
|
return { status: "ok", drifted: [] };
|
|
2736
2854
|
}
|
|
2737
|
-
const projectRulesPath =
|
|
2855
|
+
const projectRulesPath = join7(target, ".fabric", "project-rules.md");
|
|
2738
2856
|
let expectedBody = snapshot;
|
|
2739
|
-
if (
|
|
2857
|
+
if (existsSync5(projectRulesPath)) {
|
|
2740
2858
|
try {
|
|
2741
2859
|
const projectRules = await readFile5(projectRulesPath, "utf8");
|
|
2742
2860
|
expectedBody = `${snapshot}
|
|
@@ -2748,11 +2866,11 @@ ${projectRules}`;
|
|
|
2748
2866
|
const drifted = [];
|
|
2749
2867
|
let anyManagedBlockFound = false;
|
|
2750
2868
|
const blockTargets = [
|
|
2751
|
-
|
|
2752
|
-
|
|
2869
|
+
join7(target, "AGENTS.md"),
|
|
2870
|
+
join7(target, ".cursor", "rules", "fabric-bootstrap.mdc")
|
|
2753
2871
|
];
|
|
2754
2872
|
for (const abs of blockTargets) {
|
|
2755
|
-
if (!
|
|
2873
|
+
if (!existsSync5(abs)) {
|
|
2756
2874
|
continue;
|
|
2757
2875
|
}
|
|
2758
2876
|
let content;
|
|
@@ -2783,8 +2901,8 @@ ${projectRules}`;
|
|
|
2783
2901
|
drifted.push({ path: abs, expected: expectedBody, actual: body });
|
|
2784
2902
|
}
|
|
2785
2903
|
}
|
|
2786
|
-
const claudeMdPath =
|
|
2787
|
-
if (
|
|
2904
|
+
const claudeMdPath = join7(target, "CLAUDE.md");
|
|
2905
|
+
if (existsSync5(claudeMdPath)) {
|
|
2788
2906
|
let claudeContent;
|
|
2789
2907
|
try {
|
|
2790
2908
|
claudeContent = await readFile5(claudeMdPath, "utf8");
|
|
@@ -2854,11 +2972,11 @@ function createBootstrapAnchorCheck(t, inspection) {
|
|
|
2854
2972
|
);
|
|
2855
2973
|
}
|
|
2856
2974
|
function inspectKnowledgeDirMissing(projectRoot) {
|
|
2857
|
-
const knowledgeRoot =
|
|
2975
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
2858
2976
|
const missingSubdirs = [];
|
|
2859
2977
|
for (const sub of KNOWLEDGE_SUBDIRS3) {
|
|
2860
|
-
const path2 =
|
|
2861
|
-
if (!
|
|
2978
|
+
const path2 = join7(knowledgeRoot, sub);
|
|
2979
|
+
if (!existsSync5(path2)) {
|
|
2862
2980
|
missingSubdirs.push(`.fabric/knowledge/${sub}`);
|
|
2863
2981
|
}
|
|
2864
2982
|
}
|
|
@@ -2866,13 +2984,13 @@ function inspectKnowledgeDirMissing(projectRoot) {
|
|
|
2866
2984
|
}
|
|
2867
2985
|
function inspectBaselineFilenameFormat(projectRoot) {
|
|
2868
2986
|
const offenders = [];
|
|
2869
|
-
const knowledgeRoot =
|
|
2870
|
-
if (!
|
|
2987
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
2988
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
2871
2989
|
return { offenders };
|
|
2872
2990
|
}
|
|
2873
2991
|
for (const sub of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
2874
|
-
const dir =
|
|
2875
|
-
if (!
|
|
2992
|
+
const dir = join7(knowledgeRoot, sub);
|
|
2993
|
+
if (!existsSync5(dir)) {
|
|
2876
2994
|
continue;
|
|
2877
2995
|
}
|
|
2878
2996
|
let entries;
|
|
@@ -2889,10 +3007,10 @@ function inspectBaselineFilenameFormat(projectRoot) {
|
|
|
2889
3007
|
if (BASELINE_ID_PREFIXED_FILENAME_PATTERN.test(entryName)) {
|
|
2890
3008
|
continue;
|
|
2891
3009
|
}
|
|
2892
|
-
const abs =
|
|
3010
|
+
const abs = join7(dir, entryName);
|
|
2893
3011
|
let source;
|
|
2894
3012
|
try {
|
|
2895
|
-
source =
|
|
3013
|
+
source = readFileSync3(abs, "utf8");
|
|
2896
3014
|
} catch {
|
|
2897
3015
|
continue;
|
|
2898
3016
|
}
|
|
@@ -3192,6 +3310,25 @@ function createEventLedgerSchemaCompatCheck(t, ledger) {
|
|
|
3192
3310
|
t("doctor.check.event_ledger_schema_compat.remediation")
|
|
3193
3311
|
);
|
|
3194
3312
|
}
|
|
3313
|
+
function createSkillRefMirrorCheck(t, inspection) {
|
|
3314
|
+
if (inspection.status === "ok") {
|
|
3315
|
+
return okCheck(
|
|
3316
|
+
t("doctor.check.skill_ref_mirror.name"),
|
|
3317
|
+
t("doctor.check.skill_ref_mirror.ok")
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
3320
|
+
return issueCheck(
|
|
3321
|
+
t("doctor.check.skill_ref_mirror.name"),
|
|
3322
|
+
"warn",
|
|
3323
|
+
"warning",
|
|
3324
|
+
"skill_ref_mirror_drift",
|
|
3325
|
+
t("doctor.check.skill_ref_mirror.message", {
|
|
3326
|
+
count: String(inspection.driftedPaths.length),
|
|
3327
|
+
list: inspection.driftedPaths.join(", ")
|
|
3328
|
+
}),
|
|
3329
|
+
t("doctor.check.skill_ref_mirror.remediation")
|
|
3330
|
+
);
|
|
3331
|
+
}
|
|
3195
3332
|
function createEventLedgerPartialWriteCheck(t, ledger) {
|
|
3196
3333
|
if (!ledger.exists || !ledger.writable) {
|
|
3197
3334
|
return okCheck(
|
|
@@ -3247,8 +3384,8 @@ function findIssue(issues, code) {
|
|
|
3247
3384
|
};
|
|
3248
3385
|
}
|
|
3249
3386
|
async function inspectMetaManuallyDiverged(projectRoot) {
|
|
3250
|
-
const metaPath =
|
|
3251
|
-
if (!
|
|
3387
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
3388
|
+
if (!existsSync5(metaPath)) {
|
|
3252
3389
|
return { extraMetaEntries: [], hashMismatchEntries: [], readable: false };
|
|
3253
3390
|
}
|
|
3254
3391
|
let meta;
|
|
@@ -3267,13 +3404,13 @@ async function inspectMetaManuallyDiverged(projectRoot) {
|
|
|
3267
3404
|
const hashMismatchEntries = [];
|
|
3268
3405
|
for (const node of Object.values(meta.nodes)) {
|
|
3269
3406
|
const contentRef = node.content_ref ?? node.file;
|
|
3270
|
-
const absPath =
|
|
3271
|
-
if (!
|
|
3407
|
+
const absPath = join7(projectRoot, contentRef);
|
|
3408
|
+
if (!existsSync5(absPath)) {
|
|
3272
3409
|
extraMetaEntries.push(contentRef);
|
|
3273
3410
|
continue;
|
|
3274
3411
|
}
|
|
3275
3412
|
try {
|
|
3276
|
-
const content =
|
|
3413
|
+
const content = readFileSync3(absPath, "utf8");
|
|
3277
3414
|
const diskHash = sha256(content);
|
|
3278
3415
|
if (node.hash !== "" && node.hash !== diskHash) {
|
|
3279
3416
|
hashMismatchEntries.push(contentRef);
|
|
@@ -3286,7 +3423,7 @@ async function inspectMetaManuallyDiverged(projectRoot) {
|
|
|
3286
3423
|
}
|
|
3287
3424
|
function inspectKnowledgeDirUnindexed(projectRoot, meta) {
|
|
3288
3425
|
const physicalMdFiles = /* @__PURE__ */ new Set();
|
|
3289
|
-
collectMdFilesUnder(physicalMdFiles, projectRoot,
|
|
3426
|
+
collectMdFilesUnder(physicalMdFiles, projectRoot, join7(projectRoot, ".fabric", "knowledge"), ".fabric/knowledge");
|
|
3290
3427
|
if (physicalMdFiles.size === 0) {
|
|
3291
3428
|
return { unindexedFiles: [] };
|
|
3292
3429
|
}
|
|
@@ -3301,7 +3438,7 @@ function inspectKnowledgeDirUnindexed(projectRoot, meta) {
|
|
|
3301
3438
|
return { unindexedFiles };
|
|
3302
3439
|
}
|
|
3303
3440
|
function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
3304
|
-
if (!
|
|
3441
|
+
if (!existsSync5(rootDir)) {
|
|
3305
3442
|
return;
|
|
3306
3443
|
}
|
|
3307
3444
|
const stack = [rootDir];
|
|
@@ -3311,7 +3448,7 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
|
3311
3448
|
continue;
|
|
3312
3449
|
}
|
|
3313
3450
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3314
|
-
const abs =
|
|
3451
|
+
const abs = join7(dir, entry.name);
|
|
3315
3452
|
if (entry.isDirectory()) {
|
|
3316
3453
|
if (entry.name !== "pending" && entry.name !== "archive") {
|
|
3317
3454
|
stack.push(abs);
|
|
@@ -3344,8 +3481,8 @@ function createKnowledgeDirUnindexedCheck(t, inspection) {
|
|
|
3344
3481
|
}
|
|
3345
3482
|
async function inspectStableIdCollisions(projectRoot) {
|
|
3346
3483
|
const found = [];
|
|
3347
|
-
const knowledgeDir =
|
|
3348
|
-
if (
|
|
3484
|
+
const knowledgeDir = join7(projectRoot, ".fabric", "knowledge");
|
|
3485
|
+
if (existsSync5(knowledgeDir)) {
|
|
3349
3486
|
const stack = [knowledgeDir];
|
|
3350
3487
|
while (stack.length > 0) {
|
|
3351
3488
|
const dir = stack.pop();
|
|
@@ -3353,7 +3490,7 @@ async function inspectStableIdCollisions(projectRoot) {
|
|
|
3353
3490
|
continue;
|
|
3354
3491
|
}
|
|
3355
3492
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3356
|
-
const abs =
|
|
3493
|
+
const abs = join7(dir, entry.name);
|
|
3357
3494
|
if (entry.isDirectory()) {
|
|
3358
3495
|
stack.push(abs);
|
|
3359
3496
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -3418,11 +3555,11 @@ function inspectCounterDesync(meta) {
|
|
|
3418
3555
|
}
|
|
3419
3556
|
const layer = parsed.layer === "personal" ? "KP" : "KT";
|
|
3420
3557
|
const typeCode = [
|
|
3421
|
-
["
|
|
3422
|
-
["
|
|
3423
|
-
["
|
|
3424
|
-
["
|
|
3425
|
-
["
|
|
3558
|
+
["models", "MOD"],
|
|
3559
|
+
["decisions", "DEC"],
|
|
3560
|
+
["guidelines", "GLD"],
|
|
3561
|
+
["pitfalls", "PIT"],
|
|
3562
|
+
["processes", "PRO"]
|
|
3426
3563
|
].find(([t]) => t === parsed.type)?.[1];
|
|
3427
3564
|
if (typeCode === void 0) {
|
|
3428
3565
|
continue;
|
|
@@ -3532,18 +3669,18 @@ function createMetaManuallyDivergedCheck(t, inspection) {
|
|
|
3532
3669
|
}
|
|
3533
3670
|
function inspectPreexistingRootFiles(projectRoot) {
|
|
3534
3671
|
const candidates = ["CLAUDE.md", "AGENTS.md"];
|
|
3535
|
-
const detected = candidates.filter((name) =>
|
|
3672
|
+
const detected = candidates.filter((name) => existsSync5(join7(projectRoot, name)));
|
|
3536
3673
|
return { detected };
|
|
3537
3674
|
}
|
|
3538
3675
|
async function inspectFilesystemEditFallback(projectRoot) {
|
|
3539
|
-
const knowledgeRoot =
|
|
3540
|
-
if (!
|
|
3676
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3677
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
3541
3678
|
return { synthesized: 0, synthesizedStableIds: [] };
|
|
3542
3679
|
}
|
|
3543
3680
|
const canonicalIds = /* @__PURE__ */ new Set();
|
|
3544
3681
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3545
|
-
const dir =
|
|
3546
|
-
if (!
|
|
3682
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
3683
|
+
if (!existsSync5(dir)) {
|
|
3547
3684
|
continue;
|
|
3548
3685
|
}
|
|
3549
3686
|
let entries;
|
|
@@ -3583,17 +3720,40 @@ async function inspectFilesystemEditFallback(projectRoot) {
|
|
|
3583
3720
|
}
|
|
3584
3721
|
orphanIds.sort();
|
|
3585
3722
|
for (const stable_id of orphanIds) {
|
|
3586
|
-
await
|
|
3587
|
-
event_type: "knowledge_promoted",
|
|
3588
|
-
stable_id,
|
|
3589
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3590
|
-
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3591
|
-
correlation_id: "doctor-synthesized",
|
|
3592
|
-
session_id: "doctor-synthesized"
|
|
3593
|
-
});
|
|
3723
|
+
await emitSynthesizedPromotionTriplet(projectRoot, stable_id);
|
|
3594
3724
|
}
|
|
3595
3725
|
return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
|
|
3596
3726
|
}
|
|
3727
|
+
async function emitSynthesizedPromotionTriplet(projectRoot, stable_id) {
|
|
3728
|
+
const baseTs = Date.now();
|
|
3729
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
3730
|
+
event_type: "knowledge_proposed",
|
|
3731
|
+
stable_id,
|
|
3732
|
+
timestamp: new Date(baseTs).toISOString(),
|
|
3733
|
+
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3734
|
+
correlation_id: "doctor-synthesized",
|
|
3735
|
+
session_id: "doctor-synthesized",
|
|
3736
|
+
ts: baseTs
|
|
3737
|
+
});
|
|
3738
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
3739
|
+
event_type: "knowledge_promote_started",
|
|
3740
|
+
stable_id,
|
|
3741
|
+
timestamp: new Date(baseTs + 1).toISOString(),
|
|
3742
|
+
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3743
|
+
correlation_id: "doctor-synthesized",
|
|
3744
|
+
session_id: "doctor-synthesized",
|
|
3745
|
+
ts: baseTs + 1
|
|
3746
|
+
});
|
|
3747
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
3748
|
+
event_type: "knowledge_promoted",
|
|
3749
|
+
stable_id,
|
|
3750
|
+
timestamp: new Date(baseTs + 2).toISOString(),
|
|
3751
|
+
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3752
|
+
correlation_id: "doctor-synthesized",
|
|
3753
|
+
session_id: "doctor-synthesized",
|
|
3754
|
+
ts: baseTs + 2
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3597
3757
|
function createFilesystemEditFallbackCheck(t, inspection) {
|
|
3598
3758
|
if (inspection.synthesized === 0) {
|
|
3599
3759
|
return okCheck(
|
|
@@ -3752,13 +3912,13 @@ function extractKnowledgeFrontmatterCreatedAt(source) {
|
|
|
3752
3912
|
return Number.isFinite(parsed) ? parsed : null;
|
|
3753
3913
|
}
|
|
3754
3914
|
function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
|
|
3755
|
-
const knowledgeRoot =
|
|
3756
|
-
if (!
|
|
3915
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3916
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
3757
3917
|
return;
|
|
3758
3918
|
}
|
|
3759
3919
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3760
|
-
const dir =
|
|
3761
|
-
if (!
|
|
3920
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
3921
|
+
if (!existsSync5(dir)) {
|
|
3762
3922
|
continue;
|
|
3763
3923
|
}
|
|
3764
3924
|
let entries;
|
|
@@ -3776,10 +3936,10 @@ function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
|
|
|
3776
3936
|
continue;
|
|
3777
3937
|
}
|
|
3778
3938
|
const stableId = match[1];
|
|
3779
|
-
const absPath =
|
|
3939
|
+
const absPath = join7(dir, entry.name);
|
|
3780
3940
|
let source;
|
|
3781
3941
|
try {
|
|
3782
|
-
source =
|
|
3942
|
+
source = readFileSync3(absPath, "utf8");
|
|
3783
3943
|
} catch {
|
|
3784
3944
|
continue;
|
|
3785
3945
|
}
|
|
@@ -3852,13 +4012,13 @@ async function inspectStaleArchive(projectRoot, now) {
|
|
|
3852
4012
|
return { candidates };
|
|
3853
4013
|
}
|
|
3854
4014
|
function* iteratePendingFiles(projectRoot, now) {
|
|
3855
|
-
const teamRoot =
|
|
3856
|
-
const personalRoot =
|
|
4015
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
|
|
4016
|
+
const personalRoot = join7(resolvePersonalRootForPending(), ".fabric", "knowledge", "pending");
|
|
3857
4017
|
for (const [layer, root, displayPrefix] of [
|
|
3858
4018
|
["team", teamRoot, ".fabric/knowledge/pending"],
|
|
3859
4019
|
["personal", personalRoot, "~/.fabric/knowledge/pending"]
|
|
3860
4020
|
]) {
|
|
3861
|
-
if (!
|
|
4021
|
+
if (!existsSync5(root)) {
|
|
3862
4022
|
continue;
|
|
3863
4023
|
}
|
|
3864
4024
|
let typeDirs = [];
|
|
@@ -3868,7 +4028,7 @@ function* iteratePendingFiles(projectRoot, now) {
|
|
|
3868
4028
|
continue;
|
|
3869
4029
|
}
|
|
3870
4030
|
for (const typeDir of typeDirs) {
|
|
3871
|
-
const dir =
|
|
4031
|
+
const dir = join7(root, typeDir);
|
|
3872
4032
|
let entries;
|
|
3873
4033
|
try {
|
|
3874
4034
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -3879,10 +4039,10 @@ function* iteratePendingFiles(projectRoot, now) {
|
|
|
3879
4039
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
3880
4040
|
continue;
|
|
3881
4041
|
}
|
|
3882
|
-
const absPath =
|
|
4042
|
+
const absPath = join7(dir, entry.name);
|
|
3883
4043
|
let source = "";
|
|
3884
4044
|
try {
|
|
3885
|
-
source =
|
|
4045
|
+
source = readFileSync3(absPath, "utf8");
|
|
3886
4046
|
} catch {
|
|
3887
4047
|
continue;
|
|
3888
4048
|
}
|
|
@@ -3954,7 +4114,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
3954
4114
|
pending_path: visit.pending_path,
|
|
3955
4115
|
pending_path_abs: visit.pending_path_abs,
|
|
3956
4116
|
archived_to: archivedToRel,
|
|
3957
|
-
archived_to_abs:
|
|
4117
|
+
archived_to_abs: join7(projectRoot, archivedToRel),
|
|
3958
4118
|
age_days: visit.age_days
|
|
3959
4119
|
});
|
|
3960
4120
|
} else {
|
|
@@ -3963,7 +4123,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
3963
4123
|
visit.type,
|
|
3964
4124
|
visit.filename
|
|
3965
4125
|
);
|
|
3966
|
-
const archivedToAbs =
|
|
4126
|
+
const archivedToAbs = join7(
|
|
3967
4127
|
resolvePersonalRootForPending(),
|
|
3968
4128
|
".fabric",
|
|
3969
4129
|
".archive",
|
|
@@ -3987,12 +4147,12 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
3987
4147
|
}
|
|
3988
4148
|
function inspectUnderseeded(projectRoot) {
|
|
3989
4149
|
const threshold = readUnderseedThresholdFromConfig(projectRoot);
|
|
3990
|
-
const knowledgeRoot =
|
|
4150
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3991
4151
|
let nodeCount = 0;
|
|
3992
|
-
if (
|
|
4152
|
+
if (existsSync5(knowledgeRoot)) {
|
|
3993
4153
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3994
|
-
const dir =
|
|
3995
|
-
if (!
|
|
4154
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
4155
|
+
if (!existsSync5(dir)) continue;
|
|
3996
4156
|
let entries;
|
|
3997
4157
|
try {
|
|
3998
4158
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4013,8 +4173,8 @@ function inspectUnderseeded(projectRoot) {
|
|
|
4013
4173
|
};
|
|
4014
4174
|
}
|
|
4015
4175
|
function inspectSessionHintsStale(projectRoot, now) {
|
|
4016
|
-
const cacheDir =
|
|
4017
|
-
if (!
|
|
4176
|
+
const cacheDir = join7(projectRoot, ".fabric", ".cache");
|
|
4177
|
+
if (!existsSync5(cacheDir)) {
|
|
4018
4178
|
return { candidates: [] };
|
|
4019
4179
|
}
|
|
4020
4180
|
let entries;
|
|
@@ -4028,7 +4188,7 @@ function inspectSessionHintsStale(projectRoot, now) {
|
|
|
4028
4188
|
if (!entry.isFile()) continue;
|
|
4029
4189
|
if (!entry.name.startsWith(SESSION_HINTS_FILE_PREFIX)) continue;
|
|
4030
4190
|
if (!entry.name.endsWith(SESSION_HINTS_FILE_SUFFIX)) continue;
|
|
4031
|
-
const absPath =
|
|
4191
|
+
const absPath = join7(cacheDir, entry.name);
|
|
4032
4192
|
let mtimeMs = 0;
|
|
4033
4193
|
try {
|
|
4034
4194
|
mtimeMs = statSync4(absPath).mtimeMs;
|
|
@@ -4072,11 +4232,11 @@ function inspectNarrowTooFew(projectRoot, now) {
|
|
|
4072
4232
|
const structuralFlagged = total >= NARROW_MIN_TOTAL && narrowRatio < NARROW_RATIO_THRESHOLD;
|
|
4073
4233
|
const windowStartMs = now - SILENCE_WINDOW_DAYS * MS_PER_DAY;
|
|
4074
4234
|
const editFires = readCounterTimestamps(
|
|
4075
|
-
|
|
4235
|
+
join7(projectRoot, EDIT_COUNTER_FILE_REL),
|
|
4076
4236
|
windowStartMs
|
|
4077
4237
|
);
|
|
4078
4238
|
const silenceFires = readCounterTimestamps(
|
|
4079
|
-
|
|
4239
|
+
join7(projectRoot, HINT_SILENCE_COUNTER_FILE_REL),
|
|
4080
4240
|
windowStartMs
|
|
4081
4241
|
);
|
|
4082
4242
|
const telemetrySkipped = editFires === 0;
|
|
@@ -4095,10 +4255,10 @@ function inspectNarrowTooFew(projectRoot, now) {
|
|
|
4095
4255
|
};
|
|
4096
4256
|
}
|
|
4097
4257
|
function readCounterTimestamps(absPath, windowStartMs) {
|
|
4098
|
-
if (!
|
|
4258
|
+
if (!existsSync5(absPath)) return 0;
|
|
4099
4259
|
let raw;
|
|
4100
4260
|
try {
|
|
4101
|
-
raw =
|
|
4261
|
+
raw = readFileSync3(absPath, "utf8");
|
|
4102
4262
|
} catch {
|
|
4103
4263
|
return 0;
|
|
4104
4264
|
}
|
|
@@ -4114,10 +4274,10 @@ function readCounterTimestamps(absPath, windowStartMs) {
|
|
|
4114
4274
|
return count;
|
|
4115
4275
|
}
|
|
4116
4276
|
function readUnderseedThresholdFromConfig(projectRoot) {
|
|
4117
|
-
const configPath =
|
|
4118
|
-
if (!
|
|
4277
|
+
const configPath = join7(projectRoot, ".fabric", "fabric-config.json");
|
|
4278
|
+
if (!existsSync5(configPath)) return DEFAULT_UNDERSEED_NODE_THRESHOLD;
|
|
4119
4279
|
try {
|
|
4120
|
-
const raw =
|
|
4280
|
+
const raw = readFileSync3(configPath, "utf8");
|
|
4121
4281
|
const parsed = JSON.parse(raw);
|
|
4122
4282
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4123
4283
|
const v = parsed.underseed_node_threshold;
|
|
@@ -4311,11 +4471,11 @@ function extractKnowledgeFrontmatterRelevancePaths(source) {
|
|
|
4311
4471
|
}
|
|
4312
4472
|
function* iterateRelevanceFrontmatter(projectRoot) {
|
|
4313
4473
|
for (const visit of iterateCanonicalFilenames(projectRoot)) {
|
|
4314
|
-
const layerRoot = visit.layer === "team" ?
|
|
4315
|
-
const absPath =
|
|
4474
|
+
const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
|
|
4475
|
+
const absPath = join7(layerRoot, visit.type, visit.filename);
|
|
4316
4476
|
let source;
|
|
4317
4477
|
try {
|
|
4318
|
-
source =
|
|
4478
|
+
source = readFileSync3(absPath, "utf8");
|
|
4319
4479
|
} catch {
|
|
4320
4480
|
continue;
|
|
4321
4481
|
}
|
|
@@ -4377,7 +4537,7 @@ function inspectRelevancePathsDangling(projectRoot) {
|
|
|
4377
4537
|
return { entries };
|
|
4378
4538
|
}
|
|
4379
4539
|
function collectWorkspacePathsForGlobMatch(projectRoot) {
|
|
4380
|
-
if (!
|
|
4540
|
+
if (!existsSync5(projectRoot)) {
|
|
4381
4541
|
return [];
|
|
4382
4542
|
}
|
|
4383
4543
|
let rootStat;
|
|
@@ -4401,7 +4561,7 @@ function collectWorkspacePathsForGlobMatch(projectRoot) {
|
|
|
4401
4561
|
continue;
|
|
4402
4562
|
}
|
|
4403
4563
|
for (const entry of entries) {
|
|
4404
|
-
const abs =
|
|
4564
|
+
const abs = join7(current, entry.name);
|
|
4405
4565
|
const rel = normalizePath(abs.slice(projectRoot.length + 1));
|
|
4406
4566
|
if (rel.length === 0) continue;
|
|
4407
4567
|
if (entry.isDirectory()) {
|
|
@@ -4558,8 +4718,8 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4558
4718
|
const candidates = [];
|
|
4559
4719
|
let scannedCount = 0;
|
|
4560
4720
|
const FM_PATTERN = /^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---/u;
|
|
4561
|
-
const teamRoot =
|
|
4562
|
-
const personalRoot =
|
|
4721
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
|
|
4722
|
+
const personalRoot = join7(
|
|
4563
4723
|
resolvePersonalRootForPending(),
|
|
4564
4724
|
".fabric",
|
|
4565
4725
|
"knowledge",
|
|
@@ -4569,7 +4729,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4569
4729
|
[teamRoot, ".fabric/knowledge/pending"],
|
|
4570
4730
|
[personalRoot, "~/.fabric/knowledge/pending"]
|
|
4571
4731
|
]) {
|
|
4572
|
-
if (!
|
|
4732
|
+
if (!existsSync5(root)) {
|
|
4573
4733
|
continue;
|
|
4574
4734
|
}
|
|
4575
4735
|
let typeDirs = [];
|
|
@@ -4579,7 +4739,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4579
4739
|
continue;
|
|
4580
4740
|
}
|
|
4581
4741
|
for (const typeDir of typeDirs) {
|
|
4582
|
-
const dir =
|
|
4742
|
+
const dir = join7(root, typeDir);
|
|
4583
4743
|
let entries;
|
|
4584
4744
|
try {
|
|
4585
4745
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4590,10 +4750,10 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4590
4750
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
4591
4751
|
continue;
|
|
4592
4752
|
}
|
|
4593
|
-
const absPath =
|
|
4753
|
+
const absPath = join7(dir, entry.name);
|
|
4594
4754
|
let source;
|
|
4595
4755
|
try {
|
|
4596
|
-
source =
|
|
4756
|
+
source = readFileSync3(absPath, "utf8");
|
|
4597
4757
|
} catch {
|
|
4598
4758
|
continue;
|
|
4599
4759
|
}
|
|
@@ -4725,8 +4885,8 @@ var SKILL_QUOTED_VALUE_LEADS = /* @__PURE__ */ new Set(['"', "'", "[", "{", ">",
|
|
|
4725
4885
|
function inspectSkillMdYamlInvalid(projectRoot) {
|
|
4726
4886
|
const candidates = [];
|
|
4727
4887
|
for (const rootRel of SKILL_MD_FRONTMATTER_ROOTS) {
|
|
4728
|
-
const rootAbs =
|
|
4729
|
-
if (!
|
|
4888
|
+
const rootAbs = join7(projectRoot, rootRel);
|
|
4889
|
+
if (!existsSync5(rootAbs)) continue;
|
|
4730
4890
|
let dirEntries;
|
|
4731
4891
|
try {
|
|
4732
4892
|
dirEntries = readdirSync(rootAbs, { withFileTypes: true });
|
|
@@ -4735,11 +4895,11 @@ function inspectSkillMdYamlInvalid(projectRoot) {
|
|
|
4735
4895
|
}
|
|
4736
4896
|
for (const dirEntry of dirEntries) {
|
|
4737
4897
|
if (!dirEntry.isDirectory()) continue;
|
|
4738
|
-
const skillFile =
|
|
4739
|
-
if (!
|
|
4898
|
+
const skillFile = join7(rootAbs, dirEntry.name, "SKILL.md");
|
|
4899
|
+
if (!existsSync5(skillFile)) continue;
|
|
4740
4900
|
let raw;
|
|
4741
4901
|
try {
|
|
4742
|
-
raw =
|
|
4902
|
+
raw = readFileSync3(skillFile, "utf8");
|
|
4743
4903
|
} catch {
|
|
4744
4904
|
continue;
|
|
4745
4905
|
}
|
|
@@ -4821,11 +4981,11 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4821
4981
|
for (const slot of ONBOARD_SLOT_NAMES) {
|
|
4822
4982
|
filled[slot] = [];
|
|
4823
4983
|
}
|
|
4824
|
-
const knowledgeRoot =
|
|
4825
|
-
if (
|
|
4984
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
4985
|
+
if (existsSync5(knowledgeRoot)) {
|
|
4826
4986
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD) {
|
|
4827
|
-
const dir =
|
|
4828
|
-
if (!
|
|
4987
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
4988
|
+
if (!existsSync5(dir)) continue;
|
|
4829
4989
|
let entries;
|
|
4830
4990
|
try {
|
|
4831
4991
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4835,10 +4995,10 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4835
4995
|
for (const entry of entries) {
|
|
4836
4996
|
if (!entry.isFile()) continue;
|
|
4837
4997
|
if (!entry.name.endsWith(".md")) continue;
|
|
4838
|
-
const filePath =
|
|
4998
|
+
const filePath = join7(dir, entry.name);
|
|
4839
4999
|
let content;
|
|
4840
5000
|
try {
|
|
4841
|
-
content =
|
|
5001
|
+
content = readFileSync3(filePath, "utf8");
|
|
4842
5002
|
} catch {
|
|
4843
5003
|
continue;
|
|
4844
5004
|
}
|
|
@@ -4862,11 +5022,11 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4862
5022
|
return { filled, missing, opted_out: optedOut };
|
|
4863
5023
|
}
|
|
4864
5024
|
function readOnboardOptedOut(projectRoot) {
|
|
4865
|
-
const path2 =
|
|
4866
|
-
if (!
|
|
5025
|
+
const path2 = join7(projectRoot, ".fabric", "fabric-config.json");
|
|
5026
|
+
if (!existsSync5(path2)) return [];
|
|
4867
5027
|
let raw;
|
|
4868
5028
|
try {
|
|
4869
|
-
raw =
|
|
5029
|
+
raw = readFileSync3(path2, "utf8");
|
|
4870
5030
|
} catch {
|
|
4871
5031
|
return [];
|
|
4872
5032
|
}
|
|
@@ -4982,7 +5142,7 @@ function createNarrowTooFewCheck(t, inspection) {
|
|
|
4982
5142
|
}
|
|
4983
5143
|
function resolvePersonalKnowledgeRoot() {
|
|
4984
5144
|
const home = process.env.FABRIC_HOME ?? homedir3();
|
|
4985
|
-
return
|
|
5145
|
+
return join7(home, ".fabric", "knowledge");
|
|
4986
5146
|
}
|
|
4987
5147
|
function parseStableIdFromCanonicalFilename(filename) {
|
|
4988
5148
|
const match = CANONICAL_KNOWLEDGE_FILENAME_PATTERN.exec(filename);
|
|
@@ -5002,18 +5162,18 @@ function parseStableIdFromCanonicalFilename(filename) {
|
|
|
5002
5162
|
};
|
|
5003
5163
|
}
|
|
5004
5164
|
function* iterateCanonicalFilenames(projectRoot) {
|
|
5005
|
-
const teamRoot =
|
|
5165
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
5006
5166
|
const personalRoot = resolvePersonalKnowledgeRoot();
|
|
5007
5167
|
for (const [layer, root, displayPrefix] of [
|
|
5008
5168
|
["team", teamRoot, ".fabric/knowledge"],
|
|
5009
5169
|
["personal", personalRoot, "~/.fabric/knowledge"]
|
|
5010
5170
|
]) {
|
|
5011
|
-
if (!
|
|
5171
|
+
if (!existsSync5(root)) {
|
|
5012
5172
|
continue;
|
|
5013
5173
|
}
|
|
5014
5174
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
5015
|
-
const dir =
|
|
5016
|
-
if (!
|
|
5175
|
+
const dir = join7(root, typeDir);
|
|
5176
|
+
if (!existsSync5(dir)) {
|
|
5017
5177
|
continue;
|
|
5018
5178
|
}
|
|
5019
5179
|
let entries;
|
|
@@ -5184,8 +5344,8 @@ async function migrateBootstrapMarkers(projectRoot) {
|
|
|
5184
5344
|
const paths = [];
|
|
5185
5345
|
const countPerPath = {};
|
|
5186
5346
|
for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
|
|
5187
|
-
const abs =
|
|
5188
|
-
if (!
|
|
5347
|
+
const abs = join7(projectRoot, rel);
|
|
5348
|
+
if (!existsSync5(abs)) {
|
|
5189
5349
|
continue;
|
|
5190
5350
|
}
|
|
5191
5351
|
let original;
|
|
@@ -5211,8 +5371,8 @@ async function migrateBootstrapMarkers(projectRoot) {
|
|
|
5211
5371
|
return { paths, countPerPath };
|
|
5212
5372
|
}
|
|
5213
5373
|
async function rewriteThreeEndManagedBlocks(projectRoot) {
|
|
5214
|
-
const snapshotPath =
|
|
5215
|
-
if (!
|
|
5374
|
+
const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
|
|
5375
|
+
if (!existsSync5(snapshotPath)) {
|
|
5216
5376
|
return;
|
|
5217
5377
|
}
|
|
5218
5378
|
let snapshot;
|
|
@@ -5221,8 +5381,8 @@ async function rewriteThreeEndManagedBlocks(projectRoot) {
|
|
|
5221
5381
|
} catch {
|
|
5222
5382
|
return;
|
|
5223
5383
|
}
|
|
5224
|
-
const projectRulesPath =
|
|
5225
|
-
const hasProjectRules =
|
|
5384
|
+
const projectRulesPath = join7(projectRoot, ".fabric", "project-rules.md");
|
|
5385
|
+
const hasProjectRules = existsSync5(projectRulesPath);
|
|
5226
5386
|
let expectedBody = snapshot;
|
|
5227
5387
|
if (hasProjectRules) {
|
|
5228
5388
|
try {
|
|
@@ -5237,11 +5397,11 @@ ${projectRules}`;
|
|
|
5237
5397
|
${expectedBody}
|
|
5238
5398
|
${BOOTSTRAP_MARKER_END}`;
|
|
5239
5399
|
const blockTargets = [
|
|
5240
|
-
|
|
5241
|
-
|
|
5400
|
+
join7(projectRoot, "AGENTS.md"),
|
|
5401
|
+
join7(projectRoot, ".cursor", "rules", "fabric-bootstrap.mdc")
|
|
5242
5402
|
];
|
|
5243
5403
|
for (const abs of blockTargets) {
|
|
5244
|
-
if (!
|
|
5404
|
+
if (!existsSync5(abs)) {
|
|
5245
5405
|
continue;
|
|
5246
5406
|
}
|
|
5247
5407
|
let existing;
|
|
@@ -5271,8 +5431,8 @@ ${managedBlock}
|
|
|
5271
5431
|
}
|
|
5272
5432
|
await atomicWriteText4(abs, next);
|
|
5273
5433
|
}
|
|
5274
|
-
const claudeMdPath =
|
|
5275
|
-
if (
|
|
5434
|
+
const claudeMdPath = join7(projectRoot, "CLAUDE.md");
|
|
5435
|
+
if (existsSync5(claudeMdPath)) {
|
|
5276
5436
|
let claudeContent;
|
|
5277
5437
|
try {
|
|
5278
5438
|
claudeContent = await readFile5(claudeMdPath, "utf8");
|
|
@@ -5300,13 +5460,13 @@ ${managedBlock}
|
|
|
5300
5460
|
}
|
|
5301
5461
|
}
|
|
5302
5462
|
async function fixMcpConfigInWrongFile(projectRoot) {
|
|
5303
|
-
const settingsPath =
|
|
5304
|
-
if (!
|
|
5463
|
+
const settingsPath = join7(projectRoot, ".claude", "settings.json");
|
|
5464
|
+
if (!existsSync5(settingsPath)) {
|
|
5305
5465
|
return;
|
|
5306
5466
|
}
|
|
5307
5467
|
let settings;
|
|
5308
5468
|
try {
|
|
5309
|
-
const parsed = JSON.parse(
|
|
5469
|
+
const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
5310
5470
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5311
5471
|
return;
|
|
5312
5472
|
}
|
|
@@ -5334,12 +5494,12 @@ async function fixMcpConfigInWrongFile(projectRoot) {
|
|
|
5334
5494
|
}
|
|
5335
5495
|
async function ensureKnowledgeSubdirs(projectRoot) {
|
|
5336
5496
|
for (const sub of KNOWLEDGE_SUBDIRS3) {
|
|
5337
|
-
await mkdir4(
|
|
5497
|
+
await mkdir4(join7(projectRoot, ".fabric", "knowledge", sub), { recursive: true });
|
|
5338
5498
|
}
|
|
5339
5499
|
}
|
|
5340
5500
|
async function fixCounterDesync(projectRoot) {
|
|
5341
|
-
const metaPath =
|
|
5342
|
-
if (!
|
|
5501
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
5502
|
+
if (!existsSync5(metaPath)) {
|
|
5343
5503
|
return;
|
|
5344
5504
|
}
|
|
5345
5505
|
let meta;
|
|
@@ -5757,8 +5917,8 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5757
5917
|
continue;
|
|
5758
5918
|
}
|
|
5759
5919
|
bumpLayerType(citeId, kbType);
|
|
5760
|
-
if (kbType === "
|
|
5761
|
-
if (kbType === "
|
|
5920
|
+
if (kbType === "decisions" || kbType === "pitfalls") {
|
|
5921
|
+
if (kbType === "decisions") decisionsCited += 1;
|
|
5762
5922
|
else pitfallsCited += 1;
|
|
5763
5923
|
const commitment = commitments[i];
|
|
5764
5924
|
const operators = commitment?.operators ?? [];
|
|
@@ -5922,7 +6082,7 @@ function normalizePath(path2) {
|
|
|
5922
6082
|
return posix.normalize(path2.split("\\").join("/"));
|
|
5923
6083
|
}
|
|
5924
6084
|
function collectEntryPoints(root) {
|
|
5925
|
-
if (!
|
|
6085
|
+
if (!existsSync5(root) || !statSync4(root).isDirectory()) {
|
|
5926
6086
|
return [];
|
|
5927
6087
|
}
|
|
5928
6088
|
const entries = [];
|
|
@@ -5933,7 +6093,7 @@ function collectEntryPoints(root) {
|
|
|
5933
6093
|
continue;
|
|
5934
6094
|
}
|
|
5935
6095
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
5936
|
-
const absolutePath =
|
|
6096
|
+
const absolutePath = join7(current, entry.name);
|
|
5937
6097
|
const relativePath = normalizePath(absolutePath.slice(root.length + 1));
|
|
5938
6098
|
if (relativePath.length === 0) {
|
|
5939
6099
|
continue;
|
|
@@ -5999,14 +6159,14 @@ var ENRICH_DESC_FIELD_PATTERNS = {
|
|
|
5999
6159
|
async function enrichDescriptions(projectRoot, opts = {}) {
|
|
6000
6160
|
const auto = opts.auto === true;
|
|
6001
6161
|
const dryRun = opts.dryRun === true;
|
|
6002
|
-
const mode = auto ? "auto" : "
|
|
6162
|
+
const mode = auto ? dryRun ? "preview" : "auto" : "readonly";
|
|
6003
6163
|
const candidates = [];
|
|
6004
6164
|
let scanned = 0;
|
|
6005
6165
|
let modified = 0;
|
|
6006
6166
|
let skipped = 0;
|
|
6007
6167
|
for (const visit of iterateCanonicalFilenames(projectRoot)) {
|
|
6008
|
-
const layerRoot = visit.layer === "team" ?
|
|
6009
|
-
const absPath =
|
|
6168
|
+
const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
|
|
6169
|
+
const absPath = join7(layerRoot, visit.type, visit.filename);
|
|
6010
6170
|
scanned += 1;
|
|
6011
6171
|
let source;
|
|
6012
6172
|
try {
|
|
@@ -6209,7 +6369,7 @@ async function emitAutoHealEventBestEffort(projectRoot, payload) {
|
|
|
6209
6369
|
|
|
6210
6370
|
// src/services/get-knowledge.ts
|
|
6211
6371
|
import { readFile as readFile6 } from "fs/promises";
|
|
6212
|
-
import { join as
|
|
6372
|
+
import { join as join8 } from "path";
|
|
6213
6373
|
import { minimatch as minimatch2 } from "minimatch";
|
|
6214
6374
|
var PRIORITY_ORDER = {
|
|
6215
6375
|
high: 0,
|
|
@@ -6253,7 +6413,7 @@ async function loadGetKnowledgeContext(projectRoot) {
|
|
|
6253
6413
|
return cached;
|
|
6254
6414
|
}
|
|
6255
6415
|
const meta = await readAgentsMeta(projectRoot);
|
|
6256
|
-
const l0Content = await readFile6(
|
|
6416
|
+
const l0Content = await readFile6(join8(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
|
|
6257
6417
|
const context = {
|
|
6258
6418
|
meta,
|
|
6259
6419
|
l0Content,
|
|
@@ -6392,7 +6552,7 @@ async function readRuleContent(projectRoot, file, fileContentCache) {
|
|
|
6392
6552
|
if (cached !== void 0) {
|
|
6393
6553
|
return await cached;
|
|
6394
6554
|
}
|
|
6395
|
-
const pending = readFile6(
|
|
6555
|
+
const pending = readFile6(join8(projectRoot, file), "utf8");
|
|
6396
6556
|
fileContentCache.set(file, pending);
|
|
6397
6557
|
return await pending;
|
|
6398
6558
|
}
|
|
@@ -6427,6 +6587,8 @@ export {
|
|
|
6427
6587
|
invalidateKnowledgeSyncCooldown,
|
|
6428
6588
|
ensureKnowledgeFresh,
|
|
6429
6589
|
reconcileKnowledge,
|
|
6590
|
+
readPayloadLimits,
|
|
6591
|
+
readSelectionTokenTtlMs,
|
|
6430
6592
|
loadActiveMeta,
|
|
6431
6593
|
loadActiveMetaOrStale,
|
|
6432
6594
|
getKnowledge,
|