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,233 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { indexCommand } from './commands/index-cmd.js';
|
|
3
|
-
import { searchCommand } from './commands/search-cmd.js';
|
|
4
|
-
import { serveCommand } from './commands/serve-cmd.js';
|
|
5
|
-
import { statusCommand } from './commands/status-cmd.js';
|
|
6
|
-
import { graphCommand } from './commands/graph-cmd.js';
|
|
7
|
-
import { cardCommand } from './commands/card-cmd.js';
|
|
8
|
-
import { packCreateCommand, packExportCommand, packImportCommand, packListCommand, packInfoCommand } from './commands/pack-cmd.js';
|
|
9
|
-
import { decayCommand } from './commands/decay-cmd.js';
|
|
10
|
-
import { syncCommand } from './commands/sync-cmd.js';
|
|
11
|
-
import { reviewCommand } from './commands/review-cmd.js';
|
|
12
|
-
import { duplicatesCommand } from './commands/duplicates-cmd.js';
|
|
13
|
-
import { gapsCommand } from './commands/gaps-cmd.js';
|
|
14
|
-
import { clipCommand } from './commands/clip-cmd.js';
|
|
15
|
-
import { briefCommand } from './commands/brief-cmd.js';
|
|
16
|
-
import { digestCommand } from './commands/digest-cmd.js';
|
|
17
|
-
import { initCommand } from './commands/init-cmd.js';
|
|
18
|
-
import { learnCommand } from './commands/learn-cmd.js';
|
|
19
|
-
import { contradictionsCommand } from './commands/contradictions-cmd.js';
|
|
20
|
-
import { federateJoinCommand, federateStatusCommand } from './commands/federate-cmd.js';
|
|
21
|
-
import { cloudSyncCommand, cloudRestoreCommand, cloudStatusCommand } from './commands/cloud-cmd.js';
|
|
22
|
-
import { vaultAddCommand, vaultListCommand, vaultRemoveCommand, vaultSearchAllCommand } from './commands/vault-cmd.js';
|
|
23
|
-
import { captureCommand } from './commands/capture-cmd.js';
|
|
24
|
-
import { askCommand } from './commands/ask-cmd.js';
|
|
25
|
-
import { compileCommand } from './commands/compile-cmd.js';
|
|
26
|
-
import { draftCommand } from './commands/draft-cmd.js';
|
|
27
|
-
import { sessionSaveCommand } from './commands/session-cmd.js';
|
|
28
|
-
import { flushCommand } from './commands/flush-cmd.js';
|
|
29
|
-
import { adrCommand } from './commands/adr-cmd.js';
|
|
30
|
-
import { lintCommand } from './commands/lint-cmd.js';
|
|
31
|
-
import { fleetingCommand } from './commands/fleeting-cmd.js';
|
|
32
|
-
import { ingestCommand, promoteCommand } from './commands/ingest-cmd.js';
|
|
33
|
-
import { autopilotCommand } from './commands/autopilot-cmd.js';
|
|
34
|
-
const program = new Command();
|
|
35
|
-
program
|
|
36
|
-
.name('stellavault')
|
|
37
|
-
.description('Stellavault — Turn your Obsidian vault into a 3D neural knowledge graph')
|
|
38
|
-
.version('0.3.0')
|
|
39
|
-
.option('--json', 'Output in JSON format (for scripting)')
|
|
40
|
-
.option('--quiet', 'Suppress non-essential output');
|
|
41
|
-
program
|
|
42
|
-
.command('init')
|
|
43
|
-
.description('Interactive setup wizard — get started in 3 minutes')
|
|
44
|
-
.action(initCommand);
|
|
45
|
-
program
|
|
46
|
-
.command('index [vault-path]')
|
|
47
|
-
.description('Obsidian vault를 벡터화하여 인덱싱합니다')
|
|
48
|
-
.action(indexCommand);
|
|
49
|
-
program
|
|
50
|
-
.command('search <query>')
|
|
51
|
-
.description('지식 베이스에서 검색합니다')
|
|
52
|
-
.option('-l, --limit <n>', '결과 수', '5')
|
|
53
|
-
.action(searchCommand);
|
|
54
|
-
program
|
|
55
|
-
.command('serve')
|
|
56
|
-
.description('MCP 서버를 시작합니다 (Claude Code 연동)')
|
|
57
|
-
.action(serveCommand);
|
|
58
|
-
program
|
|
59
|
-
.command('status')
|
|
60
|
-
.description('인덱스 상태를 확인합니다')
|
|
61
|
-
.action(statusCommand);
|
|
62
|
-
program
|
|
63
|
-
.command('graph')
|
|
64
|
-
.description('3D Knowledge Graph API 서버를 시작합니다')
|
|
65
|
-
.action(graphCommand);
|
|
66
|
-
program
|
|
67
|
-
.command('card')
|
|
68
|
-
.description('SVG 프로필 카드를 생성합니다')
|
|
69
|
-
.option('-o, --output <path>', '출력 파일 경로', 'knowledge-card.svg')
|
|
70
|
-
.action(cardCommand);
|
|
71
|
-
program
|
|
72
|
-
.command('learn')
|
|
73
|
-
.description('AI learning path — personalized recommendations based on decay + gaps')
|
|
74
|
-
.action(learnCommand);
|
|
75
|
-
program
|
|
76
|
-
.command('contradictions')
|
|
77
|
-
.description('Detect contradicting statements across your notes')
|
|
78
|
-
.action(contradictionsCommand);
|
|
79
|
-
program
|
|
80
|
-
.command('decay')
|
|
81
|
-
.description('지식 감쇠 리포트를 출력합니다 (잊어가는 노트 확인)')
|
|
82
|
-
.action(decayCommand);
|
|
83
|
-
program
|
|
84
|
-
.command('brief')
|
|
85
|
-
.description('오늘의 지식 브리핑 (감쇠 + 갭 + 활동 요약)')
|
|
86
|
-
.action(briefCommand);
|
|
87
|
-
program
|
|
88
|
-
.command('digest')
|
|
89
|
-
.description('주간 지식 활동 리포트')
|
|
90
|
-
.option('-d, --days <n>', '기간 (일)', '7')
|
|
91
|
-
.option('-v, --visual', 'Save as .md with Mermaid charts for Obsidian')
|
|
92
|
-
.action(digestCommand);
|
|
93
|
-
program
|
|
94
|
-
.command('clip <url>')
|
|
95
|
-
.description('웹 페이지/YouTube를 Obsidian에 클리핑')
|
|
96
|
-
.option('-f, --folder <path>', 'vault 내 저장 폴더', '06_Research/clips')
|
|
97
|
-
.action(clipCommand);
|
|
98
|
-
program
|
|
99
|
-
.command('gaps')
|
|
100
|
-
.description('지식 갭을 탐지합니다 (클러스터 간 연결 부족 영역)')
|
|
101
|
-
.action(gapsCommand);
|
|
102
|
-
program
|
|
103
|
-
.command('duplicates')
|
|
104
|
-
.description('중복/유사 노트를 탐지합니다')
|
|
105
|
-
.option('-t, --threshold <n>', '유사도 임계값 (0~1)', '0.88')
|
|
106
|
-
.action(duplicatesCommand);
|
|
107
|
-
program
|
|
108
|
-
.command('review')
|
|
109
|
-
.description('일일 지식 리뷰 — 잊어가는 노트를 Obsidian에서 열어 리뷰')
|
|
110
|
-
.option('-n, --count <n>', '리뷰할 노트 수', '5')
|
|
111
|
-
.action(reviewCommand);
|
|
112
|
-
program
|
|
113
|
-
.command('sync')
|
|
114
|
-
.description('Notion → Obsidian 동기화')
|
|
115
|
-
.option('--upload', 'PDCA 문서를 Notion에 업로드')
|
|
116
|
-
.option('--watch', '5분 간격 자동 동기화')
|
|
117
|
-
.action(syncCommand);
|
|
118
|
-
const federate = program.command('federate').description('Federation — P2P knowledge network');
|
|
119
|
-
federate.command('join')
|
|
120
|
-
.description('Join the federation network (interactive mode)')
|
|
121
|
-
.option('-n, --name <name>', 'Display name for this node')
|
|
122
|
-
.action(federateJoinCommand);
|
|
123
|
-
federate.command('status')
|
|
124
|
-
.description('Show federation identity and status')
|
|
125
|
-
.action(federateStatusCommand);
|
|
126
|
-
program
|
|
127
|
-
.command('capture <audio-file>')
|
|
128
|
-
.description('Voice capture — transcribe audio to knowledge note (requires Whisper)')
|
|
129
|
-
.option('-m, --model <model>', 'Whisper model (tiny/base/small/medium/large)', 'base')
|
|
130
|
-
.option('-l, --language <lang>', 'Language (auto-detect if omitted)')
|
|
131
|
-
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
132
|
-
.option('-f, --folder <folder>', 'Vault subfolder', '01_Knowledge/voice')
|
|
133
|
-
.action(captureCommand);
|
|
134
|
-
program
|
|
135
|
-
.command('ask <question>')
|
|
136
|
-
.description('Ask a question about your knowledge base — search, compose answer, optionally save')
|
|
137
|
-
.option('-s, --save', 'Save answer as a new note in your vault')
|
|
138
|
-
.option('-q, --quotes', 'Show direct quotes from sources (Insight Extraction mode)')
|
|
139
|
-
.action((question, opts) => askCommand(question, opts));
|
|
140
|
-
program
|
|
141
|
-
.command('compile')
|
|
142
|
-
.description('Compile raw/ documents into a structured wiki')
|
|
143
|
-
.option('-r, --raw <dir>', 'Raw documents directory (default: raw/)')
|
|
144
|
-
.option('-w, --wiki <dir>', 'Wiki output directory (default: _wiki/)')
|
|
145
|
-
.option('-f, --force', 'Overwrite existing wiki files')
|
|
146
|
-
.action((opts) => compileCommand(opts));
|
|
147
|
-
program
|
|
148
|
-
.command('draft [topic]')
|
|
149
|
-
.description('Express: Generate draft from knowledge (blog/report/outline/instagram/thread/script)')
|
|
150
|
-
.option('--format <type>', 'Output format: blog, report, outline, instagram, thread, script')
|
|
151
|
-
.option('--ai', 'Use Claude API for AI-enhanced draft (requires ANTHROPIC_API_KEY)')
|
|
152
|
-
.option('--blueprint <spec>', 'Chapter structure: "Ch1:tag1,tag2; Ch2:tag3"')
|
|
153
|
-
.action((topic, opts) => draftCommand(topic, opts));
|
|
154
|
-
program
|
|
155
|
-
.command('session-save')
|
|
156
|
-
.description('Save session summary to daily log (used by Claude Code hooks)')
|
|
157
|
-
.option('-s, --summary <text>', 'Session summary text (or pipe via stdin)')
|
|
158
|
-
.option('-d, --decisions <text>', 'Key decisions made')
|
|
159
|
-
.option('-l, --lessons <text>', 'Lessons learned')
|
|
160
|
-
.option('-a, --actions <text>', 'Action items')
|
|
161
|
-
.action((opts) => sessionSaveCommand(opts));
|
|
162
|
-
program
|
|
163
|
-
.command('flush')
|
|
164
|
-
.description('Flush daily logs → wiki: extract concepts, rebuild connections (Karpathy compile)')
|
|
165
|
-
.action(() => flushCommand());
|
|
166
|
-
program
|
|
167
|
-
.command('adr <title>')
|
|
168
|
-
.description('Create an Architecture Decision Record (structured decision log)')
|
|
169
|
-
.option('--context <text>', 'Why is this decision needed?')
|
|
170
|
-
.option('--options <text>', 'What alternatives were considered?')
|
|
171
|
-
.option('--decision <text>', 'What was decided and why?')
|
|
172
|
-
.option('--consequences <text>', 'What are the implications?')
|
|
173
|
-
.action((title, opts) => adrCommand(title, opts));
|
|
174
|
-
program
|
|
175
|
-
.command('lint')
|
|
176
|
-
.description('Knowledge health check — find gaps, duplicates, contradictions, stale notes')
|
|
177
|
-
.action(() => lintCommand());
|
|
178
|
-
program
|
|
179
|
-
.command('fleeting <text>')
|
|
180
|
-
.description('Capture a fleeting idea instantly to raw/ folder')
|
|
181
|
-
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
182
|
-
.action((text, opts) => fleetingCommand(text, opts));
|
|
183
|
-
program
|
|
184
|
-
.command('ingest <input>')
|
|
185
|
-
.description('Ingest any input (URL, file, text) into your knowledge base')
|
|
186
|
-
.option('-t, --tags <tags>', 'Comma-separated tags')
|
|
187
|
-
.option('-s, --stage <stage>', 'Note stage: fleeting, literature, permanent', 'fleeting')
|
|
188
|
-
.option('--title <title>', 'Override title')
|
|
189
|
-
.action((input, opts) => ingestCommand(input, opts));
|
|
190
|
-
program
|
|
191
|
-
.command('promote <file>')
|
|
192
|
-
.description('Promote a note: fleeting → literature → permanent')
|
|
193
|
-
.requiredOption('--to <stage>', 'Target stage: literature or permanent')
|
|
194
|
-
.action((file, opts) => promoteCommand(file, opts));
|
|
195
|
-
program
|
|
196
|
-
.command('autopilot')
|
|
197
|
-
.description('Run the full knowledge flywheel: inbox → compile → lint')
|
|
198
|
-
.option('--once', 'Run once (default)')
|
|
199
|
-
.action((opts) => autopilotCommand(opts));
|
|
200
|
-
const vault = program.command('vault').description('Multi-Vault — manage and search across vaults');
|
|
201
|
-
vault.command('add <id> <path>').description('Register a vault').option('-n, --name <name>', 'Display name').option('-s, --shared', 'Allow federation sharing').action(vaultAddCommand);
|
|
202
|
-
vault.command('list').description('List registered vaults').action(vaultListCommand);
|
|
203
|
-
vault.command('remove <id>').description('Unregister a vault').action(vaultRemoveCommand);
|
|
204
|
-
vault.command('search-all <query>').description('Search across all registered vaults').option('-l, --limit <n>', 'Max results', '10').action(vaultSearchAllCommand);
|
|
205
|
-
const cloud = program.command('cloud').description('Cloud — E2E encrypted backup');
|
|
206
|
-
cloud.command('sync').description('Upload encrypted DB to cloud').action(cloudSyncCommand);
|
|
207
|
-
cloud.command('restore').description('Download and decrypt DB from cloud').action(cloudRestoreCommand);
|
|
208
|
-
cloud.command('status').description('Show last sync status').action(cloudStatusCommand);
|
|
209
|
-
const pack = program.command('pack').description('Knowledge Pack 관리');
|
|
210
|
-
pack.command('create <name>')
|
|
211
|
-
.description('검색/클러스터 기반 Knowledge Pack 생성')
|
|
212
|
-
.option('--from-search <query>', '검색 쿼리에서 생성')
|
|
213
|
-
.option('--from-cluster <id>', '클러스터 ID에서 생성')
|
|
214
|
-
.option('--author <name>', '작성자', 'anonymous')
|
|
215
|
-
.option('--license <license>', '라이선스', 'CC-BY-4.0')
|
|
216
|
-
.option('--description <desc>', '설명')
|
|
217
|
-
.option('--limit <n>', '최대 청크 수', '100')
|
|
218
|
-
.action(packCreateCommand);
|
|
219
|
-
pack.command('export <name>')
|
|
220
|
-
.description('.sv-pack 파일로 내보내기')
|
|
221
|
-
.option('-o, --output <path>', '출력 경로')
|
|
222
|
-
.action(packExportCommand);
|
|
223
|
-
pack.command('import <file>')
|
|
224
|
-
.description('.sv-pack 파일 가져오기 → 벡터 DB 병합')
|
|
225
|
-
.action(packImportCommand);
|
|
226
|
-
pack.command('list')
|
|
227
|
-
.description('설치된 팩 목록')
|
|
228
|
-
.action(packListCommand);
|
|
229
|
-
pack.command('info <name>')
|
|
230
|
-
.description('팩 상세 정보')
|
|
231
|
-
.action(packInfoCommand);
|
|
232
|
-
program.parse();
|
|
233
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@stellavault/cli",
|
|
3
|
-
"version": "0.4.0",
|
|
4
|
-
"description": "Stellavault CLI — stellavault index, search, serve, graph",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"stellavault": "./bin/stellavault.js",
|
|
8
|
-
"sv": "./bin/stellavault.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc"
|
|
12
|
-
},
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"@stellavault/core": "*",
|
|
15
|
-
"chalk": "^5.6.2",
|
|
16
|
-
"commander": "^14.0.3",
|
|
17
|
-
"open": "^11.0.0",
|
|
18
|
-
"ora": "^9.3.0"
|
|
19
|
-
},
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@types/node": "^25.5.0",
|
|
22
|
-
"typescript": "^5.7.0"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
// Web Dashboard (P3-F26) — Express에 HTML 대시보드 추가
|
|
2
|
-
// http://localhost:3333/dashboard
|
|
3
|
-
export function mountDashboard(app) {
|
|
4
|
-
app.get('/dashboard', (_req, res) => {
|
|
5
|
-
res.setHeader('Content-Type', 'text/html');
|
|
6
|
-
res.send(getDashboardHTML());
|
|
7
|
-
});
|
|
8
|
-
}
|
|
9
|
-
function getDashboardHTML() {
|
|
10
|
-
return `<!DOCTYPE html>
|
|
11
|
-
<html lang="en">
|
|
12
|
-
<head>
|
|
13
|
-
<meta charset="UTF-8">
|
|
14
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
15
|
-
<title>Stellavault Dashboard</title>
|
|
16
|
-
<style>
|
|
17
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
18
|
-
body { font-family: system-ui, -apple-system, sans-serif; background: #050510; color: #c0c0f0; min-height: 100vh; }
|
|
19
|
-
.header { padding: 16px 24px; border-bottom: 1px solid rgba(100,120,255,0.1); display: flex; align-items: center; gap: 12px; }
|
|
20
|
-
.header h1 { font-size: 18px; font-weight: 600; }
|
|
21
|
-
.header .subtitle { color: #556; font-size: 12px; }
|
|
22
|
-
.container { max-width: 1200px; margin: 0 auto; padding: 24px; }
|
|
23
|
-
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 24px; }
|
|
24
|
-
.card { background: rgba(100,120,255,0.04); border: 1px solid rgba(100,120,255,0.1); border-radius: 12px; padding: 16px; }
|
|
25
|
-
.card h3 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #556; margin-bottom: 8px; }
|
|
26
|
-
.card .value { font-size: 28px; font-weight: 700; }
|
|
27
|
-
.card .sub { font-size: 11px; color: #556; margin-top: 4px; }
|
|
28
|
-
.search-box { width: 100%; padding: 10px 16px; background: rgba(100,120,255,0.06); border: 1px solid rgba(100,120,255,0.15); border-radius: 8px; color: #c0c0f0; font-size: 14px; outline: none; margin-bottom: 16px; }
|
|
29
|
-
.search-box::placeholder { color: #445; }
|
|
30
|
-
.results { display: flex; flex-direction: column; gap: 8px; }
|
|
31
|
-
.result { padding: 12px; background: rgba(255,255,255,0.02); border-radius: 8px; cursor: pointer; }
|
|
32
|
-
.result:hover { background: rgba(100,120,255,0.06); }
|
|
33
|
-
.result .title { font-weight: 600; font-size: 13px; }
|
|
34
|
-
.result .meta { font-size: 11px; color: #556; margin-top: 4px; }
|
|
35
|
-
.section { margin-bottom: 24px; }
|
|
36
|
-
.section h2 { font-size: 14px; margin-bottom: 12px; color: #88aaff; }
|
|
37
|
-
.bar { height: 6px; background: rgba(100,120,255,0.1); border-radius: 3px; overflow: hidden; margin-top: 6px; }
|
|
38
|
-
.bar-fill { height: 100%; border-radius: 3px; transition: width 0.3s; }
|
|
39
|
-
.links { display: flex; gap: 12px; margin-top: 16px; }
|
|
40
|
-
.links a { color: #88aaff; text-decoration: none; font-size: 12px; padding: 6px 12px; border: 1px solid rgba(100,120,255,0.15); border-radius: 6px; }
|
|
41
|
-
.links a:hover { background: rgba(100,120,255,0.1); }
|
|
42
|
-
#loading { color: #556; font-size: 12px; }
|
|
43
|
-
</style>
|
|
44
|
-
</head>
|
|
45
|
-
<body>
|
|
46
|
-
<div class="header">
|
|
47
|
-
<h1>Stellavault</h1>
|
|
48
|
-
<span class="subtitle">Dashboard</span>
|
|
49
|
-
<div style="margin-left:auto" class="links">
|
|
50
|
-
<a href="/api/stats" target="_blank">API Stats</a>
|
|
51
|
-
<a href="http://localhost:5173" target="_blank">3D Graph</a>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
<div class="container">
|
|
56
|
-
<!-- Stats -->
|
|
57
|
-
<div class="grid" id="stats-grid">
|
|
58
|
-
<div class="card"><h3>Documents</h3><div class="value" id="doc-count">-</div></div>
|
|
59
|
-
<div class="card"><h3>Chunks</h3><div class="value" id="chunk-count">-</div></div>
|
|
60
|
-
<div class="card"><h3>Health Score</h3><div class="value" id="health-score">-</div><div class="bar"><div class="bar-fill" id="health-bar" style="width:0%;background:#10b981"></div></div></div>
|
|
61
|
-
<div class="card"><h3>Knowledge Gaps</h3><div class="value" id="gap-count">-</div></div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<!-- Search -->
|
|
65
|
-
<div class="section">
|
|
66
|
-
<h2>Search</h2>
|
|
67
|
-
<input class="search-box" id="search-input" placeholder="Search your knowledge..." />
|
|
68
|
-
<div class="results" id="search-results"></div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<!-- Decay -->
|
|
72
|
-
<div class="section">
|
|
73
|
-
<h2>Fading Knowledge</h2>
|
|
74
|
-
<div id="decay-list"><span id="loading">Loading...</span></div>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
<script>
|
|
79
|
-
const API = '/api';
|
|
80
|
-
// HIGH-06: XSS 방지 — HTML 이스케이프
|
|
81
|
-
function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
|
82
|
-
|
|
83
|
-
async function loadStats() {
|
|
84
|
-
try {
|
|
85
|
-
const [stats, health] = await Promise.all([
|
|
86
|
-
fetch(API + '/stats').then(r => r.json()),
|
|
87
|
-
fetch(API + '/health').then(r => r.json()).catch(() => null),
|
|
88
|
-
]);
|
|
89
|
-
document.getElementById('doc-count').textContent = stats.documentCount ?? 0;
|
|
90
|
-
document.getElementById('chunk-count').textContent = stats.chunkCount ?? 0;
|
|
91
|
-
if (health) {
|
|
92
|
-
const score = health.decay?.averageR ? Math.round(health.decay.averageR * 100) : 100;
|
|
93
|
-
document.getElementById('health-score').textContent = score + '%';
|
|
94
|
-
document.getElementById('health-bar').style.width = score + '%';
|
|
95
|
-
document.getElementById('health-bar').style.background = score > 70 ? '#10b981' : score > 40 ? '#f59e0b' : '#ef4444';
|
|
96
|
-
document.getElementById('gap-count').textContent = health.gaps?.gapCount ?? 0;
|
|
97
|
-
|
|
98
|
-
// Decay list
|
|
99
|
-
const decayEl = document.getElementById('decay-list');
|
|
100
|
-
if (health.decay?.topDecaying?.length) {
|
|
101
|
-
decayEl.innerHTML = health.decay.topDecaying.slice(0, 8).map(d => {
|
|
102
|
-
const r = Math.round((d.retrievability ?? 0) * 100);
|
|
103
|
-
const color = r > 50 ? '#10b981' : r > 30 ? '#f59e0b' : '#ef4444';
|
|
104
|
-
return '<div class="result"><div class="title">' + esc(d.title) + '</div><div class="bar"><div class="bar-fill" style="width:' + r + '%;background:' + color + '"></div></div><div class="meta">' + r + '% retrievability</div></div>';
|
|
105
|
-
}).join('');
|
|
106
|
-
} else {
|
|
107
|
-
decayEl.innerHTML = '<div style="color:#556;font-size:12px">All knowledge healthy!</div>';
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
} catch (e) { console.error(e); }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
let searchTimer;
|
|
114
|
-
document.getElementById('search-input').addEventListener('input', (e) => {
|
|
115
|
-
clearTimeout(searchTimer);
|
|
116
|
-
searchTimer = setTimeout(async () => {
|
|
117
|
-
const q = e.target.value.trim();
|
|
118
|
-
const el = document.getElementById('search-results');
|
|
119
|
-
if (!q) { el.innerHTML = ''; return; }
|
|
120
|
-
try {
|
|
121
|
-
const res = await fetch(API + '/search?q=' + encodeURIComponent(q) + '&limit=10').then(r => r.json());
|
|
122
|
-
el.innerHTML = (res.results || []).map(r =>
|
|
123
|
-
'<div class="result"><div class="title">' + esc(r.title) + ' <span style="color:#556;font-size:11px">(' + Math.round(r.score * 100) + '%)</span></div>' +
|
|
124
|
-
'<div class="meta">' + esc((r.highlights?.[0] || '').slice(0, 100)) + '</div></div>'
|
|
125
|
-
).join('') || '<div style="color:#556;font-size:12px">No results</div>';
|
|
126
|
-
} catch { el.innerHTML = '<div style="color:#ef4444;font-size:12px">Search error</div>'; }
|
|
127
|
-
}, 300);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
loadStats();
|
|
131
|
-
</script>
|
|
132
|
-
</body>
|
|
133
|
-
</html>`;
|
|
134
|
-
}
|
|
135
|
-
//# sourceMappingURL=dashboard.js.map
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { VectorStore } from '../store/types.js';
|
|
2
|
-
import type { GraphData } from '../types/graph.js';
|
|
3
|
-
export type GraphMode = 'semantic' | 'folder';
|
|
4
|
-
export interface BuildGraphOptions {
|
|
5
|
-
mode?: GraphMode;
|
|
6
|
-
edgeThreshold?: number;
|
|
7
|
-
maxEdgesPerNode?: number;
|
|
8
|
-
clusterCount?: number;
|
|
9
|
-
}
|
|
10
|
-
export declare function buildGraphData(store: VectorStore, options?: BuildGraphOptions): Promise<GraphData>;
|
|
11
|
-
//# sourceMappingURL=graph-data.d.ts.map
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
// Design Ref: §4.2 — Graph Data 생성 알고리즘
|
|
2
|
-
// Design Ref: §6.2 — K-Means 클러스터링
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
const CLUSTER_COLORS = [
|
|
5
|
-
'#6366f1', '#ec4899', '#f59e0b', '#10b981', '#3b82f6',
|
|
6
|
-
'#8b5cf6', '#ef4444', '#14b8a6', '#f97316', '#06b6d4',
|
|
7
|
-
'#84cc16', '#e879f9', '#22d3ee', '#a3e635', '#fb923c',
|
|
8
|
-
];
|
|
9
|
-
export async function buildGraphData(store, options = {}) {
|
|
10
|
-
const { edgeThreshold = 0.15, maxEdgesPerNode = 5, } = options;
|
|
11
|
-
// 1. 모든 문서 + 임베딩 로드
|
|
12
|
-
const [docs, embeddings] = await Promise.all([
|
|
13
|
-
store.getAllDocuments(),
|
|
14
|
-
store.getDocumentEmbeddings(),
|
|
15
|
-
]);
|
|
16
|
-
// 2. k-NN으로 엣지 생성
|
|
17
|
-
const edges = [];
|
|
18
|
-
const edgeCounts = new Map();
|
|
19
|
-
for (const doc of docs) {
|
|
20
|
-
const vec = embeddings.get(doc.id);
|
|
21
|
-
if (!vec)
|
|
22
|
-
continue;
|
|
23
|
-
// 모든 다른 문서와 코사인 유사도 계산
|
|
24
|
-
const similarities = [];
|
|
25
|
-
for (const other of docs) {
|
|
26
|
-
if (other.id === doc.id)
|
|
27
|
-
continue;
|
|
28
|
-
const otherVec = embeddings.get(other.id);
|
|
29
|
-
if (!otherVec)
|
|
30
|
-
continue;
|
|
31
|
-
const sim = cosineSimilarity(vec, otherVec);
|
|
32
|
-
if (sim >= edgeThreshold) {
|
|
33
|
-
similarities.push({ id: other.id, sim });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// 상위 k개만
|
|
37
|
-
similarities.sort((a, b) => b.sim - a.sim);
|
|
38
|
-
const topK = similarities.slice(0, maxEdgesPerNode);
|
|
39
|
-
for (const { id: targetId, sim } of topK) {
|
|
40
|
-
// 중복 엣지 방지 (A→B, B→A)
|
|
41
|
-
const edgeKey = [doc.id, targetId].sort().join(':');
|
|
42
|
-
if (!edgeCounts.has(edgeKey)) {
|
|
43
|
-
edges.push({ source: doc.id, target: targetId, weight: sim });
|
|
44
|
-
edgeCounts.set(edgeKey, 1);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// 3. 클러스터링 — 모드에 따라 분기
|
|
49
|
-
const mode = options.mode ?? 'semantic';
|
|
50
|
-
let assignmentMap;
|
|
51
|
-
let clusters;
|
|
52
|
-
if (mode === 'folder') {
|
|
53
|
-
// 폴더 기반: 최상위 폴더를 클러스터로 사용
|
|
54
|
-
const folderMap = new Map();
|
|
55
|
-
const folderNames = [];
|
|
56
|
-
for (const doc of docs) {
|
|
57
|
-
const topFolder = doc.filePath.split('/')[0] ?? 'root';
|
|
58
|
-
if (!folderMap.has(topFolder)) {
|
|
59
|
-
folderMap.set(topFolder, folderNames.length);
|
|
60
|
-
folderNames.push(topFolder);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
assignmentMap = new Map();
|
|
64
|
-
for (const doc of docs) {
|
|
65
|
-
const topFolder = doc.filePath.split('/')[0] ?? 'root';
|
|
66
|
-
assignmentMap.set(doc.id, folderMap.get(topFolder));
|
|
67
|
-
}
|
|
68
|
-
// 폴더별 클러스터
|
|
69
|
-
const folderCounts = new Map();
|
|
70
|
-
for (const [, cId] of assignmentMap) {
|
|
71
|
-
folderCounts.set(cId, (folderCounts.get(cId) ?? 0) + 1);
|
|
72
|
-
}
|
|
73
|
-
clusters = folderNames.map((name, i) => ({
|
|
74
|
-
id: i,
|
|
75
|
-
label: name.replace(/^\d+_/, ''), // "04_Projects" → "Projects"
|
|
76
|
-
color: CLUSTER_COLORS[i % CLUSTER_COLORS.length],
|
|
77
|
-
nodeCount: folderCounts.get(i) ?? 0,
|
|
78
|
-
}));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// 시맨틱 기반: K-means
|
|
82
|
-
const docIds = docs.filter(d => embeddings.has(d.id)).map(d => d.id);
|
|
83
|
-
const vectors = docIds.map(id => embeddings.get(id));
|
|
84
|
-
const k = Math.min(Math.max(5, Math.round(Math.sqrt(docIds.length / 5))), 10);
|
|
85
|
-
const assignments = kMeans(vectors, k);
|
|
86
|
-
// 클러스터별 문서 수집 (id + title)
|
|
87
|
-
const clusterDocInfos = new Map();
|
|
88
|
-
for (let i = 0; i < docIds.length; i++) {
|
|
89
|
-
const cId = assignments[i];
|
|
90
|
-
if (!clusterDocInfos.has(cId))
|
|
91
|
-
clusterDocInfos.set(cId, []);
|
|
92
|
-
const doc = docs.find(d => d.id === docIds[i]);
|
|
93
|
-
if (doc)
|
|
94
|
-
clusterDocInfos.get(cId).push({ id: doc.id, title: doc.title });
|
|
95
|
-
}
|
|
96
|
-
// 엣지 기반 연결 수 계산 (대표 노드 선정용)
|
|
97
|
-
const connCounts = new Map();
|
|
98
|
-
for (const e of edges) {
|
|
99
|
-
connCounts.set(e.source, (connCounts.get(e.source) ?? 0) + 1);
|
|
100
|
-
connCounts.set(e.target, (connCounts.get(e.target) ?? 0) + 1);
|
|
101
|
-
}
|
|
102
|
-
clusters = [];
|
|
103
|
-
for (const [cId, docInfos] of clusterDocInfos) {
|
|
104
|
-
// 가장 연결이 많은 노드 = 대표 노드
|
|
105
|
-
const sorted = [...docInfos].sort((a, b) => (connCounts.get(b.id) ?? 0) - (connCounts.get(a.id) ?? 0));
|
|
106
|
-
const representative = sorted[0]?.title ?? 'Cluster';
|
|
107
|
-
clusters.push({
|
|
108
|
-
id: cId,
|
|
109
|
-
label: `${representative} (${docInfos.length})`,
|
|
110
|
-
color: CLUSTER_COLORS[cId % CLUSTER_COLORS.length],
|
|
111
|
-
nodeCount: docInfos.length,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
assignmentMap = new Map();
|
|
115
|
-
for (let i = 0; i < docIds.length; i++) {
|
|
116
|
-
assignmentMap.set(docIds[i], assignments[i]);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// 4. 노드 생성
|
|
120
|
-
const connectionCounts = new Map();
|
|
121
|
-
for (const edge of edges) {
|
|
122
|
-
connectionCounts.set(edge.source, (connectionCounts.get(edge.source) ?? 0) + 1);
|
|
123
|
-
connectionCounts.set(edge.target, (connectionCounts.get(edge.target) ?? 0) + 1);
|
|
124
|
-
}
|
|
125
|
-
const maxConnections = Math.max(1, ...connectionCounts.values());
|
|
126
|
-
const nodes = docs.map(doc => {
|
|
127
|
-
const conns = connectionCounts.get(doc.id) ?? 0;
|
|
128
|
-
const ratio = conns / maxConnections; // 0~1
|
|
129
|
-
// 지수 스케일: 상위 노드만 극적으로 커짐 (ratio^0.5 → 중심부 강조)
|
|
130
|
-
const size = 1 + 6 * Math.pow(ratio, 0.5);
|
|
131
|
-
return {
|
|
132
|
-
id: doc.id,
|
|
133
|
-
label: doc.title.replace(/'/g, "'").replace(/'/g, "'").replace(/"/g, '"').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\s*-\s*YouTube$/, '').slice(0, 40),
|
|
134
|
-
filePath: doc.filePath,
|
|
135
|
-
tags: doc.tags,
|
|
136
|
-
clusterId: assignmentMap.get(doc.id) ?? 0,
|
|
137
|
-
size,
|
|
138
|
-
source: doc.source ?? 'local',
|
|
139
|
-
type: doc.type ?? 'note',
|
|
140
|
-
lastModified: doc.lastModified,
|
|
141
|
-
};
|
|
142
|
-
});
|
|
143
|
-
// 5. cache key
|
|
144
|
-
const cacheKey = createHash('md5')
|
|
145
|
-
.update(JSON.stringify({ nodeCount: nodes.length, edgeCount: edges.length }))
|
|
146
|
-
.digest('hex')
|
|
147
|
-
.slice(0, 8);
|
|
148
|
-
return {
|
|
149
|
-
nodes,
|
|
150
|
-
edges,
|
|
151
|
-
clusters,
|
|
152
|
-
stats: {
|
|
153
|
-
nodeCount: nodes.length,
|
|
154
|
-
edgeCount: edges.length,
|
|
155
|
-
clusterCount: clusters.length,
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
// --- 유틸리티 ---
|
|
160
|
-
function cosineSimilarity(a, b) {
|
|
161
|
-
let dot = 0, normA = 0, normB = 0;
|
|
162
|
-
for (let i = 0; i < a.length; i++) {
|
|
163
|
-
dot += a[i] * b[i];
|
|
164
|
-
normA += a[i] * a[i];
|
|
165
|
-
normB += b[i] * b[i];
|
|
166
|
-
}
|
|
167
|
-
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
168
|
-
return denom === 0 ? 0 : dot / denom;
|
|
169
|
-
}
|
|
170
|
-
function kMeans(vectors, k, maxIter = 50) {
|
|
171
|
-
if (vectors.length === 0)
|
|
172
|
-
return [];
|
|
173
|
-
const dims = vectors[0].length;
|
|
174
|
-
// K-means++ 초기화
|
|
175
|
-
const centroids = [vectors[Math.floor(Math.random() * vectors.length)].slice()];
|
|
176
|
-
for (let c = 1; c < k; c++) {
|
|
177
|
-
const dists = vectors.map(v => {
|
|
178
|
-
let minD = Infinity;
|
|
179
|
-
for (const cent of centroids) {
|
|
180
|
-
const d = euclideanDist(v, cent);
|
|
181
|
-
if (d < minD)
|
|
182
|
-
minD = d;
|
|
183
|
-
}
|
|
184
|
-
return minD;
|
|
185
|
-
});
|
|
186
|
-
const totalDist = dists.reduce((a, b) => a + b, 0);
|
|
187
|
-
let r = Math.random() * totalDist;
|
|
188
|
-
for (let i = 0; i < dists.length; i++) {
|
|
189
|
-
r -= dists[i];
|
|
190
|
-
if (r <= 0) {
|
|
191
|
-
centroids.push(vectors[i].slice());
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (centroids.length <= c)
|
|
196
|
-
centroids.push(vectors[Math.floor(Math.random() * vectors.length)].slice());
|
|
197
|
-
}
|
|
198
|
-
const assignments = new Array(vectors.length).fill(0);
|
|
199
|
-
for (let iter = 0; iter < maxIter; iter++) {
|
|
200
|
-
let changed = false;
|
|
201
|
-
// Assign
|
|
202
|
-
for (let i = 0; i < vectors.length; i++) {
|
|
203
|
-
let bestC = 0, bestD = Infinity;
|
|
204
|
-
for (let c = 0; c < k; c++) {
|
|
205
|
-
const d = euclideanDist(vectors[i], centroids[c]);
|
|
206
|
-
if (d < bestD) {
|
|
207
|
-
bestD = d;
|
|
208
|
-
bestC = c;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (assignments[i] !== bestC) {
|
|
212
|
-
assignments[i] = bestC;
|
|
213
|
-
changed = true;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (!changed)
|
|
217
|
-
break;
|
|
218
|
-
// Update centroids
|
|
219
|
-
for (let c = 0; c < k; c++) {
|
|
220
|
-
const members = vectors.filter((_, i) => assignments[i] === c);
|
|
221
|
-
if (members.length === 0)
|
|
222
|
-
continue;
|
|
223
|
-
for (let d = 0; d < dims; d++) {
|
|
224
|
-
centroids[c][d] = members.reduce((sum, v) => sum + v[d], 0) / members.length;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return assignments;
|
|
229
|
-
}
|
|
230
|
-
function euclideanDist(a, b) {
|
|
231
|
-
let sum = 0;
|
|
232
|
-
for (let i = 0; i < a.length; i++) {
|
|
233
|
-
const diff = a[i] - b[i];
|
|
234
|
-
sum += diff * diff;
|
|
235
|
-
}
|
|
236
|
-
return Math.sqrt(sum);
|
|
237
|
-
}
|
|
238
|
-
function extractClusterLabel(titles) {
|
|
239
|
-
// 제목에서 빈출 단어 추출 (2글자 이상)
|
|
240
|
-
const wordCounts = new Map();
|
|
241
|
-
const stopWords = new Set(['the', 'a', 'an', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'or', 'is', 'are', 'was', 'be']);
|
|
242
|
-
for (const title of titles) {
|
|
243
|
-
const words = title.replace(/[^\w가-힣\s]/g, '').split(/\s+/).filter(w => w.length >= 2 && !stopWords.has(w.toLowerCase()));
|
|
244
|
-
const seen = new Set();
|
|
245
|
-
for (const w of words) {
|
|
246
|
-
if (!seen.has(w)) {
|
|
247
|
-
wordCounts.set(w, (wordCounts.get(w) ?? 0) + 1);
|
|
248
|
-
seen.add(w);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
const sorted = [...wordCounts.entries()].sort((a, b) => b[1] - a[1]);
|
|
253
|
-
return sorted.slice(0, 3).map(([w]) => w).join(', ') || `Cluster`;
|
|
254
|
-
}
|
|
255
|
-
//# sourceMappingURL=graph-data.js.map
|