@fenglimg/fabric-server 2.0.0-rc.36 → 2.0.0-rc.38

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wangzhichao (fenglimg)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # @fenglimg/fabric-server
2
+
3
+ Fabric MCP knowledge server. Runs over stdio transport and serves Claude Code, Cursor, and Codex CLI from a single `.fabric/` directory.
4
+
5
+ ## Tools exposed
6
+
7
+ - `fab_plan_context` — neutral rule description index + selection token
8
+ - `fab_get_knowledge_sections` — fetch full markdown bodies by stable_id
9
+ - `fab_recall` — combined one-call recall (plan + sections), the rc.37+ default
10
+ - `fab_extract_knowledge` — persist a pending knowledge entry
11
+ - `fab_review` — list / approve / reject / modify / defer pending entries
12
+
13
+ ## Install
14
+
15
+ Usually installed indirectly via [`@fenglimg/fabric-cli`](https://www.npmjs.com/package/@fenglimg/fabric-cli):
16
+
17
+ ```bash
18
+ npm i -g @fenglimg/fabric-cli
19
+ fabric install
20
+ ```
21
+
22
+ Direct consumption (programmatic):
23
+
24
+ ```bash
25
+ npm i @fenglimg/fabric-server
26
+ ```
27
+
28
+ ## Repo
29
+
30
+ Source + issues + roadmap: <https://github.com/fenglimg/fabric-v2>
31
+
32
+ ## License
33
+
34
+ MIT — see [LICENSE](./LICENSE).
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
- import { Server } from 'node:http';
2
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
2
  import { AgentsMeta, KnowledgeTestIndex, AgentsLayer, AgentsTopologyType, KnowledgeType, Layer, StableId, AgentsMetaCounters, EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem } from '@fenglimg/fabric-shared';
4
3
  import { FabExtractKnowledgeInput, FabExtractKnowledgeOutput, FabReviewInput, FabReviewOutput } from '@fenglimg/fabric-shared/schemas/api-contracts';
5
- import { IOFabricError } from '@fenglimg/fabric-shared/errors';
6
4
 
7
5
  interface InFlightTracker {
8
6
  enter(requestId: string): void;
@@ -18,9 +16,11 @@ declare function createInFlightTracker(): InFlightTracker;
18
16
  declare const LEDGER_PATH = ".fabric/.intent-ledger.jsonl";
19
17
  declare const LEGACY_LEDGER_PATH = ".intent-ledger.jsonl";
20
18
  declare const EVENT_LEDGER_PATH = ".fabric/events.jsonl";
19
+ declare const METRICS_LEDGER_PATH = ".fabric/metrics.jsonl";
21
20
  declare function getLedgerPath(projectRoot: string): string;
22
21
  declare function getLegacyLedgerPath(projectRoot: string): string;
23
22
  declare function getEventLedgerPath(projectRoot: string): string;
23
+ declare function getMetricsLedgerPath(projectRoot: string): string;
24
24
 
25
25
  type DoctorStatus = "ok" | "warn" | "error";
26
26
  type DoctorIssueKind = "fixable_error" | "manual_error" | "warning" | "info";
@@ -132,6 +132,10 @@ type CiteCoverageReport = {
132
132
  recalled_unverified: number;
133
133
  expected_but_missed: number;
134
134
  total_turns: number;
135
+ cite_compliance_rate?: number | null;
136
+ compliant_cites?: number;
137
+ noncompliant_cites?: number;
138
+ uncorrelatable_edits?: number;
135
139
  };
136
140
  per_client?: Record<string, Partial<CiteCoverageReport["metrics"]>>;
137
141
  dismissed_reason_histogram?: Record<string, number>;
@@ -164,6 +168,23 @@ type ArchiveHistoryReport = {
164
168
  declare function runDoctorArchiveHistory(projectRoot: string, options: {
165
169
  since: number;
166
170
  }): Promise<ArchiveHistoryReport>;
171
+ type HistoryDayRow = {
172
+ date: string;
173
+ doctor_runs_lint: number;
174
+ doctor_runs_fix: number;
175
+ doctor_total_issues: number;
176
+ doctor_total_mutations: number;
177
+ archive_attempts: number;
178
+ archive_proposed: number;
179
+ };
180
+ type HistoryAllReport = {
181
+ rows: HistoryDayRow[];
182
+ since_ms: number;
183
+ generated_at: string;
184
+ };
185
+ declare function runDoctorHistoryAll(projectRoot: string, options: {
186
+ since: number;
187
+ }): Promise<HistoryAllReport>;
167
188
  type EnrichDescriptionsMode = "auto" | "preview" | "readonly" | "interactive";
168
189
  type EnrichDescriptionsCandidate = {
169
190
  path: string;
@@ -324,8 +345,6 @@ type PlanContextInput = {
324
345
  };
325
346
  type RequirementProfile = {
326
347
  target_path: string;
327
- path_segments: string[];
328
- extension: string;
329
348
  known_tech: string[];
330
349
  user_intent: string;
331
350
  detected_entities: string[];
@@ -333,25 +352,24 @@ type RequirementProfile = {
333
352
  type PlanContextEntry = {
334
353
  path: string;
335
354
  requirement_profile: RequirementProfile;
336
- description_index: RuleDescriptionIndexItem[];
355
+ };
356
+ type PreflightDiagnostic = {
357
+ code: "missing_description" | "empty_shell_suppressed";
358
+ severity: "warn";
359
+ message: string;
360
+ stable_ids?: string[];
361
+ path?: string;
337
362
  };
338
363
  type PlanContextResult = {
339
364
  revision_hash: string;
340
365
  stale: boolean;
341
366
  selection_token: string;
342
367
  entries: PlanContextEntry[];
343
- shared: {
344
- description_index: RuleDescriptionIndexItem[];
345
- preflight_diagnostics: Array<{
346
- code: "missing_description";
347
- severity: "warn";
348
- message: string;
349
- stable_ids?: string[];
350
- path?: string;
351
- }>;
352
- };
368
+ candidates: RuleDescriptionIndexItem[];
369
+ preflight_diagnostics: PreflightDiagnostic[];
353
370
  auto_healed?: boolean;
354
371
  previous_revision_hash?: string;
372
+ redirects?: Record<string, string>;
355
373
  };
356
374
  type SelectionTokenState = {
357
375
  token: string;
@@ -365,6 +383,126 @@ type SelectionTokenState = {
365
383
  declare function planContext(projectRoot: string, input: PlanContextInput): Promise<PlanContextResult>;
366
384
  declare function readSelectionToken(token: string, now?: number): SelectionTokenState | undefined;
367
385
 
386
+ type RecallInput = PlanContextInput & {
387
+ /**
388
+ * Optional explicit set of stable_ids to fetch bodies for. When omitted,
389
+ * fab_recall picks up every stable_id surfaced in the shared description
390
+ * index (the common case after rc.37 selectable-filter removal). When
391
+ * provided, filters the fetched body set to this intersection.
392
+ */
393
+ ids?: string[];
394
+ };
395
+ type RecallResult = PlanContextResult & {
396
+ rules: Array<{
397
+ stable_id: string;
398
+ level: "L0" | "L1" | "L2";
399
+ path: string;
400
+ body: string;
401
+ }>;
402
+ selected_stable_ids: string[];
403
+ diagnostics: Array<{
404
+ code: "missing_knowledge_metadata";
405
+ severity: "warn";
406
+ stable_id: string;
407
+ message: string;
408
+ }>;
409
+ };
410
+ declare function recall(projectRoot: string, input: RecallInput): Promise<RecallResult>;
411
+
412
+ /**
413
+ * O(1) in-memory increment for a named counter. Safe to call from any MCP
414
+ * tool handler; no I/O happens until the next `flushMetrics()` call.
415
+ *
416
+ * `delta` defaults to 1; callers can pass a positive integer (e.g. fetched
417
+ * N stable_ids in a single sections call) to fold N bumps into one.
418
+ */
419
+ declare function bumpCounter(projectRoot: string, name: string, delta?: number): void;
420
+ /**
421
+ * Snapshot the current counter accumulator and reset it. Returned map is a
422
+ * frozen copy; the live accumulator starts fresh from zero. Exposed so
423
+ * flushMetrics + tests + a future fab_metrics manual-flush CLI hook can all
424
+ * use the same primitive without racing.
425
+ */
426
+ declare function drainCounters(projectRoot: string): Record<string, number>;
427
+ /**
428
+ * Drain the current accumulator and append one JSONL row to
429
+ * `.fabric/metrics.jsonl`. Returns the appended row (or `null` when the
430
+ * accumulator was empty — no spurious zero rows). fs failures degrade
431
+ * silently; the next flush will carry the union of the failed-write
432
+ * interval + the current one.
433
+ */
434
+ declare function flushMetrics(projectRoot: string, options?: {
435
+ windowMs?: number;
436
+ now?: Date;
437
+ }): Promise<MetricsRow | null>;
438
+ /**
439
+ * Start the background flush timer for a project root. Idempotent — calling
440
+ * twice on the same root replaces the prior interval. Returns a stop handle
441
+ * the caller can invoke at shutdown to flush + clear the timer.
442
+ *
443
+ * The flush is fire-and-forget (no await on the setInterval callback) so
444
+ * the timer cadence stays accurate even when fs is slow.
445
+ */
446
+ declare function startMetricsFlush(projectRoot: string, options?: {
447
+ intervalMs?: number;
448
+ }): () => Promise<void>;
449
+ /**
450
+ * Cancel the background flush timer for a project root if one is running.
451
+ * Does NOT drain the accumulator — callers that want a final flush should
452
+ * await flushMetrics(projectRoot) afterward.
453
+ */
454
+ declare function stopMetricsFlush(projectRoot: string): void;
455
+ /**
456
+ * Read accumulated metrics rows from `.fabric/metrics.jsonl`. Missing file
457
+ * returns []. Malformed rows are dropped silently (the sidecar is best-
458
+ * effort observability; a corrupt row never blocks a reader).
459
+ *
460
+ * Exposed for the NEW-34 `fab metrics` CLI dashboard + future doctor lints
461
+ * (e.g. cite-goodhart pattern replay) that need counter trends without
462
+ * walking events.jsonl.
463
+ */
464
+ declare function readMetrics(projectRoot: string): Promise<MetricsRow[]>;
465
+ type MetricsRow = {
466
+ timestamp: string;
467
+ window: string;
468
+ counters: Record<string, number>;
469
+ };
470
+ /**
471
+ * Canonical metric counter names used by the high-frequency emitters that
472
+ * left events.jsonl as part of the rc.37 Wave B clean-slate. Centralized
473
+ * here so the B5 hard-gate (`metric_event_in_jsonl`) can grep for these
474
+ * exact strings and fail when one accidentally re-appears in the audit
475
+ * ledger emit path.
476
+ */
477
+ declare const METRIC_COUNTER_NAMES: {
478
+ readonly knowledge_consumed: "knowledge_consumed";
479
+ readonly edit_intent_checked: "edit_intent_checked";
480
+ readonly knowledge_context_planned: "knowledge_context_planned";
481
+ readonly knowledge_sections_fetched: "knowledge_sections_fetched";
482
+ };
483
+ type MetricCounterName = (typeof METRIC_COUNTER_NAMES)[keyof typeof METRIC_COUNTER_NAMES];
484
+
485
+ /**
486
+ * Start the background rotation timer for a project root. Idempotent —
487
+ * calling twice on the same root replaces the prior interval. Returns a
488
+ * stop handle the caller can invoke at shutdown.
489
+ *
490
+ * The tick fires fire-and-forget (no await on the setInterval callback) so
491
+ * the cadence stays accurate even when fs is slow. The first tick fires
492
+ * AFTER one full interval — startup-time rotation should run separately
493
+ * if desired (most callers don't, because rotation work blocks the
494
+ * connect path).
495
+ */
496
+ declare function startRotationTick(projectRoot: string, options?: {
497
+ intervalMs?: number;
498
+ }): () => void;
499
+ /**
500
+ * Cancel the background rotation timer for a project root if one is
501
+ * running. Does NOT trigger a final rotation — callers that want a final
502
+ * rotation should call rotateEventLedgerIfNeeded(projectRoot) directly.
503
+ */
504
+ declare function stopRotationTick(projectRoot: string): void;
505
+
368
506
  /**
369
507
  * Shared constants used across the server package.
370
508
  */
@@ -466,23 +604,6 @@ interface ReconcileKnowledgeOptions {
466
604
  */
467
605
  declare function reconcileKnowledge(projectRoot: string, opts?: ReconcileKnowledgeOptions): Promise<KnowledgeSyncReport>;
468
606
 
469
- declare class ServeLockHeldError extends IOFabricError {
470
- readonly code = "SERVE_LOCK_HELD";
471
- readonly httpStatus = 423;
472
- }
473
- interface LockState {
474
- pid: number;
475
- acquiredAt: number;
476
- host?: string;
477
- }
478
- interface AcquireOptions {
479
- force?: boolean;
480
- }
481
- declare function acquireLock(projectRoot: string, opts?: AcquireOptions): void;
482
- declare function releaseLock(projectRoot: string): void;
483
- declare function readLockState(projectRoot: string): LockState | null;
484
- declare function checkLockOrThrow(projectRoot: string, opts?: AcquireOptions): void;
485
-
486
607
  /**
487
608
  * Returns an info-level startup message when CLAUDE.md or AGENTS.md exist at
488
609
  * the project root, or null when neither is present.
@@ -517,12 +638,5 @@ interface ShutdownHandlerDeps {
517
638
  * `invoked` flag, so per-signal dedup is isolated.
518
639
  */
519
640
  declare function createShutdownHandler(deps: ShutdownHandlerDeps): () => void;
520
- declare function startHttpServer(options: {
521
- port: number;
522
- projectRoot: string;
523
- host?: string;
524
- authToken?: string;
525
- allowLoopbackNoAuth?: boolean;
526
- }): Promise<Server>;
527
641
 
528
- export { AGENTS_MD_RESOURCE_URI, type AcquireOptions, type ArchiveHistoryEntry, type ArchiveHistoryReport, type CiteCoverageReport, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, type InFlightTracker, KnowledgeIdAllocator, type KnowledgeMetaBuildResult, type KnowledgeMetaBuildSource, type KnowledgeSyncLedgerEvent, type KnowledgeSyncOptions, type KnowledgeSyncReport, LEDGER_PATH, LEGACY_LEDGER_PATH, type LedgerEvent, type LockState, type PlanContextInput, type PlanContextResult, type ReconcileKnowledgeOptions, type RequirementProfile, type SelectionTokenState, ServeLockHeldError, type ShutdownHandlerDeps, type StructuredWarning, type WriteKnowledgeMetaOptions, acquireLock, appendEventLedgerEvent, buildKnowledgeMeta, checkLockOrThrow, computeKnowledgeBasedAgentsMeta, computeKnowledgeTestIndex, createFabricServer, createInFlightTracker, createShutdownHandler, deriveKnowledgeMetaLayer, deriveKnowledgeMetaTopologyType, enrichDescriptions, ensureKnowledgeFresh, extractKnowledge, flushAndSyncEventLedger, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, isSameKnowledgeTestIndex, loadKbIdTypeMap, planContext, readLockState, readSelectionToken, reconcileKnowledge, releaseLock, reviewKnowledge, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorFix, runDoctorReport, stableStringify, startHttpServer, startStdioServer, writeKnowledgeMeta };
642
+ export { AGENTS_MD_RESOURCE_URI, type ArchiveHistoryEntry, type ArchiveHistoryReport, type CiteCoverageReport, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type EnrichDescriptionsCandidate, type EnrichDescriptionsMode, type EnrichDescriptionsReport, type HistoryAllReport, type HistoryDayRow, type InFlightTracker, KnowledgeIdAllocator, type KnowledgeMetaBuildResult, type KnowledgeMetaBuildSource, type KnowledgeSyncLedgerEvent, type KnowledgeSyncOptions, type KnowledgeSyncReport, LEDGER_PATH, LEGACY_LEDGER_PATH, type LedgerEvent, METRICS_LEDGER_PATH, METRIC_COUNTER_NAMES, type MetricCounterName, type MetricsRow, type PlanContextInput, type PlanContextResult, type RecallInput, type RecallResult, type ReconcileKnowledgeOptions, type RequirementProfile, type SelectionTokenState, type ShutdownHandlerDeps, type StructuredWarning, type WriteKnowledgeMetaOptions, appendEventLedgerEvent, buildKnowledgeMeta, bumpCounter, computeKnowledgeBasedAgentsMeta, computeKnowledgeTestIndex, createFabricServer, createInFlightTracker, createShutdownHandler, deriveKnowledgeMetaLayer, deriveKnowledgeMetaTopologyType, drainCounters, enrichDescriptions, ensureKnowledgeFresh, extractKnowledge, flushAndSyncEventLedger, flushMetrics, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, getMetricsLedgerPath, isSameKnowledgeTestIndex, loadKbIdTypeMap, planContext, readMetrics, readSelectionToken, recall, reconcileKnowledge, reviewKnowledge, runDoctorApplyLint, runDoctorArchiveHistory, runDoctorCiteCoverage, runDoctorFix, runDoctorHistoryAll, runDoctorReport, stableStringify, startMetricsFlush, startRotationTick, startStdioServer, stopMetricsFlush, stopRotationTick, writeKnowledgeMeta };