@fenglimg/fabric-server 2.2.0-rc.4 → 2.2.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/index.d.ts +115 -107
- package/dist/index.js +4785 -6304
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# @fenglimg/fabric-server
|
|
2
2
|
|
|
3
|
-
Fabric MCP knowledge server. Runs over stdio transport and serves Claude Code
|
|
3
|
+
Fabric MCP knowledge server. Runs over stdio transport and serves Claude Code and Codex CLI from a single `.fabric/` directory.
|
|
4
4
|
|
|
5
5
|
## Tools exposed
|
|
6
6
|
|
|
7
|
-
- `
|
|
8
|
-
- `
|
|
9
|
-
- `fab_recall` — combined one-call recall (plan + sections), the rc.37+ default
|
|
7
|
+
- `fab_recall` — single-step recall: returns candidate descriptions + native read paths (no body delivery over MCP; read a body on demand via a native Read of the returned path)
|
|
8
|
+
- `fab_archive_scan` — scan recent work for archive-worthy knowledge candidates
|
|
10
9
|
- `fab_extract_knowledge` — persist a pending knowledge entry
|
|
11
10
|
- `fab_review` — list / approve / reject / modify / defer pending entries
|
|
12
11
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { FabExtractKnowledgeInput, FabExtractKnowledgeOutput, FabReviewInput, FabReviewOutput } from '@fenglimg/fabric-shared/schemas/api-contracts';
|
|
3
3
|
import { EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem, LedgerEntry, AgentsMeta } from '@fenglimg/fabric-shared';
|
|
4
|
+
import { PayloadGuardOptions } from '@fenglimg/fabric-shared/node/mcp-payload-guard';
|
|
4
5
|
|
|
5
6
|
interface InFlightTracker {
|
|
6
7
|
enter(requestId: string): void;
|
|
@@ -22,6 +23,67 @@ declare function getLegacyLedgerPath(projectRoot: string): string;
|
|
|
22
23
|
declare function getEventLedgerPath(projectRoot: string): string;
|
|
23
24
|
declare function getMetricsLedgerPath(projectRoot: string): string;
|
|
24
25
|
|
|
26
|
+
type DoctorHealth = {
|
|
27
|
+
score: number;
|
|
28
|
+
grade: "A" | "B" | "C" | "D" | "F";
|
|
29
|
+
penalties: {
|
|
30
|
+
manual_errors: number;
|
|
31
|
+
fixable_errors: number;
|
|
32
|
+
warnings: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
interface AlwaysActiveBody {
|
|
37
|
+
/** store-qualified id (`<alias>:<stableId>`). */
|
|
38
|
+
stable_id: string;
|
|
39
|
+
/** knowledge_type — one of ALWAYS_ACTIVE_TYPES. */
|
|
40
|
+
type: string;
|
|
41
|
+
layer: "team" | "personal";
|
|
42
|
+
/** description.summary — the overflow-degrade fallback when the body cannot
|
|
43
|
+
* fit the injection char budget (the budget is enforced hook-side, D10). */
|
|
44
|
+
summary: string;
|
|
45
|
+
/** frontmatter-stripped markdown body. */
|
|
46
|
+
body: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Collect the always-active (guidelines + models) entries from the project's
|
|
50
|
+
* read-set, project-filtered identically to recall (filterByActiveProject), with
|
|
51
|
+
* their frontmatter-stripped bodies. The SessionStart hook injects these bodies
|
|
52
|
+
* into the AI context and renders category counts for the on-demand remainder.
|
|
53
|
+
*
|
|
54
|
+
* Returns [] (never throws) on any read-set resolution failure — the SessionStart
|
|
55
|
+
* banner must degrade gracefully, never crash session start.
|
|
56
|
+
*/
|
|
57
|
+
interface KnowledgeCensus {
|
|
58
|
+
/** knowledge_type → count (decisions/pitfalls/guidelines/models/processes). */
|
|
59
|
+
by_type: Record<string, number>;
|
|
60
|
+
by_layer: {
|
|
61
|
+
team: number;
|
|
62
|
+
personal: number;
|
|
63
|
+
project: number;
|
|
64
|
+
};
|
|
65
|
+
/** entries专属 to OTHER projects that filterByActiveProject removed. */
|
|
66
|
+
dropped_other_project: number;
|
|
67
|
+
/** kept (post-filter) total. */
|
|
68
|
+
total: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Build the read-set census (project-filtered counts + dropped-other-project).
|
|
72
|
+
* Reuses the cached read-set walk, so calling alongside buildAlwaysActiveBodies
|
|
73
|
+
* in one SessionStart fire costs a single walk. Never throws — degrades to an
|
|
74
|
+
* all-zero census so the banner stays renderable.
|
|
75
|
+
*/
|
|
76
|
+
declare function buildKnowledgeCensus(projectRoot: string): Promise<KnowledgeCensus>;
|
|
77
|
+
declare function buildAlwaysActiveBodies(projectRoot: string): Promise<AlwaysActiveBody[]>;
|
|
78
|
+
|
|
79
|
+
interface UnboundProjectViolation {
|
|
80
|
+
/** The store already bound as the active write target. */
|
|
81
|
+
alias: string;
|
|
82
|
+
/** Which project-scope fields are absent: `project_id` and/or `active_project`. */
|
|
83
|
+
missing: string[];
|
|
84
|
+
}
|
|
85
|
+
declare function detectUnboundProject(projectRoot: string): UnboundProjectViolation | null;
|
|
86
|
+
|
|
25
87
|
/**
|
|
26
88
|
* O(1) in-memory increment for a named counter. Safe to call from any MCP
|
|
27
89
|
* tool handler; no I/O happens until the next `flushMetrics()` call.
|
|
@@ -81,11 +143,15 @@ type MetricsRow = {
|
|
|
81
143
|
counters: Record<string, number>;
|
|
82
144
|
};
|
|
83
145
|
/**
|
|
84
|
-
* Canonical metric counter names
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
146
|
+
* Canonical metric counter names. rc.37 Wave B added a metrics.jsonl counter
|
|
147
|
+
* rollup for each (the forward-compatible signal), but — contrary to the
|
|
148
|
+
* original "these left events.jsonl" plan — every one is ALSO still appended to
|
|
149
|
+
* the audit ledger as a structured event, because a doctor lint consumes its
|
|
150
|
+
* per-id / per-path payload (see LEDGER_DUAL_WRITE_METRIC_NAMES below). The
|
|
151
|
+
* promised counter-only cutover (rc.38+; tracked by the G11 invariant test) has
|
|
152
|
+
* not happened. Centralized here so the B5 hard-gate (`metric_event_in_jsonl`)
|
|
153
|
+
* can grep for these exact strings; the gate only flags names NOT in the
|
|
154
|
+
* dual-write allowlist below.
|
|
89
155
|
*/
|
|
90
156
|
declare const METRIC_COUNTER_NAMES: {
|
|
91
157
|
readonly knowledge_consumed: "knowledge_consumed";
|
|
@@ -113,7 +179,7 @@ type CiteCoverageReport = {
|
|
|
113
179
|
marker_ts: number;
|
|
114
180
|
marker_emitted_now: boolean;
|
|
115
181
|
since_ts: number;
|
|
116
|
-
client_filter: "cc" | "codex" | "
|
|
182
|
+
client_filter: "cc" | "codex" | "all";
|
|
117
183
|
layer_filter?: "team" | "personal" | "all";
|
|
118
184
|
metrics: {
|
|
119
185
|
edits_touched: number;
|
|
@@ -162,7 +228,7 @@ type CiteCoverageReport = {
|
|
|
162
228
|
};
|
|
163
229
|
declare function runDoctorCiteCoverage(projectRoot: string, options: {
|
|
164
230
|
since: number;
|
|
165
|
-
client: "cc" | "codex" | "
|
|
231
|
+
client: "cc" | "codex" | "all";
|
|
166
232
|
layer?: "team" | "personal" | "all";
|
|
167
233
|
until?: number;
|
|
168
234
|
recallWindowMs?: number;
|
|
@@ -250,15 +316,6 @@ type DoctorSummary = {
|
|
|
250
316
|
payload_limits: DoctorPayloadLimits;
|
|
251
317
|
health: DoctorHealth;
|
|
252
318
|
};
|
|
253
|
-
type DoctorHealth = {
|
|
254
|
-
score: number;
|
|
255
|
-
grade: "A" | "B" | "C" | "D" | "F";
|
|
256
|
-
penalties: {
|
|
257
|
-
manual_errors: number;
|
|
258
|
-
fixable_errors: number;
|
|
259
|
-
warnings: number;
|
|
260
|
-
};
|
|
261
|
-
};
|
|
262
319
|
type DoctorReport = {
|
|
263
320
|
status: DoctorStatus;
|
|
264
321
|
checks: DoctorCheck[];
|
|
@@ -287,6 +344,7 @@ type DoctorApplyLintMutation = {
|
|
|
287
344
|
type DoctorApplyLintReport = {
|
|
288
345
|
changed: boolean;
|
|
289
346
|
mutations: DoctorApplyLintMutation[];
|
|
347
|
+
warnings: DoctorIssue[];
|
|
290
348
|
manual_errors: DoctorIssue[];
|
|
291
349
|
aborted: boolean;
|
|
292
350
|
abort_reason?: string;
|
|
@@ -296,6 +354,7 @@ type DoctorApplyLintReport = {
|
|
|
296
354
|
declare function runDoctorReport(target: string): Promise<DoctorReport>;
|
|
297
355
|
declare function runDoctorFix(target: string): Promise<DoctorFixReport>;
|
|
298
356
|
declare function runDoctorApplyLint(target: string): Promise<DoctorApplyLintReport>;
|
|
357
|
+
|
|
299
358
|
type EnrichDescriptionsMode = "auto" | "preview" | "readonly" | "interactive";
|
|
300
359
|
type EnrichDescriptionsCandidate = {
|
|
301
360
|
path: string;
|
|
@@ -529,11 +588,26 @@ type PlanContextInput = {
|
|
|
529
588
|
target_paths?: string[];
|
|
530
589
|
layer_filter?: "team" | "personal" | "both";
|
|
531
590
|
include_related?: boolean;
|
|
591
|
+
/**
|
|
592
|
+
* Internal MCP presentation budget. When supplied by the tool layer,
|
|
593
|
+
* candidates are byte-trimmed before the selection token is cached so the
|
|
594
|
+
* token cannot reference candidates omitted from the response.
|
|
595
|
+
*/
|
|
596
|
+
payload_budget?: PlanContextPayloadBudget;
|
|
597
|
+
};
|
|
598
|
+
type PlanContextPayloadWarning = {
|
|
599
|
+
code: string;
|
|
600
|
+
file?: string;
|
|
601
|
+
action_hint?: string;
|
|
602
|
+
};
|
|
603
|
+
type PlanContextPayloadBudget = {
|
|
604
|
+
limits?: PayloadGuardOptions;
|
|
605
|
+
warnings?: PlanContextPayloadWarning[];
|
|
606
|
+
trim_warning?: PlanContextPayloadWarning;
|
|
532
607
|
};
|
|
533
608
|
type RequirementProfile = {
|
|
534
609
|
target_path: string;
|
|
535
610
|
known_tech: string[];
|
|
536
|
-
user_intent: string;
|
|
537
611
|
detected_entities: string[];
|
|
538
612
|
};
|
|
539
613
|
type PlanContextEntry = {
|
|
@@ -552,6 +626,7 @@ type PlanContextResult = {
|
|
|
552
626
|
stale: boolean;
|
|
553
627
|
selection_token: string;
|
|
554
628
|
entries: PlanContextEntry[];
|
|
629
|
+
intent?: string;
|
|
555
630
|
candidates: RuleDescriptionIndexItem[];
|
|
556
631
|
omitted_candidate_count?: number;
|
|
557
632
|
preflight_diagnostics: PreflightDiagnostic[];
|
|
@@ -559,6 +634,10 @@ type PlanContextResult = {
|
|
|
559
634
|
previous_revision_hash?: string;
|
|
560
635
|
redirects?: Record<string, string>;
|
|
561
636
|
related_appended?: Record<string, string>;
|
|
637
|
+
/** Internal service→tool signal; stripped before MCP output. */
|
|
638
|
+
payload_trimmed?: boolean;
|
|
639
|
+
/** Internal service→tool signal; stripped before MCP output. */
|
|
640
|
+
payload_over_budget?: boolean;
|
|
562
641
|
};
|
|
563
642
|
type SelectionTokenState = {
|
|
564
643
|
token: string;
|
|
@@ -574,45 +653,33 @@ declare function readSelectionToken(token: string, now?: number): SelectionToken
|
|
|
574
653
|
|
|
575
654
|
type RecallInput = PlanContextInput & {
|
|
576
655
|
/**
|
|
577
|
-
* Optional explicit set of stable_ids to
|
|
578
|
-
*
|
|
579
|
-
* index
|
|
580
|
-
*
|
|
656
|
+
* Optional explicit set of stable_ids to SCOPE the returned read paths to.
|
|
657
|
+
* When omitted, `paths` carries one entry per surfaced candidate. The candidate
|
|
658
|
+
* DESCRIPTION index is always returned in full for discovery — `ids` only narrows
|
|
659
|
+
* which read paths are surfaced (e.g. `recall(ids)` when the agent already knows
|
|
660
|
+
* which entries it wants to Read). Stale (pre layer-flip) ids are redirect-rewritten
|
|
661
|
+
* before the match.
|
|
581
662
|
*/
|
|
582
663
|
ids?: string[];
|
|
583
664
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
* (every candidate is already fetched) or no related edges resolve in-corpus.
|
|
665
|
+
* When true, forwarded to planContext, which appends the one-hop `related` graph
|
|
666
|
+
* neighbours (H2) of the surfaced set to the candidate index (descriptions only —
|
|
667
|
+
* NO body). Their read paths are included in `paths` like any other candidate
|
|
668
|
+
* (W1-3 / KT-DEC-0031: surface the related id, do not fetch its body).
|
|
589
669
|
*/
|
|
590
670
|
include_related?: boolean;
|
|
591
671
|
};
|
|
592
|
-
type
|
|
593
|
-
|
|
594
|
-
|
|
672
|
+
type RecallPath = {
|
|
673
|
+
stable_id: string;
|
|
674
|
+
path: string;
|
|
675
|
+
store?: {
|
|
676
|
+
alias: string;
|
|
677
|
+
};
|
|
595
678
|
};
|
|
596
|
-
type RecallResult = PlanContextResult & {
|
|
597
|
-
|
|
598
|
-
stable_id: string;
|
|
599
|
-
level: "L0" | "L1" | "L2";
|
|
600
|
-
path: string;
|
|
601
|
-
body: string;
|
|
602
|
-
store?: {
|
|
603
|
-
alias: string;
|
|
604
|
-
};
|
|
605
|
-
}>;
|
|
606
|
-
selected_stable_ids: string[];
|
|
607
|
-
diagnostics: Array<{
|
|
608
|
-
code: "missing_knowledge_metadata" | "unresolved_selected_id";
|
|
609
|
-
severity: "warn";
|
|
610
|
-
stable_id: string;
|
|
611
|
-
message: string;
|
|
612
|
-
}>;
|
|
679
|
+
type RecallResult = Omit<PlanContextResult, "selection_token" | "payload_trimmed" | "payload_over_budget"> & {
|
|
680
|
+
paths: RecallPath[];
|
|
613
681
|
directive: string;
|
|
614
682
|
next_steps?: string[];
|
|
615
|
-
truncation?: RecallTruncation;
|
|
616
683
|
};
|
|
617
684
|
declare function recall(projectRoot: string, input: RecallInput): Promise<RecallResult>;
|
|
618
685
|
|
|
@@ -680,65 +747,6 @@ declare class ContextCache {
|
|
|
680
747
|
}
|
|
681
748
|
declare const contextCache: ContextCache;
|
|
682
749
|
|
|
683
|
-
interface KnowledgeSyncOptions {
|
|
684
|
-
mode?: "incremental" | "full";
|
|
685
|
-
/** When true, invalid frontmatter throws RuleValidationError (default: false — collect as warning). */
|
|
686
|
-
throwOnInvalidFrontmatter?: boolean;
|
|
687
|
-
/**
|
|
688
|
-
* v2.0.0-rc.29 TASK-005 (BUG-G1): originally opted the hot path into a
|
|
689
|
-
* follow-up `reconcileKnowledge` (rewrite agents.meta.json) so every
|
|
690
|
-
* detected drift got a paired heal event.
|
|
691
|
-
*
|
|
692
|
-
* v2.2 W5 R2 (agents.meta decolo): retained as a NO-OP for backward
|
|
693
|
-
* compatibility — the co-location agents.meta.json it used to rebuild no
|
|
694
|
-
* longer exists (knowledge lives in stores; read paths cut over to the
|
|
695
|
-
* cross-store model). MCP tool call sites still pass `autoHealOnDrift: true`;
|
|
696
|
-
* it is now ignored.
|
|
697
|
-
*/
|
|
698
|
-
autoHealOnDrift?: boolean;
|
|
699
|
-
}
|
|
700
|
-
interface StructuredWarning {
|
|
701
|
-
code: string;
|
|
702
|
-
file: string;
|
|
703
|
-
line?: number;
|
|
704
|
-
action_hint: string;
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Granular ledger event shape for knowledge-sync operations.
|
|
708
|
-
* These are returned in KnowledgeSyncReport and also appended to the event ledger
|
|
709
|
-
* using the nearest available ledger event type (knowledge_drift_detected).
|
|
710
|
-
* The shape below is what callers receive in `.events`.
|
|
711
|
-
*/
|
|
712
|
-
interface KnowledgeSyncLedgerEvent {
|
|
713
|
-
type: "rule_content_changed" | "rule_added" | "rule_removed";
|
|
714
|
-
stable_id: string;
|
|
715
|
-
path: string;
|
|
716
|
-
prev_hash: string | null;
|
|
717
|
-
new_hash: string | null;
|
|
718
|
-
changed_fields: string[];
|
|
719
|
-
source: "ensureKnowledgeFresh" | "reconcileKnowledge";
|
|
720
|
-
}
|
|
721
|
-
/** Alias so the public API says LedgerEvent (as documented). */
|
|
722
|
-
type LedgerEvent = KnowledgeSyncLedgerEvent;
|
|
723
|
-
interface KnowledgeSyncReport {
|
|
724
|
-
status: "fresh" | "reconciled" | "errors";
|
|
725
|
-
events: LedgerEvent[];
|
|
726
|
-
warnings: StructuredWarning[];
|
|
727
|
-
reconciled_files?: string[];
|
|
728
|
-
}
|
|
729
|
-
/**
|
|
730
|
-
* Clear the knowledge-sync cooldown for a projectRoot so the next ensureKnowledgeFresh
|
|
731
|
-
* call performs a real I/O scan. Called by the chokidar watcher when a rule
|
|
732
|
-
* file changes (see http.ts handleCacheWatcherEvent).
|
|
733
|
-
*/
|
|
734
|
-
declare function invalidateKnowledgeSyncCooldown(projectRoot: string): void;
|
|
735
|
-
/**
|
|
736
|
-
* Detects drift between disk and agents.meta.json, emits ledger events, and
|
|
737
|
-
* invalidates the cache. Does NOT rewrite agents.meta.json. Optimised for
|
|
738
|
-
* hot-path consumers (MCP tools).
|
|
739
|
-
*/
|
|
740
|
-
declare function ensureKnowledgeFresh(projectRoot: string, opts?: KnowledgeSyncOptions): Promise<KnowledgeSyncReport>;
|
|
741
|
-
|
|
742
750
|
type LedgerSourceFilter = "ai" | "human";
|
|
743
751
|
type StoredLedgerEntry = LedgerEntry & {
|
|
744
752
|
id: string;
|
|
@@ -815,4 +823,4 @@ interface ShutdownHandlerDeps {
|
|
|
815
823
|
*/
|
|
816
824
|
declare function createShutdownHandler(deps: ShutdownHandlerDeps): () => void;
|
|
817
825
|
|
|
818
|
-
export { AGENTS_MD_RESOURCE_URI, type ArchiveHistoryEntry, type ArchiveHistoryReport, type CiteCoverageReport, type ConflictEntry, type ConflictJudge, type ConflictLintReport, type ConflictPair, type ConflictVerdict, DEFAULT_CONFLICT_SIMILARITY_THRESHOLD, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, FABRIC_SERVER_INSTRUCTIONS, type HistoryAllReport, type HistoryDayRow, type InFlightTracker, type
|
|
826
|
+
export { AGENTS_MD_RESOURCE_URI, type AlwaysActiveBody, type ArchiveHistoryEntry, type ArchiveHistoryReport, type CiteCoverageReport, type ConflictEntry, type ConflictJudge, type ConflictLintReport, type ConflictPair, type ConflictVerdict, DEFAULT_CONFLICT_SIMILARITY_THRESHOLD, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, FABRIC_SERVER_INSTRUCTIONS, type HistoryAllReport, type HistoryDayRow, type InFlightTracker, type KnowledgeCensus, LEDGER_PATH, LEGACY_LEDGER_PATH, METRICS_LEDGER_PATH, METRIC_COUNTER_NAMES, type MetricCounterName, type MetricsRow, type PlanContextInput, type PlanContextResult, type RecallInput, type RecallResult, type RequirementProfile, type SelectionTokenState, type ShutdownHandlerDeps, type UnboundProjectViolation, appendEventLedgerEvent, buildAlwaysActiveBodies, buildKnowledgeCensus, bumpCounter, contextCache, createFabricServer, createInFlightTracker, createShutdownHandler, detectUnboundProject, drainCounters, enrichDescriptions, extractKnowledge, findConflictCandidates, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, lintConflicts, loadConflictEntries, pairSimilarity, planContext, readEventLedger, readLedger, readMetrics, readSelectionToken, recall, rehydrateAgentsMetaAt, resolveLedgerPaths, reviewKnowledge, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorConflictLint, runDoctorFix, runDoctorHistoryAll, runDoctorReport, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick };
|