@fenglimg/fabric-server 2.0.0-rc.28 → 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-EEVSGTHG.js → chunk-CTQ4UMO4.js} +255 -165
- package/dist/{http-3IIPL3HQ.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,
|
|
@@ -1914,11 +1963,27 @@ async function runDoctorReport(target) {
|
|
|
1914
1963
|
warningCount: warnings.length,
|
|
1915
1964
|
infoCount: infos.length,
|
|
1916
1965
|
targetFiles: Object.fromEntries(
|
|
1917
|
-
TARGET_FILE_PATHS.map((path2) => [path2,
|
|
1918
|
-
)
|
|
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)
|
|
1919
1972
|
}
|
|
1920
1973
|
};
|
|
1921
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
|
+
}
|
|
1922
1987
|
async function runDoctorFix(target) {
|
|
1923
1988
|
const projectRoot = normalizeTarget(target);
|
|
1924
1989
|
const before = await runDoctorReport(projectRoot);
|
|
@@ -1941,7 +2006,7 @@ async function runDoctorFix(target) {
|
|
|
1941
2006
|
}
|
|
1942
2007
|
}
|
|
1943
2008
|
if (before.fixable_errors.some((issue) => issue.code === "bootstrap_snapshot_drift")) {
|
|
1944
|
-
const snapshotPath =
|
|
2009
|
+
const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
|
|
1945
2010
|
await ensureParentDirectory(snapshotPath);
|
|
1946
2011
|
await atomicWriteText4(snapshotPath, BOOTSTRAP_CANONICAL);
|
|
1947
2012
|
fixed.push(findIssue(before.fixable_errors, "bootstrap_snapshot_drift"));
|
|
@@ -2015,7 +2080,7 @@ async function runDoctorFix(target) {
|
|
|
2015
2080
|
if (before.infos.some((issue) => issue.code === "stale_serve_lock")) {
|
|
2016
2081
|
const lockInspection = inspectStaleServeLock(projectRoot, Date.now());
|
|
2017
2082
|
if (lockInspection.present && !lockInspection.pidAlive) {
|
|
2018
|
-
const lockFilePath =
|
|
2083
|
+
const lockFilePath = join7(projectRoot, ".fabric", ".serve.lock");
|
|
2019
2084
|
try {
|
|
2020
2085
|
await unlink(lockFilePath);
|
|
2021
2086
|
} catch (err) {
|
|
@@ -2172,7 +2237,7 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
|
|
|
2172
2237
|
};
|
|
2173
2238
|
}
|
|
2174
2239
|
const detail = `${candidate.maturity} -> ${next}`;
|
|
2175
|
-
const absPath =
|
|
2240
|
+
const absPath = join7(projectRoot, candidate.path);
|
|
2176
2241
|
try {
|
|
2177
2242
|
const source = await readFile5(absPath, "utf8");
|
|
2178
2243
|
const rewritten = rewriteFrontmatterMaturity(source, next);
|
|
@@ -2239,11 +2304,11 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
|
|
|
2239
2304
|
}
|
|
2240
2305
|
}
|
|
2241
2306
|
async function applyStaleArchive(projectRoot, candidate, now) {
|
|
2242
|
-
const sourceAbs =
|
|
2243
|
-
const destAbs =
|
|
2307
|
+
const sourceAbs = join7(projectRoot, candidate.path);
|
|
2308
|
+
const destAbs = join7(projectRoot, candidate.archive_path);
|
|
2244
2309
|
const detail = `${candidate.path} -> ${candidate.archive_path}`;
|
|
2245
2310
|
try {
|
|
2246
|
-
await mkdir4(
|
|
2311
|
+
await mkdir4(join7(destAbs, ".."), { recursive: true });
|
|
2247
2312
|
try {
|
|
2248
2313
|
await rename(sourceAbs, destAbs);
|
|
2249
2314
|
} catch (renameError) {
|
|
@@ -2302,7 +2367,7 @@ async function applyStaleArchive(projectRoot, candidate, now) {
|
|
|
2302
2367
|
async function applyPendingAutoArchive(projectRoot, candidate, now) {
|
|
2303
2368
|
const detail = `${candidate.pending_path} -> ${candidate.archived_to}`;
|
|
2304
2369
|
try {
|
|
2305
|
-
await mkdir4(
|
|
2370
|
+
await mkdir4(join7(candidate.archived_to_abs, ".."), { recursive: true });
|
|
2306
2371
|
let moved = false;
|
|
2307
2372
|
if (candidate.layer === "team") {
|
|
2308
2373
|
try {
|
|
@@ -2379,7 +2444,7 @@ function relativePosix(projectRoot, absolutePath) {
|
|
|
2379
2444
|
}
|
|
2380
2445
|
async function applySessionHintsStaleCleanup(projectRoot, candidate) {
|
|
2381
2446
|
const detail = `deleted (${candidate.age_days}d old)`;
|
|
2382
|
-
const absPath =
|
|
2447
|
+
const absPath = join7(projectRoot, candidate.path);
|
|
2383
2448
|
try {
|
|
2384
2449
|
const { unlink: unlink2 } = await import("fs/promises");
|
|
2385
2450
|
await unlink2(absPath);
|
|
@@ -2400,7 +2465,7 @@ async function applySessionHintsStaleCleanup(projectRoot, candidate) {
|
|
|
2400
2465
|
}
|
|
2401
2466
|
}
|
|
2402
2467
|
async function applyIndexDriftFix(projectRoot, inspection) {
|
|
2403
|
-
const metaPath =
|
|
2468
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
2404
2469
|
const detailParts = [];
|
|
2405
2470
|
try {
|
|
2406
2471
|
const meta = agentsMetaSchema4.parse(JSON.parse(await readFile5(metaPath, "utf8")));
|
|
@@ -2436,7 +2501,7 @@ function truncateErrorMessage(error) {
|
|
|
2436
2501
|
return raw.length > 240 ? `${raw.slice(0, 237)}...` : raw;
|
|
2437
2502
|
}
|
|
2438
2503
|
async function inspectForensic(projectRoot) {
|
|
2439
|
-
const path2 =
|
|
2504
|
+
const path2 = join7(projectRoot, ".fabric", "forensic.json");
|
|
2440
2505
|
try {
|
|
2441
2506
|
const parsed = forensicReportSchema.parse(JSON.parse(await readFile5(path2, "utf8")));
|
|
2442
2507
|
return { present: true, valid: true, report: parsed };
|
|
@@ -2448,12 +2513,12 @@ async function inspectForensic(projectRoot) {
|
|
|
2448
2513
|
}
|
|
2449
2514
|
}
|
|
2450
2515
|
function inspectMcpConfigInWrongFile(projectRoot) {
|
|
2451
|
-
const settingsPath =
|
|
2452
|
-
if (!
|
|
2516
|
+
const settingsPath = join7(projectRoot, ".claude", "settings.json");
|
|
2517
|
+
if (!existsSync5(settingsPath)) {
|
|
2453
2518
|
return { hasWrongEntry: false, settingsPath };
|
|
2454
2519
|
}
|
|
2455
2520
|
try {
|
|
2456
|
-
const parsed = JSON.parse(
|
|
2521
|
+
const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
2457
2522
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2458
2523
|
return { hasWrongEntry: false, settingsPath };
|
|
2459
2524
|
}
|
|
@@ -2469,7 +2534,7 @@ function inspectMcpConfigInWrongFile(projectRoot) {
|
|
|
2469
2534
|
}
|
|
2470
2535
|
}
|
|
2471
2536
|
async function inspectMeta(projectRoot) {
|
|
2472
|
-
const metaPath =
|
|
2537
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
2473
2538
|
const built = await tryBuildRuleMeta(projectRoot);
|
|
2474
2539
|
try {
|
|
2475
2540
|
const raw = await readFile5(metaPath, "utf8");
|
|
@@ -2542,7 +2607,7 @@ function inspectContentRefs(projectRoot, meta) {
|
|
|
2542
2607
|
if (isPersonalKnowledge) {
|
|
2543
2608
|
continue;
|
|
2544
2609
|
}
|
|
2545
|
-
if (!
|
|
2610
|
+
if (!existsSync5(join7(projectRoot, contentRef))) {
|
|
2546
2611
|
missing.push(contentRef);
|
|
2547
2612
|
}
|
|
2548
2613
|
}
|
|
@@ -2550,7 +2615,7 @@ function inspectContentRefs(projectRoot, meta) {
|
|
|
2550
2615
|
}
|
|
2551
2616
|
async function inspectEventLedger(projectRoot) {
|
|
2552
2617
|
const path2 = getEventLedgerPath(projectRoot);
|
|
2553
|
-
const exists =
|
|
2618
|
+
const exists = existsSync5(path2);
|
|
2554
2619
|
if (!exists) {
|
|
2555
2620
|
return {
|
|
2556
2621
|
exists: false,
|
|
@@ -2626,8 +2691,8 @@ function inspectSkillRefMirror(projectRoot) {
|
|
|
2626
2691
|
const skillSlugs = ["fabric-archive", "fabric-review", "fabric-import"];
|
|
2627
2692
|
const driftedPaths = [];
|
|
2628
2693
|
for (const slug of skillSlugs) {
|
|
2629
|
-
const claudeRef =
|
|
2630
|
-
const codexRef =
|
|
2694
|
+
const claudeRef = join7(projectRoot, ".claude", "skills", slug, "ref");
|
|
2695
|
+
const codexRef = join7(projectRoot, ".codex", "skills", slug, "ref");
|
|
2631
2696
|
let claudeFiles = null;
|
|
2632
2697
|
let codexFiles = null;
|
|
2633
2698
|
try {
|
|
@@ -2652,12 +2717,12 @@ function inspectSkillRefMirror(projectRoot) {
|
|
|
2652
2717
|
let claudeBody;
|
|
2653
2718
|
let codexBody;
|
|
2654
2719
|
try {
|
|
2655
|
-
claudeBody =
|
|
2720
|
+
claudeBody = readFileSync3(join7(claudeRef, fname), "utf8");
|
|
2656
2721
|
} catch {
|
|
2657
2722
|
continue;
|
|
2658
2723
|
}
|
|
2659
2724
|
try {
|
|
2660
|
-
codexBody =
|
|
2725
|
+
codexBody = readFileSync3(join7(codexRef, fname), "utf8");
|
|
2661
2726
|
} catch {
|
|
2662
2727
|
continue;
|
|
2663
2728
|
}
|
|
@@ -2670,7 +2735,7 @@ function inspectSkillRefMirror(projectRoot) {
|
|
|
2670
2735
|
return { status: "drift", driftedPaths };
|
|
2671
2736
|
}
|
|
2672
2737
|
async function inspectKnowledgeTestIndex(projectRoot) {
|
|
2673
|
-
const path2 =
|
|
2738
|
+
const path2 = join7(projectRoot, ".fabric", ".cache", "knowledge-test.index.json");
|
|
2674
2739
|
const built = await tryBuildRuleMeta(projectRoot);
|
|
2675
2740
|
try {
|
|
2676
2741
|
const index = knowledgeTestIndexSchema2.parse(JSON.parse(await readFile5(path2, "utf8")));
|
|
@@ -2694,8 +2759,8 @@ async function inspectKnowledgeTestIndex(projectRoot) {
|
|
|
2694
2759
|
}
|
|
2695
2760
|
function inspectBootstrapAnchor(projectRoot) {
|
|
2696
2761
|
return {
|
|
2697
|
-
hasAgentsMd:
|
|
2698
|
-
hasClaudeMd:
|
|
2762
|
+
hasAgentsMd: existsSync5(join7(projectRoot, "AGENTS.md")),
|
|
2763
|
+
hasClaudeMd: existsSync5(join7(projectRoot, "CLAUDE.md"))
|
|
2699
2764
|
};
|
|
2700
2765
|
}
|
|
2701
2766
|
var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
|
|
@@ -2707,8 +2772,8 @@ var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
|
|
|
2707
2772
|
async function inspectBootstrapMarkerMigration(target) {
|
|
2708
2773
|
const filesNeedingMigration = [];
|
|
2709
2774
|
for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
|
|
2710
|
-
const abs =
|
|
2711
|
-
if (!
|
|
2775
|
+
const abs = join7(target, rel);
|
|
2776
|
+
if (!existsSync5(abs)) {
|
|
2712
2777
|
continue;
|
|
2713
2778
|
}
|
|
2714
2779
|
let content;
|
|
@@ -2745,8 +2810,8 @@ function createBootstrapMarkerMigrationCheck(t, inspection) {
|
|
|
2745
2810
|
);
|
|
2746
2811
|
}
|
|
2747
2812
|
async function inspectL1BootstrapSnapshotDrift(target) {
|
|
2748
|
-
const abs =
|
|
2749
|
-
if (!
|
|
2813
|
+
const abs = join7(target, ".fabric", "AGENTS.md");
|
|
2814
|
+
if (!existsSync5(abs)) {
|
|
2750
2815
|
return { status: "missing", canonical: BOOTSTRAP_CANONICAL, onDisk: null };
|
|
2751
2816
|
}
|
|
2752
2817
|
let onDisk;
|
|
@@ -2777,8 +2842,8 @@ function createL1BootstrapSnapshotDriftCheck(t, inspection) {
|
|
|
2777
2842
|
);
|
|
2778
2843
|
}
|
|
2779
2844
|
async function inspectL2ManagedBlockDrift(target) {
|
|
2780
|
-
const snapshotPath =
|
|
2781
|
-
if (!
|
|
2845
|
+
const snapshotPath = join7(target, ".fabric", "AGENTS.md");
|
|
2846
|
+
if (!existsSync5(snapshotPath)) {
|
|
2782
2847
|
return { status: "ok", drifted: [] };
|
|
2783
2848
|
}
|
|
2784
2849
|
let snapshot;
|
|
@@ -2787,9 +2852,9 @@ async function inspectL2ManagedBlockDrift(target) {
|
|
|
2787
2852
|
} catch {
|
|
2788
2853
|
return { status: "ok", drifted: [] };
|
|
2789
2854
|
}
|
|
2790
|
-
const projectRulesPath =
|
|
2855
|
+
const projectRulesPath = join7(target, ".fabric", "project-rules.md");
|
|
2791
2856
|
let expectedBody = snapshot;
|
|
2792
|
-
if (
|
|
2857
|
+
if (existsSync5(projectRulesPath)) {
|
|
2793
2858
|
try {
|
|
2794
2859
|
const projectRules = await readFile5(projectRulesPath, "utf8");
|
|
2795
2860
|
expectedBody = `${snapshot}
|
|
@@ -2801,11 +2866,11 @@ ${projectRules}`;
|
|
|
2801
2866
|
const drifted = [];
|
|
2802
2867
|
let anyManagedBlockFound = false;
|
|
2803
2868
|
const blockTargets = [
|
|
2804
|
-
|
|
2805
|
-
|
|
2869
|
+
join7(target, "AGENTS.md"),
|
|
2870
|
+
join7(target, ".cursor", "rules", "fabric-bootstrap.mdc")
|
|
2806
2871
|
];
|
|
2807
2872
|
for (const abs of blockTargets) {
|
|
2808
|
-
if (!
|
|
2873
|
+
if (!existsSync5(abs)) {
|
|
2809
2874
|
continue;
|
|
2810
2875
|
}
|
|
2811
2876
|
let content;
|
|
@@ -2836,8 +2901,8 @@ ${projectRules}`;
|
|
|
2836
2901
|
drifted.push({ path: abs, expected: expectedBody, actual: body });
|
|
2837
2902
|
}
|
|
2838
2903
|
}
|
|
2839
|
-
const claudeMdPath =
|
|
2840
|
-
if (
|
|
2904
|
+
const claudeMdPath = join7(target, "CLAUDE.md");
|
|
2905
|
+
if (existsSync5(claudeMdPath)) {
|
|
2841
2906
|
let claudeContent;
|
|
2842
2907
|
try {
|
|
2843
2908
|
claudeContent = await readFile5(claudeMdPath, "utf8");
|
|
@@ -2907,11 +2972,11 @@ function createBootstrapAnchorCheck(t, inspection) {
|
|
|
2907
2972
|
);
|
|
2908
2973
|
}
|
|
2909
2974
|
function inspectKnowledgeDirMissing(projectRoot) {
|
|
2910
|
-
const knowledgeRoot =
|
|
2975
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
2911
2976
|
const missingSubdirs = [];
|
|
2912
2977
|
for (const sub of KNOWLEDGE_SUBDIRS3) {
|
|
2913
|
-
const path2 =
|
|
2914
|
-
if (!
|
|
2978
|
+
const path2 = join7(knowledgeRoot, sub);
|
|
2979
|
+
if (!existsSync5(path2)) {
|
|
2915
2980
|
missingSubdirs.push(`.fabric/knowledge/${sub}`);
|
|
2916
2981
|
}
|
|
2917
2982
|
}
|
|
@@ -2919,13 +2984,13 @@ function inspectKnowledgeDirMissing(projectRoot) {
|
|
|
2919
2984
|
}
|
|
2920
2985
|
function inspectBaselineFilenameFormat(projectRoot) {
|
|
2921
2986
|
const offenders = [];
|
|
2922
|
-
const knowledgeRoot =
|
|
2923
|
-
if (!
|
|
2987
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
2988
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
2924
2989
|
return { offenders };
|
|
2925
2990
|
}
|
|
2926
2991
|
for (const sub of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
2927
|
-
const dir =
|
|
2928
|
-
if (!
|
|
2992
|
+
const dir = join7(knowledgeRoot, sub);
|
|
2993
|
+
if (!existsSync5(dir)) {
|
|
2929
2994
|
continue;
|
|
2930
2995
|
}
|
|
2931
2996
|
let entries;
|
|
@@ -2942,10 +3007,10 @@ function inspectBaselineFilenameFormat(projectRoot) {
|
|
|
2942
3007
|
if (BASELINE_ID_PREFIXED_FILENAME_PATTERN.test(entryName)) {
|
|
2943
3008
|
continue;
|
|
2944
3009
|
}
|
|
2945
|
-
const abs =
|
|
3010
|
+
const abs = join7(dir, entryName);
|
|
2946
3011
|
let source;
|
|
2947
3012
|
try {
|
|
2948
|
-
source =
|
|
3013
|
+
source = readFileSync3(abs, "utf8");
|
|
2949
3014
|
} catch {
|
|
2950
3015
|
continue;
|
|
2951
3016
|
}
|
|
@@ -3319,8 +3384,8 @@ function findIssue(issues, code) {
|
|
|
3319
3384
|
};
|
|
3320
3385
|
}
|
|
3321
3386
|
async function inspectMetaManuallyDiverged(projectRoot) {
|
|
3322
|
-
const metaPath =
|
|
3323
|
-
if (!
|
|
3387
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
3388
|
+
if (!existsSync5(metaPath)) {
|
|
3324
3389
|
return { extraMetaEntries: [], hashMismatchEntries: [], readable: false };
|
|
3325
3390
|
}
|
|
3326
3391
|
let meta;
|
|
@@ -3339,13 +3404,13 @@ async function inspectMetaManuallyDiverged(projectRoot) {
|
|
|
3339
3404
|
const hashMismatchEntries = [];
|
|
3340
3405
|
for (const node of Object.values(meta.nodes)) {
|
|
3341
3406
|
const contentRef = node.content_ref ?? node.file;
|
|
3342
|
-
const absPath =
|
|
3343
|
-
if (!
|
|
3407
|
+
const absPath = join7(projectRoot, contentRef);
|
|
3408
|
+
if (!existsSync5(absPath)) {
|
|
3344
3409
|
extraMetaEntries.push(contentRef);
|
|
3345
3410
|
continue;
|
|
3346
3411
|
}
|
|
3347
3412
|
try {
|
|
3348
|
-
const content =
|
|
3413
|
+
const content = readFileSync3(absPath, "utf8");
|
|
3349
3414
|
const diskHash = sha256(content);
|
|
3350
3415
|
if (node.hash !== "" && node.hash !== diskHash) {
|
|
3351
3416
|
hashMismatchEntries.push(contentRef);
|
|
@@ -3358,7 +3423,7 @@ async function inspectMetaManuallyDiverged(projectRoot) {
|
|
|
3358
3423
|
}
|
|
3359
3424
|
function inspectKnowledgeDirUnindexed(projectRoot, meta) {
|
|
3360
3425
|
const physicalMdFiles = /* @__PURE__ */ new Set();
|
|
3361
|
-
collectMdFilesUnder(physicalMdFiles, projectRoot,
|
|
3426
|
+
collectMdFilesUnder(physicalMdFiles, projectRoot, join7(projectRoot, ".fabric", "knowledge"), ".fabric/knowledge");
|
|
3362
3427
|
if (physicalMdFiles.size === 0) {
|
|
3363
3428
|
return { unindexedFiles: [] };
|
|
3364
3429
|
}
|
|
@@ -3373,7 +3438,7 @@ function inspectKnowledgeDirUnindexed(projectRoot, meta) {
|
|
|
3373
3438
|
return { unindexedFiles };
|
|
3374
3439
|
}
|
|
3375
3440
|
function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
3376
|
-
if (!
|
|
3441
|
+
if (!existsSync5(rootDir)) {
|
|
3377
3442
|
return;
|
|
3378
3443
|
}
|
|
3379
3444
|
const stack = [rootDir];
|
|
@@ -3383,7 +3448,7 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
|
|
|
3383
3448
|
continue;
|
|
3384
3449
|
}
|
|
3385
3450
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3386
|
-
const abs =
|
|
3451
|
+
const abs = join7(dir, entry.name);
|
|
3387
3452
|
if (entry.isDirectory()) {
|
|
3388
3453
|
if (entry.name !== "pending" && entry.name !== "archive") {
|
|
3389
3454
|
stack.push(abs);
|
|
@@ -3416,8 +3481,8 @@ function createKnowledgeDirUnindexedCheck(t, inspection) {
|
|
|
3416
3481
|
}
|
|
3417
3482
|
async function inspectStableIdCollisions(projectRoot) {
|
|
3418
3483
|
const found = [];
|
|
3419
|
-
const knowledgeDir =
|
|
3420
|
-
if (
|
|
3484
|
+
const knowledgeDir = join7(projectRoot, ".fabric", "knowledge");
|
|
3485
|
+
if (existsSync5(knowledgeDir)) {
|
|
3421
3486
|
const stack = [knowledgeDir];
|
|
3422
3487
|
while (stack.length > 0) {
|
|
3423
3488
|
const dir = stack.pop();
|
|
@@ -3425,7 +3490,7 @@ async function inspectStableIdCollisions(projectRoot) {
|
|
|
3425
3490
|
continue;
|
|
3426
3491
|
}
|
|
3427
3492
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3428
|
-
const abs =
|
|
3493
|
+
const abs = join7(dir, entry.name);
|
|
3429
3494
|
if (entry.isDirectory()) {
|
|
3430
3495
|
stack.push(abs);
|
|
3431
3496
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -3490,11 +3555,11 @@ function inspectCounterDesync(meta) {
|
|
|
3490
3555
|
}
|
|
3491
3556
|
const layer = parsed.layer === "personal" ? "KP" : "KT";
|
|
3492
3557
|
const typeCode = [
|
|
3493
|
-
["
|
|
3494
|
-
["
|
|
3495
|
-
["
|
|
3496
|
-
["
|
|
3497
|
-
["
|
|
3558
|
+
["models", "MOD"],
|
|
3559
|
+
["decisions", "DEC"],
|
|
3560
|
+
["guidelines", "GLD"],
|
|
3561
|
+
["pitfalls", "PIT"],
|
|
3562
|
+
["processes", "PRO"]
|
|
3498
3563
|
].find(([t]) => t === parsed.type)?.[1];
|
|
3499
3564
|
if (typeCode === void 0) {
|
|
3500
3565
|
continue;
|
|
@@ -3604,18 +3669,18 @@ function createMetaManuallyDivergedCheck(t, inspection) {
|
|
|
3604
3669
|
}
|
|
3605
3670
|
function inspectPreexistingRootFiles(projectRoot) {
|
|
3606
3671
|
const candidates = ["CLAUDE.md", "AGENTS.md"];
|
|
3607
|
-
const detected = candidates.filter((name) =>
|
|
3672
|
+
const detected = candidates.filter((name) => existsSync5(join7(projectRoot, name)));
|
|
3608
3673
|
return { detected };
|
|
3609
3674
|
}
|
|
3610
3675
|
async function inspectFilesystemEditFallback(projectRoot) {
|
|
3611
|
-
const knowledgeRoot =
|
|
3612
|
-
if (!
|
|
3676
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3677
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
3613
3678
|
return { synthesized: 0, synthesizedStableIds: [] };
|
|
3614
3679
|
}
|
|
3615
3680
|
const canonicalIds = /* @__PURE__ */ new Set();
|
|
3616
3681
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3617
|
-
const dir =
|
|
3618
|
-
if (!
|
|
3682
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
3683
|
+
if (!existsSync5(dir)) {
|
|
3619
3684
|
continue;
|
|
3620
3685
|
}
|
|
3621
3686
|
let entries;
|
|
@@ -3655,17 +3720,40 @@ async function inspectFilesystemEditFallback(projectRoot) {
|
|
|
3655
3720
|
}
|
|
3656
3721
|
orphanIds.sort();
|
|
3657
3722
|
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
|
-
});
|
|
3723
|
+
await emitSynthesizedPromotionTriplet(projectRoot, stable_id);
|
|
3666
3724
|
}
|
|
3667
3725
|
return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
|
|
3668
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
|
+
}
|
|
3669
3757
|
function createFilesystemEditFallbackCheck(t, inspection) {
|
|
3670
3758
|
if (inspection.synthesized === 0) {
|
|
3671
3759
|
return okCheck(
|
|
@@ -3824,13 +3912,13 @@ function extractKnowledgeFrontmatterCreatedAt(source) {
|
|
|
3824
3912
|
return Number.isFinite(parsed) ? parsed : null;
|
|
3825
3913
|
}
|
|
3826
3914
|
function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
|
|
3827
|
-
const knowledgeRoot =
|
|
3828
|
-
if (!
|
|
3915
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
3916
|
+
if (!existsSync5(knowledgeRoot)) {
|
|
3829
3917
|
return;
|
|
3830
3918
|
}
|
|
3831
3919
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
3832
|
-
const dir =
|
|
3833
|
-
if (!
|
|
3920
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
3921
|
+
if (!existsSync5(dir)) {
|
|
3834
3922
|
continue;
|
|
3835
3923
|
}
|
|
3836
3924
|
let entries;
|
|
@@ -3848,10 +3936,10 @@ function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
|
|
|
3848
3936
|
continue;
|
|
3849
3937
|
}
|
|
3850
3938
|
const stableId = match[1];
|
|
3851
|
-
const absPath =
|
|
3939
|
+
const absPath = join7(dir, entry.name);
|
|
3852
3940
|
let source;
|
|
3853
3941
|
try {
|
|
3854
|
-
source =
|
|
3942
|
+
source = readFileSync3(absPath, "utf8");
|
|
3855
3943
|
} catch {
|
|
3856
3944
|
continue;
|
|
3857
3945
|
}
|
|
@@ -3924,13 +4012,13 @@ async function inspectStaleArchive(projectRoot, now) {
|
|
|
3924
4012
|
return { candidates };
|
|
3925
4013
|
}
|
|
3926
4014
|
function* iteratePendingFiles(projectRoot, now) {
|
|
3927
|
-
const teamRoot =
|
|
3928
|
-
const personalRoot =
|
|
4015
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
|
|
4016
|
+
const personalRoot = join7(resolvePersonalRootForPending(), ".fabric", "knowledge", "pending");
|
|
3929
4017
|
for (const [layer, root, displayPrefix] of [
|
|
3930
4018
|
["team", teamRoot, ".fabric/knowledge/pending"],
|
|
3931
4019
|
["personal", personalRoot, "~/.fabric/knowledge/pending"]
|
|
3932
4020
|
]) {
|
|
3933
|
-
if (!
|
|
4021
|
+
if (!existsSync5(root)) {
|
|
3934
4022
|
continue;
|
|
3935
4023
|
}
|
|
3936
4024
|
let typeDirs = [];
|
|
@@ -3940,7 +4028,7 @@ function* iteratePendingFiles(projectRoot, now) {
|
|
|
3940
4028
|
continue;
|
|
3941
4029
|
}
|
|
3942
4030
|
for (const typeDir of typeDirs) {
|
|
3943
|
-
const dir =
|
|
4031
|
+
const dir = join7(root, typeDir);
|
|
3944
4032
|
let entries;
|
|
3945
4033
|
try {
|
|
3946
4034
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -3951,10 +4039,10 @@ function* iteratePendingFiles(projectRoot, now) {
|
|
|
3951
4039
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
3952
4040
|
continue;
|
|
3953
4041
|
}
|
|
3954
|
-
const absPath =
|
|
4042
|
+
const absPath = join7(dir, entry.name);
|
|
3955
4043
|
let source = "";
|
|
3956
4044
|
try {
|
|
3957
|
-
source =
|
|
4045
|
+
source = readFileSync3(absPath, "utf8");
|
|
3958
4046
|
} catch {
|
|
3959
4047
|
continue;
|
|
3960
4048
|
}
|
|
@@ -4026,7 +4114,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
4026
4114
|
pending_path: visit.pending_path,
|
|
4027
4115
|
pending_path_abs: visit.pending_path_abs,
|
|
4028
4116
|
archived_to: archivedToRel,
|
|
4029
|
-
archived_to_abs:
|
|
4117
|
+
archived_to_abs: join7(projectRoot, archivedToRel),
|
|
4030
4118
|
age_days: visit.age_days
|
|
4031
4119
|
});
|
|
4032
4120
|
} else {
|
|
@@ -4035,7 +4123,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
4035
4123
|
visit.type,
|
|
4036
4124
|
visit.filename
|
|
4037
4125
|
);
|
|
4038
|
-
const archivedToAbs =
|
|
4126
|
+
const archivedToAbs = join7(
|
|
4039
4127
|
resolvePersonalRootForPending(),
|
|
4040
4128
|
".fabric",
|
|
4041
4129
|
".archive",
|
|
@@ -4059,12 +4147,12 @@ function inspectPendingAutoArchive(projectRoot, now) {
|
|
|
4059
4147
|
}
|
|
4060
4148
|
function inspectUnderseeded(projectRoot) {
|
|
4061
4149
|
const threshold = readUnderseedThresholdFromConfig(projectRoot);
|
|
4062
|
-
const knowledgeRoot =
|
|
4150
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
4063
4151
|
let nodeCount = 0;
|
|
4064
|
-
if (
|
|
4152
|
+
if (existsSync5(knowledgeRoot)) {
|
|
4065
4153
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
4066
|
-
const dir =
|
|
4067
|
-
if (!
|
|
4154
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
4155
|
+
if (!existsSync5(dir)) continue;
|
|
4068
4156
|
let entries;
|
|
4069
4157
|
try {
|
|
4070
4158
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4085,8 +4173,8 @@ function inspectUnderseeded(projectRoot) {
|
|
|
4085
4173
|
};
|
|
4086
4174
|
}
|
|
4087
4175
|
function inspectSessionHintsStale(projectRoot, now) {
|
|
4088
|
-
const cacheDir =
|
|
4089
|
-
if (!
|
|
4176
|
+
const cacheDir = join7(projectRoot, ".fabric", ".cache");
|
|
4177
|
+
if (!existsSync5(cacheDir)) {
|
|
4090
4178
|
return { candidates: [] };
|
|
4091
4179
|
}
|
|
4092
4180
|
let entries;
|
|
@@ -4100,7 +4188,7 @@ function inspectSessionHintsStale(projectRoot, now) {
|
|
|
4100
4188
|
if (!entry.isFile()) continue;
|
|
4101
4189
|
if (!entry.name.startsWith(SESSION_HINTS_FILE_PREFIX)) continue;
|
|
4102
4190
|
if (!entry.name.endsWith(SESSION_HINTS_FILE_SUFFIX)) continue;
|
|
4103
|
-
const absPath =
|
|
4191
|
+
const absPath = join7(cacheDir, entry.name);
|
|
4104
4192
|
let mtimeMs = 0;
|
|
4105
4193
|
try {
|
|
4106
4194
|
mtimeMs = statSync4(absPath).mtimeMs;
|
|
@@ -4144,11 +4232,11 @@ function inspectNarrowTooFew(projectRoot, now) {
|
|
|
4144
4232
|
const structuralFlagged = total >= NARROW_MIN_TOTAL && narrowRatio < NARROW_RATIO_THRESHOLD;
|
|
4145
4233
|
const windowStartMs = now - SILENCE_WINDOW_DAYS * MS_PER_DAY;
|
|
4146
4234
|
const editFires = readCounterTimestamps(
|
|
4147
|
-
|
|
4235
|
+
join7(projectRoot, EDIT_COUNTER_FILE_REL),
|
|
4148
4236
|
windowStartMs
|
|
4149
4237
|
);
|
|
4150
4238
|
const silenceFires = readCounterTimestamps(
|
|
4151
|
-
|
|
4239
|
+
join7(projectRoot, HINT_SILENCE_COUNTER_FILE_REL),
|
|
4152
4240
|
windowStartMs
|
|
4153
4241
|
);
|
|
4154
4242
|
const telemetrySkipped = editFires === 0;
|
|
@@ -4167,10 +4255,10 @@ function inspectNarrowTooFew(projectRoot, now) {
|
|
|
4167
4255
|
};
|
|
4168
4256
|
}
|
|
4169
4257
|
function readCounterTimestamps(absPath, windowStartMs) {
|
|
4170
|
-
if (!
|
|
4258
|
+
if (!existsSync5(absPath)) return 0;
|
|
4171
4259
|
let raw;
|
|
4172
4260
|
try {
|
|
4173
|
-
raw =
|
|
4261
|
+
raw = readFileSync3(absPath, "utf8");
|
|
4174
4262
|
} catch {
|
|
4175
4263
|
return 0;
|
|
4176
4264
|
}
|
|
@@ -4186,10 +4274,10 @@ function readCounterTimestamps(absPath, windowStartMs) {
|
|
|
4186
4274
|
return count;
|
|
4187
4275
|
}
|
|
4188
4276
|
function readUnderseedThresholdFromConfig(projectRoot) {
|
|
4189
|
-
const configPath =
|
|
4190
|
-
if (!
|
|
4277
|
+
const configPath = join7(projectRoot, ".fabric", "fabric-config.json");
|
|
4278
|
+
if (!existsSync5(configPath)) return DEFAULT_UNDERSEED_NODE_THRESHOLD;
|
|
4191
4279
|
try {
|
|
4192
|
-
const raw =
|
|
4280
|
+
const raw = readFileSync3(configPath, "utf8");
|
|
4193
4281
|
const parsed = JSON.parse(raw);
|
|
4194
4282
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4195
4283
|
const v = parsed.underseed_node_threshold;
|
|
@@ -4383,11 +4471,11 @@ function extractKnowledgeFrontmatterRelevancePaths(source) {
|
|
|
4383
4471
|
}
|
|
4384
4472
|
function* iterateRelevanceFrontmatter(projectRoot) {
|
|
4385
4473
|
for (const visit of iterateCanonicalFilenames(projectRoot)) {
|
|
4386
|
-
const layerRoot = visit.layer === "team" ?
|
|
4387
|
-
const absPath =
|
|
4474
|
+
const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
|
|
4475
|
+
const absPath = join7(layerRoot, visit.type, visit.filename);
|
|
4388
4476
|
let source;
|
|
4389
4477
|
try {
|
|
4390
|
-
source =
|
|
4478
|
+
source = readFileSync3(absPath, "utf8");
|
|
4391
4479
|
} catch {
|
|
4392
4480
|
continue;
|
|
4393
4481
|
}
|
|
@@ -4449,7 +4537,7 @@ function inspectRelevancePathsDangling(projectRoot) {
|
|
|
4449
4537
|
return { entries };
|
|
4450
4538
|
}
|
|
4451
4539
|
function collectWorkspacePathsForGlobMatch(projectRoot) {
|
|
4452
|
-
if (!
|
|
4540
|
+
if (!existsSync5(projectRoot)) {
|
|
4453
4541
|
return [];
|
|
4454
4542
|
}
|
|
4455
4543
|
let rootStat;
|
|
@@ -4473,7 +4561,7 @@ function collectWorkspacePathsForGlobMatch(projectRoot) {
|
|
|
4473
4561
|
continue;
|
|
4474
4562
|
}
|
|
4475
4563
|
for (const entry of entries) {
|
|
4476
|
-
const abs =
|
|
4564
|
+
const abs = join7(current, entry.name);
|
|
4477
4565
|
const rel = normalizePath(abs.slice(projectRoot.length + 1));
|
|
4478
4566
|
if (rel.length === 0) continue;
|
|
4479
4567
|
if (entry.isDirectory()) {
|
|
@@ -4630,8 +4718,8 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4630
4718
|
const candidates = [];
|
|
4631
4719
|
let scannedCount = 0;
|
|
4632
4720
|
const FM_PATTERN = /^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---/u;
|
|
4633
|
-
const teamRoot =
|
|
4634
|
-
const personalRoot =
|
|
4721
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
|
|
4722
|
+
const personalRoot = join7(
|
|
4635
4723
|
resolvePersonalRootForPending(),
|
|
4636
4724
|
".fabric",
|
|
4637
4725
|
"knowledge",
|
|
@@ -4641,7 +4729,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4641
4729
|
[teamRoot, ".fabric/knowledge/pending"],
|
|
4642
4730
|
[personalRoot, "~/.fabric/knowledge/pending"]
|
|
4643
4731
|
]) {
|
|
4644
|
-
if (!
|
|
4732
|
+
if (!existsSync5(root)) {
|
|
4645
4733
|
continue;
|
|
4646
4734
|
}
|
|
4647
4735
|
let typeDirs = [];
|
|
@@ -4651,7 +4739,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4651
4739
|
continue;
|
|
4652
4740
|
}
|
|
4653
4741
|
for (const typeDir of typeDirs) {
|
|
4654
|
-
const dir =
|
|
4742
|
+
const dir = join7(root, typeDir);
|
|
4655
4743
|
let entries;
|
|
4656
4744
|
try {
|
|
4657
4745
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4662,10 +4750,10 @@ function inspectRelevanceFieldsMissing(projectRoot) {
|
|
|
4662
4750
|
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
4663
4751
|
continue;
|
|
4664
4752
|
}
|
|
4665
|
-
const absPath =
|
|
4753
|
+
const absPath = join7(dir, entry.name);
|
|
4666
4754
|
let source;
|
|
4667
4755
|
try {
|
|
4668
|
-
source =
|
|
4756
|
+
source = readFileSync3(absPath, "utf8");
|
|
4669
4757
|
} catch {
|
|
4670
4758
|
continue;
|
|
4671
4759
|
}
|
|
@@ -4797,8 +4885,8 @@ var SKILL_QUOTED_VALUE_LEADS = /* @__PURE__ */ new Set(['"', "'", "[", "{", ">",
|
|
|
4797
4885
|
function inspectSkillMdYamlInvalid(projectRoot) {
|
|
4798
4886
|
const candidates = [];
|
|
4799
4887
|
for (const rootRel of SKILL_MD_FRONTMATTER_ROOTS) {
|
|
4800
|
-
const rootAbs =
|
|
4801
|
-
if (!
|
|
4888
|
+
const rootAbs = join7(projectRoot, rootRel);
|
|
4889
|
+
if (!existsSync5(rootAbs)) continue;
|
|
4802
4890
|
let dirEntries;
|
|
4803
4891
|
try {
|
|
4804
4892
|
dirEntries = readdirSync(rootAbs, { withFileTypes: true });
|
|
@@ -4807,11 +4895,11 @@ function inspectSkillMdYamlInvalid(projectRoot) {
|
|
|
4807
4895
|
}
|
|
4808
4896
|
for (const dirEntry of dirEntries) {
|
|
4809
4897
|
if (!dirEntry.isDirectory()) continue;
|
|
4810
|
-
const skillFile =
|
|
4811
|
-
if (!
|
|
4898
|
+
const skillFile = join7(rootAbs, dirEntry.name, "SKILL.md");
|
|
4899
|
+
if (!existsSync5(skillFile)) continue;
|
|
4812
4900
|
let raw;
|
|
4813
4901
|
try {
|
|
4814
|
-
raw =
|
|
4902
|
+
raw = readFileSync3(skillFile, "utf8");
|
|
4815
4903
|
} catch {
|
|
4816
4904
|
continue;
|
|
4817
4905
|
}
|
|
@@ -4893,11 +4981,11 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4893
4981
|
for (const slot of ONBOARD_SLOT_NAMES) {
|
|
4894
4982
|
filled[slot] = [];
|
|
4895
4983
|
}
|
|
4896
|
-
const knowledgeRoot =
|
|
4897
|
-
if (
|
|
4984
|
+
const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
4985
|
+
if (existsSync5(knowledgeRoot)) {
|
|
4898
4986
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD) {
|
|
4899
|
-
const dir =
|
|
4900
|
-
if (!
|
|
4987
|
+
const dir = join7(knowledgeRoot, typeDir);
|
|
4988
|
+
if (!existsSync5(dir)) continue;
|
|
4901
4989
|
let entries;
|
|
4902
4990
|
try {
|
|
4903
4991
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -4907,10 +4995,10 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4907
4995
|
for (const entry of entries) {
|
|
4908
4996
|
if (!entry.isFile()) continue;
|
|
4909
4997
|
if (!entry.name.endsWith(".md")) continue;
|
|
4910
|
-
const filePath =
|
|
4998
|
+
const filePath = join7(dir, entry.name);
|
|
4911
4999
|
let content;
|
|
4912
5000
|
try {
|
|
4913
|
-
content =
|
|
5001
|
+
content = readFileSync3(filePath, "utf8");
|
|
4914
5002
|
} catch {
|
|
4915
5003
|
continue;
|
|
4916
5004
|
}
|
|
@@ -4934,11 +5022,11 @@ function inspectOnboardCoverage(projectRoot) {
|
|
|
4934
5022
|
return { filled, missing, opted_out: optedOut };
|
|
4935
5023
|
}
|
|
4936
5024
|
function readOnboardOptedOut(projectRoot) {
|
|
4937
|
-
const path2 =
|
|
4938
|
-
if (!
|
|
5025
|
+
const path2 = join7(projectRoot, ".fabric", "fabric-config.json");
|
|
5026
|
+
if (!existsSync5(path2)) return [];
|
|
4939
5027
|
let raw;
|
|
4940
5028
|
try {
|
|
4941
|
-
raw =
|
|
5029
|
+
raw = readFileSync3(path2, "utf8");
|
|
4942
5030
|
} catch {
|
|
4943
5031
|
return [];
|
|
4944
5032
|
}
|
|
@@ -5054,7 +5142,7 @@ function createNarrowTooFewCheck(t, inspection) {
|
|
|
5054
5142
|
}
|
|
5055
5143
|
function resolvePersonalKnowledgeRoot() {
|
|
5056
5144
|
const home = process.env.FABRIC_HOME ?? homedir3();
|
|
5057
|
-
return
|
|
5145
|
+
return join7(home, ".fabric", "knowledge");
|
|
5058
5146
|
}
|
|
5059
5147
|
function parseStableIdFromCanonicalFilename(filename) {
|
|
5060
5148
|
const match = CANONICAL_KNOWLEDGE_FILENAME_PATTERN.exec(filename);
|
|
@@ -5074,18 +5162,18 @@ function parseStableIdFromCanonicalFilename(filename) {
|
|
|
5074
5162
|
};
|
|
5075
5163
|
}
|
|
5076
5164
|
function* iterateCanonicalFilenames(projectRoot) {
|
|
5077
|
-
const teamRoot =
|
|
5165
|
+
const teamRoot = join7(projectRoot, ".fabric", "knowledge");
|
|
5078
5166
|
const personalRoot = resolvePersonalKnowledgeRoot();
|
|
5079
5167
|
for (const [layer, root, displayPrefix] of [
|
|
5080
5168
|
["team", teamRoot, ".fabric/knowledge"],
|
|
5081
5169
|
["personal", personalRoot, "~/.fabric/knowledge"]
|
|
5082
5170
|
]) {
|
|
5083
|
-
if (!
|
|
5171
|
+
if (!existsSync5(root)) {
|
|
5084
5172
|
continue;
|
|
5085
5173
|
}
|
|
5086
5174
|
for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
|
|
5087
|
-
const dir =
|
|
5088
|
-
if (!
|
|
5175
|
+
const dir = join7(root, typeDir);
|
|
5176
|
+
if (!existsSync5(dir)) {
|
|
5089
5177
|
continue;
|
|
5090
5178
|
}
|
|
5091
5179
|
let entries;
|
|
@@ -5256,8 +5344,8 @@ async function migrateBootstrapMarkers(projectRoot) {
|
|
|
5256
5344
|
const paths = [];
|
|
5257
5345
|
const countPerPath = {};
|
|
5258
5346
|
for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
|
|
5259
|
-
const abs =
|
|
5260
|
-
if (!
|
|
5347
|
+
const abs = join7(projectRoot, rel);
|
|
5348
|
+
if (!existsSync5(abs)) {
|
|
5261
5349
|
continue;
|
|
5262
5350
|
}
|
|
5263
5351
|
let original;
|
|
@@ -5283,8 +5371,8 @@ async function migrateBootstrapMarkers(projectRoot) {
|
|
|
5283
5371
|
return { paths, countPerPath };
|
|
5284
5372
|
}
|
|
5285
5373
|
async function rewriteThreeEndManagedBlocks(projectRoot) {
|
|
5286
|
-
const snapshotPath =
|
|
5287
|
-
if (!
|
|
5374
|
+
const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
|
|
5375
|
+
if (!existsSync5(snapshotPath)) {
|
|
5288
5376
|
return;
|
|
5289
5377
|
}
|
|
5290
5378
|
let snapshot;
|
|
@@ -5293,8 +5381,8 @@ async function rewriteThreeEndManagedBlocks(projectRoot) {
|
|
|
5293
5381
|
} catch {
|
|
5294
5382
|
return;
|
|
5295
5383
|
}
|
|
5296
|
-
const projectRulesPath =
|
|
5297
|
-
const hasProjectRules =
|
|
5384
|
+
const projectRulesPath = join7(projectRoot, ".fabric", "project-rules.md");
|
|
5385
|
+
const hasProjectRules = existsSync5(projectRulesPath);
|
|
5298
5386
|
let expectedBody = snapshot;
|
|
5299
5387
|
if (hasProjectRules) {
|
|
5300
5388
|
try {
|
|
@@ -5309,11 +5397,11 @@ ${projectRules}`;
|
|
|
5309
5397
|
${expectedBody}
|
|
5310
5398
|
${BOOTSTRAP_MARKER_END}`;
|
|
5311
5399
|
const blockTargets = [
|
|
5312
|
-
|
|
5313
|
-
|
|
5400
|
+
join7(projectRoot, "AGENTS.md"),
|
|
5401
|
+
join7(projectRoot, ".cursor", "rules", "fabric-bootstrap.mdc")
|
|
5314
5402
|
];
|
|
5315
5403
|
for (const abs of blockTargets) {
|
|
5316
|
-
if (!
|
|
5404
|
+
if (!existsSync5(abs)) {
|
|
5317
5405
|
continue;
|
|
5318
5406
|
}
|
|
5319
5407
|
let existing;
|
|
@@ -5343,8 +5431,8 @@ ${managedBlock}
|
|
|
5343
5431
|
}
|
|
5344
5432
|
await atomicWriteText4(abs, next);
|
|
5345
5433
|
}
|
|
5346
|
-
const claudeMdPath =
|
|
5347
|
-
if (
|
|
5434
|
+
const claudeMdPath = join7(projectRoot, "CLAUDE.md");
|
|
5435
|
+
if (existsSync5(claudeMdPath)) {
|
|
5348
5436
|
let claudeContent;
|
|
5349
5437
|
try {
|
|
5350
5438
|
claudeContent = await readFile5(claudeMdPath, "utf8");
|
|
@@ -5372,13 +5460,13 @@ ${managedBlock}
|
|
|
5372
5460
|
}
|
|
5373
5461
|
}
|
|
5374
5462
|
async function fixMcpConfigInWrongFile(projectRoot) {
|
|
5375
|
-
const settingsPath =
|
|
5376
|
-
if (!
|
|
5463
|
+
const settingsPath = join7(projectRoot, ".claude", "settings.json");
|
|
5464
|
+
if (!existsSync5(settingsPath)) {
|
|
5377
5465
|
return;
|
|
5378
5466
|
}
|
|
5379
5467
|
let settings;
|
|
5380
5468
|
try {
|
|
5381
|
-
const parsed = JSON.parse(
|
|
5469
|
+
const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
|
|
5382
5470
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
5383
5471
|
return;
|
|
5384
5472
|
}
|
|
@@ -5406,12 +5494,12 @@ async function fixMcpConfigInWrongFile(projectRoot) {
|
|
|
5406
5494
|
}
|
|
5407
5495
|
async function ensureKnowledgeSubdirs(projectRoot) {
|
|
5408
5496
|
for (const sub of KNOWLEDGE_SUBDIRS3) {
|
|
5409
|
-
await mkdir4(
|
|
5497
|
+
await mkdir4(join7(projectRoot, ".fabric", "knowledge", sub), { recursive: true });
|
|
5410
5498
|
}
|
|
5411
5499
|
}
|
|
5412
5500
|
async function fixCounterDesync(projectRoot) {
|
|
5413
|
-
const metaPath =
|
|
5414
|
-
if (!
|
|
5501
|
+
const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
|
|
5502
|
+
if (!existsSync5(metaPath)) {
|
|
5415
5503
|
return;
|
|
5416
5504
|
}
|
|
5417
5505
|
let meta;
|
|
@@ -5829,8 +5917,8 @@ async function runDoctorCiteCoverage(projectRoot, options) {
|
|
|
5829
5917
|
continue;
|
|
5830
5918
|
}
|
|
5831
5919
|
bumpLayerType(citeId, kbType);
|
|
5832
|
-
if (kbType === "
|
|
5833
|
-
if (kbType === "
|
|
5920
|
+
if (kbType === "decisions" || kbType === "pitfalls") {
|
|
5921
|
+
if (kbType === "decisions") decisionsCited += 1;
|
|
5834
5922
|
else pitfallsCited += 1;
|
|
5835
5923
|
const commitment = commitments[i];
|
|
5836
5924
|
const operators = commitment?.operators ?? [];
|
|
@@ -5994,7 +6082,7 @@ function normalizePath(path2) {
|
|
|
5994
6082
|
return posix.normalize(path2.split("\\").join("/"));
|
|
5995
6083
|
}
|
|
5996
6084
|
function collectEntryPoints(root) {
|
|
5997
|
-
if (!
|
|
6085
|
+
if (!existsSync5(root) || !statSync4(root).isDirectory()) {
|
|
5998
6086
|
return [];
|
|
5999
6087
|
}
|
|
6000
6088
|
const entries = [];
|
|
@@ -6005,7 +6093,7 @@ function collectEntryPoints(root) {
|
|
|
6005
6093
|
continue;
|
|
6006
6094
|
}
|
|
6007
6095
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
6008
|
-
const absolutePath =
|
|
6096
|
+
const absolutePath = join7(current, entry.name);
|
|
6009
6097
|
const relativePath = normalizePath(absolutePath.slice(root.length + 1));
|
|
6010
6098
|
if (relativePath.length === 0) {
|
|
6011
6099
|
continue;
|
|
@@ -6071,14 +6159,14 @@ var ENRICH_DESC_FIELD_PATTERNS = {
|
|
|
6071
6159
|
async function enrichDescriptions(projectRoot, opts = {}) {
|
|
6072
6160
|
const auto = opts.auto === true;
|
|
6073
6161
|
const dryRun = opts.dryRun === true;
|
|
6074
|
-
const mode = auto ? "auto" : "
|
|
6162
|
+
const mode = auto ? dryRun ? "preview" : "auto" : "readonly";
|
|
6075
6163
|
const candidates = [];
|
|
6076
6164
|
let scanned = 0;
|
|
6077
6165
|
let modified = 0;
|
|
6078
6166
|
let skipped = 0;
|
|
6079
6167
|
for (const visit of iterateCanonicalFilenames(projectRoot)) {
|
|
6080
|
-
const layerRoot = visit.layer === "team" ?
|
|
6081
|
-
const absPath =
|
|
6168
|
+
const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
|
|
6169
|
+
const absPath = join7(layerRoot, visit.type, visit.filename);
|
|
6082
6170
|
scanned += 1;
|
|
6083
6171
|
let source;
|
|
6084
6172
|
try {
|
|
@@ -6281,7 +6369,7 @@ async function emitAutoHealEventBestEffort(projectRoot, payload) {
|
|
|
6281
6369
|
|
|
6282
6370
|
// src/services/get-knowledge.ts
|
|
6283
6371
|
import { readFile as readFile6 } from "fs/promises";
|
|
6284
|
-
import { join as
|
|
6372
|
+
import { join as join8 } from "path";
|
|
6285
6373
|
import { minimatch as minimatch2 } from "minimatch";
|
|
6286
6374
|
var PRIORITY_ORDER = {
|
|
6287
6375
|
high: 0,
|
|
@@ -6325,7 +6413,7 @@ async function loadGetKnowledgeContext(projectRoot) {
|
|
|
6325
6413
|
return cached;
|
|
6326
6414
|
}
|
|
6327
6415
|
const meta = await readAgentsMeta(projectRoot);
|
|
6328
|
-
const l0Content = await readFile6(
|
|
6416
|
+
const l0Content = await readFile6(join8(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
|
|
6329
6417
|
const context = {
|
|
6330
6418
|
meta,
|
|
6331
6419
|
l0Content,
|
|
@@ -6464,7 +6552,7 @@ async function readRuleContent(projectRoot, file, fileContentCache) {
|
|
|
6464
6552
|
if (cached !== void 0) {
|
|
6465
6553
|
return await cached;
|
|
6466
6554
|
}
|
|
6467
|
-
const pending = readFile6(
|
|
6555
|
+
const pending = readFile6(join8(projectRoot, file), "utf8");
|
|
6468
6556
|
fileContentCache.set(file, pending);
|
|
6469
6557
|
return await pending;
|
|
6470
6558
|
}
|
|
@@ -6499,6 +6587,8 @@ export {
|
|
|
6499
6587
|
invalidateKnowledgeSyncCooldown,
|
|
6500
6588
|
ensureKnowledgeFresh,
|
|
6501
6589
|
reconcileKnowledge,
|
|
6590
|
+
readPayloadLimits,
|
|
6591
|
+
readSelectionTokenTtlMs,
|
|
6502
6592
|
loadActiveMeta,
|
|
6503
6593
|
loadActiveMetaOrStale,
|
|
6504
6594
|
getKnowledge,
|