@fenglimg/fabric-server 1.2.0 → 1.3.0
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-U3IQH5H6.js → chunk-DQ7RCYKB.js} +180 -14
- package/dist/{http-EQBDM4C7.js → http-YPXWM5QS.js} +127 -15
- package/dist/index.d.ts +7 -1
- package/dist/index.js +148 -75
- package/dist/static/assets/index-Btq99IfR.js +10 -0
- package/dist/static/index.html +1 -1
- package/package.json +3 -3
- package/dist/static/assets/index-_hQ_P7Zz.js +0 -5
package/dist/index.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
|
+
AGENTS_MD_RESOURCE_URI,
|
|
2
3
|
FABRIC_DIR,
|
|
3
4
|
appendEditIntentAuditEvents,
|
|
4
5
|
appendGetRulesAuditEvent,
|
|
5
6
|
appendLedgerEntry,
|
|
6
7
|
atomicWriteText,
|
|
8
|
+
contextCache,
|
|
7
9
|
readAgentsMeta,
|
|
8
10
|
readHumanLock,
|
|
9
11
|
resolveProjectRoot,
|
|
10
12
|
runDoctorAuditReport,
|
|
11
13
|
runDoctorReport,
|
|
12
14
|
sha256
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-DQ7RCYKB.js";
|
|
14
16
|
|
|
15
17
|
// src/index.ts
|
|
16
|
-
import {
|
|
18
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
19
|
+
import { join as join3, resolve } from "path";
|
|
17
20
|
import { fileURLToPath } from "url";
|
|
18
21
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
22
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
23
|
|
|
21
24
|
// src/tools/append-intent.ts
|
|
22
25
|
import { aiLedgerEntrySchema } from "@fenglimg/fabric-shared";
|
|
26
|
+
import { z } from "zod";
|
|
23
27
|
|
|
24
28
|
// src/services/append-intent.ts
|
|
25
29
|
async function appendIntent(projectRoot, input) {
|
|
@@ -29,19 +33,22 @@ async function appendIntent(projectRoot, input) {
|
|
|
29
33
|
ts,
|
|
30
34
|
source: "ai"
|
|
31
35
|
});
|
|
36
|
+
let compliance;
|
|
32
37
|
try {
|
|
33
|
-
await appendEditIntentAuditEvents(projectRoot, {
|
|
38
|
+
const auditResult = await appendEditIntentAuditEvents(projectRoot, {
|
|
34
39
|
affected_paths: entry.affected_paths,
|
|
35
40
|
intent: entry.intent,
|
|
36
41
|
ledger_entry_id: entry.id,
|
|
37
42
|
ts
|
|
38
43
|
});
|
|
44
|
+
compliance = auditResult.compliance;
|
|
39
45
|
} catch {
|
|
40
46
|
}
|
|
41
47
|
return {
|
|
42
48
|
success: true,
|
|
43
49
|
timestamp: ts,
|
|
44
|
-
entry
|
|
50
|
+
entry,
|
|
51
|
+
compliance
|
|
45
52
|
};
|
|
46
53
|
}
|
|
47
54
|
|
|
@@ -53,31 +60,38 @@ var inputSchema = {
|
|
|
53
60
|
ts: true
|
|
54
61
|
})
|
|
55
62
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
}
|
|
63
|
+
var outputSchema = z.object({
|
|
64
|
+
success: z.boolean(),
|
|
65
|
+
timestamp: z.number(),
|
|
66
|
+
entry: z.record(z.unknown()),
|
|
67
|
+
compliance: z.object({
|
|
68
|
+
compliant: z.boolean(),
|
|
69
|
+
matched_get_rules_ts: z.string().nullable(),
|
|
70
|
+
window_ms: z.number()
|
|
71
|
+
}).optional()
|
|
72
|
+
});
|
|
66
73
|
function registerAppendIntent(server) {
|
|
67
|
-
server.
|
|
74
|
+
server.registerTool(
|
|
68
75
|
"fab_append_intent",
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
{
|
|
77
|
+
description: "Call after a completed task to append an intent ledger entry for Fabric.",
|
|
78
|
+
inputSchema,
|
|
79
|
+
outputSchema,
|
|
80
|
+
annotations: { readOnlyHint: false }
|
|
81
|
+
},
|
|
71
82
|
async ({ entry }) => {
|
|
72
83
|
const projectRoot = resolveProjectRoot();
|
|
73
84
|
const result = await appendIntent(projectRoot, { entry });
|
|
74
|
-
return
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
87
|
+
structuredContent: result
|
|
88
|
+
};
|
|
75
89
|
}
|
|
76
90
|
);
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
// src/tools/get-rules.ts
|
|
80
|
-
import { z } from "zod";
|
|
94
|
+
import { z as z2 } from "zod";
|
|
81
95
|
|
|
82
96
|
// src/services/get-rules.ts
|
|
83
97
|
import { readFile } from "fs/promises";
|
|
@@ -107,17 +121,23 @@ async function getRules(projectRoot, input) {
|
|
|
107
121
|
return result;
|
|
108
122
|
}
|
|
109
123
|
async function loadGetRulesContext(projectRoot) {
|
|
110
|
-
const
|
|
124
|
+
const cached = contextCache.get("context", projectRoot);
|
|
125
|
+
if (cached !== void 0) {
|
|
126
|
+
return cached;
|
|
127
|
+
}
|
|
128
|
+
const meta = await readAgentsMeta(projectRoot);
|
|
111
129
|
const l0Content = await readFile(join(projectRoot, "AGENTS.md"), "utf8");
|
|
112
130
|
const humanLockedNearby = (await readHumanLock(projectRoot)).map((entry) => ({
|
|
113
131
|
file: entry.file,
|
|
114
132
|
excerpt: JSON.stringify(entry)
|
|
115
133
|
}));
|
|
116
|
-
|
|
134
|
+
const context = {
|
|
117
135
|
meta,
|
|
118
136
|
l0Content,
|
|
119
137
|
humanLockedNearby
|
|
120
138
|
};
|
|
139
|
+
contextCache.set("context", projectRoot, context);
|
|
140
|
+
return context;
|
|
121
141
|
}
|
|
122
142
|
async function resolveRulesForPath(projectRoot, context, path, options = {}) {
|
|
123
143
|
const loadedRules = await loadRulesForPath(projectRoot, context.meta, path);
|
|
@@ -189,34 +209,43 @@ function dedupeEntriesByPath(entries) {
|
|
|
189
209
|
|
|
190
210
|
// src/tools/get-rules.ts
|
|
191
211
|
var inputSchema2 = {
|
|
192
|
-
path:
|
|
193
|
-
client_hash:
|
|
212
|
+
path: z2.string().describe("Target file path to query rules for"),
|
|
213
|
+
client_hash: z2.string().optional().describe("Revision hash from prior fab_get_rules response; enables stale detection")
|
|
194
214
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
var rulesEntrySchema = z2.object({ path: z2.string(), content: z2.string() });
|
|
216
|
+
var humanLockedSchema = z2.object({ file: z2.string(), excerpt: z2.string() });
|
|
217
|
+
var outputSchema2 = z2.object({
|
|
218
|
+
revision_hash: z2.string(),
|
|
219
|
+
stale: z2.boolean(),
|
|
220
|
+
rules: z2.object({
|
|
221
|
+
L0: z2.string(),
|
|
222
|
+
L1: z2.array(rulesEntrySchema),
|
|
223
|
+
L2: z2.array(rulesEntrySchema),
|
|
224
|
+
human_locked_nearby: z2.array(humanLockedSchema)
|
|
225
|
+
})
|
|
226
|
+
});
|
|
205
227
|
function registerGetRules(server) {
|
|
206
|
-
server.
|
|
228
|
+
server.registerTool(
|
|
207
229
|
"fab_get_rules",
|
|
208
|
-
|
|
209
|
-
|
|
230
|
+
{
|
|
231
|
+
description: "Call before modifying any file to retrieve Fabric rules for a target path.",
|
|
232
|
+
inputSchema: inputSchema2,
|
|
233
|
+
outputSchema: outputSchema2,
|
|
234
|
+
annotations: { readOnlyHint: true }
|
|
235
|
+
},
|
|
210
236
|
async ({ path, client_hash }) => {
|
|
211
237
|
const projectRoot = resolveProjectRoot();
|
|
212
238
|
const result = await getRules(projectRoot, { path, client_hash });
|
|
213
|
-
return
|
|
239
|
+
return {
|
|
240
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
241
|
+
structuredContent: result
|
|
242
|
+
};
|
|
214
243
|
}
|
|
215
244
|
);
|
|
216
245
|
}
|
|
217
246
|
|
|
218
247
|
// src/tools/plan-context.ts
|
|
219
|
-
import { z as
|
|
248
|
+
import { z as z3 } from "zod";
|
|
220
249
|
|
|
221
250
|
// src/services/plan-context.ts
|
|
222
251
|
async function planContext(projectRoot, input) {
|
|
@@ -249,41 +278,56 @@ function dedupePaths(paths) {
|
|
|
249
278
|
|
|
250
279
|
// src/tools/plan-context.ts
|
|
251
280
|
var inputSchema3 = {
|
|
252
|
-
paths:
|
|
253
|
-
client_hash:
|
|
281
|
+
paths: z3.array(z3.string()).min(2).describe("Candidate file paths to query rules for during planning or architecture review"),
|
|
282
|
+
client_hash: z3.string().optional().describe("Revision hash from a prior fab_plan_context response; enables stale detection")
|
|
254
283
|
};
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
284
|
+
var rulesEntrySchema2 = z3.object({ path: z3.string(), content: z3.string() });
|
|
285
|
+
var humanLockedSchema2 = z3.object({ file: z3.string(), excerpt: z3.string() });
|
|
286
|
+
var rulesPayloadSchema = z3.object({
|
|
287
|
+
L0: z3.string(),
|
|
288
|
+
L1: z3.array(rulesEntrySchema2),
|
|
289
|
+
L2: z3.array(rulesEntrySchema2),
|
|
290
|
+
human_locked_nearby: z3.array(humanLockedSchema2)
|
|
291
|
+
});
|
|
292
|
+
var outputSchema3 = z3.object({
|
|
293
|
+
revision_hash: z3.string(),
|
|
294
|
+
stale: z3.boolean(),
|
|
295
|
+
entries: z3.array(
|
|
296
|
+
z3.object({
|
|
297
|
+
path: z3.string(),
|
|
298
|
+
rules: rulesPayloadSchema
|
|
299
|
+
})
|
|
300
|
+
)
|
|
301
|
+
});
|
|
265
302
|
function registerPlanContext(server) {
|
|
266
|
-
server.
|
|
303
|
+
server.registerTool(
|
|
267
304
|
"fab_plan_context",
|
|
268
|
-
|
|
269
|
-
|
|
305
|
+
{
|
|
306
|
+
description: "Use during plan or architecture phases to batch-query Fabric rules for multiple candidate paths in one round-trip. Use fab_get_rules for single-file queries; use fab_plan_context for 2+ files.",
|
|
307
|
+
inputSchema: inputSchema3,
|
|
308
|
+
outputSchema: outputSchema3,
|
|
309
|
+
annotations: { readOnlyHint: true }
|
|
310
|
+
},
|
|
270
311
|
async ({ paths, client_hash }) => {
|
|
271
312
|
const projectRoot = resolveProjectRoot();
|
|
272
313
|
const result = await planContext(projectRoot, { paths, client_hash });
|
|
273
|
-
return
|
|
314
|
+
return {
|
|
315
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
316
|
+
structuredContent: result
|
|
317
|
+
};
|
|
274
318
|
}
|
|
275
319
|
);
|
|
276
320
|
}
|
|
277
321
|
|
|
278
322
|
// src/tools/update-registry.ts
|
|
279
|
-
import { z as
|
|
323
|
+
import { z as z4 } from "zod";
|
|
280
324
|
|
|
281
325
|
// src/services/update-registry.ts
|
|
282
326
|
import { agentsMetaNodeSchema } from "@fenglimg/fabric-shared";
|
|
283
327
|
import { join as join2 } from "path";
|
|
284
328
|
async function updateRegistry(projectRoot, input) {
|
|
285
329
|
const metaPath = join2(projectRoot, FABRIC_DIR, "agents.meta.json");
|
|
286
|
-
const currentMeta = readAgentsMeta(projectRoot);
|
|
330
|
+
const currentMeta = await readAgentsMeta(projectRoot);
|
|
287
331
|
const nextMeta = applyRegistryOperation(currentMeta, input.op, input.node_id, input.data);
|
|
288
332
|
const newRevision = computeRevision(nextMeta);
|
|
289
333
|
await atomicWriteText(
|
|
@@ -298,6 +342,7 @@ async function updateRegistry(projectRoot, input) {
|
|
|
298
342
|
)}
|
|
299
343
|
`
|
|
300
344
|
);
|
|
345
|
+
contextCache.invalidate("meta_write", projectRoot);
|
|
301
346
|
return {
|
|
302
347
|
revision_hash: newRevision,
|
|
303
348
|
success: true
|
|
@@ -345,29 +390,35 @@ function applyRegistryOperation(meta, op, nodeId, data) {
|
|
|
345
390
|
|
|
346
391
|
// src/tools/update-registry.ts
|
|
347
392
|
var inputSchema4 = {
|
|
348
|
-
op:
|
|
349
|
-
node_id:
|
|
350
|
-
data:
|
|
393
|
+
op: z4.enum(["add-node", "remove-node", "update-node"]),
|
|
394
|
+
node_id: z4.string(),
|
|
395
|
+
data: z4.object({
|
|
396
|
+
file: z4.string(),
|
|
397
|
+
scope_glob: z4.string(),
|
|
398
|
+
deps: z4.array(z4.string()).optional(),
|
|
399
|
+
priority: z4.number().optional()
|
|
400
|
+
}).optional()
|
|
351
401
|
};
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
type: "text",
|
|
357
|
-
text: JSON.stringify(payload)
|
|
358
|
-
}
|
|
359
|
-
]
|
|
360
|
-
};
|
|
361
|
-
}
|
|
402
|
+
var outputSchema4 = z4.object({
|
|
403
|
+
success: z4.boolean(),
|
|
404
|
+
revision_hash: z4.string()
|
|
405
|
+
});
|
|
362
406
|
function registerUpdateRegistry(server) {
|
|
363
|
-
server.
|
|
407
|
+
server.registerTool(
|
|
364
408
|
"fab_update_registry",
|
|
365
|
-
|
|
366
|
-
|
|
409
|
+
{
|
|
410
|
+
description: "Call to add, remove, or update Fabric registry nodes. Use instead of editing .fabric/agents.meta.json directly.",
|
|
411
|
+
inputSchema: inputSchema4,
|
|
412
|
+
outputSchema: outputSchema4,
|
|
413
|
+
annotations: { destructiveHint: true }
|
|
414
|
+
},
|
|
367
415
|
async ({ op, node_id, data }) => {
|
|
368
416
|
const projectRoot = resolveProjectRoot();
|
|
369
417
|
const result = await updateRegistry(projectRoot, { op, node_id, data });
|
|
370
|
-
return
|
|
418
|
+
return {
|
|
419
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
420
|
+
structuredContent: result
|
|
421
|
+
};
|
|
371
422
|
}
|
|
372
423
|
);
|
|
373
424
|
}
|
|
@@ -386,12 +437,33 @@ function formatError(error) {
|
|
|
386
437
|
function createFabricServer() {
|
|
387
438
|
const server = new McpServer({
|
|
388
439
|
name: "fabric-context-server",
|
|
389
|
-
version: "1.
|
|
440
|
+
version: "1.3.0"
|
|
390
441
|
});
|
|
391
442
|
registerGetRules(server);
|
|
392
443
|
registerPlanContext(server);
|
|
393
444
|
registerAppendIntent(server);
|
|
394
445
|
registerUpdateRegistry(server);
|
|
446
|
+
server.registerResource(
|
|
447
|
+
"AGENTS.md",
|
|
448
|
+
AGENTS_MD_RESOURCE_URI,
|
|
449
|
+
{
|
|
450
|
+
description: "L0 fabric rules file \u2014 global agent instructions for this project",
|
|
451
|
+
mimeType: "text/markdown"
|
|
452
|
+
},
|
|
453
|
+
async (_uri) => {
|
|
454
|
+
const projectRoot = process.env.FABRIC_PROJECT_ROOT ?? process.cwd();
|
|
455
|
+
const content = await readFile2(join3(projectRoot, "AGENTS.md"), "utf8");
|
|
456
|
+
return {
|
|
457
|
+
contents: [
|
|
458
|
+
{
|
|
459
|
+
uri: AGENTS_MD_RESOURCE_URI,
|
|
460
|
+
mimeType: "text/markdown",
|
|
461
|
+
text: content
|
|
462
|
+
}
|
|
463
|
+
]
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
);
|
|
395
467
|
return server;
|
|
396
468
|
}
|
|
397
469
|
async function startStdioServer() {
|
|
@@ -400,7 +472,7 @@ async function startStdioServer() {
|
|
|
400
472
|
await server.connect(transport);
|
|
401
473
|
}
|
|
402
474
|
async function startHttpServer(options) {
|
|
403
|
-
const { createFabricHttpApp } = await import("./http-
|
|
475
|
+
const { createFabricHttpApp } = await import("./http-YPXWM5QS.js");
|
|
404
476
|
const { port, projectRoot, host = "127.0.0.1", authToken, dashboardDistPath, dev } = options;
|
|
405
477
|
const app = createFabricHttpApp({ projectRoot, host, authToken, dashboardDistPath, dev });
|
|
406
478
|
return await new Promise((resolveServer, rejectServer) => {
|
|
@@ -423,6 +495,7 @@ if (isMainModule) {
|
|
|
423
495
|
});
|
|
424
496
|
}
|
|
425
497
|
export {
|
|
498
|
+
AGENTS_MD_RESOURCE_URI,
|
|
426
499
|
createFabricServer,
|
|
427
500
|
runDoctorAuditReport,
|
|
428
501
|
runDoctorReport,
|