stellavault 0.3.0 → 0.4.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.
- package/CHANGELOG.md +84 -0
- package/README.md +109 -119
- package/package.json +2 -2
- package/packages/cli/dist/commands/ask-cmd.d.ts +4 -0
- package/packages/cli/dist/commands/ask-cmd.js +35 -0
- package/packages/cli/dist/commands/autopilot-cmd.d.ts +4 -0
- package/packages/cli/dist/commands/autopilot-cmd.js +76 -0
- package/packages/cli/dist/commands/compile-cmd.d.ts +6 -0
- package/packages/cli/dist/commands/compile-cmd.js +30 -0
- package/packages/cli/dist/commands/digest-cmd.d.ts +1 -0
- package/packages/cli/dist/commands/digest-cmd.js +57 -0
- package/packages/cli/dist/commands/draft-cmd.d.ts +5 -0
- package/packages/cli/dist/commands/draft-cmd.js +99 -0
- package/packages/cli/dist/commands/fleeting-cmd.d.ts +4 -0
- package/packages/cli/dist/commands/fleeting-cmd.js +45 -0
- package/packages/cli/dist/commands/graph-cmd.js +13 -1
- package/packages/cli/dist/commands/ingest-cmd.d.ts +9 -0
- package/packages/cli/dist/commands/ingest-cmd.js +161 -0
- package/packages/cli/dist/commands/init-cmd.js +39 -1
- package/packages/cli/dist/commands/lint-cmd.d.ts +2 -0
- package/packages/cli/dist/commands/lint-cmd.js +61 -0
- package/packages/cli/dist/index.js +53 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/dist/api/server.js +393 -0
- package/packages/core/dist/config.d.ts +8 -0
- package/packages/core/dist/config.js +9 -1
- package/packages/core/dist/i18n/note-strings.d.ts +5 -0
- package/packages/core/dist/i18n/note-strings.js +94 -0
- package/packages/core/dist/index.d.ts +11 -2
- package/packages/core/dist/index.js +6 -1
- package/packages/core/dist/intelligence/ask-engine.d.ts +23 -0
- package/packages/core/dist/intelligence/ask-engine.js +108 -0
- package/packages/core/dist/intelligence/draft-generator.d.ts +19 -0
- package/packages/core/dist/intelligence/draft-generator.js +161 -0
- package/packages/core/dist/intelligence/file-extractors.d.ts +18 -0
- package/packages/core/dist/intelligence/file-extractors.js +127 -0
- package/packages/core/dist/intelligence/ingest-pipeline.d.ts +32 -0
- package/packages/core/dist/intelligence/ingest-pipeline.js +209 -0
- package/packages/core/dist/intelligence/knowledge-lint.d.ts +27 -0
- package/packages/core/dist/intelligence/knowledge-lint.js +132 -0
- package/packages/core/dist/intelligence/wiki-compiler.d.ts +30 -0
- package/packages/core/dist/intelligence/wiki-compiler.js +222 -0
- package/packages/core/dist/intelligence/youtube-extractor.d.ts +29 -0
- package/packages/core/dist/intelligence/youtube-extractor.js +311 -0
- package/packages/core/dist/intelligence/zettelkasten.d.ts +59 -0
- package/packages/core/dist/intelligence/zettelkasten.js +234 -0
- package/packages/core/dist/mcp/server.d.ts +2 -0
- package/packages/core/dist/mcp/server.js +24 -1
- package/packages/core/dist/mcp/tools/agentic-graph.d.ts +6 -0
- package/packages/core/dist/mcp/tools/agentic-graph.js +35 -7
- package/packages/core/dist/mcp/tools/ask.d.ts +29 -0
- package/packages/core/dist/mcp/tools/ask.js +40 -0
- package/packages/core/dist/mcp/tools/generate-draft.d.ts +34 -0
- package/packages/core/dist/mcp/tools/generate-draft.js +120 -0
- package/packages/core/package.json +21 -2
|
@@ -29,6 +29,10 @@ export declare function createAgenticGraphTools(store: VectorStore, embedder: Em
|
|
|
29
29
|
type: string;
|
|
30
30
|
description: string;
|
|
31
31
|
};
|
|
32
|
+
autoLink: {
|
|
33
|
+
type: string;
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
32
36
|
sourceTitle?: undefined;
|
|
33
37
|
targetTitle?: undefined;
|
|
34
38
|
context?: undefined;
|
|
@@ -41,6 +45,7 @@ export declare function createAgenticGraphTools(store: VectorStore, embedder: Em
|
|
|
41
45
|
tags?: string[];
|
|
42
46
|
type?: string;
|
|
43
47
|
folder?: string;
|
|
48
|
+
autoLink?: boolean;
|
|
44
49
|
}): Promise<{
|
|
45
50
|
content: {
|
|
46
51
|
type: "text";
|
|
@@ -70,6 +75,7 @@ export declare function createAgenticGraphTools(store: VectorStore, embedder: Em
|
|
|
70
75
|
tags?: undefined;
|
|
71
76
|
type?: undefined;
|
|
72
77
|
folder?: undefined;
|
|
78
|
+
autoLink?: undefined;
|
|
73
79
|
};
|
|
74
80
|
required: string[];
|
|
75
81
|
};
|
|
@@ -6,20 +6,42 @@ export function createAgenticGraphTools(store, embedder, vaultPath) {
|
|
|
6
6
|
return [
|
|
7
7
|
{
|
|
8
8
|
name: 'create-knowledge-node',
|
|
9
|
-
description: 'Create a
|
|
9
|
+
description: 'Create a wiki-quality knowledge note in the vault. Auto-finds related documents, generates backlinks, and adds concept tags. The note is saved with proper frontmatter and cross-references.',
|
|
10
10
|
inputSchema: {
|
|
11
11
|
type: 'object',
|
|
12
12
|
properties: {
|
|
13
13
|
title: { type: 'string', description: 'Note title' },
|
|
14
14
|
content: { type: 'string', description: 'Note content (markdown)' },
|
|
15
15
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags for categorization' },
|
|
16
|
-
type: { type: 'string', description: 'Note type: note, decision, insight, summary' },
|
|
16
|
+
type: { type: 'string', description: 'Note type: note, decision, insight, summary, wiki' },
|
|
17
17
|
folder: { type: 'string', description: 'Vault subfolder (default: 01_Knowledge)' },
|
|
18
|
+
autoLink: { type: 'boolean', description: 'Auto-discover and add links to related notes (default: true)' },
|
|
18
19
|
},
|
|
19
20
|
required: ['title', 'content'],
|
|
20
21
|
},
|
|
21
22
|
async handler(args) {
|
|
22
|
-
const { title, content, tags = [], type = 'note', folder = '01_Knowledge' } = args;
|
|
23
|
+
const { title, content, tags = [], type = 'note', folder = '01_Knowledge', autoLink = true } = args;
|
|
24
|
+
// 관련 문서 자동 탐색
|
|
25
|
+
let relatedSection = '';
|
|
26
|
+
if (autoLink) {
|
|
27
|
+
try {
|
|
28
|
+
const docs = await store.getAllDocuments();
|
|
29
|
+
const titleWords = title.toLowerCase().split(/\s+/).filter(w => w.length > 3);
|
|
30
|
+
const related = docs
|
|
31
|
+
.filter(d => {
|
|
32
|
+
const dTitle = d.title.toLowerCase();
|
|
33
|
+
return titleWords.some(w => dTitle.includes(w)) ||
|
|
34
|
+
tags.some(t => d.tags.includes(t));
|
|
35
|
+
})
|
|
36
|
+
.slice(0, 5);
|
|
37
|
+
if (related.length > 0) {
|
|
38
|
+
relatedSection = '\n\n## Related Notes\n' +
|
|
39
|
+
related.map(r => `- [[${r.title}]]`).join('\n') +
|
|
40
|
+
'\n';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch { /* skip auto-link on error */ }
|
|
44
|
+
}
|
|
23
45
|
// frontmatter 생성
|
|
24
46
|
const date = new Date().toISOString().slice(0, 10);
|
|
25
47
|
const fm = [
|
|
@@ -32,17 +54,23 @@ export function createAgenticGraphTools(store, embedder, vaultPath) {
|
|
|
32
54
|
`auto_generated: true`,
|
|
33
55
|
'---',
|
|
34
56
|
].join('\n');
|
|
35
|
-
const fullContent = `${fm}\n\n# ${title}\n\n${content}`;
|
|
36
|
-
// vault에 파일 저장
|
|
57
|
+
const fullContent = `${fm}\n\n# ${title}\n\n${content}${relatedSection}`;
|
|
58
|
+
// vault에 파일 저장 (path traversal 방지)
|
|
37
59
|
const safeTitle = title.replace(/[<>:"/\\|?*]/g, '').replace(/\s+/g, ' ').trim().slice(0, 80);
|
|
38
60
|
const dir = join(vaultPath, folder);
|
|
39
|
-
mkdirSync(dir, { recursive: true });
|
|
40
61
|
const filePath = join(dir, `${safeTitle}.md`);
|
|
62
|
+
const resolvedPath = require('node:path').resolve(filePath);
|
|
63
|
+
const resolvedVault = require('node:path').resolve(vaultPath);
|
|
64
|
+
if (!resolvedPath.startsWith(resolvedVault)) {
|
|
65
|
+
return { content: [{ type: 'text', text: 'Error: invalid folder path.' }] };
|
|
66
|
+
}
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
41
68
|
writeFileSync(filePath, fullContent, 'utf-8');
|
|
69
|
+
const relatedCount = relatedSection ? relatedSection.split('\n').filter(l => l.startsWith('- ')).length : 0;
|
|
42
70
|
return {
|
|
43
71
|
content: [{
|
|
44
72
|
type: 'text',
|
|
45
|
-
text: `Created
|
|
73
|
+
text: `Created wiki-quality note: "${title}" at ${folder}/${safeTitle}.md\nTags: ${tags.join(', ') || 'none'}\nType: ${type}\nAuto-linked: ${relatedCount} related notes\n\nThe note will appear in the graph after next index.`,
|
|
46
74
|
}],
|
|
47
75
|
};
|
|
48
76
|
},
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SearchEngine } from '../../search/index.js';
|
|
2
|
+
export declare function createAskTool(searchEngine: SearchEngine, vaultPath: string): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {
|
|
8
|
+
question: {
|
|
9
|
+
type: "string";
|
|
10
|
+
description: string;
|
|
11
|
+
};
|
|
12
|
+
save: {
|
|
13
|
+
type: "boolean";
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
required: readonly ["question"];
|
|
18
|
+
};
|
|
19
|
+
handler: (args: {
|
|
20
|
+
question: string;
|
|
21
|
+
save?: boolean;
|
|
22
|
+
}) => Promise<{
|
|
23
|
+
content: {
|
|
24
|
+
type: "text";
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=ask.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// MCP Tool: ask — Q&A with auto-filing to vault
|
|
2
|
+
import { askVault } from '../../intelligence/ask-engine.js';
|
|
3
|
+
export function createAskTool(searchEngine, vaultPath) {
|
|
4
|
+
return {
|
|
5
|
+
name: 'ask',
|
|
6
|
+
description: 'Ask a question about your knowledge base. Searches vault using hybrid AI search (BM25 + vector + RRF), returns structured results with sources. Use the results to compose your own AI-powered answer. Optionally saves as a new note.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
question: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
description: 'The question to ask about your knowledge',
|
|
13
|
+
},
|
|
14
|
+
save: {
|
|
15
|
+
type: 'boolean',
|
|
16
|
+
description: 'Save the answer as a new note in the vault (default: false)',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ['question'],
|
|
20
|
+
},
|
|
21
|
+
handler: async (args) => {
|
|
22
|
+
const result = await askVault(searchEngine, args.question, {
|
|
23
|
+
limit: 10,
|
|
24
|
+
save: args.save ?? false,
|
|
25
|
+
vaultPath,
|
|
26
|
+
});
|
|
27
|
+
const text = [
|
|
28
|
+
result.answer,
|
|
29
|
+
'',
|
|
30
|
+
result.savedTo ? `Saved to: ${result.savedTo}` : '',
|
|
31
|
+
'',
|
|
32
|
+
`Sources: ${result.sources.length} documents found`,
|
|
33
|
+
].filter(Boolean).join('\n');
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: 'text', text }],
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SearchEngine } from '../../search/index.js';
|
|
2
|
+
export declare function createGenerateDraftTool(searchEngine: SearchEngine, vaultPath: string): {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {
|
|
8
|
+
topic: {
|
|
9
|
+
type: string;
|
|
10
|
+
description: string;
|
|
11
|
+
};
|
|
12
|
+
format: {
|
|
13
|
+
type: string;
|
|
14
|
+
enum: string[];
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
maxSources: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
handler: (args: {
|
|
24
|
+
topic?: string;
|
|
25
|
+
format?: string;
|
|
26
|
+
maxSources?: number;
|
|
27
|
+
}) => Promise<{
|
|
28
|
+
content: {
|
|
29
|
+
type: "text";
|
|
30
|
+
text: string;
|
|
31
|
+
}[];
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=generate-draft.d.ts.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// MCP Tool: generate-draft — Claude Code에서 직접 초안 생성
|
|
2
|
+
// Claude가 vault 컨텍스트를 받아 직접 글을 쓰도록 구조화된 재료를 반환
|
|
3
|
+
// 비용: $0 (이미 Claude Code 세션 안)
|
|
4
|
+
import { scanRawDirectory, extractConcepts } from '../../intelligence/wiki-compiler.js';
|
|
5
|
+
import { loadConfig, DEFAULT_FOLDERS } from '../../config.js';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
export function createGenerateDraftTool(searchEngine, vaultPath) {
|
|
9
|
+
return {
|
|
10
|
+
name: 'generate-draft',
|
|
11
|
+
description: 'Gather knowledge from your vault to write a draft. Returns structured context (notes, concepts, excerpts) so you can compose a blog post, report, or outline. Use this when the user asks you to "write a draft", "blog post from my notes", or "summarize my knowledge about X".',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
topic: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description: 'Topic or keyword to focus on. Leave empty for all knowledge.',
|
|
18
|
+
},
|
|
19
|
+
format: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
enum: ['blog', 'report', 'outline'],
|
|
22
|
+
description: 'Desired output format (default: blog)',
|
|
23
|
+
},
|
|
24
|
+
maxSources: {
|
|
25
|
+
type: 'number',
|
|
26
|
+
description: 'Maximum number of source documents to include (default: 10)',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
handler: async (args) => {
|
|
31
|
+
const { topic, format = 'blog', maxSources = 10 } = args;
|
|
32
|
+
const config = loadConfig();
|
|
33
|
+
const folders = config.folders ?? DEFAULT_FOLDERS;
|
|
34
|
+
// 1. 토픽 기반 검색 (있으면)
|
|
35
|
+
let searchResults = [];
|
|
36
|
+
if (topic) {
|
|
37
|
+
const results = await searchEngine.search({ query: topic, limit: maxSources });
|
|
38
|
+
searchResults = results.map(r => ({
|
|
39
|
+
title: r.document?.title ?? 'Untitled',
|
|
40
|
+
content: r.chunk?.content ?? '',
|
|
41
|
+
filePath: r.document?.filePath ?? '',
|
|
42
|
+
score: r.score ?? 0,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
// 2. vault 전체 스캔 (검색 결과가 부족하면)
|
|
46
|
+
const allDocs = [];
|
|
47
|
+
for (const dir of [folders.fleeting, folders.literature, folders.permanent, folders.wiki]) {
|
|
48
|
+
const fullDir = resolve(vaultPath, dir);
|
|
49
|
+
if (existsSync(fullDir)) {
|
|
50
|
+
const docs = scanRawDirectory(fullDir);
|
|
51
|
+
allDocs.push(...docs);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 토픽 필터
|
|
55
|
+
const relevantDocs = topic
|
|
56
|
+
? allDocs.filter(d => d.title.toLowerCase().includes(topic.toLowerCase()) ||
|
|
57
|
+
d.tags.some(t => t.toLowerCase().includes(topic.toLowerCase())) ||
|
|
58
|
+
d.content.toLowerCase().includes(topic.toLowerCase()))
|
|
59
|
+
: allDocs;
|
|
60
|
+
// 3. 개념 추출
|
|
61
|
+
const concepts = extractConcepts(relevantDocs.length > 0 ? relevantDocs : allDocs);
|
|
62
|
+
const topConcepts = [...concepts.entries()]
|
|
63
|
+
.sort((a, b) => b[1].length - a[1].length)
|
|
64
|
+
.slice(0, 8)
|
|
65
|
+
.map(([name, docs]) => ({ name, documentCount: docs.length }));
|
|
66
|
+
// 4. 발췌 준비
|
|
67
|
+
const excerpts = (searchResults.length > 0 ? searchResults : relevantDocs.slice(0, maxSources))
|
|
68
|
+
.map(doc => {
|
|
69
|
+
const body = doc.content
|
|
70
|
+
.replace(/^---[\s\S]*?---\n?/, '') // frontmatter 제거
|
|
71
|
+
.replace(/^#+\s+.+\n/m, '') // 첫 heading 제거
|
|
72
|
+
.trim()
|
|
73
|
+
.slice(0, 500);
|
|
74
|
+
return {
|
|
75
|
+
title: doc.title,
|
|
76
|
+
excerpt: body,
|
|
77
|
+
filePath: doc.filePath,
|
|
78
|
+
};
|
|
79
|
+
})
|
|
80
|
+
.filter(e => e.excerpt.length > 20);
|
|
81
|
+
// 5. 구조화된 컨텍스트 반환
|
|
82
|
+
const context = {
|
|
83
|
+
topic: topic ?? 'All Knowledge',
|
|
84
|
+
format,
|
|
85
|
+
totalDocuments: relevantDocs.length || allDocs.length,
|
|
86
|
+
concepts: topConcepts,
|
|
87
|
+
sources: excerpts,
|
|
88
|
+
instruction: `Use the sources above to write a ${format}. Rules:
|
|
89
|
+
- Only use information from the provided sources
|
|
90
|
+
- Cite sources using [[title]] wikilink format
|
|
91
|
+
- Write in the same language as the source material
|
|
92
|
+
- Add your analysis connecting ideas across sources
|
|
93
|
+
- Format: ${format === 'blog' ? 'Engaging blog post with intro, body sections, conclusion' : format === 'report' ? 'Formal report with executive summary, sections, conclusion' : 'Hierarchical outline with numbered sections'}`,
|
|
94
|
+
};
|
|
95
|
+
const text = `# Draft Context: ${context.topic}
|
|
96
|
+
|
|
97
|
+
## Format: ${format}
|
|
98
|
+
## Sources: ${context.totalDocuments} documents, ${excerpts.length} excerpts
|
|
99
|
+
|
|
100
|
+
## Key Concepts
|
|
101
|
+
${topConcepts.map(c => `- **${c.name}** (${c.documentCount} documents)`).join('\n')}
|
|
102
|
+
|
|
103
|
+
## Source Excerpts
|
|
104
|
+
|
|
105
|
+
${excerpts.map(e => `### ${e.title}
|
|
106
|
+
> ${e.excerpt}
|
|
107
|
+
`).join('\n')}
|
|
108
|
+
|
|
109
|
+
## Instructions
|
|
110
|
+
${context.instruction}
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
Please write the ${format} draft based on the context above. Save the result to the vault's _drafts/ folder.`;
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: 'text', text }],
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=generate-draft.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stellavault/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Stellavault core engine — indexer, hybrid search, vector store, MCP server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./intelligence/youtube-extractor": {
|
|
14
|
+
"import": "./dist/intelligence/youtube-extractor.js",
|
|
15
|
+
"types": "./dist/intelligence/youtube-extractor.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./intelligence/file-extractors": {
|
|
18
|
+
"import": "./dist/intelligence/file-extractors.js",
|
|
19
|
+
"types": "./dist/intelligence/file-extractors.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./intelligence/draft-generator": {
|
|
22
|
+
"import": "./dist/intelligence/draft-generator.js",
|
|
23
|
+
"types": "./dist/intelligence/draft-generator.d.ts"
|
|
12
24
|
}
|
|
13
25
|
},
|
|
14
26
|
"scripts": {
|
|
@@ -26,7 +38,9 @@
|
|
|
26
38
|
"vitest": "^3.0.0"
|
|
27
39
|
},
|
|
28
40
|
"dependencies": {
|
|
41
|
+
"@anthropic-ai/sdk": "^0.82.0",
|
|
29
42
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
43
|
+
"@types/multer": "^2.1.0",
|
|
30
44
|
"@xenova/transformers": "^2.17.2",
|
|
31
45
|
"b4a": "^1.8.0",
|
|
32
46
|
"better-sqlite3": "^12.8.0",
|
|
@@ -35,6 +49,11 @@
|
|
|
35
49
|
"express": "^5.2.1",
|
|
36
50
|
"gray-matter": "^4.0.3",
|
|
37
51
|
"hyperswarm": "^4.17.0",
|
|
38
|
-
"
|
|
52
|
+
"mammoth": "^1.12.0",
|
|
53
|
+
"multer": "^2.1.1",
|
|
54
|
+
"officeparser": "^6.0.7",
|
|
55
|
+
"sqlite-vec": "^0.1.7",
|
|
56
|
+
"unpdf": "^1.4.0",
|
|
57
|
+
"xlsx": "^0.18.5"
|
|
39
58
|
}
|
|
40
59
|
}
|