@fenglimg/fabric-server 2.0.0-rc.28 → 2.0.0-rc.30
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-EEVSGTHG.js → chunk-4DLGRSYE.js} +261 -178
- package/dist/{http-3IIPL3HQ.js → http-ALTGDHLT.js} +29 -3
- package/dist/index.d.ts +29 -9
- package/dist/index.js +69 -92
- 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}`, {
|
|
@@ -851,8 +851,9 @@ function createDefaultNodeMeta(contentRef) {
|
|
|
851
851
|
scope_glob: deriveScopeGlob(contentRef),
|
|
852
852
|
deps: layer === "L0" ? [] : ["L0"],
|
|
853
853
|
priority: layer === "L0" ? "high" : "medium",
|
|
854
|
+
// v2.0.0-rc.30 TASK-004: dropped duplicate `layer:` write — was always
|
|
855
|
+
// identical to `level:`; AgentsMetaNode no longer carries the field.
|
|
854
856
|
level: layer,
|
|
855
|
-
layer,
|
|
856
857
|
topology_type: topologyType,
|
|
857
858
|
hash: ""
|
|
858
859
|
};
|
|
@@ -1078,9 +1079,17 @@ function extractKnowledgeFieldsFromFrontmatter(frontmatter) {
|
|
|
1078
1079
|
`);
|
|
1079
1080
|
}
|
|
1080
1081
|
}
|
|
1082
|
+
const SINGULAR_TO_PLURAL = {
|
|
1083
|
+
model: "models",
|
|
1084
|
+
decision: "decisions",
|
|
1085
|
+
guideline: "guidelines",
|
|
1086
|
+
pitfall: "pitfalls",
|
|
1087
|
+
process: "processes"
|
|
1088
|
+
};
|
|
1081
1089
|
let knowledge_type;
|
|
1082
1090
|
if (rawType !== void 0) {
|
|
1083
|
-
const
|
|
1091
|
+
const normalized = SINGULAR_TO_PLURAL[rawType] ?? rawType;
|
|
1092
|
+
const parsed = KnowledgeTypeSchema.safeParse(normalized);
|
|
1084
1093
|
if (parsed.success) {
|
|
1085
1094
|
knowledge_type = parsed.data;
|
|
1086
1095
|
} else {
|
|
@@ -1429,6 +1438,12 @@ async function ensureKnowledgeFresh(projectRoot, opts) {
|
|
|
1429
1438
|
contextCache.invalidate("file_watch", projectRoot);
|
|
1430
1439
|
}
|
|
1431
1440
|
freshSyncCooldown.delete(projectRoot);
|
|
1441
|
+
if (opts?.autoHealOnDrift === true && events.length > 0) {
|
|
1442
|
+
try {
|
|
1443
|
+
await reconcileKnowledge(projectRoot, { trigger: "auto-heal-after-drift" });
|
|
1444
|
+
} catch {
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1432
1447
|
const status = warnings.length > 0 ? "errors" : "reconciled";
|
|
1433
1448
|
return {
|
|
1434
1449
|
status,
|
|
@@ -1620,11 +1635,11 @@ function checkLockOrThrow(projectRoot, opts) {
|
|
|
1620
1635
|
|
|
1621
1636
|
// src/services/doctor.ts
|
|
1622
1637
|
import { execFileSync } from "child_process";
|
|
1623
|
-
import { existsSync as
|
|
1638
|
+
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
|
|
1624
1639
|
import { access, mkdir as mkdir4, readFile as readFile5, rename, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
1625
1640
|
import { constants } from "fs";
|
|
1626
1641
|
import { homedir as homedir3 } from "os";
|
|
1627
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1642
|
+
import { isAbsolute as isAbsolute2, join as join7, posix, relative as nodeRelative, resolve as resolve3, sep as sep3 } from "path";
|
|
1628
1643
|
import { minimatch } from "minimatch";
|
|
1629
1644
|
import {
|
|
1630
1645
|
agentsMetaSchema as agentsMetaSchema4,
|
|
@@ -1643,6 +1658,41 @@ import {
|
|
|
1643
1658
|
resolveFabricLocale as resolveFabricLocale2
|
|
1644
1659
|
} from "@fenglimg/fabric-shared";
|
|
1645
1660
|
import { detectFramework } from "@fenglimg/fabric-shared/node";
|
|
1661
|
+
import {
|
|
1662
|
+
PAYLOAD_LIMIT_DEFAULT_HARD_BYTES,
|
|
1663
|
+
PAYLOAD_LIMIT_DEFAULT_WARN_BYTES
|
|
1664
|
+
} from "@fenglimg/fabric-shared/node/mcp-payload-guard";
|
|
1665
|
+
|
|
1666
|
+
// src/config-loader.ts
|
|
1667
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
1668
|
+
import { join as join6 } from "path";
|
|
1669
|
+
import { selectionTokenTtlMsSchema } from "@fenglimg/fabric-shared";
|
|
1670
|
+
function readFabricConfig(projectRoot) {
|
|
1671
|
+
const configPath = join6(projectRoot, "fabric.config.json");
|
|
1672
|
+
if (!existsSync4(configPath)) {
|
|
1673
|
+
return {};
|
|
1674
|
+
}
|
|
1675
|
+
const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
|
|
1676
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1677
|
+
throw new Error(`Expected object in ${configPath}`);
|
|
1678
|
+
}
|
|
1679
|
+
return parsed;
|
|
1680
|
+
}
|
|
1681
|
+
function readPayloadLimits(projectRoot) {
|
|
1682
|
+
return readFabricConfig(projectRoot).mcpPayloadLimits;
|
|
1683
|
+
}
|
|
1684
|
+
function readSelectionTokenTtlMs(projectRoot) {
|
|
1685
|
+
try {
|
|
1686
|
+
const raw = readFabricConfig(projectRoot).selection_token_ttl_ms;
|
|
1687
|
+
if (raw === void 0) return void 0;
|
|
1688
|
+
const parsed = selectionTokenTtlMsSchema.safeParse(raw);
|
|
1689
|
+
return parsed.success ? parsed.data : void 0;
|
|
1690
|
+
} catch {
|
|
1691
|
+
return void 0;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/services/doctor.ts
|
|
1646
1696
|
import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
|
|
1647
1697
|
var ORPHAN_DEMOTE_THRESHOLD_DAYS = {
|
|
1648
1698
|
stable: 90,
|
|
@@ -1914,11 +1964,27 @@ async function runDoctorReport(target) {
|
|
|
1914
1964
|
warningCount: warnings.length,
|
|
1915
1965
|
infoCount: infos.length,
|
|
1916
1966
|
targetFiles: Object.fromEntries(
|
|
1917
|
-
TARGET_FILE_PATHS.map((path2) => [path2,
|
|
1918
|
-
)
|
|
1967
|
+
TARGET_FILE_PATHS.map((path2) => [path2, existsSync5(join7(projectRoot, path2))])
|
|
1968
|
+
),
|
|
1969
|
+
// v2.0.0-rc.29 TASK-008 (BUG-F2): resolve and surface payload thresholds.
|
|
1970
|
+
// Best-effort: a corrupt fabric.config.json should not fail doctor; on
|
|
1971
|
+
// any read/parse error fall back to library defaults with source="default".
|
|
1972
|
+
payload_limits: resolvePayloadLimits(projectRoot)
|
|
1919
1973
|
}
|
|
1920
1974
|
};
|
|
1921
1975
|
}
|
|
1976
|
+
function resolvePayloadLimits(projectRoot) {
|
|
1977
|
+
let override;
|
|
1978
|
+
try {
|
|
1979
|
+
override = readPayloadLimits(projectRoot);
|
|
1980
|
+
} catch {
|
|
1981
|
+
override = void 0;
|
|
1982
|
+
}
|
|
1983
|
+
const warn = override?.warnBytes ?? PAYLOAD_LIMIT_DEFAULT_WARN_BYTES;
|
|
1984
|
+
const hard = override?.hardBytes ?? PAYLOAD_LIMIT_DEFAULT_HARD_BYTES;
|
|
1985
|
+
const source = override?.warnBytes !== void 0 || override?.hardBytes !== void 0 ? "config" : "default";
|
|
1986
|
+
return { warn_bytes: warn, hard_bytes: hard, source };
|
|
1987
|
+
}
|
|
1922
1988
|
async function runDoctorFix(target) {
|
|
1923
1989
|
const projectRoot = normalizeTarget(target);
|
|
1924
1990
|
const before = await runDoctorReport(projectRoot);
|
|
@@ -1941,7 +2007,7 @@ async function runDoctorFix(target) {
|
|
|
1941
2007
|
}
|
|
1942
2008
|
}
|
|
1943
2009
|
if (before.fixable_errors.some((issue) => issue.code === "bootstrap_snapshot_drift")) {
|
|
1944
|
-
const snapshotPath =
|
|
2010
|
+
const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
|
|
1945
2011
|
await ensureParentDirectory(snapshotPath);
|
|
1946
2012
|
await atomicWriteText4(snapshotPath, BOOTSTRAP_CANONICAL);
|
|
1947
2013
|
fixed.push(findIssue(before.fixable_errors, "bootstrap_snapshot_drift"));
|
|
@@ -2015,7 +2081,7 @@ async function runDoctorFix(target) {
|
|
|
2015
2081
|
if (before.infos.some((issue) => issue.code === "stale_serve_lock")) {
|
|
2016
2082
|
const lockInspection = inspectStaleServeLock(projectRoot, Date.now());
|
|
2017
2083
|
if (lockInspection.present && !lockInspection.pidAlive) {
|
|
2018
|
-
const lockFilePath =
|
|
2084
|
+
const lockFilePath = join7(projectRoot, ".fabric", ".serve.lock");
|
|
2019
2085
|
try {
|
|
2020
2086
|
await unlink(lockFilePath);
|
|
2021
2087
|
} catch (err) {
|
|
@@ -2172,7 +2238,7 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
|
|
|
2172
2238
|
};
|
|
2173
2239
|
}
|
|
2174
2240
|
const detail = `${candidate.maturity} -> ${next}`;
|
|
2175
|
-
const absPath =
|
|
2241
|
+
const absPath = join7(projectRoot, candidate.path);
|
|
2176
2242
|
try {
|
|
2177
2243
|
const source = await readFile5(absPath, "utf8");
|
|
2178
2244
|
const rewritten = rewriteFrontmatterMaturity(source, next);
|
|
@@ -2239,11 +2305,11 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
|
|
|
2239
2305
|
}
|
|
2240
2306
|
}
|
|
2241
2307
|
async function applyStaleArchive(projectRoot, candidate, now) {
|
|
2242
|
-
const sourceAbs =
|
|
2243
|
-
const destAbs =
|
|
2308
|
+
const sourceAbs = join7(projectRoot, candidate.path);
|
|
2309
|
+
const destAbs = join7(projectRoot, candidate.archive_path);
|
|
2244
2310
|
const detail = `${candidate.path} -> ${candidate.archive_path}`;
|
|
2245
2311
|
try {
|
|
2246
|
-
await mkdir4(
|
|
2312
|
+
await mkdir4(join7(destAbs, ".."), { recursive: true });
|
|
2247
2313
|
try {
|
|
2248
2314
|
await rename(sourceAbs, destAbs);
|
|
2249
2315
|
} catch (renameError) {
|
|
@@ -2302,7 +2368,7 @@ async function applyStaleArchive(projectRoot, candidate, now) {
|
|
|
2302
2368
|
async function applyPendingAutoArchive(projectRoot, candidate, now) {
|
|
2303
2369
|
const detail = `${candidate.pending_path} -> ${candidate.archived_to}`;
|
|
2304
2370
|
try {
|
|
2305
|
-
await mkdir4(
|
|
2371
|
+
await mkdir4(join7(candidate.archived_to_abs, ".."), { recursive: true });
|
|
2306
2372
|
let moved = false;
|
|
2307
2373
|
if (candidate.layer === "team") {
|
|
2308
2374
|
try {
|
|
@@ -2379,7 +2445,7 @@ function relativePosix(projectRoot, absolutePath) {
|
|
|
2379
2445
|
}
|
|
2380
2446
|
async function applySessionHintsStaleCleanup(projectRoot, candidate) {
|
|
2381
2447
|
const detail = `deleted (${candidate.age_days}d old)`;
|
|
2382
|
-
const absPath =
|
|
2448
|
+
const absPath = join7(projectRoot, candidate.path);
|
|
2383
2449
|
try {
|
|
2384
2450
|
const { unlink: unlink2 } = await import("fs/promises");
|
|
2385
2451
|
await unlink2(absPath);
|
|
@@ -2400,7 +2466,7 @@ async function applySessionHintsStaleCleanup(projectRoot, candidate) {
|
|
|
2400
2466
|
}
|
|
2401
2467
|
}
|
|
2402
2468
|
async function applyIndexDriftFix(projectRoot, inspection) {
|
|
2403
|
-
const metaPath =
|
|
2469
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
2404
2470
|
const detailParts = [];
|
|
2405
2471
|
try {
|
|
2406
2472
|
const meta = agentsMetaSchema4.parse(JSON.parse(await readFile5(metaPath, "utf8")));
|
|
@@ -2436,7 +2502,7 @@ function truncateErrorMessage(error) {
|
|
|
2436
2502
|
return raw.length > 240 ? `${raw.slice(0, 237)}...` : raw;
|
|
2437
2503
|
}
|
|
2438
2504
|
async function inspectForensic(projectRoot) {
|
|
2439
|
-
const path2 =
|
|
2505
|
+
const path2 = join7(projectRoot, ".fabric", "forensic.json");
|
|
2440
2506
|
try {
|
|
2441
2507
|
const parsed = forensicReportSchema.parse(JSON.parse(await readFile5(path2, "utf8")));
|
|
2442
2508
|
return { present: true, valid: true, report: parsed };
|
|
@@ -2448,12 +2514,12 @@ async function inspectForensic(projectRoot) {
|
|
|
2448
2514
|
}
|
|
2449
2515
|
}
|
|
2450
2516
|
function inspectMcpConfigInWrongFile(projectRoot) {
|
|
2451
|
-
const settingsPath =
|
|
2452
|
-
if (!
|
|
2517
|
+
const settingsPath = join7(projectRoot, ".claude", "settings.json");
|
|
2518
|
+
if (!existsSync5(settingsPath)) {
|
|
2453
2519
|
return { hasWrongEntry: false, settingsPath };
|
|
2454
2520
|
}
|
|
2455
2521
|
try {
|
|
2456
|
-
const parsed = JSON.parse(
|
|
2522
|
+
const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
2457
2523
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2458
2524
|
return { hasWrongEntry: false, settingsPath };
|
|
2459
2525
|
}
|
|
@@ -2469,7 +2535,7 @@ function inspectMcpConfigInWrongFile(projectRoot) {
|
|
|
2469
2535
|
}
|
|
2470
2536
|
}
|
|
2471
2537
|
async function inspectMeta(projectRoot) {
|
|
2472
|
-
const metaPath =
|
|
2538
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
2473
2539
|
const built = await tryBuildRuleMeta(projectRoot);
|
|
2474
2540
|
try {
|
|
2475
2541
|
const raw = await readFile5(metaPath, "utf8");
|
|
@@ -2542,7 +2608,7 @@ function inspectContentRefs(projectRoot, meta) {
|
|
|
2542
2608
|
if (isPersonalKnowledge) {
|
|
2543
2609
|
continue;
|
|
2544
2610
|
}
|
|
2545
|
-
if (!
|
|
2611
|
+
if (!existsSync5(join7(projectRoot, contentRef))) {
|
|
2546
2612
|
missing.push(contentRef);
|
|
2547
2613
|
}
|
|
2548
2614
|
}
|
|
@@ -2550,7 +2616,7 @@ function inspectContentRefs(projectRoot, meta) {
|
|
|
2550
2616
|
}
|
|
2551
2617
|
async function inspectEventLedger(projectRoot) {
|
|
2552
2618
|
const path2 = getEventLedgerPath(projectRoot);
|
|
2553
|
-
const exists =
|
|
2619
|
+
const exists = existsSync5(path2);
|
|
2554
2620
|
if (!exists) {
|
|
2555
2621
|
return {
|
|
2556
2622
|
exists: false,
|
|
@@ -2626,8 +2692,8 @@ function inspectSkillRefMirror(projectRoot) {
|
|
|
2626
2692
|
const skillSlugs = ["fabric-archive", "fabric-review", "fabric-import"];
|
|
2627
2693
|
const driftedPaths = [];
|
|
2628
2694
|
for (const slug of skillSlugs) {
|
|
2629
|
-
const claudeRef =
|
|
2630
|
-
const codexRef =
|
|
2695
|
+
const claudeRef = join7(projectRoot, ".claude", "skills", slug, "ref");
|
|
2696
|
+
const codexRef = join7(projectRoot, ".codex", "skills", slug, "ref");
|
|
2631
2697
|
let claudeFiles = null;
|
|
2632
2698
|
let codexFiles = null;
|
|
2633
2699
|
try {
|
|
@@ -2652,12 +2718,12 @@ function inspectSkillRefMirror(projectRoot) {
|
|
|
2652
2718
|
let claudeBody;
|
|
2653
2719
|
let codexBody;
|
|
2654
2720
|
try {
|
|
2655
|
-
claudeBody =
|
|
2721
|
+
claudeBody = readFileSync3(join7(claudeRef, fname), "utf8");
|
|
2656
2722
|
} catch {
|
|
2657
2723
|
continue;
|
|
2658
2724
|
}
|
|
2659
2725
|
try {
|
|
2660
|
-
codexBody =
|
|
2726
|
+
codexBody = readFileSync3(join7(codexRef, fname), "utf8");
|
|
2661
2727
|
} catch {
|
|
2662
2728
|
continue;
|
|
2663
2729
|
}
|
|
@@ -2670,7 +2736,7 @@ function inspectSkillRefMirror(projectRoot) {
|
|
|
2670
2736
|
return { status: "drift", driftedPaths };
|
|
2671
2737
|
}
|
|
2672
2738
|
async function inspectKnowledgeTestIndex(projectRoot) {
|
|
2673
|
-
const path2 =
|
|
2739
|
+
const path2 = join7(projectRoot, ".fabric", ".cache", "knowledge-test.index.json");
|
|
2674
2740
|
const built = await tryBuildRuleMeta(projectRoot);
|
|
2675
2741
|
try {
|
|
2676
2742
|
const index = knowledgeTestIndexSchema2.parse(JSON.parse(await readFile5(path2, "utf8")));
|
|
@@ -2694,8 +2760,8 @@ async function inspectKnowledgeTestIndex(projectRoot) {
|
|
|
2694
2760
|
}
|
|
2695
2761
|
function inspectBootstrapAnchor(projectRoot) {
|
|
2696
2762
|
return {
|
|
2697
|
-
hasAgentsMd:
|
|
2698
|
-
hasClaudeMd:
|
|
2763
|
+
hasAgentsMd: existsSync5(join7(projectRoot, "AGENTS.md")),
|
|
2764
|
+
hasClaudeMd: existsSync5(join7(projectRoot, "CLAUDE.md"))
|
|
2699
2765
|
};
|
|
2700
2766
|
}
|
|
2701
2767
|
var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
|
|
@@ -2707,8 +2773,8 @@ var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
|
|
|
2707
2773
|
async function inspectBootstrapMarkerMigration(target) {
|
|
2708
2774
|
const filesNeedingMigration = [];
|
|
2709
2775
|
for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
|
|
2710
|
-
const abs =
|
|
2711
|
-
if (!
|
|
2776
|
+
const abs = join7(target, rel);
|
|
2777
|
+
if (!existsSync5(abs)) {
|
|
2712
2778
|
continue;
|
|
2713
2779
|
}
|
|
2714
2780
|
let content;
|
|
@@ -2745,8 +2811,8 @@ function createBootstrapMarkerMigrationCheck(t, inspection) {
|
|
|
2745
2811
|
);
|
|
2746
2812
|
}
|
|
2747
2813
|
async function inspectL1BootstrapSnapshotDrift(target) {
|
|
2748
|
-
const abs =
|
|
2749
|
-
if (!
|
|
2814
|
+
const abs = join7(target, ".fabric", "AGENTS.md");
|
|
2815
|
+
if (!existsSync5(abs)) {
|
|
2750
2816
|
return { status: "missing", canonical: BOOTSTRAP_CANONICAL, onDisk: null };
|
|
2751
2817
|
}
|
|
2752
2818
|
let onDisk;
|
|
@@ -2777,8 +2843,8 @@ function createL1BootstrapSnapshotDriftCheck(t, inspection) {
|
|
|
2777
2843
|
);
|
|
2778
2844
|
}
|
|
2779
2845
|
async function inspectL2ManagedBlockDrift(target) {
|
|
2780
|
-
const snapshotPath =
|
|
2781
|
-
if (!
|
|
2846
|
+
const snapshotPath = join7(target, ".fabric", "AGENTS.md");
|
|
2847
|
+
if (!existsSync5(snapshotPath)) {
|
|
2782
2848
|
return { status: "ok", drifted: [] };
|
|
2783
2849
|
}
|
|
2784
2850
|
let snapshot;
|
|
@@ -2787,9 +2853,9 @@ async function inspectL2ManagedBlockDrift(target) {
|
|
|
2787
2853
|
} catch {
|
|
2788
2854
|
return { status: "ok", drifted: [] };
|
|
2789
2855
|
}
|
|
2790
|
-
const projectRulesPath =
|
|
2856
|
+
const projectRulesPath = join7(target, ".fabric", "project-rules.md");
|
|
2791
2857
|
let expectedBody = snapshot;
|
|
2792
|
-
if (
|
|
2858
|
+
if (existsSync5(projectRulesPath)) {
|
|
2793
2859
|
try {
|
|
2794
2860
|
const projectRules = await readFile5(projectRulesPath, "utf8");
|
|
2795
2861
|
expectedBody = `${snapshot}
|
|
@@ -2801,11 +2867,11 @@ ${projectRules}`;
|
|
|
2801
2867
|
const drifted = [];
|
|
2802
2868
|
let anyManagedBlockFound = false;
|
|
2803
2869
|
const blockTargets = [
|
|
2804
|
-
|
|
2805
|
-
|
|
2870
|
+
join7(target, "AGENTS.md"),
|
|
2871
|
+
join7(target, ".cursor", "rules", "fabric-bootstrap.mdc")
|
|
2806
2872
|
];
|
|
2807
2873
|
for (const abs of blockTargets) {
|
|
2808
|
-
if (!
|
|
2874
|
+
if (!existsSync5(abs)) {
|
|
2809
2875
|
continue;
|
|
2810
2876
|
}
|
|
2811
2877
|
let content;
|
|
@@ -2836,8 +2902,8 @@ ${projectRules}`;
|
|
|
2836
2902
|
drifted.push({ path: abs, expected: expectedBody, actual: body });
|
|
2837
2903
|
}
|
|
2838
2904
|
}
|
|
2839
|
-
const claudeMdPath =
|
|
2840
|
-
if (
|
|
2905
|
+
const claudeMdPath = join7(target, "CLAUDE.md");
|
|
2906
|
+
if (existsSync5(claudeMdPath)) {
|
|
2841
2907
|
let claudeContent;
|
|
2842
2908
|
try {
|
|
2843
2909
|
claudeContent = await readFile5(claudeMdPath, "utf8");
|
|
@@ -2907,11 +2973,11 @@ function createBootstrapAnchorCheck(t, inspection) {
|
|
|
2907
2973
|
);
|
|
2908
2974
|
}
|
|
2909
2975
|
function inspectKnowledgeDirMissing(projectRoot) {
|
|
2910
|
-
const knowledgeRoot =
|
|
2976
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
2911
2977
|
const missingSubdirs = [];
|
|
2912
2978
|
for (const sub of KNOWLEDGE_SUBDIRS3) {
|
|
2913
|
-
const path2 =
|
|
2914
|
-
if (!
|
|
2979
|
+
const path2 = join7(knowledgeRoot, sub);
|
|
2980
|
+
if (!existsSync5(path2)) {
|
|
2915
2981
|
missingSubdirs.push(`.fabric/knowledge/${sub}`);
|
|
2916
2982
|
}
|
|
2917
2983
|
}
|
|
@@ -2919,13 +2985,13 @@ function inspectKnowledgeDirMissing(projectRoot) {
|
|
|
2919
2985
|
}
|
|
2920
2986
|
function inspectBaselineFilenameFormat(projectRoot) {
|
|
2921
2987
|
const offenders = [];
|
|
2922
|
-
const knowledgeRoot =
|
|
2923
|
-
if (!
|
|
2988
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
2989
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
2924
2990
|
return { offenders };
|
|
2925
2991
|
}
|
|
2926
2992
|
for (const sub of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
2927
|
-
const dir =
|
|
2928
|
-
if (!
|
|
2993
|
+
const dir = join7(knowledgeRoot, sub);
|
|
2994
|
+
if (!existsSync5(dir)) {
|
|
2929
2995
|
continue;
|
|
2930
2996
|
}
|
|
2931
2997
|
let entries;
|
|
@@ -2942,10 +3008,10 @@ function inspectBaselineFilenameFormat(projectRoot) {
|
|
|
2942
3008
|
if (BASELINE_ID_PREFIXED_FILENAME_PATTERN.test(entryName)) {
|
|
2943
3009
|
continue;
|
|
2944
3010
|
}
|
|
2945
|
-
const abs =
|
|
3011
|
+
const abs = join7(dir, entryName);
|
|
2946
3012
|
let source;
|
|
2947
3013
|
try {
|
|
2948
|
-
source =
|
|
3014
|
+
source = readFileSync3(abs, "utf8");
|
|
2949
3015
|
} catch {
|
|
2950
3016
|
continue;
|
|
2951
3017
|
}
|
|
@@ -3319,8 +3385,8 @@ function findIssue(issues, code) {
|
|
|
3319
3385
|
};
|
|
3320
3386
|
}
|
|
3321
3387
|
async function inspectMetaManuallyDiverged(projectRoot) {
|
|
3322
|
-
const metaPath =
|
|
3323
|
-
if (!
|
|
3388
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
3389
|
+
if (!existsSync5(metaPath)) {
|
|
3324
3390
|
return { extraMetaEntries: [], hashMismatchEntries: [], readable: false };
|
|
3325
3391
|
}
|
|
3326
3392
|
let meta;
|
|
@@ -3339,13 +3405,13 @@ async function inspectMetaManuallyDiverged(projectRoot) {
|
|
|
3339
3405
|
const hashMismatchEntries = [];
|
|
3340
3406
|
for (const node of Object.values(meta.nodes)) {
|
|
3341
3407
|
const contentRef = node.content_ref ?? node.file;
|
|
3342
|
-
const absPath =
|
|
3343
|
-
if (!
|
|
3408
|
+
const absPath = join7(projectRoot, contentRef);
|
|
3409
|
+
if (!existsSync5(absPath)) {
|
|
3344
3410
|
extraMetaEntries.push(contentRef);
|
|
3345
3411
|
continue;
|
|
3346
3412
|
}
|
|
3347
3413
|
try {
|
|
3348
|
-
const content =
|
|
3414
|
+
const content = readFileSync3(absPath, "utf8");
|
|
3349
3415
|
const diskHash = sha256(content);
|
|
3350
3416
|
if (node.hash !== "" && node.hash !== diskHash) {
|
|
3351
3417
|
hashMismatchEntries.push(contentRef);
|
|
@@ -3358,7 +3424,7 @@ async function inspectMetaManuallyDiverged(projectRoot) {
|
|
|
3358
3424
|
}
|
|
3359
3425
|
function inspectKnowledgeDirUnindexed(projectRoot, meta) {
|
|
3360
3426
|
const physicalMdFiles = /* @__PURE__ */ new Set();
|
|
3361
|
-
collectMdFilesUnder(physicalMdFiles, projectRoot,
|
|
3427
|
+
collectMdFilesUnder(physicalMdFiles, projectRoot, join7(projectRoot, ".fabric", "knowledge"), ".fabric/knowledge");
|
|
3362
3428
|
if (physicalMdFiles.size === 0) {
|
|
3363
3429
|
return { unindexedFiles: [] };
|
|
3364
3430
|
}
|
|
@@ -3373,7 +3439,7 @@ function inspectKnowledgeDirUnindexed(projectRoot, meta) {
|
|
|
3373
3439
|
return { unindexedFiles };
|
|
3374
3440
|
}
|
|
3375
3441
|
function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
3376
|
-
if (!
|
|
3442
|
+
if (!existsSync5(rootDir)) {
|
|
3377
3443
|
return;
|
|
3378
3444
|
}
|
|
3379
3445
|
const stack = [rootDir];
|
|
@@ -3383,7 +3449,7 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
|
3383
3449
|
continue;
|
|
3384
3450
|
}
|
|
3385
3451
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3386
|
-
const abs =
|
|
3452
|
+
const abs = join7(dir, entry.name);
|
|
3387
3453
|
if (entry.isDirectory()) {
|
|
3388
3454
|
if (entry.name !== "pending" && entry.name !== "archive") {
|
|
3389
3455
|
stack.push(abs);
|
|
@@ -3416,8 +3482,8 @@ function createKnowledgeDirUnindexedCheck(t, inspection) {
|
|
|
3416
3482
|
}
|
|
3417
3483
|
async function inspectStableIdCollisions(projectRoot) {
|
|
3418
3484
|
const found = [];
|
|
3419
|
-
const knowledgeDir =
|
|
3420
|
-
if (
|
|
3485
|
+
const knowledgeDir = join7(projectRoot, ".fabric", "knowledge");
|
|
3486
|
+
if (existsSync5(knowledgeDir)) {
|
|
3421
3487
|
const stack = [knowledgeDir];
|
|
3422
3488
|
while (stack.length > 0) {
|
|
3423
3489
|
const dir = stack.pop();
|
|
@@ -3425,7 +3491,7 @@ async function inspectStableIdCollisions(projectRoot) {
|
|
|
3425
3491
|
continue;
|
|
3426
3492
|
}
|
|
3427
3493
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3428
|
-
const abs =
|
|
3494
|
+
const abs = join7(dir, entry.name);
|
|
3429
3495
|
if (entry.isDirectory()) {
|
|
3430
3496
|
stack.push(abs);
|
|
3431
3497
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -3490,11 +3556,11 @@ function inspectCounterDesync(meta) {
|
|
|
3490
3556
|
}
|
|
3491
3557
|
const layer = parsed.layer === "personal" ? "KP" : "KT";
|
|
3492
3558
|
const typeCode = [
|
|
3493
|
-
["
|
|
3494
|
-
["
|
|
3495
|
-
["
|
|
3496
|
-
["
|
|
3497
|
-
["
|
|
3559
|
+
["models", "MOD"],
|
|
3560
|
+
["decisions", "DEC"],
|
|
3561
|
+
["guidelines", "GLD"],
|
|
3562
|
+
["pitfalls", "PIT"],
|
|
3563
|
+
["processes", "PRO"]
|
|
3498
3564
|
].find(([t]) => t === parsed.type)?.[1];
|
|
3499
3565
|
if (typeCode === void 0) {
|
|
3500
3566
|
continue;
|
|
@@ -3604,18 +3670,18 @@ function createMetaManuallyDivergedCheck(t, inspection) {
|
|
|
3604
3670
|
}
|
|
3605
3671
|
function inspectPreexistingRootFiles(projectRoot) {
|
|
3606
3672
|
const candidates = ["CLAUDE.md", "AGENTS.md"];
|
|
3607
|
-
const detected = candidates.filter((name) =>
|
|
3673
|
+
const detected = candidates.filter((name) => existsSync5(join7(projectRoot, name)));
|
|
3608
3674
|
return { detected };
|
|
3609
3675
|
}
|
|
3610
3676
|
async function inspectFilesystemEditFallback(projectRoot) {
|
|
3611
|
-
const knowledgeRoot =
|
|
3612
|
-
if (!
|
|
3677
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3678
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
3613
3679
|
return { synthesized: 0, synthesizedStableIds: [] };
|
|
3614
3680
|
}
|
|
3615
3681
|
const canonicalIds = /* @__PURE__ */ new Set();
|
|
3616
3682
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3617
|
-
const dir =
|
|
3618
|
-
if (!
|
|
3683
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
3684
|
+
if (!existsSync5(dir)) {
|
|
3619
3685
|
continue;
|
|
3620
3686
|
}
|
|
3621
3687
|
let entries;
|
|
@@ -3655,17 +3721,40 @@ async function inspectFilesystemEditFallback(projectRoot) {
|
|
|
3655
3721
|
}
|
|
3656
3722
|
orphanIds.sort();
|
|
3657
3723
|
for (const stable_id of orphanIds) {
|
|
3658
|
-
await
|
|
3659
|
-
event_type: "knowledge_promoted",
|
|
3660
|
-
stable_id,
|
|
3661
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3662
|
-
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3663
|
-
correlation_id: "doctor-synthesized",
|
|
3664
|
-
session_id: "doctor-synthesized"
|
|
3665
|
-
});
|
|
3724
|
+
await emitSynthesizedPromotionTriplet(projectRoot, stable_id);
|
|
3666
3725
|
}
|
|
3667
3726
|
return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
|
|
3668
3727
|
}
|
|
3728
|
+
async function emitSynthesizedPromotionTriplet(projectRoot, stable_id) {
|
|
3729
|
+
const baseTs = Date.now();
|
|
3730
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
3731
|
+
event_type: "knowledge_proposed",
|
|
3732
|
+
stable_id,
|
|
3733
|
+
timestamp: new Date(baseTs).toISOString(),
|
|
3734
|
+
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3735
|
+
correlation_id: "doctor-synthesized",
|
|
3736
|
+
session_id: "doctor-synthesized",
|
|
3737
|
+
ts: baseTs
|
|
3738
|
+
});
|
|
3739
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
3740
|
+
event_type: "knowledge_promote_started",
|
|
3741
|
+
stable_id,
|
|
3742
|
+
timestamp: new Date(baseTs + 1).toISOString(),
|
|
3743
|
+
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3744
|
+
correlation_id: "doctor-synthesized",
|
|
3745
|
+
session_id: "doctor-synthesized",
|
|
3746
|
+
ts: baseTs + 1
|
|
3747
|
+
});
|
|
3748
|
+
await appendEventLedgerEvent(projectRoot, {
|
|
3749
|
+
event_type: "knowledge_promoted",
|
|
3750
|
+
stable_id,
|
|
3751
|
+
timestamp: new Date(baseTs + 2).toISOString(),
|
|
3752
|
+
reason: SYNTHESIZED_PROMOTED_REASON,
|
|
3753
|
+
correlation_id: "doctor-synthesized",
|
|
3754
|
+
session_id: "doctor-synthesized",
|
|
3755
|
+
ts: baseTs + 2
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3669
3758
|
function createFilesystemEditFallbackCheck(t, inspection) {
|
|
3670
3759
|
if (inspection.synthesized === 0) {
|
|
3671
3760
|
return okCheck(
|
|
@@ -3824,13 +3913,13 @@ function extractKnowledgeFrontmatterCreatedAt(source) {
|
|
|
3824
3913
|
return Number.isFinite(parsed) ? parsed : null;
|
|
3825
3914
|
}
|
|
3826
3915
|
function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
|
|
3827
|
-
const knowledgeRoot =
|
|
3828
|
-
if (!
|
|
3916
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3917
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
3829
3918
|
return;
|
|
3830
3919
|
}
|
|
3831
3920
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3832
|
-
const dir =
|
|
3833
|
-
if (!
|
|
3921
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
3922
|
+
if (!existsSync5(dir)) {
|
|
3834
3923
|
continue;
|
|
3835
3924
|
}
|
|
3836
3925
|
let entries;
|
|
@@ -3848,10 +3937,10 @@ function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
|
|
|
3848
3937
|
continue;
|
|
3849
3938
|
}
|
|
3850
3939
|
const stableId = match[1];
|
|
3851
|
-
const absPath =
|
|
3940
|
+
const absPath = join7(dir, entry.name);
|
|
3852
3941
|
let source;
|
|
3853
3942
|
try {
|
|
3854
|
-
source =
|
|
3943
|
+
source = readFileSync3(absPath, "utf8");
|
|
3855
3944
|
} catch {
|
|
3856
3945
|
continue;
|
|
3857
3946
|
}
|
|
@@ -3924,13 +4013,13 @@ async function inspectStaleArchive(projectRoot, now) {
|
|
|
3924
4013
|
return { candidates };
|
|
3925
4014
|
}
|
|
3926
4015
|
function* iteratePendingFiles(projectRoot, now) {
|
|
3927
|
-
const teamRoot =
|
|
3928
|
-
const personalRoot =
|
|
4016
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
|
|
4017
|
+
const personalRoot = join7(resolvePersonalRootForPending(), ".fabric", "knowledge", "pending");
|
|
3929
4018
|
for (const [layer, root, displayPrefix] of [
|
|
3930
4019
|
["team", teamRoot, ".fabric/knowledge/pending"],
|
|
3931
4020
|
["personal", personalRoot, "~/.fabric/knowledge/pending"]
|
|
3932
4021
|
]) {
|
|
3933
|
-
if (!
|
|
4022
|
+
if (!existsSync5(root)) {
|
|
3934
4023
|
continue;
|
|
3935
4024
|
}
|
|
3936
4025
|
let typeDirs = [];
|
|
@@ -3940,7 +4029,7 @@ function* iteratePendingFiles(projectRoot, now) {
|
|
|
3940
4029
|
continue;
|
|
3941
4030
|
}
|
|
3942
4031
|
for (const typeDir of typeDirs) {
|
|
3943
|
-
const dir =
|
|
4032
|
+
const dir = join7(root, typeDir);
|
|
3944
4033
|
let entries;
|
|
3945
4034
|
try {
|
|
3946
4035
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -3951,10 +4040,10 @@ function* iteratePendingFiles(projectRoot, now) {
|
|
|
3951
4040
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
3952
4041
|
continue;
|
|
3953
4042
|
}
|
|
3954
|
-
const absPath =
|
|
4043
|
+
const absPath = join7(dir, entry.name);
|
|
3955
4044
|
let source = "";
|
|
3956
4045
|
try {
|
|
3957
|
-
source =
|
|
4046
|
+
source = readFileSync3(absPath, "utf8");
|
|
3958
4047
|
} catch {
|
|
3959
4048
|
continue;
|
|
3960
4049
|
}
|
|
@@ -4026,7 +4115,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
4026
4115
|
pending_path: visit.pending_path,
|
|
4027
4116
|
pending_path_abs: visit.pending_path_abs,
|
|
4028
4117
|
archived_to: archivedToRel,
|
|
4029
|
-
archived_to_abs:
|
|
4118
|
+
archived_to_abs: join7(projectRoot, archivedToRel),
|
|
4030
4119
|
age_days: visit.age_days
|
|
4031
4120
|
});
|
|
4032
4121
|
} else {
|
|
@@ -4035,7 +4124,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
4035
4124
|
visit.type,
|
|
4036
4125
|
visit.filename
|
|
4037
4126
|
);
|
|
4038
|
-
const archivedToAbs =
|
|
4127
|
+
const archivedToAbs = join7(
|
|
4039
4128
|
resolvePersonalRootForPending(),
|
|
4040
4129
|
".fabric",
|
|
4041
4130
|
".archive",
|
|
@@ -4059,12 +4148,12 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
4059
4148
|
}
|
|
4060
4149
|
function inspectUnderseeded(projectRoot) {
|
|
4061
4150
|
const threshold = readUnderseedThresholdFromConfig(projectRoot);
|
|
4062
|
-
const knowledgeRoot =
|
|
4151
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
4063
4152
|
let nodeCount = 0;
|
|
4064
|
-
if (
|
|
4153
|
+
if (existsSync5(knowledgeRoot)) {
|
|
4065
4154
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
4066
|
-
const dir =
|
|
4067
|
-
if (!
|
|
4155
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
4156
|
+
if (!existsSync5(dir)) continue;
|
|
4068
4157
|
let entries;
|
|
4069
4158
|
try {
|
|
4070
4159
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4085,8 +4174,8 @@ function inspectUnderseeded(projectRoot) {
|
|
|
4085
4174
|
};
|
|
4086
4175
|
}
|
|
4087
4176
|
function inspectSessionHintsStale(projectRoot, now) {
|
|
4088
|
-
const cacheDir =
|
|
4089
|
-
if (!
|
|
4177
|
+
const cacheDir = join7(projectRoot, ".fabric", ".cache");
|
|
4178
|
+
if (!existsSync5(cacheDir)) {
|
|
4090
4179
|
return { candidates: [] };
|
|
4091
4180
|
}
|
|
4092
4181
|
let entries;
|
|
@@ -4100,7 +4189,7 @@ function inspectSessionHintsStale(projectRoot, now) {
|
|
|
4100
4189
|
if (!entry.isFile()) continue;
|
|
4101
4190
|
if (!entry.name.startsWith(SESSION_HINTS_FILE_PREFIX)) continue;
|
|
4102
4191
|
if (!entry.name.endsWith(SESSION_HINTS_FILE_SUFFIX)) continue;
|
|
4103
|
-
const absPath =
|
|
4192
|
+
const absPath = join7(cacheDir, entry.name);
|
|
4104
4193
|
let mtimeMs = 0;
|
|
4105
4194
|
try {
|
|
4106
4195
|
mtimeMs = statSync4(absPath).mtimeMs;
|
|
@@ -4144,11 +4233,11 @@ function inspectNarrowTooFew(projectRoot, now) {
|
|
|
4144
4233
|
const structuralFlagged = total >= NARROW_MIN_TOTAL && narrowRatio < NARROW_RATIO_THRESHOLD;
|
|
4145
4234
|
const windowStartMs = now - SILENCE_WINDOW_DAYS * MS_PER_DAY;
|
|
4146
4235
|
const editFires = readCounterTimestamps(
|
|
4147
|
-
|
|
4236
|
+
join7(projectRoot, EDIT_COUNTER_FILE_REL),
|
|
4148
4237
|
windowStartMs
|
|
4149
4238
|
);
|
|
4150
4239
|
const silenceFires = readCounterTimestamps(
|
|
4151
|
-
|
|
4240
|
+
join7(projectRoot, HINT_SILENCE_COUNTER_FILE_REL),
|
|
4152
4241
|
windowStartMs
|
|
4153
4242
|
);
|
|
4154
4243
|
const telemetrySkipped = editFires === 0;
|
|
@@ -4167,10 +4256,10 @@ function inspectNarrowTooFew(projectRoot, now) {
|
|
|
4167
4256
|
};
|
|
4168
4257
|
}
|
|
4169
4258
|
function readCounterTimestamps(absPath, windowStartMs) {
|
|
4170
|
-
if (!
|
|
4259
|
+
if (!existsSync5(absPath)) return 0;
|
|
4171
4260
|
let raw;
|
|
4172
4261
|
try {
|
|
4173
|
-
raw =
|
|
4262
|
+
raw = readFileSync3(absPath, "utf8");
|
|
4174
4263
|
} catch {
|
|
4175
4264
|
return 0;
|
|
4176
4265
|
}
|
|
@@ -4186,10 +4275,10 @@ function readCounterTimestamps(absPath, windowStartMs) {
|
|
|
4186
4275
|
return count;
|
|
4187
4276
|
}
|
|
4188
4277
|
function readUnderseedThresholdFromConfig(projectRoot) {
|
|
4189
|
-
const configPath =
|
|
4190
|
-
if (!
|
|
4278
|
+
const configPath = join7(projectRoot, ".fabric", "fabric-config.json");
|
|
4279
|
+
if (!existsSync5(configPath)) return DEFAULT_UNDERSEED_NODE_THRESHOLD;
|
|
4191
4280
|
try {
|
|
4192
|
-
const raw =
|
|
4281
|
+
const raw = readFileSync3(configPath, "utf8");
|
|
4193
4282
|
const parsed = JSON.parse(raw);
|
|
4194
4283
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4195
4284
|
const v = parsed.underseed_node_threshold;
|
|
@@ -4383,11 +4472,11 @@ function extractKnowledgeFrontmatterRelevancePaths(source) {
|
|
|
4383
4472
|
}
|
|
4384
4473
|
function* iterateRelevanceFrontmatter(projectRoot) {
|
|
4385
4474
|
for (const visit of iterateCanonicalFilenames(projectRoot)) {
|
|
4386
|
-
const layerRoot = visit.layer === "team" ?
|
|
4387
|
-
const absPath =
|
|
4475
|
+
const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
|
|
4476
|
+
const absPath = join7(layerRoot, visit.type, visit.filename);
|
|
4388
4477
|
let source;
|
|
4389
4478
|
try {
|
|
4390
|
-
source =
|
|
4479
|
+
source = readFileSync3(absPath, "utf8");
|
|
4391
4480
|
} catch {
|
|
4392
4481
|
continue;
|
|
4393
4482
|
}
|
|
@@ -4449,7 +4538,7 @@ function inspectRelevancePathsDangling(projectRoot) {
|
|
|
4449
4538
|
return { entries };
|
|
4450
4539
|
}
|
|
4451
4540
|
function collectWorkspacePathsForGlobMatch(projectRoot) {
|
|
4452
|
-
if (!
|
|
4541
|
+
if (!existsSync5(projectRoot)) {
|
|
4453
4542
|
return [];
|
|
4454
4543
|
}
|
|
4455
4544
|
let rootStat;
|
|
@@ -4473,7 +4562,7 @@ function collectWorkspacePathsForGlobMatch(projectRoot) {
|
|
|
4473
4562
|
continue;
|
|
4474
4563
|
}
|
|
4475
4564
|
for (const entry of entries) {
|
|
4476
|
-
const abs =
|
|
4565
|
+
const abs = join7(current, entry.name);
|
|
4477
4566
|
const rel = normalizePath(abs.slice(projectRoot.length + 1));
|
|
4478
4567
|
if (rel.length === 0) continue;
|
|
4479
4568
|
if (entry.isDirectory()) {
|
|
@@ -4630,8 +4719,8 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4630
4719
|
const candidates = [];
|
|
4631
4720
|
let scannedCount = 0;
|
|
4632
4721
|
const FM_PATTERN = /^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---/u;
|
|
4633
|
-
const teamRoot =
|
|
4634
|
-
const personalRoot =
|
|
4722
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
|
|
4723
|
+
const personalRoot = join7(
|
|
4635
4724
|
resolvePersonalRootForPending(),
|
|
4636
4725
|
".fabric",
|
|
4637
4726
|
"knowledge",
|
|
@@ -4641,7 +4730,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4641
4730
|
[teamRoot, ".fabric/knowledge/pending"],
|
|
4642
4731
|
[personalRoot, "~/.fabric/knowledge/pending"]
|
|
4643
4732
|
]) {
|
|
4644
|
-
if (!
|
|
4733
|
+
if (!existsSync5(root)) {
|
|
4645
4734
|
continue;
|
|
4646
4735
|
}
|
|
4647
4736
|
let typeDirs = [];
|
|
@@ -4651,7 +4740,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4651
4740
|
continue;
|
|
4652
4741
|
}
|
|
4653
4742
|
for (const typeDir of typeDirs) {
|
|
4654
|
-
const dir =
|
|
4743
|
+
const dir = join7(root, typeDir);
|
|
4655
4744
|
let entries;
|
|
4656
4745
|
try {
|
|
4657
4746
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4662,10 +4751,10 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4662
4751
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
4663
4752
|
continue;
|
|
4664
4753
|
}
|
|
4665
|
-
const absPath =
|
|
4754
|
+
const absPath = join7(dir, entry.name);
|
|
4666
4755
|
let source;
|
|
4667
4756
|
try {
|
|
4668
|
-
source =
|
|
4757
|
+
source = readFileSync3(absPath, "utf8");
|
|
4669
4758
|
} catch {
|
|
4670
4759
|
continue;
|
|
4671
4760
|
}
|
|
@@ -4797,8 +4886,8 @@ var SKILL_QUOTED_VALUE_LEADS = /* @__PURE__ */ new Set(['"', "'", "[", "{", ">",
|
|
|
4797
4886
|
function inspectSkillMdYamlInvalid(projectRoot) {
|
|
4798
4887
|
const candidates = [];
|
|
4799
4888
|
for (const rootRel of SKILL_MD_FRONTMATTER_ROOTS) {
|
|
4800
|
-
const rootAbs =
|
|
4801
|
-
if (!
|
|
4889
|
+
const rootAbs = join7(projectRoot, rootRel);
|
|
4890
|
+
if (!existsSync5(rootAbs)) continue;
|
|
4802
4891
|
let dirEntries;
|
|
4803
4892
|
try {
|
|
4804
4893
|
dirEntries = readdirSync(rootAbs, { withFileTypes: true });
|
|
@@ -4807,11 +4896,11 @@ function inspectSkillMdYamlInvalid(projectRoot) {
|
|
|
4807
4896
|
}
|
|
4808
4897
|
for (const dirEntry of dirEntries) {
|
|
4809
4898
|
if (!dirEntry.isDirectory()) continue;
|
|
4810
|
-
const skillFile =
|
|
4811
|
-
if (!
|
|
4899
|
+
const skillFile = join7(rootAbs, dirEntry.name, "SKILL.md");
|
|
4900
|
+
if (!existsSync5(skillFile)) continue;
|
|
4812
4901
|
let raw;
|
|
4813
4902
|
try {
|
|
4814
|
-
raw =
|
|
4903
|
+
raw = readFileSync3(skillFile, "utf8");
|
|
4815
4904
|
} catch {
|
|
4816
4905
|
continue;
|
|
4817
4906
|
}
|
|
@@ -4893,11 +4982,11 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4893
4982
|
for (const slot of ONBOARD_SLOT_NAMES) {
|
|
4894
4983
|
filled[slot] = [];
|
|
4895
4984
|
}
|
|
4896
|
-
const knowledgeRoot =
|
|
4897
|
-
if (
|
|
4985
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
4986
|
+
if (existsSync5(knowledgeRoot)) {
|
|
4898
4987
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD) {
|
|
4899
|
-
const dir =
|
|
4900
|
-
if (!
|
|
4988
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
4989
|
+
if (!existsSync5(dir)) continue;
|
|
4901
4990
|
let entries;
|
|
4902
4991
|
try {
|
|
4903
4992
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4907,10 +4996,10 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4907
4996
|
for (const entry of entries) {
|
|
4908
4997
|
if (!entry.isFile()) continue;
|
|
4909
4998
|
if (!entry.name.endsWith(".md")) continue;
|
|
4910
|
-
const filePath =
|
|
4999
|
+
const filePath = join7(dir, entry.name);
|
|
4911
5000
|
let content;
|
|
4912
5001
|
try {
|
|
4913
|
-
content =
|
|
5002
|
+
content = readFileSync3(filePath, "utf8");
|
|
4914
5003
|
} catch {
|
|
4915
5004
|
continue;
|
|
4916
5005
|
}
|
|
@@ -4934,11 +5023,11 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4934
5023
|
return { filled, missing, opted_out: optedOut };
|
|
4935
5024
|
}
|
|
4936
5025
|
function readOnboardOptedOut(projectRoot) {
|
|
4937
|
-
const path2 =
|
|
4938
|
-
if (!
|
|
5026
|
+
const path2 = join7(projectRoot, ".fabric", "fabric-config.json");
|
|
5027
|
+
if (!existsSync5(path2)) return [];
|
|
4939
5028
|
let raw;
|
|
4940
5029
|
try {
|
|
4941
|
-
raw =
|
|
5030
|
+
raw = readFileSync3(path2, "utf8");
|
|
4942
5031
|
} catch {
|
|
4943
5032
|
return [];
|
|
4944
5033
|
}
|
|
@@ -5054,7 +5143,7 @@ function createNarrowTooFewCheck(t, inspection) {
|
|
|
5054
5143
|
}
|
|
5055
5144
|
function resolvePersonalKnowledgeRoot() {
|
|
5056
5145
|
const home = process.env.FABRIC_HOME ?? homedir3();
|
|
5057
|
-
return
|
|
5146
|
+
return join7(home, ".fabric", "knowledge");
|
|
5058
5147
|
}
|
|
5059
5148
|
function parseStableIdFromCanonicalFilename(filename) {
|
|
5060
5149
|
const match = CANONICAL_KNOWLEDGE_FILENAME_PATTERN.exec(filename);
|
|
@@ -5074,18 +5163,18 @@ function parseStableIdFromCanonicalFilename(filename) {
|
|
|
5074
5163
|
};
|
|
5075
5164
|
}
|
|
5076
5165
|
function* iterateCanonicalFilenames(projectRoot) {
|
|
5077
|
-
const teamRoot =
|
|
5166
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
5078
5167
|
const personalRoot = resolvePersonalKnowledgeRoot();
|
|
5079
5168
|
for (const [layer, root, displayPrefix] of [
|
|
5080
5169
|
["team", teamRoot, ".fabric/knowledge"],
|
|
5081
5170
|
["personal", personalRoot, "~/.fabric/knowledge"]
|
|
5082
5171
|
]) {
|
|
5083
|
-
if (!
|
|
5172
|
+
if (!existsSync5(root)) {
|
|
5084
5173
|
continue;
|
|
5085
5174
|
}
|
|
5086
5175
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
5087
|
-
const dir =
|
|
5088
|
-
if (!
|
|
5176
|
+
const dir = join7(root, typeDir);
|
|
5177
|
+
if (!existsSync5(dir)) {
|
|
5089
5178
|
continue;
|
|
5090
5179
|
}
|
|
5091
5180
|
let entries;
|
|
@@ -5256,8 +5345,8 @@ async function migrateBootstrapMarkers(projectRoot) {
|
|
|
5256
5345
|
const paths = [];
|
|
5257
5346
|
const countPerPath = {};
|
|
5258
5347
|
for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
|
|
5259
|
-
const abs =
|
|
5260
|
-
if (!
|
|
5348
|
+
const abs = join7(projectRoot, rel);
|
|
5349
|
+
if (!existsSync5(abs)) {
|
|
5261
5350
|
continue;
|
|
5262
5351
|
}
|
|
5263
5352
|
let original;
|
|
@@ -5283,8 +5372,8 @@ async function migrateBootstrapMarkers(projectRoot) {
|
|
|
5283
5372
|
return { paths, countPerPath };
|
|
5284
5373
|
}
|
|
5285
5374
|
async function rewriteThreeEndManagedBlocks(projectRoot) {
|
|
5286
|
-
const snapshotPath =
|
|
5287
|
-
if (!
|
|
5375
|
+
const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
|
|
5376
|
+
if (!existsSync5(snapshotPath)) {
|
|
5288
5377
|
return;
|
|
5289
5378
|
}
|
|
5290
5379
|
let snapshot;
|
|
@@ -5293,8 +5382,8 @@ async function rewriteThreeEndManagedBlocks(projectRoot) {
|
|
|
5293
5382
|
} catch {
|
|
5294
5383
|
return;
|
|
5295
5384
|
}
|
|
5296
|
-
const projectRulesPath =
|
|
5297
|
-
const hasProjectRules =
|
|
5385
|
+
const projectRulesPath = join7(projectRoot, ".fabric", "project-rules.md");
|
|
5386
|
+
const hasProjectRules = existsSync5(projectRulesPath);
|
|
5298
5387
|
let expectedBody = snapshot;
|
|
5299
5388
|
if (hasProjectRules) {
|
|
5300
5389
|
try {
|
|
@@ -5309,11 +5398,11 @@ ${projectRules}`;
|
|
|
5309
5398
|
${expectedBody}
|
|
5310
5399
|
${BOOTSTRAP_MARKER_END}`;
|
|
5311
5400
|
const blockTargets = [
|
|
5312
|
-
|
|
5313
|
-
|
|
5401
|
+
join7(projectRoot, "AGENTS.md"),
|
|
5402
|
+
join7(projectRoot, ".cursor", "rules", "fabric-bootstrap.mdc")
|
|
5314
5403
|
];
|
|
5315
5404
|
for (const abs of blockTargets) {
|
|
5316
|
-
if (!
|
|
5405
|
+
if (!existsSync5(abs)) {
|
|
5317
5406
|
continue;
|
|
5318
5407
|
}
|
|
5319
5408
|
let existing;
|
|
@@ -5343,8 +5432,8 @@ ${managedBlock}
|
|
|
5343
5432
|
}
|
|
5344
5433
|
await atomicWriteText4(abs, next);
|
|
5345
5434
|
}
|
|
5346
|
-
const claudeMdPath =
|
|
5347
|
-
if (
|
|
5435
|
+
const claudeMdPath = join7(projectRoot, "CLAUDE.md");
|
|
5436
|
+
if (existsSync5(claudeMdPath)) {
|
|
5348
5437
|
let claudeContent;
|
|
5349
5438
|
try {
|
|
5350
5439
|
claudeContent = await readFile5(claudeMdPath, "utf8");
|
|
@@ -5372,13 +5461,13 @@ ${managedBlock}
|
|
|
5372
5461
|
}
|
|
5373
5462
|
}
|
|
5374
5463
|
async function fixMcpConfigInWrongFile(projectRoot) {
|
|
5375
|
-
const settingsPath =
|
|
5376
|
-
if (!
|
|
5464
|
+
const settingsPath = join7(projectRoot, ".claude", "settings.json");
|
|
5465
|
+
if (!existsSync5(settingsPath)) {
|
|
5377
5466
|
return;
|
|
5378
5467
|
}
|
|
5379
5468
|
let settings;
|
|
5380
5469
|
try {
|
|
5381
|
-
const parsed = JSON.parse(
|
|
5470
|
+
const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
5382
5471
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5383
5472
|
return;
|
|
5384
5473
|
}
|
|
@@ -5406,12 +5495,12 @@ async function fixMcpConfigInWrongFile(projectRoot) {
|
|
|
5406
5495
|
}
|
|
5407
5496
|
async function ensureKnowledgeSubdirs(projectRoot) {
|
|
5408
5497
|
for (const sub of KNOWLEDGE_SUBDIRS3) {
|
|
5409
|
-
await mkdir4(
|
|
5498
|
+
await mkdir4(join7(projectRoot, ".fabric", "knowledge", sub), { recursive: true });
|
|
5410
5499
|
}
|
|
5411
5500
|
}
|
|
5412
5501
|
async function fixCounterDesync(projectRoot) {
|
|
5413
|
-
const metaPath =
|
|
5414
|
-
if (!
|
|
5502
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
5503
|
+
if (!existsSync5(metaPath)) {
|
|
5415
5504
|
return;
|
|
5416
5505
|
}
|
|
5417
5506
|
let meta;
|
|
@@ -5829,8 +5918,8 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5829
5918
|
continue;
|
|
5830
5919
|
}
|
|
5831
5920
|
bumpLayerType(citeId, kbType);
|
|
5832
|
-
if (kbType === "
|
|
5833
|
-
if (kbType === "
|
|
5921
|
+
if (kbType === "decisions" || kbType === "pitfalls") {
|
|
5922
|
+
if (kbType === "decisions") decisionsCited += 1;
|
|
5834
5923
|
else pitfallsCited += 1;
|
|
5835
5924
|
const commitment = commitments[i];
|
|
5836
5925
|
const operators = commitment?.operators ?? [];
|
|
@@ -5994,7 +6083,7 @@ function normalizePath(path2) {
|
|
|
5994
6083
|
return posix.normalize(path2.split("\\").join("/"));
|
|
5995
6084
|
}
|
|
5996
6085
|
function collectEntryPoints(root) {
|
|
5997
|
-
if (!
|
|
6086
|
+
if (!existsSync5(root) || !statSync4(root).isDirectory()) {
|
|
5998
6087
|
return [];
|
|
5999
6088
|
}
|
|
6000
6089
|
const entries = [];
|
|
@@ -6005,7 +6094,7 @@ function collectEntryPoints(root) {
|
|
|
6005
6094
|
continue;
|
|
6006
6095
|
}
|
|
6007
6096
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
6008
|
-
const absolutePath =
|
|
6097
|
+
const absolutePath = join7(current, entry.name);
|
|
6009
6098
|
const relativePath = normalizePath(absolutePath.slice(root.length + 1));
|
|
6010
6099
|
if (relativePath.length === 0) {
|
|
6011
6100
|
continue;
|
|
@@ -6071,14 +6160,14 @@ var ENRICH_DESC_FIELD_PATTERNS = {
|
|
|
6071
6160
|
async function enrichDescriptions(projectRoot, opts = {}) {
|
|
6072
6161
|
const auto = opts.auto === true;
|
|
6073
6162
|
const dryRun = opts.dryRun === true;
|
|
6074
|
-
const mode = auto ? "auto" : "
|
|
6163
|
+
const mode = auto ? dryRun ? "preview" : "auto" : "readonly";
|
|
6075
6164
|
const candidates = [];
|
|
6076
6165
|
let scanned = 0;
|
|
6077
6166
|
let modified = 0;
|
|
6078
6167
|
let skipped = 0;
|
|
6079
6168
|
for (const visit of iterateCanonicalFilenames(projectRoot)) {
|
|
6080
|
-
const layerRoot = visit.layer === "team" ?
|
|
6081
|
-
const absPath =
|
|
6169
|
+
const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
|
|
6170
|
+
const absPath = join7(layerRoot, visit.type, visit.filename);
|
|
6082
6171
|
scanned += 1;
|
|
6083
6172
|
let source;
|
|
6084
6173
|
try {
|
|
@@ -6281,13 +6370,9 @@ async function emitAutoHealEventBestEffort(projectRoot, payload) {
|
|
|
6281
6370
|
|
|
6282
6371
|
// src/services/get-knowledge.ts
|
|
6283
6372
|
import { readFile as readFile6 } from "fs/promises";
|
|
6284
|
-
import { join as
|
|
6373
|
+
import { join as join8 } from "path";
|
|
6374
|
+
import { deriveAgentsMetaLayer as deriveAgentsMetaLayer2 } from "@fenglimg/fabric-shared";
|
|
6285
6375
|
import { minimatch as minimatch2 } from "minimatch";
|
|
6286
|
-
var PRIORITY_ORDER = {
|
|
6287
|
-
high: 0,
|
|
6288
|
-
medium: 1,
|
|
6289
|
-
low: 2
|
|
6290
|
-
};
|
|
6291
6376
|
async function getKnowledge(projectRoot, input) {
|
|
6292
6377
|
const metaResult = await loadActiveMeta(projectRoot, { caller: "getKnowledge" });
|
|
6293
6378
|
if (metaResult.auto_healed) {
|
|
@@ -6325,7 +6410,7 @@ async function loadGetKnowledgeContext(projectRoot) {
|
|
|
6325
6410
|
return cached;
|
|
6326
6411
|
}
|
|
6327
6412
|
const meta = await readAgentsMeta(projectRoot);
|
|
6328
|
-
const l0Content = await readFile6(
|
|
6413
|
+
const l0Content = await readFile6(join8(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
|
|
6329
6414
|
const context = {
|
|
6330
6415
|
meta,
|
|
6331
6416
|
l0Content,
|
|
@@ -6344,12 +6429,7 @@ function normalizeKnowledgePath(value) {
|
|
|
6344
6429
|
}
|
|
6345
6430
|
function matchRuleNodes(meta, path2) {
|
|
6346
6431
|
const requestedPath = normalizeKnowledgePath(path2);
|
|
6347
|
-
return Object.entries(meta.nodes).filter(([, node]) => shouldLoadNodeForPath(requestedPath, node)).sort((left, right) => {
|
|
6348
|
-
const [leftId, leftNode] = left;
|
|
6349
|
-
const [rightId, rightNode] = right;
|
|
6350
|
-
const priorityDelta = PRIORITY_ORDER[leftNode.priority ?? "medium"] - PRIORITY_ORDER[rightNode.priority ?? "medium"];
|
|
6351
|
-
return priorityDelta !== 0 ? priorityDelta : leftId.localeCompare(rightId);
|
|
6352
|
-
}).map(([nodeId, node]) => ({
|
|
6432
|
+
return Object.entries(meta.nodes).filter(([, node]) => shouldLoadNodeForPath(requestedPath, node)).sort((left, right) => left[0].localeCompare(right[0])).map(([nodeId, node]) => ({
|
|
6353
6433
|
node_id: nodeId,
|
|
6354
6434
|
level: classifyNode(nodeId, node),
|
|
6355
6435
|
stable_id: node.stable_id ?? nodeId,
|
|
@@ -6403,7 +6483,8 @@ function classifyNode(nodeId, node) {
|
|
|
6403
6483
|
if (nodeId.startsWith("L2/")) {
|
|
6404
6484
|
return "L2";
|
|
6405
6485
|
}
|
|
6406
|
-
|
|
6486
|
+
const layer = node.level ?? deriveAgentsMetaLayer2(node.file);
|
|
6487
|
+
return layer === "L0" ? null : layer;
|
|
6407
6488
|
}
|
|
6408
6489
|
function partitionRulesByLevel(loadedRules, dedupeByPath) {
|
|
6409
6490
|
const l1 = [];
|
|
@@ -6464,7 +6545,7 @@ async function readRuleContent(projectRoot, file, fileContentCache) {
|
|
|
6464
6545
|
if (cached !== void 0) {
|
|
6465
6546
|
return await cached;
|
|
6466
6547
|
}
|
|
6467
|
-
const pending = readFile6(
|
|
6548
|
+
const pending = readFile6(join8(projectRoot, file), "utf8");
|
|
6468
6549
|
fileContentCache.set(file, pending);
|
|
6469
6550
|
return await pending;
|
|
6470
6551
|
}
|
|
@@ -6499,6 +6580,8 @@ export {
|
|
|
6499
6580
|
invalidateKnowledgeSyncCooldown,
|
|
6500
6581
|
ensureKnowledgeFresh,
|
|
6501
6582
|
reconcileKnowledge,
|
|
6583
|
+
readPayloadLimits,
|
|
6584
|
+
readSelectionTokenTtlMs,
|
|
6502
6585
|
loadActiveMeta,
|
|
6503
6586
|
loadActiveMetaOrStale,
|
|
6504
6587
|
getKnowledge,
|