@fenglimg/fabric-server 2.0.0-rc.25 → 2.0.0-rc.27

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.
@@ -14,7 +14,7 @@ import {
14
14
  readEventLedger,
15
15
  runDoctorReport,
16
16
  sha256
17
- } from "./chunk-HAXROPQM.js";
17
+ } from "./chunk-NZSGNQKE.js";
18
18
 
19
19
  // src/http.ts
20
20
  import { randomUUID as randomUUID2 } from "crypto";
package/dist/index.d.ts CHANGED
@@ -38,6 +38,7 @@ type DoctorIssue = {
38
38
  name: string;
39
39
  message: string;
40
40
  path?: string;
41
+ actionHint?: string;
41
42
  };
42
43
  type DoctorSummary = {
43
44
  target: string;
@@ -424,8 +425,14 @@ interface ReconcileKnowledgeOptions {
424
425
  * v2.0.0-rc.23 TASK-005 (a-B): `auto-heal-description` added so plan_context
425
426
  * can drive a full reconcile when it detects nodes with `description === undefined`
426
427
  * (legacy meta drift the revision-hash gate cannot detect).
428
+ *
429
+ * v2.0.0-rc.27 TASK-001 (§2.9 root): `post-approve` / `post-modify` added so
430
+ * `fab_review` approve/modify-layer-flip can drive an immediate meta rebuild
431
+ * — without this the new entry's `nodes[id]` stays empty until the next
432
+ * plan_context call's auto-heal, which leaves the entry undiscoverable in
433
+ * the description_index window between approve and the next hint call.
427
434
  */
428
- trigger?: "startup" | "doctor" | "manual" | "auto-heal-description";
435
+ trigger?: "startup" | "doctor" | "manual" | "auto-heal-description" | "post-approve" | "post-modify";
429
436
  }
430
437
  /**
431
438
  * Full scan + rewrites agents.meta.json with ground-truth disk state + emits
package/dist/index.js CHANGED
@@ -37,7 +37,7 @@ import {
37
37
  sha256,
38
38
  stableStringify,
39
39
  writeKnowledgeMeta
40
- } from "./chunk-HAXROPQM.js";
40
+ } from "./chunk-NZSGNQKE.js";
41
41
 
42
42
  // src/index.ts
43
43
  import { existsSync as existsSync4 } from "fs";
@@ -250,7 +250,24 @@ async function extractKnowledge(projectRoot, input) {
250
250
  const existing = await readFile(absolutePath, "utf8");
251
251
  const existingKey = readFrontmatterKey(existing, "x-fabric-idempotency-key");
252
252
  if (existingKey === idempotencyKey) {
253
- const augmented = mergeEvidenceNotes(existing, summary, input.recent_paths);
253
+ const fresh2 = renderFreshEntry({
254
+ type: input.type,
255
+ sourceSessions,
256
+ idempotencyKey,
257
+ summary,
258
+ recentPaths: input.recent_paths,
259
+ layer,
260
+ proposedReason: input.proposed_reason,
261
+ sessionContext: input.session_context,
262
+ relevanceScope,
263
+ relevancePaths,
264
+ intentClues: input.intent_clues,
265
+ techStack: input.tech_stack,
266
+ impact: input.impact,
267
+ mustReadIf: input.must_read_if,
268
+ onboardSlot: input.onboard_slot
269
+ });
270
+ const augmented = mergeEvidenceNotes(existing, fresh2);
254
271
  await atomicWriteText(absolutePath, augmented);
255
272
  await emitEventBestEffort(projectRoot, {
256
273
  event_type: "knowledge_proposed",
@@ -402,62 +419,18 @@ function renderEvidenceBlock(summary, recentPaths) {
402
419
  `- ${summary.trim()}`
403
420
  ].join("\n");
404
421
  }
405
- function mergeEvidenceNotes(existing, newSummary, newRecentPaths) {
406
- const beforeMatch = /^([\s\S]*?)(\n## Evidence(?:\s*\(call \d+\))?\s*\n)/u.exec(
407
- existing.endsWith("\n") ? existing : `${existing}
408
- `
409
- );
410
- if (beforeMatch === null) {
411
- const trimmed = existing.endsWith("\n") ? existing : `${existing}
412
- `;
413
- return `${trimmed}
414
- ## Evidence
415
-
416
- ${renderEvidenceBlock(newSummary, newRecentPaths)}
422
+ function mergeEvidenceNotes(existing, fresh) {
423
+ const freshSplit = splitAtEvidence(fresh);
424
+ if (freshSplit === null) {
425
+ return fresh.endsWith("\n") ? fresh : `${fresh}
417
426
  `;
418
427
  }
419
- const head = beforeMatch[1] ?? "";
420
- const existingNotes = [];
421
- const existingPaths = [];
422
- const evidenceBlockRe = /\n## Evidence(?:\s*\(call \d+\))?\s*\n([\s\S]*?)(?=\n## |$)/gu;
423
- let m;
424
- while ((m = evidenceBlockRe.exec(`${existing}
425
- `)) !== null) {
426
- const block = m[1] ?? "";
427
- const pathSection = /Recent paths:\s*\n([\s\S]*?)(?:\n\s*Notes:|$)/u.exec(block);
428
- if (pathSection !== null) {
429
- for (const rawLine of (pathSection[1] ?? "").split(/\r?\n/u)) {
430
- const t = rawLine.trim();
431
- if (t.startsWith("- ")) {
432
- existingPaths.push(t.slice(2).trim());
433
- }
434
- }
435
- }
436
- const notesSection = /Notes:\s*\n([\s\S]*?)$/u.exec(block);
437
- const noteBody = (notesSection !== null ? notesSection[1] : block) ?? "";
438
- const bulletLines = [];
439
- let prose = [];
440
- for (const rawLine of noteBody.split(/\r?\n/u)) {
441
- const t = rawLine.trim();
442
- if (t.length === 0) continue;
443
- if (t.startsWith("- ")) {
444
- if (prose.length > 0) {
445
- existingNotes.push(prose.join(" ").trim());
446
- prose = [];
447
- }
448
- bulletLines.push(t.slice(2).trim());
449
- } else {
450
- prose.push(t);
451
- }
452
- }
453
- if (prose.length > 0) existingNotes.push(prose.join(" ").trim());
454
- for (const n of bulletLines) existingNotes.push(n);
455
- }
428
+ const freshHead = freshSplit.head;
429
+ const oldEvidence = collectEvidenceItems(existing);
430
+ const freshEvidence = collectEvidenceItems(fresh);
456
431
  const mergedNotes = [];
457
432
  const seenNotes = /* @__PURE__ */ new Set();
458
- const incomingNote = newSummary.trim();
459
- const candidates = [...existingNotes, incomingNote];
460
- for (const note of candidates) {
433
+ for (const note of [...oldEvidence.notes, ...freshEvidence.notes]) {
461
434
  const key = note.replace(/\s+/gu, " ").trim();
462
435
  if (key.length === 0) continue;
463
436
  if (seenNotes.has(key)) continue;
@@ -466,7 +439,7 @@ ${renderEvidenceBlock(newSummary, newRecentPaths)}
466
439
  }
467
440
  const mergedPaths = [];
468
441
  const seenPaths = /* @__PURE__ */ new Set();
469
- for (const p of [...existingPaths, ...newRecentPaths]) {
442
+ for (const p of [...oldEvidence.paths, ...freshEvidence.paths]) {
470
443
  const key = p.trim();
471
444
  if (key.length === 0) continue;
472
445
  if (seenPaths.has(key)) continue;
@@ -484,12 +457,58 @@ ${renderEvidenceBlock(newSummary, newRecentPaths)}
484
457
  "",
485
458
  noteLines
486
459
  ].join("\n");
487
- return `${head}
460
+ return `${freshHead}
488
461
  ## Evidence
489
462
 
490
463
  ${evidenceBody}
491
464
  `;
492
465
  }
466
+ function splitAtEvidence(content) {
467
+ const tail = content.endsWith("\n") ? content : `${content}
468
+ `;
469
+ const match = /^([\s\S]*?)(\n## Evidence(?:\s*\(call \d+\))?\s*\n)/u.exec(tail);
470
+ if (match === null) return null;
471
+ return { head: match[1] ?? "" };
472
+ }
473
+ function collectEvidenceItems(content) {
474
+ const notes = [];
475
+ const paths = [];
476
+ const evidenceBlockRe = /\n## Evidence(?:\s*\(call \d+\))?\s*\n([\s\S]*?)(?=\n## |$)/gu;
477
+ let m;
478
+ while ((m = evidenceBlockRe.exec(`${content}
479
+ `)) !== null) {
480
+ const block = m[1] ?? "";
481
+ const pathSection = /Recent paths:\s*\n([\s\S]*?)(?:\n\s*Notes:|$)/u.exec(block);
482
+ if (pathSection !== null) {
483
+ for (const rawLine of (pathSection[1] ?? "").split(/\r?\n/u)) {
484
+ const t = rawLine.trim();
485
+ if (t.startsWith("- ")) {
486
+ paths.push(t.slice(2).trim());
487
+ }
488
+ }
489
+ }
490
+ const notesSection = /Notes:\s*\n([\s\S]*?)$/u.exec(block);
491
+ const noteBody = (notesSection !== null ? notesSection[1] : block) ?? "";
492
+ const bulletLines = [];
493
+ let prose = [];
494
+ for (const rawLine of noteBody.split(/\r?\n/u)) {
495
+ const t = rawLine.trim();
496
+ if (t.length === 0) continue;
497
+ if (t.startsWith("- ")) {
498
+ if (prose.length > 0) {
499
+ notes.push(prose.join(" ").trim());
500
+ prose = [];
501
+ }
502
+ bulletLines.push(t.slice(2).trim());
503
+ } else {
504
+ prose.push(t);
505
+ }
506
+ }
507
+ if (prose.length > 0) notes.push(prose.join(" ").trim());
508
+ for (const n of bulletLines) notes.push(n);
509
+ }
510
+ return { notes, paths };
511
+ }
493
512
  function readFrontmatterKey(content, key) {
494
513
  const match = /^---\n([\s\S]*?)\n---/u.exec(content);
495
514
  if (match === null) {
@@ -567,7 +586,34 @@ import { minimatch } from "minimatch";
567
586
  import { deriveAgentsMetaLayer } from "@fenglimg/fabric-shared";
568
587
  var SELECTION_TOKEN_TTL_MS = 5 * 60 * 1e3;
569
588
  var selectionTokenCache = /* @__PURE__ */ new Map();
589
+ function assertPathInSandbox(rawPath) {
590
+ if (rawPath === "**" || rawPath === "*") return;
591
+ const normalized = rawPath.replaceAll("\\", "/");
592
+ if (normalized.startsWith("/")) {
593
+ throw new Error(
594
+ `plan_context: absolute paths are not allowed (got "${rawPath}"); pass a path relative to the project root`
595
+ );
596
+ }
597
+ if (normalized.startsWith("~/") || normalized === "~") {
598
+ throw new Error(
599
+ `plan_context: shell sigil "~" is not allowed (got "${rawPath}"); expand to a project-relative path before calling`
600
+ );
601
+ }
602
+ if (normalized.split("/").some((seg) => seg === "..")) {
603
+ throw new Error(
604
+ `plan_context: ".." traversal is not allowed (got "${rawPath}"); pass a path that resolves under the project root`
605
+ );
606
+ }
607
+ }
570
608
  async function planContext(projectRoot, input) {
609
+ for (const p of input.paths) {
610
+ assertPathInSandbox(p);
611
+ }
612
+ if (input.target_paths !== void 0) {
613
+ for (const p of input.target_paths) {
614
+ assertPathInSandbox(p);
615
+ }
616
+ }
571
617
  let metaResult = await loadActiveMetaOrStale(projectRoot, { caller: "planContext" });
572
618
  let meta = metaResult.meta;
573
619
  let firstSeenPreviousRevision = metaResult.previous_revision_hash;
@@ -1042,6 +1088,23 @@ function resolveSandboxedPath(projectRoot, candidate, options = {}) {
1042
1088
  }
1043
1089
  throw new Error(`path escapes knowledge root: ${candidate}`);
1044
1090
  }
1091
+ function extractBody(content) {
1092
+ const match = /^(?:\uFEFF)?---\r?\n[\s\S]*?\r?\n---\s*(?:\r?\n|$)/u.exec(content);
1093
+ if (match === null) {
1094
+ return content.trim();
1095
+ }
1096
+ return content.slice(match[0].length).trim();
1097
+ }
1098
+ function isVisibleByLifecycle(fm, filters) {
1099
+ if (fm.status === "rejected" && filters?.include_rejected !== true) {
1100
+ return false;
1101
+ }
1102
+ if (fm.status === "deferred" && filters?.include_deferred !== true) {
1103
+ if (fm.deferred_until === void 0) return false;
1104
+ if (fm.deferred_until > (/* @__PURE__ */ new Date()).toISOString()) return false;
1105
+ }
1106
+ return true;
1107
+ }
1045
1108
  async function listPending(projectRoot, filters) {
1046
1109
  const items = [];
1047
1110
  const typesToScan = filters?.type !== void 0 ? [filters.type] : PLURAL_TYPES;
@@ -1090,14 +1153,27 @@ async function listPending(projectRoot, filters) {
1090
1153
  continue;
1091
1154
  }
1092
1155
  }
1156
+ if (!isVisibleByLifecycle(fm, filters)) {
1157
+ continue;
1158
+ }
1093
1159
  const reportedPath = source.origin === "personal" ? `~/${relative2(resolvePersonalRoot2(), absolutePath)}` : relative2(projectRoot, absolutePath);
1094
1160
  items.push({
1095
1161
  pending_path: reportedPath,
1162
+ // v2.0.0-rc.27 TASK-001 (§2.12): absolute path companion for
1163
+ // personal entries so programmatic consumers (Read, fs.readFile)
1164
+ // don't need to shell-expand the `~` themselves.
1165
+ ...source.origin === "personal" ? { pending_path_absolute: absolutePath } : {},
1096
1166
  type,
1097
1167
  layer,
1098
1168
  maturity,
1099
1169
  origin: source.origin,
1100
- ...fm.tags !== void 0 && fm.tags.length > 0 ? { tags: fm.tags } : {}
1170
+ ...fm.tags !== void 0 && fm.tags.length > 0 ? { tags: fm.tags } : {},
1171
+ ...fm.status !== void 0 ? { status: fm.status } : {},
1172
+ ...fm.deferred_until !== void 0 ? { deferred_until: fm.deferred_until } : {},
1173
+ // v2.0.0-rc.27 TASK-006 (audit §2.23): full body when caller
1174
+ // opted in. Reviewer UI consumes this to scan for prompt-injection
1175
+ // payloads hidden under `## Evidence` body.
1176
+ ...filters?.include_body === true ? { body: extractBody(content) } : {}
1101
1177
  });
1102
1178
  }
1103
1179
  }
@@ -1189,6 +1265,10 @@ async function approveOne(projectRoot, pendingPath, allocator) {
1189
1265
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1190
1266
  reason: `approve:${slug}`
1191
1267
  });
1268
+ try {
1269
+ await reconcileKnowledge(projectRoot, { trigger: "post-approve" });
1270
+ } catch {
1271
+ }
1192
1272
  return { pending_path: pendingPath, stable_id: stableId };
1193
1273
  } catch (err) {
1194
1274
  if (writtenTarget && targetAbs !== void 0 && existsSync3(targetAbs)) {
@@ -1210,6 +1290,17 @@ async function approveOne(projectRoot, pendingPath, allocator) {
1210
1290
  async function rejectAll(projectRoot, pendingPaths, reason) {
1211
1291
  const rejected = [];
1212
1292
  for (const pendingPath of pendingPaths) {
1293
+ try {
1294
+ const sandboxed = resolveSandboxedPath(projectRoot, pendingPath, { allowPersonal: true });
1295
+ if (existsSync3(sandboxed.abs)) {
1296
+ const content = await readFile3(sandboxed.abs, "utf8");
1297
+ const merged = rewriteFrontmatterMerge(content, { status: "rejected" });
1298
+ if (merged !== content) {
1299
+ await atomicWriteText(sandboxed.abs, merged);
1300
+ }
1301
+ }
1302
+ } catch {
1303
+ }
1213
1304
  await emitEventBestEffort2(projectRoot, {
1214
1305
  event_type: "knowledge_rejected",
1215
1306
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1268,6 +1359,11 @@ function extractSlug(path) {
1268
1359
  return file.replace(/^K[PT]-(MOD|DEC|GLD|PIT|PRO)-\d+--/u, "");
1269
1360
  }
1270
1361
  async function modifyLayerFlip(projectRoot, target, content, fm, changes) {
1362
+ if (target.absPath.includes("/pending/")) {
1363
+ throw new Error(
1364
+ "layer-flip not allowed on pending entries; approve first, then modify the canonical entry's layer"
1365
+ );
1366
+ }
1271
1367
  const fromLayer = fm.layer ?? "team";
1272
1368
  const toLayer = changes.layer;
1273
1369
  const pluralType = fm.type ?? target.inferredType;
@@ -1334,6 +1430,10 @@ async function modifyLayerFlip(projectRoot, target, content, fm, changes) {
1334
1430
  });
1335
1431
  }
1336
1432
  const responsePath = toLayer === "team" ? relative2(projectRoot, toAbs) : `~/${relative2(resolvePersonalRoot2(), toAbs)}`;
1433
+ try {
1434
+ await reconcileKnowledge(projectRoot, { trigger: "post-modify" });
1435
+ } catch {
1436
+ }
1337
1437
  return {
1338
1438
  action: "modify",
1339
1439
  pending_path: responsePath,
@@ -1390,17 +1490,25 @@ async function searchEntries(projectRoot, query, filters) {
1390
1490
  continue;
1391
1491
  }
1392
1492
  }
1493
+ if (!isVisibleByLifecycle(fm, filters)) {
1494
+ continue;
1495
+ }
1496
+ const bodyForSearch = filters?.include_body === true ? extractBody(content) : "";
1393
1497
  const haystacks = [
1394
1498
  fm.title ?? "",
1395
1499
  fm.summary ?? "",
1396
1500
  ...fm.tags ?? [],
1397
- name
1501
+ name,
1502
+ bodyForSearch
1398
1503
  ].map((s) => s.toLowerCase());
1399
1504
  const matches = haystacks.some((h) => h.includes(lowerQuery));
1400
1505
  if (!matches) continue;
1401
1506
  const reportedPath = source.isPersonal ? `~/${relative2(resolvePersonalRoot2(), absolutePath)}` : relative2(projectRoot, absolutePath);
1402
1507
  items.push({
1403
1508
  pending_path: reportedPath,
1509
+ // v2.0.0-rc.27 TASK-001 (§2.12): absolute companion for personal
1510
+ // entries (mirrors listPending).
1511
+ ...source.isPersonal ? { pending_path_absolute: absolutePath } : {},
1404
1512
  type,
1405
1513
  layer,
1406
1514
  maturity,
@@ -1409,7 +1517,14 @@ async function searchEntries(projectRoot, query, filters) {
1409
1517
  ...source.isPending ? { origin: source.isPersonal ? "personal" : "team" } : {},
1410
1518
  ...fm.tags !== void 0 && fm.tags.length > 0 ? { tags: fm.tags } : {},
1411
1519
  ...fm.title !== void 0 ? { title: fm.title } : {},
1412
- ...fm.summary !== void 0 ? { summary: fm.summary } : {}
1520
+ ...fm.summary !== void 0 ? { summary: fm.summary } : {},
1521
+ ...fm.status !== void 0 ? { status: fm.status } : {},
1522
+ ...fm.deferred_until !== void 0 ? { deferred_until: fm.deferred_until } : {},
1523
+ // v2.0.0-rc.27 TASK-006 (audit §2.23): body emission when opted in.
1524
+ // Reuse the already-computed bodyForSearch to avoid a second pass
1525
+ // over the content (the search loop above extracted it iff
1526
+ // include_body=true).
1527
+ ...filters?.include_body === true ? { body: bodyForSearch } : {}
1413
1528
  });
1414
1529
  }
1415
1530
  }
@@ -1419,6 +1534,21 @@ async function searchEntries(projectRoot, query, filters) {
1419
1534
  async function deferAll(projectRoot, pendingPaths, until, reason) {
1420
1535
  const deferred = [];
1421
1536
  for (const pendingPath of pendingPaths) {
1537
+ try {
1538
+ const sandboxed = resolveSandboxedPath(projectRoot, pendingPath, { allowPersonal: true });
1539
+ if (existsSync3(sandboxed.abs)) {
1540
+ const content = await readFile3(sandboxed.abs, "utf8");
1541
+ const patch = {
1542
+ status: "deferred",
1543
+ ...until !== void 0 ? { deferred_until: until } : {}
1544
+ };
1545
+ const merged = rewriteFrontmatterMerge(content, patch);
1546
+ if (merged !== content) {
1547
+ await atomicWriteText(sandboxed.abs, merged);
1548
+ }
1549
+ }
1550
+ } catch {
1551
+ }
1422
1552
  await emitEventBestEffort2(projectRoot, {
1423
1553
  event_type: "knowledge_deferred",
1424
1554
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1488,6 +1618,14 @@ function parseFrontmatter(content) {
1488
1618
  case "relevance_paths":
1489
1619
  out.relevance_paths = parseFlowArray(value);
1490
1620
  break;
1621
+ case "status":
1622
+ if (value === "active" || value === "rejected" || value === "deferred") {
1623
+ out.status = value;
1624
+ }
1625
+ break;
1626
+ case "deferred_until":
1627
+ out.deferred_until = stripQuotes(value);
1628
+ break;
1491
1629
  default:
1492
1630
  break;
1493
1631
  }
@@ -1549,6 +1687,8 @@ ${content}`;
1549
1687
  if (patch.tags !== void 0) updates.tags = `tags: [${patch.tags.join(", ")}]`;
1550
1688
  if (patch.relevance_scope !== void 0) updates.relevance_scope = `relevance_scope: ${patch.relevance_scope}`;
1551
1689
  if (patch.relevance_paths !== void 0) updates.relevance_paths = `relevance_paths: [${patch.relevance_paths.join(", ")}]`;
1690
+ if (patch.status !== void 0) updates.status = `status: ${patch.status}`;
1691
+ if (patch.deferred_until !== void 0) updates.deferred_until = `deferred_until: ${quoteIfNeeded(patch.deferred_until)}`;
1552
1692
  const lines = block.split(/\r?\n/u);
1553
1693
  const seen = /* @__PURE__ */ new Set();
1554
1694
  const newLines = [];
@@ -1582,6 +1722,8 @@ function appendPatchLines(lines, patch) {
1582
1722
  if (patch.tags !== void 0) lines.push(`tags: [${patch.tags.join(", ")}]`);
1583
1723
  if (patch.relevance_scope !== void 0) lines.push(`relevance_scope: ${patch.relevance_scope}`);
1584
1724
  if (patch.relevance_paths !== void 0) lines.push(`relevance_paths: [${patch.relevance_paths.join(", ")}]`);
1725
+ if (patch.status !== void 0) lines.push(`status: ${patch.status}`);
1726
+ if (patch.deferred_until !== void 0) lines.push(`deferred_until: ${quoteIfNeeded(patch.deferred_until)}`);
1585
1727
  }
1586
1728
  function quoteIfNeeded(value) {
1587
1729
  if (/[\n\r]/u.test(value)) {
@@ -1661,7 +1803,7 @@ var PRIORITY_ORDER = {
1661
1803
  medium: 1,
1662
1804
  low: 2
1663
1805
  };
1664
- function extractBody(content) {
1806
+ function extractBody2(content) {
1665
1807
  const match = /^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)/u.exec(content);
1666
1808
  if (match === null) {
1667
1809
  return content.replace(/^\uFEFF/u, "");
@@ -1681,7 +1823,7 @@ async function getKnowledgeSections(projectRoot, input) {
1681
1823
  const rules = [];
1682
1824
  for (const rule of selectedRules) {
1683
1825
  const content = await readFile4(resolveRuleSourcePath(projectRoot, rule.path), "utf8");
1684
- const body = extractBody(content);
1826
+ const body = extractBody2(content);
1685
1827
  const description = rule.node.description;
1686
1828
  if (description !== void 0 && description.knowledge_type === void 0 && description.knowledge_layer === void 0) {
1687
1829
  diagnostics.push({
@@ -1894,7 +2036,7 @@ function formatPreexistingRootMessage(projectRoot) {
1894
2036
  function createFabricServer(tracker) {
1895
2037
  const server = new McpServer({
1896
2038
  name: "fabric-knowledge-server",
1897
- version: "2.0.0-rc.25"
2039
+ version: "2.0.0-rc.27"
1898
2040
  });
1899
2041
  registerPlanContext(server, tracker);
1900
2042
  registerKnowledgeSections(server, tracker);
@@ -2002,7 +2144,7 @@ function createShutdownHandler(deps) {
2002
2144
  };
2003
2145
  }
2004
2146
  async function startHttpServer(options) {
2005
- const { createFabricHttpApp } = await import("./http-QBGLHCHA.js");
2147
+ const { createFabricHttpApp } = await import("./http-3WADEK3O.js");
2006
2148
  const { port, projectRoot, host = "127.0.0.1", authToken } = options;
2007
2149
  const app = createFabricHttpApp({ projectRoot, host, authToken });
2008
2150
  return await new Promise((resolveServer, rejectServer) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fenglimg/fabric-server",
3
- "version": "2.0.0-rc.25",
3
+ "version": "2.0.0-rc.27",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -13,7 +13,7 @@
13
13
  "express": "^5.2.1",
14
14
  "minimatch": "^10.0.1",
15
15
  "zod": "^3.25.0",
16
- "@fenglimg/fabric-shared": "2.0.0-rc.25"
16
+ "@fenglimg/fabric-shared": "2.0.0-rc.27"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/express": "^5.0.6",