@echoes-io/mcp-server 4.1.0 → 4.1.1

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 (47) hide show
  1. package/cli/index.d.ts +2 -0
  2. package/cli/index.js +186 -0
  3. package/package.json +2 -1
  4. package/src/database/index.d.ts +6 -0
  5. package/src/database/index.js +26 -0
  6. package/src/database/relations.d.ts +744 -0
  7. package/src/database/relations.js +52 -0
  8. package/src/database/schema.d.ts +733 -0
  9. package/src/database/schema.js +69 -0
  10. package/src/database/vector.d.ts +25 -0
  11. package/src/database/vector.js +98 -0
  12. package/src/index.d.ts +5 -0
  13. package/src/index.js +5 -0
  14. package/src/rag/character-ner.d.ts +36 -0
  15. package/src/rag/character-ner.js +416 -0
  16. package/src/rag/database-sync.d.ts +38 -0
  17. package/src/rag/database-sync.js +158 -0
  18. package/src/rag/embeddings.d.ts +74 -0
  19. package/src/rag/embeddings.js +164 -0
  20. package/src/rag/graph-rag.d.ts +69 -0
  21. package/src/rag/graph-rag.js +311 -0
  22. package/src/rag/hybrid-rag.d.ts +109 -0
  23. package/src/rag/hybrid-rag.js +255 -0
  24. package/src/rag/index.d.ts +16 -0
  25. package/src/rag/index.js +33 -0
  26. package/src/server.d.ts +43 -0
  27. package/src/server.js +177 -0
  28. package/src/tools/index-rag.d.ts +19 -0
  29. package/src/tools/index-rag.js +85 -0
  30. package/src/tools/index-tracker.d.ts +17 -0
  31. package/src/tools/index-tracker.js +89 -0
  32. package/src/tools/index.d.ts +5 -0
  33. package/src/tools/index.js +5 -0
  34. package/src/tools/rag-context.d.ts +34 -0
  35. package/src/tools/rag-context.js +51 -0
  36. package/src/tools/rag-search.d.ts +35 -0
  37. package/src/tools/rag-search.js +60 -0
  38. package/src/tools/words-count.d.ts +15 -0
  39. package/src/tools/words-count.js +28 -0
  40. package/src/types/frontmatter.d.ts +35 -0
  41. package/src/types/frontmatter.js +1 -0
  42. package/src/utils/index.d.ts +1 -0
  43. package/src/utils/index.js +1 -0
  44. package/src/utils/markdown.d.ts +6 -0
  45. package/src/utils/markdown.js +36 -0
  46. package/src/utils/timeline-detection.d.ts +13 -0
  47. package/src/utils/timeline-detection.js +76 -0
@@ -0,0 +1,28 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { z } from 'zod';
3
+ import { parseMarkdown } from '../utils/markdown.js';
4
+ export const wordsCountSchema = z.object({
5
+ filePath: z.string().describe('Path to the markdown file'),
6
+ detailed: z.boolean().optional().describe('Include detailed statistics'),
7
+ });
8
+ export async function wordsCount(input) {
9
+ const { filePath, detailed = false } = wordsCountSchema.parse(input);
10
+ const content = readFileSync(filePath, 'utf-8');
11
+ const { content: text } = parseMarkdown(content);
12
+ // Basic stats
13
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
14
+ const characters = text.length;
15
+ const charactersNoSpaces = text.replace(/\s/g, '').length;
16
+ const readingTimeMinutes = Math.ceil(words / 200); // 200 WPM average
17
+ const result = {
18
+ words,
19
+ characters,
20
+ charactersNoSpaces,
21
+ readingTimeMinutes,
22
+ };
23
+ if (detailed) {
24
+ result.sentences = text.split(/[.!?]+/).filter((s) => s.trim().length > 0).length;
25
+ result.paragraphs = text.split(/\n\s*\n/).filter((p) => p.trim().length > 0).length;
26
+ }
27
+ return result;
28
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Frontmatter metadata from YAML headers
3
+ */
4
+ export interface ChapterMetadata {
5
+ pov: string;
6
+ title: string;
7
+ date: string;
8
+ timeline: string;
9
+ arc: string;
10
+ episode: number;
11
+ part: number;
12
+ chapter: number;
13
+ summary: string;
14
+ location: string;
15
+ outfit?: string;
16
+ kink?: string;
17
+ }
18
+ /**
19
+ * Text statistics calculated from content
20
+ */
21
+ export interface TextStats {
22
+ words: number;
23
+ characters: number;
24
+ charactersNoSpaces: number;
25
+ paragraphs: number;
26
+ sentences: number;
27
+ readingTimeMinutes: number;
28
+ }
29
+ /**
30
+ * Result of parsing a markdown file
31
+ */
32
+ export interface ParsedMarkdown {
33
+ metadata: ChapterMetadata;
34
+ content: string;
35
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from './markdown.js';
@@ -0,0 +1 @@
1
+ export * from './markdown.js';
@@ -0,0 +1,6 @@
1
+ import type { ParsedMarkdown, TextStats } from '../types/frontmatter.js';
2
+ /**
3
+ * Markdown parsing utilities
4
+ */
5
+ export declare function parseMarkdown(markdown: string): ParsedMarkdown;
6
+ export declare function getTextStats(markdown: string): TextStats;
@@ -0,0 +1,36 @@
1
+ import matter from 'gray-matter';
2
+ import removeMd from 'remove-markdown';
3
+ /**
4
+ * Markdown parsing utilities
5
+ */
6
+ export function parseMarkdown(markdown) {
7
+ const { data, content } = matter(markdown);
8
+ // Convert Date objects to strings
9
+ if (data.date instanceof Date) {
10
+ data.date = data.date.toISOString().split('T')[0];
11
+ }
12
+ return {
13
+ metadata: data,
14
+ content: content.trim(),
15
+ };
16
+ }
17
+ export function getTextStats(markdown) {
18
+ const { content } = parseMarkdown(markdown);
19
+ // Strip markdown
20
+ const withoutHeaders = content.replace(/^#{1,6}\s+.*$/gm, '');
21
+ const plainText = removeMd(withoutHeaders);
22
+ const words = plainText.trim().match(/\b\w+\b/g)?.length || 0;
23
+ const characters = plainText.length;
24
+ const charactersNoSpaces = plainText.replace(/\s/g, '').length;
25
+ const paragraphs = plainText.split(/\n\s*\n/).filter((p) => p.trim().length > 0).length;
26
+ const sentences = plainText.split(/[.!?]+/).filter((s) => s.trim().length > 0).length;
27
+ const readingTimeMinutes = Math.ceil(words / 200);
28
+ return {
29
+ words,
30
+ characters,
31
+ charactersNoSpaces,
32
+ paragraphs,
33
+ sentences,
34
+ readingTimeMinutes,
35
+ };
36
+ }
@@ -0,0 +1,13 @@
1
+ export interface TimelineContext {
2
+ timeline: string;
3
+ contentPath: string;
4
+ mode: 'single-timeline' | 'multi-timeline' | 'test';
5
+ }
6
+ /**
7
+ * Auto-detect timeline and content path based on current working directory
8
+ */
9
+ export declare function detectTimelineContext(cwd?: string): TimelineContext;
10
+ /**
11
+ * Get timeline context with optional override
12
+ */
13
+ export declare function getTimelineContext(timelineOverride?: string, contentPathOverride?: string): TimelineContext;
@@ -0,0 +1,76 @@
1
+ import { basename, dirname, resolve } from 'node:path';
2
+ /**
3
+ * Auto-detect timeline and content path based on current working directory
4
+ */
5
+ export function detectTimelineContext(cwd = process.cwd()) {
6
+ const currentDir = basename(cwd);
7
+ // Single Timeline Mode: timeline-* directory
8
+ if (currentDir.startsWith('timeline-')) {
9
+ const timeline = currentDir.replace('timeline-', '');
10
+ const contentPath = resolve(cwd, 'content');
11
+ console.error(`[DEBUG] Mode: single-timeline "${timeline}"`);
12
+ return {
13
+ timeline,
14
+ contentPath,
15
+ mode: 'single-timeline',
16
+ };
17
+ }
18
+ // Multi-Timeline Mode: .github directory
19
+ if (currentDir === '.github') {
20
+ const parentDir = dirname(cwd);
21
+ console.error(`[DEBUG] Mode: multi-timeline (scanning ${parentDir})`);
22
+ // For multi-timeline, we need the timeline parameter
23
+ throw new Error('Multi-timeline mode requires explicit timeline parameter');
24
+ }
25
+ // Test Mode: mcp-server directory
26
+ if (currentDir === 'mcp-server') {
27
+ console.error('[DEBUG] Mode: test from mcp-server (in-memory)');
28
+ return {
29
+ timeline: 'test',
30
+ contentPath: resolve(cwd, 'test/fixtures'),
31
+ mode: 'test',
32
+ };
33
+ }
34
+ // Fallback: try to find timeline-* in parent directories
35
+ let searchDir = cwd;
36
+ for (let i = 0; i < 5; i++) {
37
+ const dirName = basename(searchDir);
38
+ if (dirName.startsWith('timeline-')) {
39
+ const timeline = dirName.replace('timeline-', '');
40
+ const contentPath = resolve(searchDir, 'content');
41
+ console.error(`[DEBUG] Mode: single-timeline "${timeline}" (found in parent)`);
42
+ return {
43
+ timeline,
44
+ contentPath,
45
+ mode: 'single-timeline',
46
+ };
47
+ }
48
+ const parent = dirname(searchDir);
49
+ if (parent === searchDir)
50
+ break; // reached root
51
+ searchDir = parent;
52
+ }
53
+ throw new Error('Could not detect timeline context. Run from:\n' +
54
+ ' - timeline-* directory (single timeline mode)\n' +
55
+ ' - .github directory (multi-timeline mode)\n' +
56
+ ' - mcp-server directory (test mode)\n' +
57
+ 'Or provide explicit timeline parameter');
58
+ }
59
+ /**
60
+ * Get timeline context with optional override
61
+ */
62
+ export function getTimelineContext(timelineOverride, contentPathOverride) {
63
+ if (timelineOverride && contentPathOverride) {
64
+ return {
65
+ timeline: timelineOverride,
66
+ contentPath: contentPathOverride,
67
+ mode: 'single-timeline',
68
+ };
69
+ }
70
+ const detected = detectTimelineContext();
71
+ return {
72
+ timeline: timelineOverride || detected.timeline,
73
+ contentPath: contentPathOverride || detected.contentPath,
74
+ mode: detected.mode,
75
+ };
76
+ }