bluera-knowledge 0.10.0 → 0.11.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.
Files changed (49) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +98 -2
  4. package/commands/sync.md +96 -0
  5. package/dist/{chunk-ITH6FWQY.js → chunk-2WBITQWZ.js} +24 -3
  6. package/dist/{chunk-ITH6FWQY.js.map → chunk-2WBITQWZ.js.map} +1 -1
  7. package/dist/{chunk-CUHYSPRV.js → chunk-565OVW3C.js} +999 -2
  8. package/dist/chunk-565OVW3C.js.map +1 -0
  9. package/dist/{chunk-DWAIT2OD.js → chunk-TRDMYKGC.js} +190 -5
  10. package/dist/chunk-TRDMYKGC.js.map +1 -0
  11. package/dist/index.js +217 -5
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp/server.js +2 -2
  14. package/dist/workers/background-worker-cli.js +2 -2
  15. package/package.json +1 -1
  16. package/src/analysis/adapter-registry.test.ts +211 -0
  17. package/src/analysis/adapter-registry.ts +155 -0
  18. package/src/analysis/language-adapter.ts +127 -0
  19. package/src/analysis/parser-factory.test.ts +79 -1
  20. package/src/analysis/parser-factory.ts +8 -0
  21. package/src/analysis/zil/index.ts +34 -0
  22. package/src/analysis/zil/zil-adapter.test.ts +187 -0
  23. package/src/analysis/zil/zil-adapter.ts +121 -0
  24. package/src/analysis/zil/zil-lexer.test.ts +222 -0
  25. package/src/analysis/zil/zil-lexer.ts +239 -0
  26. package/src/analysis/zil/zil-parser.test.ts +210 -0
  27. package/src/analysis/zil/zil-parser.ts +360 -0
  28. package/src/analysis/zil/zil-special-forms.ts +193 -0
  29. package/src/cli/commands/sync.test.ts +54 -0
  30. package/src/cli/commands/sync.ts +264 -0
  31. package/src/cli/index.ts +1 -0
  32. package/src/crawl/claude-client.test.ts +56 -0
  33. package/src/crawl/claude-client.ts +27 -1
  34. package/src/index.ts +8 -0
  35. package/src/mcp/commands/index.ts +2 -0
  36. package/src/mcp/commands/sync.commands.test.ts +283 -0
  37. package/src/mcp/commands/sync.commands.ts +233 -0
  38. package/src/mcp/server.ts +9 -1
  39. package/src/services/gitignore.service.test.ts +157 -0
  40. package/src/services/gitignore.service.ts +132 -0
  41. package/src/services/store-definition.service.test.ts +440 -0
  42. package/src/services/store-definition.service.ts +198 -0
  43. package/src/services/store.service.test.ts +279 -1
  44. package/src/services/store.service.ts +101 -4
  45. package/src/types/index.ts +18 -0
  46. package/src/types/store-definition.test.ts +492 -0
  47. package/src/types/store-definition.ts +129 -0
  48. package/dist/chunk-CUHYSPRV.js.map +0 -1
  49. package/dist/chunk-DWAIT2OD.js.map +0 -1
@@ -1,16 +1,639 @@
1
1
  import {
2
+ AdapterRegistry,
2
3
  JobService,
4
+ ProjectRootService,
3
5
  createLogger,
4
6
  createServices,
5
7
  createStoreId,
6
8
  summarizePayload
7
- } from "./chunk-DWAIT2OD.js";
9
+ } from "./chunk-TRDMYKGC.js";
8
10
 
9
11
  // src/mcp/server.ts
10
12
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
11
13
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
14
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
13
15
 
16
+ // src/analysis/zil/zil-lexer.ts
17
+ var ZilLexer = class {
18
+ input = "";
19
+ pos = 0;
20
+ line = 1;
21
+ column = 1;
22
+ tokens = [];
23
+ /**
24
+ * Tokenize ZIL source code
25
+ *
26
+ * @param input - Source code string
27
+ * @returns Array of tokens
28
+ * @throws On unterminated strings
29
+ */
30
+ tokenize(input) {
31
+ this.input = input;
32
+ this.pos = 0;
33
+ this.line = 1;
34
+ this.column = 1;
35
+ this.tokens = [];
36
+ while (!this.isAtEnd()) {
37
+ this.scanToken();
38
+ }
39
+ return this.tokens;
40
+ }
41
+ isAtEnd() {
42
+ return this.pos >= this.input.length;
43
+ }
44
+ peek() {
45
+ if (this.isAtEnd()) return "\0";
46
+ return this.input[this.pos] ?? "\0";
47
+ }
48
+ advance() {
49
+ const char = this.input[this.pos] ?? "\0";
50
+ this.pos++;
51
+ if (char === "\n") {
52
+ this.line++;
53
+ this.column = 1;
54
+ } else {
55
+ this.column++;
56
+ }
57
+ return char;
58
+ }
59
+ addToken(type, value, startLine, startColumn) {
60
+ this.tokens.push({
61
+ type,
62
+ value,
63
+ line: startLine,
64
+ column: startColumn
65
+ });
66
+ }
67
+ scanToken() {
68
+ const startLine = this.line;
69
+ const startColumn = this.column;
70
+ const char = this.advance();
71
+ switch (char) {
72
+ case "<":
73
+ this.addToken("LANGLE" /* LANGLE */, "<", startLine, startColumn);
74
+ break;
75
+ case ">":
76
+ this.addToken("RANGLE" /* RANGLE */, ">", startLine, startColumn);
77
+ break;
78
+ case "(":
79
+ this.addToken("LPAREN" /* LPAREN */, "(", startLine, startColumn);
80
+ break;
81
+ case ")":
82
+ this.addToken("RPAREN" /* RPAREN */, ")", startLine, startColumn);
83
+ break;
84
+ case '"':
85
+ this.scanString(startLine, startColumn);
86
+ break;
87
+ case ";":
88
+ this.skipComment();
89
+ break;
90
+ case " ":
91
+ case " ":
92
+ case "\r":
93
+ case "\n":
94
+ break;
95
+ default:
96
+ if (this.isDigit(char) || char === "-" && this.isDigit(this.peek())) {
97
+ this.scanNumber(char, startLine, startColumn);
98
+ } else if (this.isAtomStart(char)) {
99
+ this.scanAtom(char, startLine, startColumn);
100
+ }
101
+ break;
102
+ }
103
+ }
104
+ scanString(startLine, startColumn) {
105
+ let value = "";
106
+ while (!this.isAtEnd() && this.peek() !== '"') {
107
+ const char = this.peek();
108
+ if (char === "\\") {
109
+ this.advance();
110
+ const escaped = this.advance();
111
+ switch (escaped) {
112
+ case '"':
113
+ value += '"';
114
+ break;
115
+ case "\\":
116
+ value += "\\";
117
+ break;
118
+ case "n":
119
+ value += "\n";
120
+ break;
121
+ case "t":
122
+ value += " ";
123
+ break;
124
+ default:
125
+ value += escaped;
126
+ break;
127
+ }
128
+ } else {
129
+ value += this.advance();
130
+ }
131
+ }
132
+ if (this.isAtEnd()) {
133
+ throw new Error(
134
+ `Unterminated string at line ${String(startLine)}, column ${String(startColumn)}`
135
+ );
136
+ }
137
+ this.advance();
138
+ this.addToken("STRING" /* STRING */, value, startLine, startColumn);
139
+ }
140
+ scanNumber(firstChar, startLine, startColumn) {
141
+ let value = firstChar;
142
+ while (this.isDigit(this.peek())) {
143
+ value += this.advance();
144
+ }
145
+ this.addToken("NUMBER" /* NUMBER */, value, startLine, startColumn);
146
+ }
147
+ scanAtom(firstChar, startLine, startColumn) {
148
+ let value = firstChar;
149
+ while (this.isAtomChar(this.peek())) {
150
+ value += this.advance();
151
+ }
152
+ this.addToken("ATOM" /* ATOM */, value, startLine, startColumn);
153
+ }
154
+ skipComment() {
155
+ while (!this.isAtEnd() && this.peek() !== "\n") {
156
+ this.advance();
157
+ }
158
+ }
159
+ isDigit(char) {
160
+ return char >= "0" && char <= "9";
161
+ }
162
+ isAtomStart(char) {
163
+ return char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char === "_" || char === "," || // Global reference prefix
164
+ char === "." || // Local reference prefix
165
+ char === "%" || // Sometimes used in ZIL
166
+ char === "#";
167
+ }
168
+ isAtomChar(char) {
169
+ return char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-" || char === "?" || char === "!" || char === "," || char === "." || char === "%" || char === "#";
170
+ }
171
+ };
172
+
173
+ // src/analysis/zil/zil-special-forms.ts
174
+ var ZIL_SPECIAL_FORMS = /* @__PURE__ */ new Set([
175
+ // Conditionals
176
+ "COND",
177
+ "AND",
178
+ "OR",
179
+ "NOT",
180
+ "IF",
181
+ "ELSE",
182
+ // Assignment
183
+ "SET",
184
+ "SETG",
185
+ "BIND",
186
+ "PROG",
187
+ // Loops
188
+ "REPEAT",
189
+ "DO",
190
+ "MAP",
191
+ "MAPF",
192
+ "MAPR",
193
+ "MAPRET",
194
+ "MAPLEAVE",
195
+ // Output
196
+ "TELL",
197
+ "PRINT",
198
+ "PRINTN",
199
+ "PRINTD",
200
+ "PRINTC",
201
+ "PRINTR",
202
+ "CRLF",
203
+ // Control flow
204
+ "RETURN",
205
+ "AGAIN",
206
+ "RTRUE",
207
+ "RFALSE",
208
+ "QUIT",
209
+ // Predicates (end with ?)
210
+ "EQUAL?",
211
+ "ZERO?",
212
+ "LESS?",
213
+ "GRTR?",
214
+ "FSET?",
215
+ "IN?",
216
+ "VERB?",
217
+ "PRSO?",
218
+ "PRSI?",
219
+ "HELD?",
220
+ "HERE?",
221
+ "ACCESSIBLE?",
222
+ "VISIBLE?",
223
+ "FIRST?",
224
+ "NEXT?",
225
+ "PROB?",
226
+ "RANDOM",
227
+ // Property/flag manipulation
228
+ "FSET",
229
+ "FCLEAR",
230
+ "GETP",
231
+ "PUTP",
232
+ "GETPT",
233
+ "PTSIZE",
234
+ // Object manipulation
235
+ "MOVE",
236
+ "REMOVE",
237
+ "LOC",
238
+ "FIRST",
239
+ "NEXT",
240
+ // Arithmetic
241
+ "ADD",
242
+ "SUB",
243
+ "MUL",
244
+ "DIV",
245
+ "MOD",
246
+ "BAND",
247
+ "BOR",
248
+ "BCOM",
249
+ "LSH",
250
+ // Table operations
251
+ "GET",
252
+ "PUT",
253
+ "GETB",
254
+ "PUTB",
255
+ "TABLE",
256
+ "ITABLE",
257
+ "LTABLE",
258
+ "PTABLE",
259
+ // Stack operations
260
+ "PUSH",
261
+ "POP",
262
+ "FSTACK",
263
+ // Input
264
+ "READ",
265
+ "INPUT",
266
+ "READLINE",
267
+ // Definition forms (handled separately for symbol extraction)
268
+ "ROUTINE",
269
+ "OBJECT",
270
+ "ROOM",
271
+ "GLOBAL",
272
+ "CONSTANT",
273
+ "SYNTAX",
274
+ "INSERT-FILE",
275
+ // Misc builtins
276
+ "VERSION?",
277
+ "ASCII",
278
+ "USL",
279
+ "APPLY",
280
+ "EVAL",
281
+ "FORM",
282
+ "REST",
283
+ "LENGTH",
284
+ "NTH",
285
+ "ZGET",
286
+ "ZPUT",
287
+ "ZWSTR",
288
+ "DIROUT",
289
+ "DIRIN",
290
+ "BUFOUT",
291
+ "HLIGHT",
292
+ "COLOR",
293
+ "FONT",
294
+ "SPLIT",
295
+ "SCREEN",
296
+ "WINGET",
297
+ "WINPUT",
298
+ "WINATTR",
299
+ "PICINF",
300
+ "DISPLAY",
301
+ "DCLEAR",
302
+ "SOUND",
303
+ "INTBL?",
304
+ "CATCH",
305
+ "THROW",
306
+ "LEGAL?",
307
+ "COPYT",
308
+ "VALUE",
309
+ "GASSIGNED?",
310
+ "ASSIGNED?",
311
+ "DEFINE",
312
+ "DEFMAC"
313
+ ]);
314
+ function isSpecialForm(name) {
315
+ return ZIL_SPECIAL_FORMS.has(name.toUpperCase());
316
+ }
317
+ var ZIL_DEFINITION_FORMS = /* @__PURE__ */ new Set([
318
+ "ROUTINE",
319
+ "OBJECT",
320
+ "ROOM",
321
+ "GLOBAL",
322
+ "CONSTANT",
323
+ "SYNTAX",
324
+ "VERB",
325
+ "DEFINE",
326
+ "DEFMAC"
327
+ ]);
328
+ function isDefinitionForm(name) {
329
+ return ZIL_DEFINITION_FORMS.has(name.toUpperCase());
330
+ }
331
+
332
+ // src/analysis/zil/zil-parser.ts
333
+ var ZilParser = class {
334
+ lexer = new ZilLexer();
335
+ tokens = [];
336
+ pos = 0;
337
+ /**
338
+ * Parse ZIL source code
339
+ */
340
+ parse(input) {
341
+ this.tokens = this.lexer.tokenize(input);
342
+ this.pos = 0;
343
+ const forms = [];
344
+ const symbols = [];
345
+ const imports = [];
346
+ const calls = [];
347
+ while (!this.isAtEnd()) {
348
+ if (this.check("LANGLE" /* LANGLE */)) {
349
+ const form = this.parseForm();
350
+ if (form !== void 0) {
351
+ forms.push(form);
352
+ const symbol = this.extractSymbol(form);
353
+ if (symbol !== void 0) {
354
+ symbols.push(symbol);
355
+ }
356
+ const imp = this.extractImport(form);
357
+ if (imp !== void 0) {
358
+ imports.push(imp);
359
+ }
360
+ if (form.head.toUpperCase() === "ROUTINE") {
361
+ const routineName = this.getRoutineName(form);
362
+ if (routineName !== void 0) {
363
+ this.extractCalls(form, routineName, calls);
364
+ }
365
+ }
366
+ }
367
+ } else {
368
+ this.advance();
369
+ }
370
+ }
371
+ return { forms, symbols, imports, calls };
372
+ }
373
+ isAtEnd() {
374
+ return this.pos >= this.tokens.length;
375
+ }
376
+ peek() {
377
+ return this.tokens[this.pos];
378
+ }
379
+ check(type) {
380
+ if (this.isAtEnd()) return false;
381
+ return this.peek()?.type === type;
382
+ }
383
+ advance() {
384
+ if (!this.isAtEnd()) {
385
+ const token = this.tokens[this.pos];
386
+ this.pos++;
387
+ return token;
388
+ }
389
+ return void 0;
390
+ }
391
+ parseForm() {
392
+ if (!this.check("LANGLE" /* LANGLE */)) return void 0;
393
+ const startToken = this.advance();
394
+ const startLine = startToken?.line ?? 1;
395
+ let endLine = startLine;
396
+ let head = "";
397
+ if (this.check("ATOM" /* ATOM */)) {
398
+ head = this.advance()?.value ?? "";
399
+ }
400
+ const children = [];
401
+ while (!this.isAtEnd() && !this.check("RANGLE" /* RANGLE */)) {
402
+ const child = this.parseNode();
403
+ if (child !== void 0) {
404
+ children.push(child);
405
+ endLine = this.getNodeEndLine(child);
406
+ } else {
407
+ this.advance();
408
+ }
409
+ }
410
+ if (this.check("RANGLE" /* RANGLE */)) {
411
+ const closeToken = this.advance();
412
+ endLine = closeToken?.line ?? endLine;
413
+ }
414
+ return { head, children, startLine, endLine };
415
+ }
416
+ parseGroup() {
417
+ if (!this.check("LPAREN" /* LPAREN */)) return void 0;
418
+ const startToken = this.advance();
419
+ const startLine = startToken?.line ?? 1;
420
+ let endLine = startLine;
421
+ const children = [];
422
+ while (!this.isAtEnd() && !this.check("RPAREN" /* RPAREN */)) {
423
+ const child = this.parseNode();
424
+ if (child !== void 0) {
425
+ children.push(child);
426
+ endLine = this.getNodeEndLine(child);
427
+ } else {
428
+ this.advance();
429
+ }
430
+ }
431
+ if (this.check("RPAREN" /* RPAREN */)) {
432
+ const closeToken = this.advance();
433
+ endLine = closeToken?.line ?? endLine;
434
+ }
435
+ return { type: "group", children, startLine, endLine };
436
+ }
437
+ parseNode() {
438
+ const token = this.peek();
439
+ if (token === void 0) return void 0;
440
+ switch (token.type) {
441
+ case "LANGLE" /* LANGLE */:
442
+ return this.parseForm();
443
+ case "LPAREN" /* LPAREN */:
444
+ return this.parseGroup();
445
+ case "ATOM" /* ATOM */:
446
+ this.advance();
447
+ return { type: "atom", value: token.value, line: token.line };
448
+ case "STRING" /* STRING */:
449
+ this.advance();
450
+ return { type: "string", value: token.value, line: token.line };
451
+ case "NUMBER" /* NUMBER */:
452
+ this.advance();
453
+ return { type: "number", value: token.value, line: token.line };
454
+ default:
455
+ return void 0;
456
+ }
457
+ }
458
+ getNodeEndLine(node) {
459
+ if ("endLine" in node) {
460
+ return node.endLine;
461
+ }
462
+ return node.line;
463
+ }
464
+ extractSymbol(form) {
465
+ const headUpper = form.head.toUpperCase();
466
+ if (!isDefinitionForm(headUpper)) {
467
+ return void 0;
468
+ }
469
+ const nameNode = form.children.find((c) => "type" in c && c.type === "atom");
470
+ if (nameNode === void 0) {
471
+ return void 0;
472
+ }
473
+ const kindMap = {
474
+ ROUTINE: "routine",
475
+ OBJECT: "object",
476
+ ROOM: "room",
477
+ GLOBAL: "global",
478
+ CONSTANT: "constant",
479
+ SYNTAX: "syntax",
480
+ VERB: "verb",
481
+ DEFINE: "routine",
482
+ DEFMAC: "routine"
483
+ };
484
+ const kind = kindMap[headUpper];
485
+ if (kind === void 0) {
486
+ return void 0;
487
+ }
488
+ const result = {
489
+ name: nameNode.value,
490
+ kind,
491
+ startLine: form.startLine,
492
+ endLine: form.endLine
493
+ };
494
+ if (headUpper === "ROUTINE" || headUpper === "DEFINE" || headUpper === "DEFMAC") {
495
+ result.signature = this.extractRoutineSignature(form, nameNode.value);
496
+ }
497
+ return result;
498
+ }
499
+ extractRoutineSignature(form, name) {
500
+ const argsGroup = form.children.find((c) => "type" in c && c.type === "group");
501
+ if (argsGroup === void 0) {
502
+ return `ROUTINE ${name} ()`;
503
+ }
504
+ const args = argsGroup.children.filter((c) => "type" in c && c.type === "atom").map((c) => c.value).join(" ");
505
+ return `ROUTINE ${name} (${args})`;
506
+ }
507
+ extractImport(form) {
508
+ if (form.head.toUpperCase() !== "INSERT-FILE") {
509
+ return void 0;
510
+ }
511
+ const fileNode = form.children.find((c) => "type" in c && c.type === "string");
512
+ if (fileNode === void 0) {
513
+ return void 0;
514
+ }
515
+ return {
516
+ source: fileNode.value,
517
+ specifiers: [],
518
+ isType: false
519
+ };
520
+ }
521
+ getRoutineName(form) {
522
+ const nameNode = form.children.find((c) => "type" in c && c.type === "atom");
523
+ return nameNode?.value;
524
+ }
525
+ extractCalls(node, caller, calls) {
526
+ if ("head" in node) {
527
+ const headUpper = node.head.toUpperCase();
528
+ if (node.head !== "" && !isSpecialForm(headUpper)) {
529
+ calls.push({
530
+ caller,
531
+ callee: node.head,
532
+ line: node.startLine
533
+ });
534
+ }
535
+ for (const child of node.children) {
536
+ this.extractCalls(child, caller, calls);
537
+ }
538
+ } else if ("type" in node && node.type === "group") {
539
+ for (const child of node.children) {
540
+ this.extractCalls(child, caller, calls);
541
+ }
542
+ }
543
+ }
544
+ };
545
+
546
+ // src/analysis/zil/zil-adapter.ts
547
+ var ZilAdapter = class {
548
+ languageId = "zil";
549
+ extensions = [".zil", ".mud"];
550
+ displayName = "ZIL (Zork Implementation Language)";
551
+ parser = new ZilParser();
552
+ /**
553
+ * Parse ZIL code and extract symbols as CodeNode[]
554
+ */
555
+ parse(content, _filePath) {
556
+ const result = this.parser.parse(content);
557
+ return result.symbols.map((symbol) => {
558
+ const node = {
559
+ type: this.mapSymbolKindToNodeType(symbol.kind),
560
+ name: symbol.name,
561
+ exported: true,
562
+ // ZIL doesn't have export concept, treat all as exported
563
+ startLine: symbol.startLine,
564
+ endLine: symbol.endLine
565
+ };
566
+ if (symbol.signature !== void 0) {
567
+ node.signature = symbol.signature;
568
+ }
569
+ return node;
570
+ });
571
+ }
572
+ /**
573
+ * Extract imports from INSERT-FILE directives
574
+ */
575
+ extractImports(content, _filePath) {
576
+ const result = this.parser.parse(content);
577
+ return result.imports;
578
+ }
579
+ /**
580
+ * Chunk ZIL code by top-level forms
581
+ */
582
+ chunk(content, _filePath) {
583
+ const result = this.parser.parse(content);
584
+ const lines = content.split("\n");
585
+ return result.forms.filter((form) => form.head !== "").map((form) => {
586
+ const chunkLines = lines.slice(form.startLine - 1, form.endLine);
587
+ const chunkContent = chunkLines.join("\n");
588
+ const symbol = result.symbols.find(
589
+ (s) => s.startLine === form.startLine && s.endLine === form.endLine
590
+ );
591
+ const chunk = {
592
+ content: chunkContent,
593
+ startLine: form.startLine,
594
+ endLine: form.endLine
595
+ };
596
+ if (symbol !== void 0) {
597
+ chunk.symbolName = symbol.name;
598
+ chunk.symbolKind = symbol.kind;
599
+ }
600
+ return chunk;
601
+ });
602
+ }
603
+ /**
604
+ * Analyze call relationships within ZIL code
605
+ */
606
+ analyzeCallRelationships(content, filePath) {
607
+ const result = this.parser.parse(content);
608
+ return result.calls.map((call) => ({
609
+ from: `${filePath}:${call.caller}`,
610
+ to: `${filePath}:${call.callee}`,
611
+ type: "calls",
612
+ confidence: 0.9
613
+ // High confidence for ZIL - calls are explicit
614
+ }));
615
+ }
616
+ /**
617
+ * Map ZIL symbol kinds to CodeNode types
618
+ */
619
+ mapSymbolKindToNodeType(kind) {
620
+ switch (kind) {
621
+ case "routine":
622
+ return "function";
623
+ case "object":
624
+ case "room":
625
+ case "global":
626
+ case "constant":
627
+ return "const";
628
+ case "syntax":
629
+ case "verb":
630
+ return "const";
631
+ default:
632
+ return "const";
633
+ }
634
+ }
635
+ };
636
+
14
637
  // src/mcp/commands/job.commands.ts
15
638
  import { z as z2 } from "zod";
16
639
 
@@ -599,10 +1222,375 @@ var storeCommands = [
599
1222
  }
600
1223
  ];
601
1224
 
1225
+ // src/mcp/commands/sync.commands.ts
1226
+ import { z as z7 } from "zod";
1227
+
1228
+ // src/services/store-definition.service.ts
1229
+ import { readFile, writeFile, mkdir, access } from "fs/promises";
1230
+ import { dirname, resolve, isAbsolute, join as join2 } from "path";
1231
+
1232
+ // src/types/store-definition.ts
1233
+ import { z as z6 } from "zod";
1234
+ var BaseStoreDefinitionSchema = z6.object({
1235
+ name: z6.string().min(1, "Store name is required"),
1236
+ description: z6.string().optional(),
1237
+ tags: z6.array(z6.string()).optional()
1238
+ });
1239
+ var FileStoreDefinitionSchema = BaseStoreDefinitionSchema.extend({
1240
+ type: z6.literal("file"),
1241
+ path: z6.string().min(1, "Path is required for file stores")
1242
+ });
1243
+ var RepoStoreDefinitionSchema = BaseStoreDefinitionSchema.extend({
1244
+ type: z6.literal("repo"),
1245
+ url: z6.url("Valid URL is required for repo stores"),
1246
+ branch: z6.string().optional(),
1247
+ depth: z6.number().int().positive("Depth must be a positive integer").optional()
1248
+ });
1249
+ var WebStoreDefinitionSchema = BaseStoreDefinitionSchema.extend({
1250
+ type: z6.literal("web"),
1251
+ url: z6.url("Valid URL is required for web stores"),
1252
+ depth: z6.number().int().min(0, "Depth must be non-negative").default(1),
1253
+ maxPages: z6.number().int().positive("maxPages must be a positive integer").optional(),
1254
+ crawlInstructions: z6.string().optional(),
1255
+ extractInstructions: z6.string().optional()
1256
+ });
1257
+ var StoreDefinitionSchema = z6.discriminatedUnion("type", [
1258
+ FileStoreDefinitionSchema,
1259
+ RepoStoreDefinitionSchema,
1260
+ WebStoreDefinitionSchema
1261
+ ]);
1262
+ var StoreDefinitionsConfigSchema = z6.object({
1263
+ version: z6.literal(1),
1264
+ stores: z6.array(StoreDefinitionSchema)
1265
+ });
1266
+ function isFileStoreDefinition(def) {
1267
+ return def.type === "file";
1268
+ }
1269
+ function isRepoStoreDefinition(def) {
1270
+ return def.type === "repo";
1271
+ }
1272
+ function isWebStoreDefinition(def) {
1273
+ return def.type === "web";
1274
+ }
1275
+ var DEFAULT_STORE_DEFINITIONS_CONFIG = {
1276
+ version: 1,
1277
+ stores: []
1278
+ };
1279
+
1280
+ // src/services/store-definition.service.ts
1281
+ async function fileExists(path2) {
1282
+ try {
1283
+ await access(path2);
1284
+ return true;
1285
+ } catch {
1286
+ return false;
1287
+ }
1288
+ }
1289
+ var StoreDefinitionService = class {
1290
+ configPath;
1291
+ projectRoot;
1292
+ config = null;
1293
+ constructor(projectRoot) {
1294
+ this.projectRoot = projectRoot ?? ProjectRootService.resolve();
1295
+ this.configPath = join2(this.projectRoot, ".bluera/bluera-knowledge/stores.config.json");
1296
+ }
1297
+ /**
1298
+ * Load store definitions from config file.
1299
+ * Returns empty config if file doesn't exist.
1300
+ * Throws on parse/validation errors (fail fast per CLAUDE.md).
1301
+ */
1302
+ async load() {
1303
+ if (this.config !== null) {
1304
+ return this.config;
1305
+ }
1306
+ const exists = await fileExists(this.configPath);
1307
+ if (!exists) {
1308
+ this.config = {
1309
+ ...DEFAULT_STORE_DEFINITIONS_CONFIG,
1310
+ stores: [...DEFAULT_STORE_DEFINITIONS_CONFIG.stores]
1311
+ };
1312
+ return this.config;
1313
+ }
1314
+ const content = await readFile(this.configPath, "utf-8");
1315
+ let parsed;
1316
+ try {
1317
+ parsed = JSON.parse(content);
1318
+ } catch (error) {
1319
+ throw new Error(
1320
+ `Failed to parse store definitions at ${this.configPath}: ${error instanceof Error ? error.message : String(error)}`
1321
+ );
1322
+ }
1323
+ const result = StoreDefinitionsConfigSchema.safeParse(parsed);
1324
+ if (!result.success) {
1325
+ throw new Error(`Invalid store definitions at ${this.configPath}: ${result.error.message}`);
1326
+ }
1327
+ this.config = result.data;
1328
+ return this.config;
1329
+ }
1330
+ /**
1331
+ * Save store definitions to config file.
1332
+ */
1333
+ async save(config) {
1334
+ await mkdir(dirname(this.configPath), { recursive: true });
1335
+ await writeFile(this.configPath, JSON.stringify(config, null, 2));
1336
+ this.config = config;
1337
+ }
1338
+ /**
1339
+ * Add a store definition.
1340
+ * Throws if a definition with the same name already exists.
1341
+ */
1342
+ async addDefinition(definition) {
1343
+ const config = await this.load();
1344
+ const existing = config.stores.find((s) => s.name === definition.name);
1345
+ if (existing !== void 0) {
1346
+ throw new Error(`Store definition "${definition.name}" already exists`);
1347
+ }
1348
+ config.stores.push(definition);
1349
+ await this.save(config);
1350
+ }
1351
+ /**
1352
+ * Remove a store definition by name.
1353
+ * Returns true if removed, false if not found.
1354
+ */
1355
+ async removeDefinition(name) {
1356
+ const config = await this.load();
1357
+ const index = config.stores.findIndex((s) => s.name === name);
1358
+ if (index === -1) {
1359
+ return false;
1360
+ }
1361
+ config.stores.splice(index, 1);
1362
+ await this.save(config);
1363
+ return true;
1364
+ }
1365
+ /**
1366
+ * Update an existing store definition.
1367
+ * Only updates the provided fields, preserving others.
1368
+ * Throws if definition not found.
1369
+ */
1370
+ async updateDefinition(name, updates) {
1371
+ const config = await this.load();
1372
+ const index = config.stores.findIndex((s) => s.name === name);
1373
+ if (index === -1) {
1374
+ throw new Error(`Store definition "${name}" not found`);
1375
+ }
1376
+ const existing = config.stores[index];
1377
+ if (existing === void 0) {
1378
+ throw new Error(`Store definition "${name}" not found at index ${String(index)}`);
1379
+ }
1380
+ if (updates.description !== void 0) {
1381
+ existing.description = updates.description;
1382
+ }
1383
+ if (updates.tags !== void 0) {
1384
+ existing.tags = updates.tags;
1385
+ }
1386
+ await this.save(config);
1387
+ }
1388
+ /**
1389
+ * Get a store definition by name.
1390
+ * Returns undefined if not found.
1391
+ */
1392
+ async getByName(name) {
1393
+ const config = await this.load();
1394
+ return config.stores.find((s) => s.name === name);
1395
+ }
1396
+ /**
1397
+ * Check if any definitions exist.
1398
+ */
1399
+ async hasDefinitions() {
1400
+ const config = await this.load();
1401
+ return config.stores.length > 0;
1402
+ }
1403
+ /**
1404
+ * Resolve a file store path relative to project root.
1405
+ */
1406
+ resolvePath(path2) {
1407
+ if (isAbsolute(path2)) {
1408
+ return path2;
1409
+ }
1410
+ return resolve(this.projectRoot, path2);
1411
+ }
1412
+ /**
1413
+ * Get the config file path.
1414
+ */
1415
+ getConfigPath() {
1416
+ return this.configPath;
1417
+ }
1418
+ /**
1419
+ * Get the project root.
1420
+ */
1421
+ getProjectRoot() {
1422
+ return this.projectRoot;
1423
+ }
1424
+ /**
1425
+ * Clear the cached config (useful for testing).
1426
+ */
1427
+ clearCache() {
1428
+ this.config = null;
1429
+ }
1430
+ };
1431
+
1432
+ // src/mcp/commands/sync.commands.ts
1433
+ async function handleStoresSync(args, context) {
1434
+ const { services, options } = context;
1435
+ const projectRoot = options.projectRoot;
1436
+ if (projectRoot === void 0) {
1437
+ throw new Error("Project root is required for stores:sync");
1438
+ }
1439
+ const defService = new StoreDefinitionService(projectRoot);
1440
+ const config = await defService.load();
1441
+ const result = {
1442
+ created: [],
1443
+ skipped: [],
1444
+ failed: [],
1445
+ orphans: []
1446
+ };
1447
+ if (args.dryRun === true) {
1448
+ result.dryRun = true;
1449
+ result.wouldCreate = [];
1450
+ result.wouldPrune = [];
1451
+ }
1452
+ const existingStores = await services.store.list();
1453
+ const existingNames = new Set(existingStores.map((s) => s.name));
1454
+ for (const def of config.stores) {
1455
+ if (existingNames.has(def.name)) {
1456
+ result.skipped.push(def.name);
1457
+ continue;
1458
+ }
1459
+ if (args.dryRun === true) {
1460
+ result.wouldCreate?.push(def.name);
1461
+ continue;
1462
+ }
1463
+ const createResult = await createStoreFromDefinition(def, defService, services, context);
1464
+ if (createResult.success) {
1465
+ result.created.push(def.name);
1466
+ } else {
1467
+ result.failed.push({ name: def.name, error: createResult.error });
1468
+ }
1469
+ }
1470
+ const definedNames = new Set(config.stores.map((d) => d.name));
1471
+ for (const store of existingStores) {
1472
+ if (!definedNames.has(store.name)) {
1473
+ result.orphans.push(store.name);
1474
+ }
1475
+ }
1476
+ if (args.prune === true && result.orphans.length > 0) {
1477
+ if (args.dryRun === true) {
1478
+ result.wouldPrune = [...result.orphans];
1479
+ } else {
1480
+ result.pruned = [];
1481
+ for (const orphanName of result.orphans) {
1482
+ const store = await services.store.getByName(orphanName);
1483
+ if (store !== void 0) {
1484
+ const deleteResult = await services.store.delete(store.id, { skipDefinitionSync: true });
1485
+ if (deleteResult.success) {
1486
+ result.pruned.push(orphanName);
1487
+ }
1488
+ }
1489
+ }
1490
+ }
1491
+ }
1492
+ return {
1493
+ content: [
1494
+ {
1495
+ type: "text",
1496
+ text: JSON.stringify(result, null, 2)
1497
+ }
1498
+ ]
1499
+ };
1500
+ }
1501
+ async function createStoreFromDefinition(def, defService, services, _context) {
1502
+ try {
1503
+ if (isFileStoreDefinition(def)) {
1504
+ const resolvedPath = defService.resolvePath(def.path);
1505
+ const createResult = await services.store.create(
1506
+ {
1507
+ name: def.name,
1508
+ type: "file",
1509
+ path: resolvedPath,
1510
+ description: def.description,
1511
+ tags: def.tags
1512
+ },
1513
+ { skipDefinitionSync: true }
1514
+ // Don't re-add to definitions
1515
+ );
1516
+ if (!createResult.success) {
1517
+ return { success: false, error: createResult.error.message };
1518
+ }
1519
+ return { success: true };
1520
+ }
1521
+ if (isRepoStoreDefinition(def)) {
1522
+ const createResult = await services.store.create(
1523
+ {
1524
+ name: def.name,
1525
+ type: "repo",
1526
+ url: def.url,
1527
+ branch: def.branch,
1528
+ depth: def.depth,
1529
+ description: def.description,
1530
+ tags: def.tags
1531
+ },
1532
+ { skipDefinitionSync: true }
1533
+ );
1534
+ if (!createResult.success) {
1535
+ return { success: false, error: createResult.error.message };
1536
+ }
1537
+ return { success: true };
1538
+ }
1539
+ if (isWebStoreDefinition(def)) {
1540
+ const createResult = await services.store.create(
1541
+ {
1542
+ name: def.name,
1543
+ type: "web",
1544
+ url: def.url,
1545
+ depth: def.depth,
1546
+ description: def.description,
1547
+ tags: def.tags
1548
+ },
1549
+ { skipDefinitionSync: true }
1550
+ );
1551
+ if (!createResult.success) {
1552
+ return { success: false, error: createResult.error.message };
1553
+ }
1554
+ return { success: true };
1555
+ }
1556
+ return { success: false, error: "Unknown store definition type" };
1557
+ } catch (error) {
1558
+ return {
1559
+ success: false,
1560
+ error: error instanceof Error ? error.message : String(error)
1561
+ };
1562
+ }
1563
+ }
1564
+ var syncCommands = [
1565
+ {
1566
+ name: "stores:sync",
1567
+ description: "Sync stores from definitions config (bootstrap on fresh clone)",
1568
+ argsSchema: z7.object({
1569
+ reindex: z7.boolean().optional().describe("Re-index existing stores after sync"),
1570
+ prune: z7.boolean().optional().describe("Remove stores not in definitions"),
1571
+ dryRun: z7.boolean().optional().describe("Show what would happen without making changes")
1572
+ }),
1573
+ handler: (args, context) => {
1574
+ const syncArgs = {};
1575
+ if (typeof args["reindex"] === "boolean") {
1576
+ syncArgs.reindex = args["reindex"];
1577
+ }
1578
+ if (typeof args["prune"] === "boolean") {
1579
+ syncArgs.prune = args["prune"];
1580
+ }
1581
+ if (typeof args["dryRun"] === "boolean") {
1582
+ syncArgs.dryRun = args["dryRun"];
1583
+ }
1584
+ return handleStoresSync(syncArgs, context);
1585
+ }
1586
+ }
1587
+ ];
1588
+
602
1589
  // src/mcp/commands/index.ts
603
1590
  commandRegistry.registerAll(storeCommands);
604
1591
  commandRegistry.registerAll(jobCommands);
605
1592
  commandRegistry.registerAll(metaCommands);
1593
+ commandRegistry.registerAll(syncCommands);
606
1594
 
607
1595
  // src/mcp/handlers/execute.handler.ts
608
1596
  var handleExecute = async (args, context) => {
@@ -927,6 +1915,10 @@ var tools = [
927
1915
 
928
1916
  // src/mcp/server.ts
929
1917
  var logger2 = createLogger("mcp-server");
1918
+ var registry = AdapterRegistry.getInstance();
1919
+ if (!registry.hasExtension(".zil")) {
1920
+ registry.register(new ZilAdapter());
1921
+ }
930
1922
  function createMCPServer(options) {
931
1923
  const server = new Server(
932
1924
  {
@@ -1092,7 +2084,12 @@ if (isMCPServerEntry) {
1092
2084
  }
1093
2085
 
1094
2086
  export {
2087
+ ZilAdapter,
2088
+ isFileStoreDefinition,
2089
+ isRepoStoreDefinition,
2090
+ isWebStoreDefinition,
2091
+ StoreDefinitionService,
1095
2092
  createMCPServer,
1096
2093
  runMCPServer
1097
2094
  };
1098
- //# sourceMappingURL=chunk-CUHYSPRV.js.map
2095
+ //# sourceMappingURL=chunk-565OVW3C.js.map