stellavault 0.4.4 → 0.5.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/README.md +52 -16
- package/SECURITY.md +50 -0
- package/dist/stellavault.js +8968 -0
- package/package.json +70 -33
- package/.env.example +0 -12
- package/CHANGELOG.md +0 -106
- package/CLAUDE.md +0 -42
- package/CONTRIBUTING.md +0 -65
- package/index.html +0 -509
- package/packages/cli/bin/ekh.js +0 -2
- package/packages/cli/bin/stellavault.js +0 -2
- package/packages/cli/dist/commands/adr-cmd.d.ts +0 -7
- package/packages/cli/dist/commands/adr-cmd.js +0 -45
- package/packages/cli/dist/commands/ask-cmd.d.ts +0 -5
- package/packages/cli/dist/commands/ask-cmd.js +0 -36
- package/packages/cli/dist/commands/autopilot-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/autopilot-cmd.js +0 -76
- package/packages/cli/dist/commands/brief-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/brief-cmd.js +0 -82
- package/packages/cli/dist/commands/capture-cmd.d.ts +0 -7
- package/packages/cli/dist/commands/capture-cmd.js +0 -31
- package/packages/cli/dist/commands/card-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/card-cmd.js +0 -26
- package/packages/cli/dist/commands/clip-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/clip-cmd.js +0 -151
- package/packages/cli/dist/commands/cloud-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/cloud-cmd.js +0 -64
- package/packages/cli/dist/commands/compile-cmd.d.ts +0 -6
- package/packages/cli/dist/commands/compile-cmd.js +0 -30
- package/packages/cli/dist/commands/contradictions-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/contradictions-cmd.js +0 -34
- package/packages/cli/dist/commands/decay-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/decay-cmd.js +0 -48
- package/packages/cli/dist/commands/digest-cmd.d.ts +0 -5
- package/packages/cli/dist/commands/digest-cmd.js +0 -136
- package/packages/cli/dist/commands/draft-cmd.d.ts +0 -6
- package/packages/cli/dist/commands/draft-cmd.js +0 -110
- package/packages/cli/dist/commands/duplicates-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/duplicates-cmd.js +0 -30
- package/packages/cli/dist/commands/federate-cmd.d.ts +0 -5
- package/packages/cli/dist/commands/federate-cmd.js +0 -247
- package/packages/cli/dist/commands/fleeting-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/fleeting-cmd.js +0 -45
- package/packages/cli/dist/commands/flush-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/flush-cmd.js +0 -58
- package/packages/cli/dist/commands/gaps-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/gaps-cmd.js +0 -33
- package/packages/cli/dist/commands/graph-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/graph-cmd.js +0 -89
- package/packages/cli/dist/commands/index-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/index-cmd.js +0 -57
- package/packages/cli/dist/commands/ingest-cmd.d.ts +0 -9
- package/packages/cli/dist/commands/ingest-cmd.js +0 -195
- package/packages/cli/dist/commands/init-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/init-cmd.js +0 -161
- package/packages/cli/dist/commands/learn-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/learn-cmd.js +0 -48
- package/packages/cli/dist/commands/lint-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/lint-cmd.js +0 -61
- package/packages/cli/dist/commands/pack-cmd.d.ts +0 -15
- package/packages/cli/dist/commands/pack-cmd.js +0 -93
- package/packages/cli/dist/commands/review-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/review-cmd.js +0 -107
- package/packages/cli/dist/commands/search-cmd.d.ts +0 -4
- package/packages/cli/dist/commands/search-cmd.js +0 -38
- package/packages/cli/dist/commands/serve-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/serve-cmd.js +0 -14
- package/packages/cli/dist/commands/session-cmd.d.ts +0 -7
- package/packages/cli/dist/commands/session-cmd.js +0 -95
- package/packages/cli/dist/commands/status-cmd.d.ts +0 -2
- package/packages/cli/dist/commands/status-cmd.js +0 -33
- package/packages/cli/dist/commands/sync-cmd.d.ts +0 -5
- package/packages/cli/dist/commands/sync-cmd.js +0 -62
- package/packages/cli/dist/commands/vault-cmd.d.ts +0 -10
- package/packages/cli/dist/commands/vault-cmd.js +0 -54
- package/packages/cli/dist/index.d.ts +0 -2
- package/packages/cli/dist/index.js +0 -233
- package/packages/cli/package.json +0 -24
- package/packages/core/dist/api/dashboard.d.ts +0 -3
- package/packages/core/dist/api/dashboard.js +0 -135
- package/packages/core/dist/api/graph-data.d.ts +0 -11
- package/packages/core/dist/api/graph-data.js +0 -255
- package/packages/core/dist/api/pwa.d.ts +0 -3
- package/packages/core/dist/api/pwa.js +0 -77
- package/packages/core/dist/api/server.d.ts +0 -16
- package/packages/core/dist/api/server.js +0 -1122
- package/packages/core/dist/capture/voice.d.ts +0 -24
- package/packages/core/dist/capture/voice.js +0 -135
- package/packages/core/dist/cloud/index.d.ts +0 -3
- package/packages/core/dist/cloud/index.js +0 -2
- package/packages/core/dist/cloud/sync.d.ts +0 -29
- package/packages/core/dist/cloud/sync.js +0 -137
- package/packages/core/dist/config.d.ts +0 -35
- package/packages/core/dist/config.js +0 -63
- package/packages/core/dist/federation/credits.d.ts +0 -26
- package/packages/core/dist/federation/credits.js +0 -56
- package/packages/core/dist/federation/identity.d.ts +0 -14
- package/packages/core/dist/federation/identity.js +0 -74
- package/packages/core/dist/federation/index.d.ts +0 -7
- package/packages/core/dist/federation/index.js +0 -5
- package/packages/core/dist/federation/node.d.ts +0 -31
- package/packages/core/dist/federation/node.js +0 -216
- package/packages/core/dist/federation/privacy.d.ts +0 -8
- package/packages/core/dist/federation/privacy.js +0 -40
- package/packages/core/dist/federation/reputation.d.ts +0 -37
- package/packages/core/dist/federation/reputation.js +0 -139
- package/packages/core/dist/federation/search.d.ts +0 -19
- package/packages/core/dist/federation/search.js +0 -101
- package/packages/core/dist/federation/sharing.d.ts +0 -72
- package/packages/core/dist/federation/sharing.js +0 -246
- package/packages/core/dist/federation/trust.d.ts +0 -15
- package/packages/core/dist/federation/trust.js +0 -60
- package/packages/core/dist/federation/types.d.ts +0 -40
- package/packages/core/dist/federation/types.js +0 -3
- package/packages/core/dist/i18n/index.d.ts +0 -6
- package/packages/core/dist/i18n/index.js +0 -81
- package/packages/core/dist/i18n/note-strings.d.ts +0 -5
- package/packages/core/dist/i18n/note-strings.js +0 -94
- package/packages/core/dist/index.d.ts +0 -123
- package/packages/core/dist/index.js +0 -74
- package/packages/core/dist/indexer/chunker.d.ts +0 -14
- package/packages/core/dist/indexer/chunker.js +0 -148
- package/packages/core/dist/indexer/embedder.d.ts +0 -8
- package/packages/core/dist/indexer/embedder.js +0 -3
- package/packages/core/dist/indexer/index.d.ts +0 -28
- package/packages/core/dist/indexer/index.js +0 -74
- package/packages/core/dist/indexer/local-embedder.d.ts +0 -3
- package/packages/core/dist/indexer/local-embedder.js +0 -29
- package/packages/core/dist/indexer/scanner.d.ts +0 -11
- package/packages/core/dist/indexer/scanner.js +0 -148
- package/packages/core/dist/indexer/watcher.d.ts +0 -19
- package/packages/core/dist/indexer/watcher.js +0 -49
- package/packages/core/dist/intelligence/ask-engine.d.ts +0 -24
- package/packages/core/dist/intelligence/ask-engine.js +0 -142
- package/packages/core/dist/intelligence/auto-linker.d.ts +0 -19
- package/packages/core/dist/intelligence/auto-linker.js +0 -126
- package/packages/core/dist/intelligence/code-linker.d.ts +0 -20
- package/packages/core/dist/intelligence/code-linker.js +0 -88
- package/packages/core/dist/intelligence/contradiction-detector.d.ts +0 -20
- package/packages/core/dist/intelligence/contradiction-detector.js +0 -115
- package/packages/core/dist/intelligence/decay-engine.d.ts +0 -27
- package/packages/core/dist/intelligence/decay-engine.js +0 -190
- package/packages/core/dist/intelligence/draft-generator.d.ts +0 -25
- package/packages/core/dist/intelligence/draft-generator.js +0 -277
- package/packages/core/dist/intelligence/duplicate-detector.d.ts +0 -20
- package/packages/core/dist/intelligence/duplicate-detector.js +0 -55
- package/packages/core/dist/intelligence/file-extractors.d.ts +0 -13
- package/packages/core/dist/intelligence/file-extractors.js +0 -155
- package/packages/core/dist/intelligence/fsrs.d.ts +0 -43
- package/packages/core/dist/intelligence/fsrs.js +0 -70
- package/packages/core/dist/intelligence/gap-detector.d.ts +0 -25
- package/packages/core/dist/intelligence/gap-detector.js +0 -78
- package/packages/core/dist/intelligence/ingest-pipeline.d.ts +0 -32
- package/packages/core/dist/intelligence/ingest-pipeline.js +0 -245
- package/packages/core/dist/intelligence/knowledge-lint.d.ts +0 -27
- package/packages/core/dist/intelligence/knowledge-lint.js +0 -132
- package/packages/core/dist/intelligence/learning-path.d.ts +0 -31
- package/packages/core/dist/intelligence/learning-path.js +0 -53
- package/packages/core/dist/intelligence/notifications.d.ts +0 -31
- package/packages/core/dist/intelligence/notifications.js +0 -65
- package/packages/core/dist/intelligence/predictive-gaps.d.ts +0 -14
- package/packages/core/dist/intelligence/predictive-gaps.js +0 -74
- package/packages/core/dist/intelligence/semantic-versioning.d.ts +0 -37
- package/packages/core/dist/intelligence/semantic-versioning.js +0 -68
- package/packages/core/dist/intelligence/types.d.ts +0 -28
- package/packages/core/dist/intelligence/types.js +0 -3
- package/packages/core/dist/intelligence/wiki-compiler.d.ts +0 -30
- package/packages/core/dist/intelligence/wiki-compiler.js +0 -222
- package/packages/core/dist/intelligence/youtube-extractor.d.ts +0 -29
- package/packages/core/dist/intelligence/youtube-extractor.js +0 -321
- package/packages/core/dist/intelligence/zettelkasten.d.ts +0 -59
- package/packages/core/dist/intelligence/zettelkasten.js +0 -234
- package/packages/core/dist/mcp/custom-tools.d.ts +0 -29
- package/packages/core/dist/mcp/custom-tools.js +0 -70
- package/packages/core/dist/mcp/index.d.ts +0 -2
- package/packages/core/dist/mcp/index.js +0 -2
- package/packages/core/dist/mcp/server.d.ts +0 -51
- package/packages/core/dist/mcp/server.js +0 -174
- package/packages/core/dist/mcp/tools/agentic-graph.d.ts +0 -93
- package/packages/core/dist/mcp/tools/agentic-graph.js +0 -116
- package/packages/core/dist/mcp/tools/ask.d.ts +0 -29
- package/packages/core/dist/mcp/tools/ask.js +0 -43
- package/packages/core/dist/mcp/tools/brief.d.ts +0 -31
- package/packages/core/dist/mcp/tools/brief.js +0 -39
- package/packages/core/dist/mcp/tools/decay.d.ts +0 -33
- package/packages/core/dist/mcp/tools/decay.js +0 -32
- package/packages/core/dist/mcp/tools/decision-journal.d.ts +0 -78
- package/packages/core/dist/mcp/tools/decision-journal.js +0 -79
- package/packages/core/dist/mcp/tools/detect-gaps.d.ts +0 -24
- package/packages/core/dist/mcp/tools/detect-gaps.js +0 -47
- package/packages/core/dist/mcp/tools/export.d.ts +0 -29
- package/packages/core/dist/mcp/tools/export.js +0 -60
- package/packages/core/dist/mcp/tools/federated-search.d.ts +0 -29
- package/packages/core/dist/mcp/tools/federated-search.js +0 -36
- package/packages/core/dist/mcp/tools/generate-claude-md.d.ts +0 -35
- package/packages/core/dist/mcp/tools/generate-claude-md.js +0 -107
- package/packages/core/dist/mcp/tools/generate-draft.d.ts +0 -34
- package/packages/core/dist/mcp/tools/generate-draft.js +0 -120
- package/packages/core/dist/mcp/tools/get-document.d.ts +0 -35
- package/packages/core/dist/mcp/tools/get-document.js +0 -25
- package/packages/core/dist/mcp/tools/get-evolution.d.ts +0 -28
- package/packages/core/dist/mcp/tools/get-evolution.js +0 -70
- package/packages/core/dist/mcp/tools/get-related.d.ts +0 -32
- package/packages/core/dist/mcp/tools/get-related.js +0 -33
- package/packages/core/dist/mcp/tools/learning-path.d.ts +0 -23
- package/packages/core/dist/mcp/tools/learning-path.js +0 -45
- package/packages/core/dist/mcp/tools/link-code.d.ts +0 -34
- package/packages/core/dist/mcp/tools/link-code.js +0 -44
- package/packages/core/dist/mcp/tools/list-topics.d.ts +0 -15
- package/packages/core/dist/mcp/tools/list-topics.js +0 -18
- package/packages/core/dist/mcp/tools/search.d.ts +0 -39
- package/packages/core/dist/mcp/tools/search.js +0 -29
- package/packages/core/dist/mcp/tools/snapshot.d.ts +0 -47
- package/packages/core/dist/mcp/tools/snapshot.js +0 -84
- package/packages/core/dist/multi-vault/index.d.ts +0 -26
- package/packages/core/dist/multi-vault/index.js +0 -80
- package/packages/core/dist/pack/creator.d.ts +0 -21
- package/packages/core/dist/pack/creator.js +0 -105
- package/packages/core/dist/pack/exporter.d.ts +0 -4
- package/packages/core/dist/pack/exporter.js +0 -18
- package/packages/core/dist/pack/importer.d.ts +0 -10
- package/packages/core/dist/pack/importer.js +0 -55
- package/packages/core/dist/pack/index.d.ts +0 -6
- package/packages/core/dist/pack/index.js +0 -5
- package/packages/core/dist/pack/marketplace.d.ts +0 -14
- package/packages/core/dist/pack/marketplace.js +0 -90
- package/packages/core/dist/pack/pii-masker.d.ts +0 -7
- package/packages/core/dist/pack/pii-masker.js +0 -29
- package/packages/core/dist/pack/types.d.ts +0 -36
- package/packages/core/dist/pack/types.js +0 -3
- package/packages/core/dist/plugins/index.d.ts +0 -35
- package/packages/core/dist/plugins/index.js +0 -57
- package/packages/core/dist/plugins/webhooks.d.ts +0 -30
- package/packages/core/dist/plugins/webhooks.js +0 -79
- package/packages/core/dist/search/adaptive.d.ts +0 -16
- package/packages/core/dist/search/adaptive.js +0 -67
- package/packages/core/dist/search/bm25.d.ts +0 -4
- package/packages/core/dist/search/bm25.js +0 -10
- package/packages/core/dist/search/index.d.ts +0 -15
- package/packages/core/dist/search/index.js +0 -64
- package/packages/core/dist/search/rrf.d.ts +0 -7
- package/packages/core/dist/search/rrf.js +0 -21
- package/packages/core/dist/search/semantic.d.ts +0 -5
- package/packages/core/dist/search/semantic.js +0 -6
- package/packages/core/dist/store/index.d.ts +0 -3
- package/packages/core/dist/store/index.js +0 -2
- package/packages/core/dist/store/sqlite-vec.d.ts +0 -6
- package/packages/core/dist/store/sqlite-vec.js +0 -251
- package/packages/core/dist/store/types.d.ts +0 -20
- package/packages/core/dist/store/types.js +0 -3
- package/packages/core/dist/team/index.d.ts +0 -25
- package/packages/core/dist/team/index.js +0 -97
- package/packages/core/dist/types/chunk.d.ts +0 -23
- package/packages/core/dist/types/chunk.js +0 -3
- package/packages/core/dist/types/document.d.ts +0 -23
- package/packages/core/dist/types/document.js +0 -3
- package/packages/core/dist/types/graph.d.ts +0 -39
- package/packages/core/dist/types/graph.js +0 -3
- package/packages/core/dist/types/index.d.ts +0 -5
- package/packages/core/dist/types/index.js +0 -2
- package/packages/core/dist/types/search.d.ts +0 -39
- package/packages/core/dist/types/search.js +0 -3
- package/packages/core/dist/utils/retry.d.ts +0 -25
- package/packages/core/dist/utils/retry.js +0 -59
- package/packages/core/package.json +0 -67
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// Design Ref: §6 — Indexer (scanner: glob + frontmatter 파싱)
|
|
2
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
3
|
-
import { join, relative, extname } from 'node:path';
|
|
4
|
-
import { createHash } from 'node:crypto';
|
|
5
|
-
import matter from 'gray-matter';
|
|
6
|
-
/**
|
|
7
|
-
* vault 디렉토리에서 모든 .md 파일을 스캔하여 Document 목록 반환
|
|
8
|
-
*/
|
|
9
|
-
export function scanVault(vaultPath) {
|
|
10
|
-
const documents = [];
|
|
11
|
-
let skippedFiles = 0;
|
|
12
|
-
const mdFiles = findMdFiles(vaultPath);
|
|
13
|
-
for (const filePath of mdFiles) {
|
|
14
|
-
try {
|
|
15
|
-
const doc = parseDocument(vaultPath, filePath);
|
|
16
|
-
documents.push(doc);
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
skippedFiles++;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return { documents, scannedFiles: mdFiles.length, skippedFiles };
|
|
23
|
-
}
|
|
24
|
-
function findMdFiles(dir, files = []) {
|
|
25
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
26
|
-
const fullPath = join(dir, entry.name);
|
|
27
|
-
if (entry.isDirectory()) {
|
|
28
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'zh-CN')
|
|
29
|
-
continue;
|
|
30
|
-
findMdFiles(fullPath, files);
|
|
31
|
-
}
|
|
32
|
-
else if (extname(entry.name) === '.md') {
|
|
33
|
-
files.push(fullPath);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return files;
|
|
37
|
-
}
|
|
38
|
-
function parseDocument(vaultPath, filePath) {
|
|
39
|
-
const raw = readFileSync(filePath, 'utf-8');
|
|
40
|
-
const stat = statSync(filePath);
|
|
41
|
-
const { data: frontmatter, content } = matter(raw);
|
|
42
|
-
const relativePath = relative(vaultPath, filePath).replace(/\\/g, '/');
|
|
43
|
-
const id = createHash('sha256').update(relativePath).digest('hex').slice(0, 16);
|
|
44
|
-
const contentHash = createHash('sha256').update(raw).digest('hex').slice(0, 16);
|
|
45
|
-
const title = frontmatter.title
|
|
46
|
-
?? extractFirstHeading(content)
|
|
47
|
-
?? relativePath.replace(/\.md$/, '');
|
|
48
|
-
const tags = extractTags(frontmatter, content);
|
|
49
|
-
// Obsidian frontmatter 호환: aliases를 tags에 추가
|
|
50
|
-
if (frontmatter.aliases) {
|
|
51
|
-
const aliases = Array.isArray(frontmatter.aliases) ? frontmatter.aliases : [frontmatter.aliases];
|
|
52
|
-
for (const alias of aliases) {
|
|
53
|
-
if (typeof alias === 'string' && alias.length > 1)
|
|
54
|
-
tags.push(alias);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// source/type 자동 추출 (원본 파일 수정 없이 DB에만 저장)
|
|
58
|
-
const source = inferSource(frontmatter, relativePath);
|
|
59
|
-
const type = inferType(frontmatter, relativePath);
|
|
60
|
-
// Obsidian frontmatter 호환: date/created 우선 사용
|
|
61
|
-
const fmDate = frontmatter.date ?? frontmatter.created ?? frontmatter.created_at;
|
|
62
|
-
const lastModified = fmDate ? new Date(fmDate).toISOString() : stat.mtime.toISOString();
|
|
63
|
-
return {
|
|
64
|
-
id,
|
|
65
|
-
filePath: relativePath,
|
|
66
|
-
title,
|
|
67
|
-
content,
|
|
68
|
-
frontmatter,
|
|
69
|
-
tags,
|
|
70
|
-
lastModified,
|
|
71
|
-
contentHash,
|
|
72
|
-
source,
|
|
73
|
-
type,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function inferSource(frontmatter, filePath) {
|
|
77
|
-
// frontmatter에 명시된 경우
|
|
78
|
-
if (frontmatter.source && typeof frontmatter.source === 'string') {
|
|
79
|
-
if (frontmatter.source.startsWith('http'))
|
|
80
|
-
return 'clip';
|
|
81
|
-
return frontmatter.source;
|
|
82
|
-
}
|
|
83
|
-
// 경로 기반 추론
|
|
84
|
-
if (filePath.includes('clips/') || filePath.includes('clip/'))
|
|
85
|
-
return 'clip';
|
|
86
|
-
if (filePath.includes('PDCA') || filePath.includes('pdca'))
|
|
87
|
-
return 'local';
|
|
88
|
-
if (frontmatter['x-i18n'])
|
|
89
|
-
return 'notion'; // Notion 번역 문서
|
|
90
|
-
if (frontmatter.clipped)
|
|
91
|
-
return 'clip';
|
|
92
|
-
return 'local';
|
|
93
|
-
}
|
|
94
|
-
function inferType(frontmatter, filePath) {
|
|
95
|
-
// frontmatter에 명시된 경우
|
|
96
|
-
if (frontmatter.type && typeof frontmatter.type === 'string')
|
|
97
|
-
return frontmatter.type;
|
|
98
|
-
// tags 기반
|
|
99
|
-
const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
|
|
100
|
-
if (tags.includes('bridge') || tags.includes('auto-generated'))
|
|
101
|
-
return 'bridge';
|
|
102
|
-
if (tags.includes('clip') || tags.includes('youtube'))
|
|
103
|
-
return 'clip';
|
|
104
|
-
if (tags.includes('decision'))
|
|
105
|
-
return 'decision';
|
|
106
|
-
// 경로 기반
|
|
107
|
-
if (filePath.includes('clips/'))
|
|
108
|
-
return 'clip';
|
|
109
|
-
if (filePath.includes('Decisions/') || filePath.includes('decisions/'))
|
|
110
|
-
return 'decision';
|
|
111
|
-
if (filePath.includes('Sessions/') || filePath.includes('sessions/'))
|
|
112
|
-
return 'session';
|
|
113
|
-
if (filePath.includes('Research/'))
|
|
114
|
-
return 'research';
|
|
115
|
-
if (filePath.includes('Lessons/'))
|
|
116
|
-
return 'lesson';
|
|
117
|
-
if (filePath.includes('Templates/'))
|
|
118
|
-
return 'template';
|
|
119
|
-
return 'note';
|
|
120
|
-
}
|
|
121
|
-
function extractFirstHeading(content) {
|
|
122
|
-
const match = content.match(/^#\s+(.+)$/m);
|
|
123
|
-
return match ? match[1].trim() : null;
|
|
124
|
-
}
|
|
125
|
-
function extractTags(frontmatter, content) {
|
|
126
|
-
const tags = new Set();
|
|
127
|
-
// frontmatter tags
|
|
128
|
-
const fmTags = frontmatter.tags;
|
|
129
|
-
if (Array.isArray(fmTags)) {
|
|
130
|
-
fmTags.forEach(t => tags.add(String(t)));
|
|
131
|
-
}
|
|
132
|
-
else if (typeof fmTags === 'string') {
|
|
133
|
-
fmTags.split(',').map(t => t.trim()).filter(Boolean).forEach(t => tags.add(t));
|
|
134
|
-
}
|
|
135
|
-
// inline #tags (CSS 컬러코드, 순수 숫자, heading # 제외)
|
|
136
|
-
const inlineTags = content.match(/(?:^|\s)#([a-zA-Z가-힣][a-zA-Z가-힣\w-]*)/g);
|
|
137
|
-
if (inlineTags) {
|
|
138
|
-
for (const raw of inlineTags) {
|
|
139
|
-
const tag = raw.trim().slice(1);
|
|
140
|
-
// CSS hex 컬러 (#fff, #6c5ce7 등) 제외
|
|
141
|
-
if (/^[0-9a-fA-F]{3,8}$/.test(tag))
|
|
142
|
-
continue;
|
|
143
|
-
tags.add(tag);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return [...tags];
|
|
147
|
-
}
|
|
148
|
-
//# sourceMappingURL=scanner.js.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Embedder } from './embedder.js';
|
|
2
|
-
import type { VectorStore } from '../store/types.js';
|
|
3
|
-
import type { ChunkOptions } from './chunker.js';
|
|
4
|
-
export interface WatcherOptions {
|
|
5
|
-
vaultPath: string;
|
|
6
|
-
store: VectorStore;
|
|
7
|
-
embedder: Embedder;
|
|
8
|
-
chunkOptions?: Partial<ChunkOptions>;
|
|
9
|
-
debounceMs?: number;
|
|
10
|
-
onReindex?: (result: {
|
|
11
|
-
indexed: number;
|
|
12
|
-
skipped: number;
|
|
13
|
-
}) => void;
|
|
14
|
-
}
|
|
15
|
-
export declare function createWatcher(options: WatcherOptions): {
|
|
16
|
-
start(): void;
|
|
17
|
-
stop(): void;
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// Design Ref: §6.3 — 파일 감시 + 증분 인덱싱 (debounce 5s)
|
|
2
|
-
import { watch } from 'chokidar';
|
|
3
|
-
import { extname } from 'node:path';
|
|
4
|
-
import { indexVault } from './index.js';
|
|
5
|
-
export function createWatcher(options) {
|
|
6
|
-
const { vaultPath, store, embedder, chunkOptions, debounceMs = 5000, onReindex } = options;
|
|
7
|
-
let watcher = null;
|
|
8
|
-
let debounceTimer = null;
|
|
9
|
-
let reindexing = false;
|
|
10
|
-
async function triggerReindex() {
|
|
11
|
-
if (reindexing)
|
|
12
|
-
return;
|
|
13
|
-
reindexing = true;
|
|
14
|
-
try {
|
|
15
|
-
const result = await indexVault(vaultPath, { store, embedder, chunkOptions });
|
|
16
|
-
onReindex?.({ indexed: result.indexed, skipped: result.skipped });
|
|
17
|
-
}
|
|
18
|
-
finally {
|
|
19
|
-
reindexing = false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function scheduleReindex() {
|
|
23
|
-
if (debounceTimer)
|
|
24
|
-
clearTimeout(debounceTimer);
|
|
25
|
-
debounceTimer = setTimeout(() => triggerReindex(), debounceMs);
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
start() {
|
|
29
|
-
watcher = watch(vaultPath, {
|
|
30
|
-
ignored: /(^|[\/\\])\.|node_modules/,
|
|
31
|
-
persistent: true,
|
|
32
|
-
ignoreInitial: true,
|
|
33
|
-
});
|
|
34
|
-
watcher.on('all', (event, path) => {
|
|
35
|
-
if (extname(path) !== '.md')
|
|
36
|
-
return;
|
|
37
|
-
if (['add', 'change', 'unlink'].includes(event)) {
|
|
38
|
-
scheduleReindex();
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
stop() {
|
|
43
|
-
if (debounceTimer)
|
|
44
|
-
clearTimeout(debounceTimer);
|
|
45
|
-
watcher?.close();
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
//# sourceMappingURL=watcher.js.map
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { SearchEngine } from '../search/index.js';
|
|
2
|
-
export interface AskResult {
|
|
3
|
-
question: string;
|
|
4
|
-
answer: string;
|
|
5
|
-
sources: Array<{
|
|
6
|
-
title: string;
|
|
7
|
-
filePath: string;
|
|
8
|
-
score: number;
|
|
9
|
-
snippet: string;
|
|
10
|
-
}>;
|
|
11
|
-
savedTo: string | null;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* 질문에 대해 vault를 검색하고 구조화된 답변을 생성.
|
|
15
|
-
* LLM 없이 검색 결과를 구조화하는 버전 (LLM 연동은 MCP ask tool에서 처리).
|
|
16
|
-
*/
|
|
17
|
-
export declare function askVault(searchEngine: SearchEngine, question: string, options?: {
|
|
18
|
-
limit?: number;
|
|
19
|
-
save?: boolean;
|
|
20
|
-
vaultPath?: string;
|
|
21
|
-
outputDir?: string;
|
|
22
|
-
mode?: 'default' | 'quotes';
|
|
23
|
-
}): Promise<AskResult>;
|
|
24
|
-
//# sourceMappingURL=ask-engine.d.ts.map
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// Feature: stellavault ask — Q&A + auto-filing
|
|
2
|
-
// vault 대상 자연어 질문 → 검색 → 답변 구조화 → vault에 .md 저장
|
|
3
|
-
import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
4
|
-
import { join, resolve } from 'node:path';
|
|
5
|
-
/**
|
|
6
|
-
* 질문에 대해 vault를 검색하고 구조화된 답변을 생성.
|
|
7
|
-
* LLM 없이 검색 결과를 구조화하는 버전 (LLM 연동은 MCP ask tool에서 처리).
|
|
8
|
-
*/
|
|
9
|
-
export async function askVault(searchEngine, question, options = {}) {
|
|
10
|
-
const { limit = 10, save = false, vaultPath, outputDir = '_stellavault/answers', mode = 'default' } = options;
|
|
11
|
-
// 1. 검색
|
|
12
|
-
const results = await searchEngine.search({ query: question, limit });
|
|
13
|
-
// 2. 소스 정리
|
|
14
|
-
const sources = results.map((r) => ({
|
|
15
|
-
title: r.document.title,
|
|
16
|
-
filePath: r.document.filePath,
|
|
17
|
-
score: Math.round(r.score * 1000) / 1000,
|
|
18
|
-
snippet: r.chunk?.content?.substring(0, 200) ?? '',
|
|
19
|
-
}));
|
|
20
|
-
// 3. 답변 구성 (검색 결과 기반 구조화)
|
|
21
|
-
const answer = mode === 'quotes'
|
|
22
|
-
? composeQuotes(question, results)
|
|
23
|
-
: composeAnswer(question, results);
|
|
24
|
-
// 4. vault에 저장 (선택)
|
|
25
|
-
let savedTo = null;
|
|
26
|
-
if (save && vaultPath) {
|
|
27
|
-
savedTo = saveAnswerToVault(question, answer, sources, vaultPath, outputDir);
|
|
28
|
-
}
|
|
29
|
-
return { question, answer, sources, savedTo };
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* 검색 결과를 구조화된 답변으로 구성.
|
|
33
|
-
* LLM 없이도 유용한 요약을 만듦.
|
|
34
|
-
*/
|
|
35
|
-
function composeAnswer(question, results) {
|
|
36
|
-
if (results.length === 0) {
|
|
37
|
-
return `No results found for "${question}". Try different keywords or create a note on this topic.`;
|
|
38
|
-
}
|
|
39
|
-
const lines = [];
|
|
40
|
-
lines.push(`## ${question}\n`);
|
|
41
|
-
// Related documents
|
|
42
|
-
lines.push(`### Related Documents (${results.length})\n`);
|
|
43
|
-
for (const r of results.slice(0, 5)) {
|
|
44
|
-
const score = Math.round(r.score * 100);
|
|
45
|
-
lines.push(`- **${r.document.title}** (${score}% 관련)`);
|
|
46
|
-
if (r.chunk?.content) {
|
|
47
|
-
const snippet = r.chunk.content.substring(0, 150).replace(/\n/g, ' ').trim();
|
|
48
|
-
lines.push(` > ${snippet}...`);
|
|
49
|
-
}
|
|
50
|
-
if (r.document.tags.length > 0) {
|
|
51
|
-
lines.push(` Tags: ${r.document.tags.map(t => `#${t}`).join(' ')}`);
|
|
52
|
-
}
|
|
53
|
-
lines.push('');
|
|
54
|
-
}
|
|
55
|
-
// Related tags
|
|
56
|
-
const allTags = new Set();
|
|
57
|
-
for (const r of results) {
|
|
58
|
-
r.document.tags.forEach((t) => allTags.add(t));
|
|
59
|
-
}
|
|
60
|
-
if (allTags.size > 0) {
|
|
61
|
-
lines.push(`### Related Tags`);
|
|
62
|
-
lines.push([...allTags].map(t => `#${t}`).join(' '));
|
|
63
|
-
lines.push('');
|
|
64
|
-
}
|
|
65
|
-
// Explore further
|
|
66
|
-
lines.push(`### Explore Further`);
|
|
67
|
-
lines.push(`- Dig deeper: \`stellavault ask "${question} advanced"\``);
|
|
68
|
-
lines.push(`- Find knowledge gaps: \`stellavault gaps\``);
|
|
69
|
-
return lines.join('\n');
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Quotes 모드: 원문 인용을 카드 형태로 나열.
|
|
73
|
-
*/
|
|
74
|
-
function composeQuotes(question, results) {
|
|
75
|
-
if (results.length === 0) {
|
|
76
|
-
return `No quotes found for "${question}".`;
|
|
77
|
-
}
|
|
78
|
-
const lines = [];
|
|
79
|
-
lines.push(`## Quotes: "${question}"\n`);
|
|
80
|
-
lines.push(`*${results.length} sources found. Each quote is a direct excerpt.*\n`);
|
|
81
|
-
for (let i = 0; i < Math.min(8, results.length); i++) {
|
|
82
|
-
const r = results[i];
|
|
83
|
-
const quote = r.chunk?.content
|
|
84
|
-
?.replace(/^---[\s\S]*?---\n?/, '')
|
|
85
|
-
?.replace(/^#+\s+.+\n/m, '')
|
|
86
|
-
?.trim()
|
|
87
|
-
?.substring(0, 300) ?? '';
|
|
88
|
-
if (!quote)
|
|
89
|
-
continue;
|
|
90
|
-
const score = Math.round(r.score * 100);
|
|
91
|
-
lines.push(`---`);
|
|
92
|
-
lines.push(`**[${i + 1}] ${r.document.title}** (${score}% match)`);
|
|
93
|
-
lines.push(`> ${quote.replace(/\n/g, '\n> ')}`);
|
|
94
|
-
if (r.document.tags.length > 0) {
|
|
95
|
-
lines.push(`Tags: ${r.document.tags.slice(0, 5).map(t => `#${t}`).join(' ')}`);
|
|
96
|
-
}
|
|
97
|
-
lines.push('');
|
|
98
|
-
}
|
|
99
|
-
lines.push('---');
|
|
100
|
-
lines.push(`*Use \`stellavault ask "${question}" --save\` to save these quotes as a note.*`);
|
|
101
|
-
return lines.join('\n');
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* 답변을 vault에 .md 파일로 저장.
|
|
105
|
-
* "_stellavault/answers/YYYY-MM-DD-question.md" 형식.
|
|
106
|
-
*/
|
|
107
|
-
function saveAnswerToVault(question, answer, sources, vaultPath, outputDir) {
|
|
108
|
-
const dir = resolve(vaultPath, outputDir);
|
|
109
|
-
if (!existsSync(dir)) {
|
|
110
|
-
mkdirSync(dir, { recursive: true });
|
|
111
|
-
}
|
|
112
|
-
const date = new Date().toISOString().split('T')[0];
|
|
113
|
-
const slug = question
|
|
114
|
-
.toLowerCase()
|
|
115
|
-
.replace(/[^a-z0-9가-힣\s]/g, '')
|
|
116
|
-
.replace(/\s+/g, '-')
|
|
117
|
-
.substring(0, 60);
|
|
118
|
-
const filename = `${date}-${slug}.md`;
|
|
119
|
-
const filePath = join(outputDir, filename);
|
|
120
|
-
const fullPath = resolve(vaultPath, filePath);
|
|
121
|
-
// vault 경로 내인지 확인 (path traversal 방지)
|
|
122
|
-
if (!fullPath.startsWith(resolve(vaultPath))) {
|
|
123
|
-
throw new Error('Invalid output path');
|
|
124
|
-
}
|
|
125
|
-
const content = [
|
|
126
|
-
'---',
|
|
127
|
-
`title: "${question}"`,
|
|
128
|
-
`date: ${new Date().toISOString()}`,
|
|
129
|
-
'type: answer',
|
|
130
|
-
'source: stellavault-ask',
|
|
131
|
-
`tags: [${sources.slice(0, 5).flatMap(s => s.title.split(/\s+/).slice(0, 2)).map(t => `"${t}"`).join(', ')}]`,
|
|
132
|
-
'---',
|
|
133
|
-
'',
|
|
134
|
-
answer,
|
|
135
|
-
'',
|
|
136
|
-
'---',
|
|
137
|
-
'*Generated by `stellavault ask`. Sources from your vault.*',
|
|
138
|
-
].join('\n');
|
|
139
|
-
writeFileSync(fullPath, content, 'utf-8');
|
|
140
|
-
return filePath;
|
|
141
|
-
}
|
|
142
|
-
//# sourceMappingURL=ask-engine.js.map
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type FolderNames } from '../config.js';
|
|
2
|
-
/**
|
|
3
|
-
* vault의 모든 .md 노트 제목을 수집.
|
|
4
|
-
* frontmatter title 또는 첫 heading 또는 파일명.
|
|
5
|
-
*/
|
|
6
|
-
export declare function collectVaultTitles(vaultPath: string, folders?: FolderNames): string[];
|
|
7
|
-
/**
|
|
8
|
-
* 노트 본문에서 기존 노트 제목/핵심 구문과 매칭하여 [[wikilink]]를 삽입.
|
|
9
|
-
* - 이미 [[...]]로 감싸진 부분은 건드리지 않음
|
|
10
|
-
* - frontmatter 영역은 건드리지 않음
|
|
11
|
-
* - 자기 자신의 제목은 건드리지 않음
|
|
12
|
-
* - heading(#) 줄은 건드리지 않음
|
|
13
|
-
*/
|
|
14
|
-
export declare function insertWikilinks(body: string, vaultTitles: string[], selfTitle?: string): string;
|
|
15
|
-
/**
|
|
16
|
-
* vault 스캔 + wikilink 삽입을 한 번에 수행하는 편의 함수.
|
|
17
|
-
*/
|
|
18
|
-
export declare function autoLink(body: string, vaultPath: string, selfTitle?: string, folders?: FolderNames): string;
|
|
19
|
-
//# sourceMappingURL=auto-linker.d.ts.map
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
// Auto-Linker: 노트 본문에서 기존 노트 제목과 매칭하여 [[wikilink]] 자동 삽입
|
|
2
|
-
// 제텔카스텐 핵심: 노트 간 연결이 지식 네트워크를 만든다
|
|
3
|
-
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
4
|
-
import { join, resolve, extname } from 'node:path';
|
|
5
|
-
import { DEFAULT_FOLDERS } from '../config.js';
|
|
6
|
-
/**
|
|
7
|
-
* vault의 모든 .md 노트 제목을 수집.
|
|
8
|
-
* frontmatter title 또는 첫 heading 또는 파일명.
|
|
9
|
-
*/
|
|
10
|
-
export function collectVaultTitles(vaultPath, folders = DEFAULT_FOLDERS) {
|
|
11
|
-
const titles = new Set();
|
|
12
|
-
const dirs = [folders.fleeting, folders.literature, folders.permanent, folders.wiki, '_drafts'];
|
|
13
|
-
for (const dir of dirs) {
|
|
14
|
-
const fullDir = resolve(vaultPath, dir);
|
|
15
|
-
if (!existsSync(fullDir))
|
|
16
|
-
continue;
|
|
17
|
-
try {
|
|
18
|
-
const files = readdirSync(fullDir).filter(f => extname(f) === '.md');
|
|
19
|
-
for (const file of files) {
|
|
20
|
-
try {
|
|
21
|
-
const content = readFileSync(join(fullDir, file), 'utf-8');
|
|
22
|
-
// frontmatter title
|
|
23
|
-
const titleMatch = content.match(/^title:\s*"?([^"\n]+)"?\s*$/m);
|
|
24
|
-
if (titleMatch && titleMatch[1].length > 2) {
|
|
25
|
-
titles.add(titleMatch[1].trim());
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
// first heading
|
|
29
|
-
const headingMatch = content.match(/^#\s+(.+)$/m);
|
|
30
|
-
if (headingMatch && headingMatch[1].length > 2) {
|
|
31
|
-
titles.add(headingMatch[1].trim());
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
// filename without extension + timestamp prefix
|
|
35
|
-
const name = file.replace(/^\d{4}-\d{2}-\d{2}T[\d-]+-/, '').replace(/\.md$/, '').replace(/-/g, ' ');
|
|
36
|
-
if (name.length > 2)
|
|
37
|
-
titles.add(name);
|
|
38
|
-
}
|
|
39
|
-
catch { /* skip unreadable files */ }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch { /* dir not readable */ }
|
|
43
|
-
}
|
|
44
|
-
return [...titles].filter(t => t.length > 3); // ignore very short titles
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* 제목에서 링크 가능한 핵심 구문(phrases)을 추출.
|
|
48
|
-
* 예: "Steve Jobs' 2005 Stanford Commencement Address"
|
|
49
|
-
* → ["Steve Jobs", "Stanford Commencement Address", "Stanford"]
|
|
50
|
-
*/
|
|
51
|
-
function extractLinkablePhrases(title) {
|
|
52
|
-
const phrases = [];
|
|
53
|
-
// 전체 제목 (짧으면)
|
|
54
|
-
if (title.length <= 40)
|
|
55
|
-
phrases.push(title);
|
|
56
|
-
// 고유명사/핵심 구문 추출: 대문자로 시작하는 연속 단어
|
|
57
|
-
const properNouns = title.match(/[A-Z\uAC00-\uD7A3][a-z\uAC00-\uD7A3]+(?:\s+[A-Z\uAC00-\uD7A3][a-z\uAC00-\uD7A3]+)*/g);
|
|
58
|
-
if (properNouns) {
|
|
59
|
-
for (const pn of properNouns) {
|
|
60
|
-
if (pn.length > 4)
|
|
61
|
-
phrases.push(pn);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// 한국어: 긴 제목에서 의미 있는 부분 (4글자 이상 한글 연속)
|
|
65
|
-
const koreanPhrases = title.match(/[\uAC00-\uD7A3]{4,}/g);
|
|
66
|
-
if (koreanPhrases)
|
|
67
|
-
phrases.push(...koreanPhrases);
|
|
68
|
-
return [...new Set(phrases)].filter(p => p.length > 3);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* 노트 본문에서 기존 노트 제목/핵심 구문과 매칭하여 [[wikilink]]를 삽입.
|
|
72
|
-
* - 이미 [[...]]로 감싸진 부분은 건드리지 않음
|
|
73
|
-
* - frontmatter 영역은 건드리지 않음
|
|
74
|
-
* - 자기 자신의 제목은 건드리지 않음
|
|
75
|
-
* - heading(#) 줄은 건드리지 않음
|
|
76
|
-
*/
|
|
77
|
-
export function insertWikilinks(body, vaultTitles, selfTitle) {
|
|
78
|
-
if (vaultTitles.length === 0)
|
|
79
|
-
return body;
|
|
80
|
-
// frontmatter 분리
|
|
81
|
-
const fmMatch = body.match(/^(---[\s\S]*?---\n?)/);
|
|
82
|
-
const frontmatter = fmMatch ? fmMatch[1] : '';
|
|
83
|
-
let content = fmMatch ? body.slice(frontmatter.length) : body;
|
|
84
|
-
// 제목 → 링크 가능 구문 매핑
|
|
85
|
-
const phraseToTitle = new Map();
|
|
86
|
-
for (const title of vaultTitles) {
|
|
87
|
-
if (title === selfTitle)
|
|
88
|
-
continue;
|
|
89
|
-
for (const phrase of extractLinkablePhrases(title)) {
|
|
90
|
-
if (!phraseToTitle.has(phrase)) {
|
|
91
|
-
phraseToTitle.set(phrase, title);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// 긴 구문부터 매칭 (짧은 구문이 긴 구문의 부분 매칭되는 것 방지)
|
|
96
|
-
const sortedPhrases = [...phraseToTitle.keys()].sort((a, b) => b.length - a.length);
|
|
97
|
-
const linkedPhrases = new Set(); // 이미 링크된 구문 (중복 방지)
|
|
98
|
-
const contentLower = content.toLowerCase();
|
|
99
|
-
for (const phrase of sortedPhrases) {
|
|
100
|
-
const targetTitle = phraseToTitle.get(phrase);
|
|
101
|
-
if (linkedPhrases.has(targetTitle))
|
|
102
|
-
continue;
|
|
103
|
-
// 사전 필터: 본문에 포함되지 않으면 regex 생성 스킵 (성능 최적화)
|
|
104
|
-
if (!contentLower.includes(phrase.toLowerCase()))
|
|
105
|
-
continue;
|
|
106
|
-
const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
107
|
-
const regex = new RegExp(`(?<!\\[\\[)(?<!#\\s)\\b(${escaped})\\b(?!\\]\\])(?![^\\[]*\\]\\])`, 'gi');
|
|
108
|
-
let replaced = false;
|
|
109
|
-
content = content.replace(regex, (match) => {
|
|
110
|
-
if (replaced)
|
|
111
|
-
return match;
|
|
112
|
-
replaced = true;
|
|
113
|
-
linkedPhrases.add(targetTitle);
|
|
114
|
-
return `[[${targetTitle}|${match}]]`;
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
return frontmatter + content;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* vault 스캔 + wikilink 삽입을 한 번에 수행하는 편의 함수.
|
|
121
|
-
*/
|
|
122
|
-
export function autoLink(body, vaultPath, selfTitle, folders = DEFAULT_FOLDERS) {
|
|
123
|
-
const titles = collectVaultTitles(vaultPath, folders);
|
|
124
|
-
return insertWikilinks(body, titles, selfTitle);
|
|
125
|
-
}
|
|
126
|
-
//# sourceMappingURL=auto-linker.js.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { SearchEngine } from '../search/index.js';
|
|
2
|
-
export interface CodeLink {
|
|
3
|
-
filePath: string;
|
|
4
|
-
keywords: string[];
|
|
5
|
-
relatedNotes: Array<{
|
|
6
|
-
documentId: string;
|
|
7
|
-
title: string;
|
|
8
|
-
score: number;
|
|
9
|
-
matchedKeywords: string[];
|
|
10
|
-
}>;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Extract meaningful keywords from a code file path and optional content.
|
|
14
|
-
*/
|
|
15
|
-
export declare function extractCodeKeywords(filePath: string, content?: string): string[];
|
|
16
|
-
/**
|
|
17
|
-
* Link a code file to related knowledge notes via keyword search.
|
|
18
|
-
*/
|
|
19
|
-
export declare function linkCodeToKnowledge(searchEngine: SearchEngine, filePath: string, content?: string, limit?: number): Promise<CodeLink>;
|
|
20
|
-
//# sourceMappingURL=code-linker.d.ts.map
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// Design Ref: F15 — 코드-지식 링커
|
|
2
|
-
// 코드 파일/함수에서 키워드 추출 → 관련 노트 자동 매칭
|
|
3
|
-
// Plan SC: SC-03 코드 파일에서 관련 노트 3개+ 반환
|
|
4
|
-
/**
|
|
5
|
-
* Extract meaningful keywords from a code file path and optional content.
|
|
6
|
-
*/
|
|
7
|
-
export function extractCodeKeywords(filePath, content) {
|
|
8
|
-
const keywords = new Set();
|
|
9
|
-
// From file path: directory names + file name parts
|
|
10
|
-
const skipParts = new Set(['src', 'lib', 'dist', 'node_modules', '.', '']);
|
|
11
|
-
const parts = filePath
|
|
12
|
-
.replace(/\\/g, '/')
|
|
13
|
-
.split('/')
|
|
14
|
-
.filter((p) => !skipParts.has(p));
|
|
15
|
-
for (const part of parts) {
|
|
16
|
-
// Split camelCase/PascalCase/kebab-case/snake_case
|
|
17
|
-
const words = part
|
|
18
|
-
.replace(/\.[^.]+$/, '') // remove extension
|
|
19
|
-
.replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase split
|
|
20
|
-
.replace(/[-_]/g, ' ')
|
|
21
|
-
.split(/\s+/)
|
|
22
|
-
.filter((w) => w.length > 2)
|
|
23
|
-
.map((w) => w.toLowerCase());
|
|
24
|
-
words.forEach((w) => keywords.add(w));
|
|
25
|
-
}
|
|
26
|
-
// From content: extract identifiers and comments
|
|
27
|
-
if (content) {
|
|
28
|
-
// Extract import statements
|
|
29
|
-
const imports = content.match(/import\s+.*?from\s+['"]([^'"]+)['"]/g) ?? [];
|
|
30
|
-
for (const imp of imports) {
|
|
31
|
-
const match = imp.match(/from\s+['"]([^'"]+)['"]/);
|
|
32
|
-
if (match) {
|
|
33
|
-
const modName = match[1].split('/').pop()?.replace(/\.[^.]+$/, '') ?? '';
|
|
34
|
-
if (modName.length > 2)
|
|
35
|
-
keywords.add(modName.toLowerCase());
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// Extract TODO/FIXME comments
|
|
39
|
-
const comments = content.match(/\/\/\s*(TODO|FIXME|NOTE):\s*(.+)/gi) ?? [];
|
|
40
|
-
for (const c of comments) {
|
|
41
|
-
const words = c.replace(/\/\/\s*(TODO|FIXME|NOTE):\s*/i, '').split(/\s+/);
|
|
42
|
-
words.filter((w) => w.length > 3).forEach((w) => keywords.add(w.toLowerCase()));
|
|
43
|
-
}
|
|
44
|
-
// Extract function/class names
|
|
45
|
-
const funcNames = content.match(/(?:function|class|const|let|var)\s+([A-Za-z]\w{3,})/g) ?? [];
|
|
46
|
-
for (const fn of funcNames) {
|
|
47
|
-
const name = fn.split(/\s+/)[1];
|
|
48
|
-
if (name) {
|
|
49
|
-
// Split camelCase
|
|
50
|
-
const words = name.replace(/([a-z])([A-Z])/g, '$1 $2').split(/\s+/);
|
|
51
|
-
words.filter((w) => w.length > 2).forEach((w) => keywords.add(w.toLowerCase()));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Remove common noise words
|
|
56
|
-
const noise = new Set(['the', 'and', 'for', 'with', 'from', 'this', 'that', 'not', 'but', 'are', 'was', 'has', 'have', 'new', 'get', 'set', 'use', 'type', 'void', 'null', 'true', 'false', 'return', 'async', 'await', 'export', 'default', 'import', 'const', 'string', 'number', 'boolean', 'interface', 'function', 'index', 'main', 'app', 'test', 'spec', 'utils', 'helpers']);
|
|
57
|
-
return [...keywords].filter((k) => !noise.has(k)).slice(0, 20);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Link a code file to related knowledge notes via keyword search.
|
|
61
|
-
*/
|
|
62
|
-
export async function linkCodeToKnowledge(searchEngine, filePath, content, limit = 5) {
|
|
63
|
-
const keywords = extractCodeKeywords(filePath, content);
|
|
64
|
-
if (keywords.length === 0) {
|
|
65
|
-
return { filePath, keywords: [], relatedNotes: [] };
|
|
66
|
-
}
|
|
67
|
-
// Search using top keywords as query
|
|
68
|
-
const query = keywords.slice(0, 8).join(' ');
|
|
69
|
-
const results = await searchEngine.search({
|
|
70
|
-
query,
|
|
71
|
-
limit: limit * 2,
|
|
72
|
-
threshold: 0.1,
|
|
73
|
-
});
|
|
74
|
-
// Score which keywords matched in each result
|
|
75
|
-
const relatedNotes = results.slice(0, limit).map((r) => {
|
|
76
|
-
const titleLower = r.document.title.toLowerCase();
|
|
77
|
-
const contentLower = r.chunk.content.toLowerCase();
|
|
78
|
-
const matchedKeywords = keywords.filter((k) => titleLower.includes(k) || contentLower.includes(k) || r.document.tags.some((t) => t.toLowerCase().includes(k)));
|
|
79
|
-
return {
|
|
80
|
-
documentId: r.document.id,
|
|
81
|
-
title: r.document.title,
|
|
82
|
-
score: r.score,
|
|
83
|
-
matchedKeywords,
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
return { filePath, keywords, relatedNotes };
|
|
87
|
-
}
|
|
88
|
-
//# sourceMappingURL=code-linker.js.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { VectorStore } from '../store/types.js';
|
|
2
|
-
export interface ContradictionPair {
|
|
3
|
-
docA: {
|
|
4
|
-
id: string;
|
|
5
|
-
title: string;
|
|
6
|
-
filePath: string;
|
|
7
|
-
statement: string;
|
|
8
|
-
};
|
|
9
|
-
docB: {
|
|
10
|
-
id: string;
|
|
11
|
-
title: string;
|
|
12
|
-
filePath: string;
|
|
13
|
-
statement: string;
|
|
14
|
-
};
|
|
15
|
-
similarity: number;
|
|
16
|
-
confidence: number;
|
|
17
|
-
type: 'negation' | 'value_conflict' | 'temporal' | 'semantic';
|
|
18
|
-
}
|
|
19
|
-
export declare function detectContradictions(store: VectorStore, limit?: number): Promise<ContradictionPair[]>;
|
|
20
|
-
//# sourceMappingURL=contradiction-detector.d.ts.map
|