stellavault 0.4.1 → 0.4.2

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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.2] - 2026-04-07
4
+
5
+ ### Features — Karpathy Architecture Complete
6
+ - **Session Hooks** — `stellavault session-save`: auto-capture session summaries to daily logs
7
+ - Pipe via stdin or --summary flag
8
+ - Creates `raw/_daily-logs/daily-log-YYYY-MM-DD.md`
9
+ - Auto-compiles wiki after save
10
+ - Works with Claude Code hooks (PreCompact, Stop)
11
+ - **Flush Process** — `stellavault flush`: daily logs → wiki compilation
12
+ - Extracts concepts and connections from all daily logs
13
+ - Rebuilds wiki index with backlinks
14
+ - Karpathy's "source code → executable" compilation
15
+ - **Wikilink Auto-Connect** — Auto-insert [[wikilinks]] matching existing note titles on ingest
16
+ - **Hooks Setup Guide** — `docs/hooks-setup.md` with Claude Code settings.json config
17
+
18
+ ### The Compounding Loop
19
+ ```
20
+ Session → session-save → daily-log → flush → wiki
21
+ ↑ ↓
22
+ └── Claude reads wiki via MCP for better answers ←┘
23
+ ```
24
+
3
25
  ## [0.4.1] - 2026-04-07
4
26
 
5
27
  ### Features
package/index.html ADDED
@@ -0,0 +1,483 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Stellavault — Self-Compiling Knowledge MCP Server</title>
7
+ <meta name="description" content="Drop anything. It organizes itself. Claude knows everything you know. Self-compiling Zettelkasten with 20 MCP tools.">
8
+ <meta name="theme-color" content="#6366f1">
9
+ <meta property="og:title" content="Stellavault — Self-Compiling Knowledge MCP Server">
10
+ <meta property="og:description" content="Drop anything. It organizes itself. Claude knows everything you know.">
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Inter:wght@300;400;500;600&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet">
14
+ <style>
15
+ *,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
16
+ :root{
17
+ --void:#050508;--surface:#0c0c14;--surface2:#131320;
18
+ --border:#252535;--border-subtle:#1a1a28;--border-glow:#6366f140;
19
+ --text:#eeeef2;--text-dim:#9898b8;--text-muted:#72728a;
20
+ --violet:#6366f1;--violet-bright:#818cf8;--violet-glow:#6366f160;
21
+ --cyan:#22d3ee;--emerald:#34d399;--rose:#f43f5e;
22
+ --font-display:'Instrument Serif',Georgia,serif;
23
+ --font-body:'Inter',system-ui,sans-serif;
24
+ --font-mono:'DM Mono',monospace;
25
+ }
26
+ html{scroll-behavior:smooth;background:var(--void);color:var(--text);font-family:var(--font-body);font-weight:400;font-size:15px;line-height:1.6}
27
+ body{overflow-x:hidden;background:var(--void)}
28
+ code,.mono{font-family:var(--font-mono)}
29
+ a{color:var(--violet-bright);text-decoration:none;transition:color .2s}
30
+ a:hover{color:var(--cyan)}
31
+ ::selection{background:var(--violet);color:#fff}
32
+
33
+ /* ═══ COSMOS ═══ */
34
+ .cosmos{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
35
+ .cosmos .g1{position:absolute;width:800px;height:800px;top:-300px;right:-200px;background:radial-gradient(ellipse,rgba(99,102,241,.3) 0%,rgba(99,102,241,.08) 40%,transparent 70%);filter:blur(60px);animation:drift 20s ease-in-out infinite}
36
+ .cosmos .g2{position:absolute;width:500px;height:500px;bottom:15%;left:-120px;background:radial-gradient(ellipse,rgba(34,211,238,.15) 0%,rgba(52,211,153,.05) 50%,transparent 70%);filter:blur(80px);animation:drift 25s ease-in-out infinite reverse}
37
+ .cosmos .g3{position:absolute;width:300px;height:300px;top:40%;left:50%;background:radial-gradient(circle,rgba(99,102,241,.1) 0%,transparent 60%);filter:blur(40px);animation:drift 15s 5s ease-in-out infinite}
38
+ @keyframes drift{0%,100%{transform:translate(0,0)}50%{transform:translate(40px,-30px)}}
39
+ .star-field{position:fixed;inset:0;z-index:0;pointer-events:none}
40
+
41
+ /* ═══ LAYOUT ═══ */
42
+ .container{max-width:1200px;margin:0 auto;padding:0 32px;position:relative;z-index:1}
43
+
44
+ /* ═══ HERO ═══ */
45
+ .hero{min-height:100vh;display:flex;align-items:center;justify-content:center;text-align:center;position:relative;padding:80px 0}
46
+ .hero-inner{max-width:860px}
47
+ .hero-badge{display:inline-flex;align-items:center;gap:8px;padding:7px 18px;border:1px solid var(--border);border-radius:100px;font-family:var(--font-mono);font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:var(--text-dim);margin-bottom:36px;animation:fadeUp .8s ease both}
48
+ .hero-badge .sep{color:var(--border);font-size:8px}
49
+ .hero h1{font-family:var(--font-display);font-size:clamp(52px,9vw,104px);font-weight:400;line-height:1.0;letter-spacing:-2px;margin-bottom:28px;animation:fadeUp .8s .1s ease both}
50
+ .hero h1 em{font-style:italic;color:var(--violet-bright);position:relative}
51
+ .hero h1 em::after{content:'';position:absolute;bottom:2px;left:0;width:100%;height:2px;background:linear-gradient(90deg,transparent,var(--violet),transparent);opacity:.6}
52
+ .hero-sub{font-size:18px;color:var(--text-dim);max-width:560px;margin:0 auto 44px;line-height:1.65;animation:fadeUp .8s .2s ease both}
53
+ .hero-cta{display:flex;gap:12px;justify-content:center;align-items:center;flex-wrap:wrap;animation:fadeUp .8s .3s ease both}
54
+ .cta-install{display:inline-flex;align-items:center;gap:12px;padding:15px 28px;background:var(--surface2);border:1px solid var(--border);border-radius:10px;font-family:var(--font-mono);font-size:14px;color:var(--text);cursor:pointer;transition:all .3s cubic-bezier(.16,1,.3,1)}
55
+ .cta-install:hover{border-color:var(--violet);box-shadow:0 0 40px rgba(99,102,241,.2),0 0 0 1px rgba(99,102,241,.3) inset;transform:translateY(-2px)}
56
+ .cta-install.copied{border-color:var(--emerald);box-shadow:0 0 30px rgba(52,211,153,.2)}
57
+ .cta-install.copied .prompt{color:var(--emerald)}
58
+ .cta-install .prompt{color:var(--violet-bright);transition:color .2s}
59
+ .cta-install .copy-hint{font-size:10px;color:var(--text-muted);margin-left:4px;opacity:0;transition:opacity .2s}
60
+ .cta-install:hover .copy-hint{opacity:1}
61
+ .cta-secondary{display:inline-flex;align-items:center;gap:6px;padding:15px 24px;color:var(--text-dim);font-size:14px;border:1px solid var(--border);border-radius:10px;transition:all .2s}
62
+ .cta-secondary:hover{color:var(--text);border-color:var(--border-glow)}
63
+ .cta-mobile{display:none;padding:16px 32px;background:var(--violet);color:#fff;border-radius:10px;font-weight:500;font-size:14px}
64
+ .hero-proof{margin-top:28px;display:flex;gap:16px;justify-content:center;align-items:center;font-size:12px;color:var(--text-muted);animation:fadeUp .8s .5s ease both}
65
+ .proof-num{color:var(--violet-bright);font-weight:500;font-family:var(--font-mono)}
66
+ .proof-sep{color:var(--border);font-size:6px}
67
+
68
+ /* ═══ GRAPH BG ═══ */
69
+ .graph-visual{position:absolute;inset:0;z-index:-1;opacity:.2;overflow:hidden}
70
+ .node{position:absolute;border-radius:50%;animation:pulse 3s ease-in-out infinite}
71
+ .node.v{background:var(--violet);box-shadow:0 0 20px var(--violet-glow)}
72
+ .node.c{background:var(--cyan);box-shadow:0 0 15px #22d3ee40}
73
+ .node.e{background:var(--emerald);box-shadow:0 0 15px #34d39940}
74
+ .edge{position:absolute;height:1px;background:linear-gradient(90deg,transparent,var(--violet-glow),transparent);transform-origin:left center}
75
+ @keyframes pulse{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.4)}}
76
+
77
+ /* ═══ SECTIONS ═══ */
78
+ .section-label{font-family:var(--font-mono);font-size:11px;letter-spacing:3px;text-transform:uppercase;color:var(--violet-bright);margin-bottom:14px}
79
+ .section-title{font-family:var(--font-display);font-size:clamp(32px,5vw,56px);font-weight:400;line-height:1.1;letter-spacing:-.5px;margin-bottom:18px}
80
+ .section-desc{color:var(--text-dim);max-width:560px;font-size:16px;line-height:1.65}
81
+ .divider{height:1px;background:linear-gradient(90deg,transparent 0%,var(--border) 20%,var(--border) 80%,transparent 100%);max-width:600px;margin:0 auto}
82
+
83
+ /* ═══ PROBLEMS ═══ */
84
+ .s-problems{padding:120px 0 100px}
85
+ .problems{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px;margin-top:56px}
86
+ .problem-card{padding:36px;background:var(--surface);border:1px solid var(--border);border-radius:14px;transition:all .4s cubic-bezier(.16,1,.3,1);position:relative;overflow:hidden}
87
+ .problem-card:hover{transform:translateY(-6px) scale(1.005);border-color:rgba(99,102,241,.5);box-shadow:0 4px 6px rgba(0,0,0,.3),0 20px 40px rgba(99,102,241,.12),0 0 0 1px rgba(99,102,241,.15) inset}
88
+ .problem-card::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,rgba(99,102,241,.8),transparent);opacity:0;transition:opacity .3s}
89
+ .problem-card:hover::before{opacity:1}
90
+ .problem-icon{font-size:28px;margin-bottom:20px;display:block}
91
+ .problem-label{font-family:var(--font-mono);font-size:10px;letter-spacing:2px;text-transform:uppercase;color:var(--text-muted);margin-bottom:10px}
92
+ .problem-title{font-family:var(--font-display);font-size:24px;margin-bottom:14px;line-height:1.2}
93
+ .problem-desc{color:var(--text-dim);font-size:14px;line-height:1.6}
94
+ .problem-arrow{color:var(--violet-bright);font-size:18px;margin:14px 0;display:block}
95
+ .problem-solution{color:var(--text);font-size:14px;font-weight:500}
96
+
97
+ /* ═══ PIPELINE ═══ */
98
+ .s-pipeline{padding:100px 0}
99
+ .pipeline-flow{display:grid;grid-template-columns:repeat(4,1fr);gap:2px;margin-top:40px}
100
+ .pipeline-step{text-align:center;padding:32px 18px;background:var(--surface);border:1px solid var(--border);position:relative;transition:all .3s cubic-bezier(.16,1,.3,1)}
101
+ .pipeline-step:first-child{border-radius:14px 0 0 14px}
102
+ .pipeline-step:last-child{border-radius:0 14px 14px 0}
103
+ .pipeline-step:hover{background:var(--surface2);border-color:var(--violet)}
104
+ .pipeline-step::after{content:'';position:absolute;right:-8px;top:50%;width:14px;height:14px;border-right:2px solid var(--violet);border-top:2px solid var(--violet);transform:translateY(-50%) rotate(45deg);z-index:2}
105
+ .pipeline-step:last-child::after{display:none}
106
+ .step-num{font-family:var(--font-mono);font-size:10px;color:var(--violet-bright);letter-spacing:2px;margin-bottom:10px}
107
+ .step-name{font-family:var(--font-display);font-size:22px;margin-bottom:10px}
108
+ .step-desc{color:var(--text-dim);font-size:12px;line-height:1.6}
109
+
110
+ /* ═══ SCREENSHOTS ═══ */
111
+ .s-screenshots{padding:100px 0}
112
+ .screenshot-hero{margin-top:40px;border-radius:14px;overflow:hidden;border:1px solid var(--border);box-shadow:0 20px 60px rgba(0,0,0,.5)}
113
+ .screenshot-hero img,.screenshot-hero video{width:100%;display:block}
114
+ .screenshot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;margin-top:20px}
115
+ .screenshot-card{border-radius:12px;overflow:hidden;border:1px solid var(--border);transition:all .3s cubic-bezier(.16,1,.3,1);position:relative;cursor:pointer}
116
+ .screenshot-card:hover{border-color:var(--violet);transform:translateY(-4px) scale(1.01);box-shadow:0 12px 40px rgba(99,102,241,.2)}
117
+ .screenshot-card img{width:100%;display:block;transition:transform .4s cubic-bezier(.16,1,.3,1)}
118
+ .screenshot-card:hover img{transform:scale(1.03)}
119
+ .screenshot-card .caption{position:absolute;bottom:0;left:0;right:0;padding:12px 16px;background:linear-gradient(transparent,rgba(5,5,8,.9));font-size:12px;color:var(--text-dim);transition:padding .3s}
120
+ .screenshot-card:hover .caption{padding-bottom:16px}
121
+ .screenshot-card .expand-hint{position:absolute;top:12px;right:12px;width:28px;height:28px;background:rgba(5,5,8,.7);border:1px solid var(--border);border-radius:6px;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s;font-size:14px;color:var(--text-dim)}
122
+ .screenshot-card:hover .expand-hint{opacity:1}
123
+
124
+ /* Lightbox */
125
+ .lightbox{position:fixed;inset:0;z-index:9999;background:rgba(5,5,8,.92);backdrop-filter:blur(20px);display:none;align-items:center;justify-content:center;cursor:zoom-out;animation:lbIn .25s ease}
126
+ .lightbox.active{display:flex}
127
+ .lightbox img{max-width:92vw;max-height:90vh;border-radius:12px;border:1px solid var(--border);box-shadow:0 24px 80px rgba(0,0,0,.6);animation:lbZoom .3s cubic-bezier(.16,1,.3,1)}
128
+ .lightbox .lb-caption{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);color:var(--text-dim);font-size:13px;background:rgba(5,5,8,.8);padding:8px 20px;border-radius:8px;border:1px solid var(--border)}
129
+ .lightbox .lb-close{position:absolute;top:20px;right:24px;width:36px;height:36px;background:rgba(255,255,255,.08);border:1px solid var(--border);border-radius:8px;display:flex;align-items:center;justify-content:center;color:var(--text);font-size:18px;cursor:pointer;transition:background .2s}
130
+ .lightbox .lb-close:hover{background:rgba(255,255,255,.15)}
131
+ .lightbox .lb-nav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;background:rgba(255,255,255,.06);border:1px solid var(--border);border-radius:10px;display:flex;align-items:center;justify-content:center;color:var(--text);font-size:20px;cursor:pointer;transition:all .2s}
132
+ .lightbox .lb-nav:hover{background:rgba(99,102,241,.2);border-color:var(--violet)}
133
+ .lightbox .lb-prev{left:20px}
134
+ .lightbox .lb-next{right:20px}
135
+ @keyframes lbIn{from{opacity:0}to{opacity:1}}
136
+ @keyframes lbZoom{from{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}
137
+
138
+ /* ═══ FORMATS ═══ */
139
+ .s-formats{padding:80px 0 60px}
140
+ .formats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-top:36px}
141
+ .format-chip{padding:20px;background:var(--surface);border:1px solid var(--border);border-radius:10px;text-align:center;transition:all .3s cubic-bezier(.16,1,.3,1)}
142
+ .format-chip:hover{border-color:var(--violet);background:var(--surface2);transform:translateY(-2px)}
143
+ .format-ext{font-family:var(--font-mono);font-size:20px;font-weight:500;color:var(--violet-bright);margin-bottom:6px}
144
+ .format-name{font-size:12px;color:var(--text-muted)}
145
+
146
+ /* ═══ MCP ═══ */
147
+ .s-mcp{padding:100px 0;background:linear-gradient(180deg,transparent 0%,var(--surface) 6%,var(--surface) 94%,transparent 100%)}
148
+ .mcp-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:8px;margin-top:28px}
149
+ .mcp-tool{padding:11px 16px;border:1px solid var(--border);border-radius:8px;font-size:13px;transition:all .2s cubic-bezier(.16,1,.3,1);display:flex;align-items:center;gap:10px}
150
+ .mcp-tool:hover{border-color:rgba(99,102,241,.35);background:rgba(99,102,241,.05)}
151
+ .mcp-tool .dot{width:6px;height:6px;border-radius:50%;background:var(--emerald);flex-shrink:0;box-shadow:0 0 6px rgba(52,211,153,.5);animation:dotPulse 2.5s ease-in-out infinite}
152
+ .mcp-tool code{font-family:var(--font-mono);color:var(--violet-bright);font-size:12px}
153
+ .mcp-tool span.desc{color:var(--text-dim);font-size:12px}
154
+ @keyframes dotPulse{0%,100%{opacity:.6;transform:scale(1)}50%{opacity:1;transform:scale(1.4)}}
155
+ .mcp-connect{margin-top:36px;padding:22px 24px;background:var(--void);border:1px solid var(--border);border-radius:10px;font-family:var(--font-mono);font-size:13px;display:flex;align-items:center;gap:10px}
156
+ .mcp-connect .prompt{color:var(--violet-bright)}
157
+ .mcp-connect code{color:var(--text)}
158
+
159
+ /* ═══ SETUP ═══ */
160
+ .s-setup{padding:140px 0 120px}
161
+ .setup-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:24px;margin-top:40px}
162
+ .setup-step{padding:32px;background:var(--surface);border:1px solid var(--border);border-radius:14px;position:relative;transition:border-color .3s}
163
+ .setup-step:hover{border-color:var(--violet)}
164
+ .setup-num{position:absolute;top:-13px;left:22px;background:var(--violet);color:#fff;width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:var(--font-mono);font-size:12px;font-weight:500}
165
+ .setup-cmd{display:block;margin-top:14px;padding:14px;background:var(--void);border:1px solid var(--border-subtle);border-radius:8px;font-family:var(--font-mono);font-size:13px;color:var(--cyan)}
166
+ .setup-label{font-size:14px;font-weight:500}
167
+ .setup-desc{font-size:13px;color:var(--text-muted);margin-top:6px}
168
+
169
+ /* ═══ TECH ═══ */
170
+ .s-tech{padding:80px 0}
171
+ .tech-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-top:28px}
172
+ .tech-item{padding:16px 20px;border:1px solid var(--border);border-radius:10px;display:flex;justify-content:space-between;align-items:center;font-size:13px;transition:border-color .2s}
173
+ .tech-item:hover{border-color:var(--border-glow)}
174
+ .tech-item .label{color:var(--text-muted)}
175
+ .tech-item .value{color:var(--text);font-weight:500}
176
+
177
+ /* ═══ FOOTER ═══ */
178
+ footer{padding:60px 0;text-align:center}
179
+ .footer-cta{padding-bottom:48px;margin-bottom:48px;border-bottom:1px solid var(--border)}
180
+ .footer-cta .footer-brand{font-family:var(--font-display);font-size:32px;margin-bottom:8px}
181
+ .footer-cta .footer-tagline{color:var(--text-muted);font-size:13px;margin-bottom:24px}
182
+ .footer-links{display:flex;gap:28px;justify-content:center;font-size:13px;margin-bottom:20px}
183
+ .footer-links a{color:var(--text-dim);transition:color .2s}
184
+ .footer-links a:hover{color:var(--text)}
185
+ .footer-copy{font-size:11px;color:var(--text-muted)}
186
+
187
+ /* ═══ ANIMATIONS ═══ */
188
+ @keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
189
+ .reveal{opacity:0;transform:translateY(16px);transition:opacity .5s cubic-bezier(.16,1,.3,1),transform .5s cubic-bezier(.16,1,.3,1)}
190
+ .reveal.visible{opacity:1;transform:translateY(0)}
191
+ .problems .problem-card:nth-child(1){transition-delay:0ms}
192
+ .problems .problem-card:nth-child(2){transition-delay:80ms}
193
+ .problems .problem-card:nth-child(3){transition-delay:160ms}
194
+ .pipeline-flow .pipeline-step:nth-child(1){transition-delay:0ms}
195
+ .pipeline-flow .pipeline-step:nth-child(2){transition-delay:60ms}
196
+ .pipeline-flow .pipeline-step:nth-child(3){transition-delay:120ms}
197
+ .pipeline-flow .pipeline-step:nth-child(4){transition-delay:180ms}
198
+
199
+ /* ═══ RESPONSIVE ═══ */
200
+ @media(max-width:768px){
201
+ .hero{min-height:auto;padding:100px 0 60px}
202
+ .hero h1{letter-spacing:-1px}
203
+ .s-problems,.s-pipeline,.s-setup{padding:80px 0}
204
+ .s-setup{padding:100px 0 80px}
205
+ .problems{grid-template-columns:1fr}
206
+ .pipeline-flow{grid-template-columns:1fr}
207
+ .pipeline-step{border-radius:0!important}
208
+ .pipeline-step:first-child{border-radius:14px 14px 0 0!important}
209
+ .pipeline-step:last-child{border-radius:0 0 14px 14px!important}
210
+ .pipeline-step::after{right:50%;top:auto;bottom:-8px;transform:translateX(50%) rotate(135deg)}
211
+ .mcp-grid{grid-template-columns:1fr 1fr}
212
+ .cta-install{display:none}
213
+ .cta-secondary{display:none}
214
+ .cta-mobile{display:inline-flex}
215
+ .hero-cta{flex-direction:column}
216
+ }
217
+ @media(max-width:480px){
218
+ .container{padding:0 20px}
219
+ .mcp-grid{grid-template-columns:1fr}
220
+ .formats-grid{grid-template-columns:repeat(3,1fr)}
221
+ .hero-proof{flex-wrap:wrap;gap:8px}
222
+ }
223
+ </style>
224
+ </head>
225
+ <body>
226
+
227
+ <div class="cosmos"><div class="g1"></div><div class="g2"></div><div class="g3"></div></div>
228
+ <div class="star-field" id="stars"></div>
229
+
230
+ <!-- ═══ HERO ═══ -->
231
+ <section class="hero">
232
+ <div class="graph-visual" id="graphVisual"></div>
233
+ <div class="hero-inner container">
234
+ <div class="hero-badge">open source <span class="sep">&#9679;</span> local-first <span class="sep">&#9679;</span> MIT license</div>
235
+ <h1>Drop anything.<br>It <em>compiles itself</em><br>into knowledge.</h1>
236
+ <p class="hero-sub">Self-compiling Zettelkasten MCP server. Ingest PDFs, YouTube, documents&mdash;auto-organized into linked wiki. Claude accesses your entire knowledge base through 20 MCP tools.</p>
237
+ <div class="hero-cta">
238
+ <button class="cta-install" id="ctaInstall">
239
+ <span class="prompt">$</span> npm install -g stellavault
240
+ <span class="copy-hint">click to copy</span>
241
+ </button>
242
+ <a href="https://github.com/Evanciel/stellavault" class="cta-secondary">View on GitHub &rarr;</a>
243
+ <a href="https://github.com/Evanciel/stellavault" class="cta-mobile">View on GitHub &rarr;</a>
244
+ </div>
245
+ <div class="hero-proof">
246
+ <span><span class="proof-num">157KB</span> on npm</span>
247
+ <span class="proof-sep">&#9679;</span>
248
+ <span><span class="proof-num">20</span> MCP tools</span>
249
+ <span class="proof-sep">&#9679;</span>
250
+ <span><span class="proof-num">127</span> tests passing</span>
251
+ <span class="proof-sep">&#9679;</span>
252
+ <span>No cloud required</span>
253
+ </div>
254
+ </div>
255
+ </section>
256
+
257
+ <div class="divider"></div>
258
+
259
+ <!-- ═══ PROBLEMS ═══ -->
260
+ <section class="s-problems">
261
+ <div class="container">
262
+ <div class="section-label reveal">The Problem</div>
263
+ <h2 class="section-title reveal">Notes pile up.<br>Knowledge doesn't.</h2>
264
+ <p class="section-desc reveal">Three things break every knowledge system. Stellavault fixes all three automatically.</p>
265
+ <div class="problems">
266
+ <div class="problem-card reveal">
267
+ <span class="problem-icon">&#128466;</span>
268
+ <div class="problem-label">Problem</div>
269
+ <div class="problem-title">Notes rot in folders</div>
270
+ <div class="problem-desc">You save articles, highlight books, clip videos. They never become connected knowledge.</div>
271
+ <span class="problem-arrow">&darr;</span>
272
+ <div class="problem-solution">Auto-compiling wiki &mdash; raw notes become linked concepts in &lt;100ms</div>
273
+ </div>
274
+ <div class="problem-card reveal">
275
+ <span class="problem-icon">&#128270;</span>
276
+ <div class="problem-label">Problem</div>
277
+ <div class="problem-title">Search finds words, not meaning</div>
278
+ <div class="problem-desc">Keyword search misses semantic connections. You know you wrote it, but can't find it.</div>
279
+ <span class="problem-arrow">&darr;</span>
280
+ <div class="problem-solution">Hybrid AI search &mdash; BM25 + vector + RRF, 50+ languages, locally</div>
281
+ </div>
282
+ <div class="problem-card reveal">
283
+ <span class="problem-icon">&#129302;</span>
284
+ <div class="problem-label">Problem</div>
285
+ <div class="problem-title">AI doesn't know what you know</div>
286
+ <div class="problem-desc">ChatGPT has the internet. It doesn't have your notes, your decisions, your context.</div>
287
+ <span class="problem-arrow">&darr;</span>
288
+ <div class="problem-solution">20 MCP tools &mdash; Claude searches, asks, drafts from your vault</div>
289
+ </div>
290
+ </div>
291
+ </div>
292
+ </section>
293
+
294
+ <div class="divider"></div>
295
+
296
+ <!-- ═══ PIPELINE ═══ -->
297
+ <section class="s-pipeline">
298
+ <div class="container">
299
+ <div class="section-label reveal">The Pipeline</div>
300
+ <h2 class="section-title reveal">Karpathy's self-compiling loop,<br>built for your brain.</h2>
301
+ <p class="section-desc reveal">Inspired by Andrej Karpathy and Niklas Luhmann. Every input flows through the same pipeline. You never manually organize.</p>
302
+ <div class="pipeline-flow reveal">
303
+ <div class="pipeline-step"><div class="step-num">01</div><div class="step-name">Capture</div><div class="step-desc">Drop PDF, paste YouTube, type thought. Drag files in browser. Any format.</div></div>
304
+ <div class="pipeline-step"><div class="step-num">02</div><div class="step-name">Organize</div><div class="step-desc">Zettelkasten auto-classification. Index codes, tags, wikilinks auto-inserted.</div></div>
305
+ <div class="pipeline-step"><div class="step-num">03</div><div class="step-name">Distill</div><div class="step-desc">Compile into wiki. Extract concepts. Detect gaps, contradictions, duplicates.</div></div>
306
+ <div class="pipeline-step"><div class="step-num">04</div><div class="step-name">Express</div><div class="step-desc">Generate drafts. Blog, report, outline. AI-enhanced via Claude. Ship it.</div></div>
307
+ </div>
308
+ </div>
309
+ </section>
310
+
311
+ <!-- ═══ SCREENSHOTS ═══ -->
312
+ <section class="s-screenshots">
313
+ <div class="container">
314
+ <div class="section-label reveal">See It In Action</div>
315
+ <h2 class="section-title reveal">Your vault as a<br>living neural network.</h2>
316
+ <div class="screenshot-hero reveal">
317
+ <video autoplay loop muted playsinline style="width:100%;display:block;border-radius:14px">
318
+ <source src="images/screenshots/stellavault-demo.webm" type="video/webm">
319
+ <img src="images/screenshots/graph-main-2.png" alt="Stellavault 3D Knowledge Graph" loading="lazy">
320
+ </video>
321
+ </div>
322
+ <div class="screenshot-grid">
323
+ <div class="screenshot-card reveal" data-lb="0">
324
+ <img src="images/screenshots/search-active.png" alt="Semantic Search" loading="lazy">
325
+ <div class="caption">Hybrid AI search &mdash; meaning, not just keywords</div>
326
+ <div class="expand-hint">&#x26F6;</div>
327
+ </div>
328
+ <div class="screenshot-card reveal" data-lb="1">
329
+ <img src="images/screenshots/graph-heatmap.png" alt="Knowledge Heatmap" loading="lazy">
330
+ <div class="caption">Heatmap overlay &mdash; see where your knowledge lives</div>
331
+ <div class="expand-hint">&#x26F6;</div>
332
+ </div>
333
+ <div class="screenshot-card reveal" data-lb="2">
334
+ <img src="images/screenshots/graph-light-mode.png" alt="Light Mode" loading="lazy">
335
+ <div class="caption">Light mode &mdash; monochrome with color on interaction</div>
336
+ <div class="expand-hint">&#x26F6;</div>
337
+ </div>
338
+ </div>
339
+ </div>
340
+ </section>
341
+
342
+ <div class="divider"></div>
343
+
344
+ <!-- ═══ MCP ═══ -->
345
+ <section class="s-mcp">
346
+ <div class="container">
347
+ <div class="section-label reveal">Claude Integration</div>
348
+ <h2 class="section-title reveal">20 MCP tools. Claude knows<br>everything you know.</h2>
349
+ <p class="section-desc reveal">One command connects your vault to Claude Code. No API key needed.</p>
350
+ <div class="mcp-connect reveal"><span class="prompt">$</span> <code>claude mcp add stellavault -- stellavault serve</code></div>
351
+ <div class="mcp-grid reveal">
352
+ <div class="mcp-tool"><span class="dot"></span><code>search</code><span class="desc">hybrid AI search</span></div>
353
+ <div class="mcp-tool"><span class="dot"></span><code>ask</code><span class="desc">Q&A from vault</span></div>
354
+ <div class="mcp-tool"><span class="dot"></span><code>generate-draft</code><span class="desc">AI writes drafts</span></div>
355
+ <div class="mcp-tool"><span class="dot"></span><code>get-document</code><span class="desc">full doc + meta</span></div>
356
+ <div class="mcp-tool"><span class="dot"></span><code>get-related</code><span class="desc">semantic neighbors</span></div>
357
+ <div class="mcp-tool"><span class="dot"></span><code>detect-gaps</code><span class="desc">knowledge gaps</span></div>
358
+ <div class="mcp-tool"><span class="dot"></span><code>get-decay-status</code><span class="desc">fading memories</span></div>
359
+ <div class="mcp-tool"><span class="dot"></span><code>get-learning-path</code><span class="desc">review order</span></div>
360
+ <div class="mcp-tool"><span class="dot"></span><code>get-evolution</code><span class="desc">idea drift tracking</span></div>
361
+ <div class="mcp-tool"><span class="dot"></span><code>link-code</code><span class="desc">code-knowledge links</span></div>
362
+ <div class="mcp-tool"><span class="dot"></span><code>create-knowledge-node</code><span class="desc">AI writes notes</span></div>
363
+ <div class="mcp-tool"><span class="dot"></span><code>create-knowledge-link</code><span class="desc">auto-connect</span></div>
364
+ <div class="mcp-tool"><span class="dot"></span><code>log-decision</code><span class="desc">decision journal</span></div>
365
+ <div class="mcp-tool"><span class="dot"></span><code>get-morning-brief</code><span class="desc">daily briefing</span></div>
366
+ <div class="mcp-tool"><span class="dot"></span><code>list-topics</code><span class="desc">topic cloud</span></div>
367
+ <div class="mcp-tool"><span class="dot"></span><code>export</code><span class="desc">JSON / CSV</span></div>
368
+ </div>
369
+ </div>
370
+ </section>
371
+
372
+ <div class="divider"></div>
373
+
374
+ <!-- ═══ FORMATS ═══ -->
375
+ <section class="s-formats">
376
+ <div class="container">
377
+ <div class="section-label reveal">Ingest Anything</div>
378
+ <h2 class="section-title reveal">Every format. One command.</h2>
379
+ <p class="section-desc reveal">Text extraction built-in. No external services. Transcripts with timestamps.</p>
380
+ <div class="formats-grid reveal">
381
+ <div class="format-chip"><div class="format-ext">.pdf</div><div class="format-name">unpdf</div></div>
382
+ <div class="format-chip"><div class="format-ext">.docx</div><div class="format-name">mammoth</div></div>
383
+ <div class="format-chip"><div class="format-ext">.pptx</div><div class="format-name">officeparser</div></div>
384
+ <div class="format-chip"><div class="format-ext">.xlsx</div><div class="format-name">SheetJS</div></div>
385
+ <div class="format-chip"><div class="format-ext" style="color:var(--rose)">YouTube</div><div class="format-name">yt-dlp transcript</div></div>
386
+ <div class="format-chip"><div class="format-ext" style="color:var(--cyan)">URL</div><div class="format-name">HTML to text</div></div>
387
+ </div>
388
+ </div>
389
+ </section>
390
+
391
+ <!-- ═══ SETUP ═══ -->
392
+ <section class="s-setup">
393
+ <div class="container">
394
+ <div class="section-label reveal">Get Started</div>
395
+ <h2 class="section-title reveal">5 minutes to a living<br>knowledge graph.</h2>
396
+ <div class="setup-steps">
397
+ <div class="setup-step reveal"><div class="setup-num">1</div><div class="setup-label">Install globally</div><code class="setup-cmd">npm install -g stellavault</code><div class="setup-desc">157KB. Node.js 20+ required.</div></div>
398
+ <div class="setup-step reveal"><div class="setup-num">2</div><div class="setup-label">Initialize your vault</div><code class="setup-cmd">stellavault init</code><div class="setup-desc">Interactive setup. Points to your Obsidian vault. Indexes everything.</div></div>
399
+ <div class="setup-step reveal"><div class="setup-num">3</div><div class="setup-label">Launch the graph</div><code class="setup-cmd">stellavault graph</code><div class="setup-desc">3D neural graph in browser. Drag files to ingest. Mobile ready.</div></div>
400
+ </div>
401
+ </div>
402
+ </section>
403
+
404
+ <div class="divider"></div>
405
+
406
+ <!-- ═══ TECH ═══ -->
407
+ <section class="s-tech">
408
+ <div class="container">
409
+ <div class="section-label reveal">Under The Hood</div>
410
+ <h2 class="section-title reveal">Local-first. No cloud required.</h2>
411
+ <p class="section-desc reveal">Your knowledge stays on your machine. No API keys needed for core features.</p>
412
+ <div class="tech-grid reveal">
413
+ <div class="tech-item"><span class="label">Embeddings</span><span class="value">multilingual-MiniLM</span></div>
414
+ <div class="tech-item"><span class="label">Vector Store</span><span class="value">SQLite-vec</span></div>
415
+ <div class="tech-item"><span class="label">Search</span><span class="value">BM25 + Cosine + RRF</span></div>
416
+ <div class="tech-item"><span class="label">Memory</span><span class="value">FSRS repetition</span></div>
417
+ <div class="tech-item"><span class="label">3D Graph</span><span class="value">React Three Fiber</span></div>
418
+ <div class="tech-item"><span class="label">AI Protocol</span><span class="value">MCP (20 tools)</span></div>
419
+ <div class="tech-item"><span class="label">Runtime</span><span class="value">Node.js 20+ ESM</span></div>
420
+ <div class="tech-item"><span class="label">Package</span><span class="value">157KB on npm</span></div>
421
+ </div>
422
+ </div>
423
+ </section>
424
+
425
+ <!-- ═══ FOOTER ═══ -->
426
+ <footer>
427
+ <div class="container">
428
+ <div class="footer-cta reveal">
429
+ <div class="footer-brand">Stellavault</div>
430
+ <div class="footer-tagline">Self-compiling knowledge MCP server</div>
431
+ <button class="cta-install" onclick="navigator.clipboard.writeText('npm install -g stellavault');this.classList.add('copied');setTimeout(()=>this.classList.remove('copied'),2000)">
432
+ <span class="prompt">$</span> npm install -g stellavault
433
+ <span class="copy-hint">click to copy</span>
434
+ </button>
435
+ </div>
436
+ <div class="footer-links">
437
+ <a href="https://github.com/Evanciel/stellavault">GitHub</a>
438
+ <a href="https://www.npmjs.com/package/stellavault">npm</a>
439
+ <a href="https://github.com/Evanciel/stellavault/releases">Releases</a>
440
+ <a href="https://github.com/Evanciel/stellavault-obsidian">Obsidian Plugin</a>
441
+ </div>
442
+ <div class="footer-copy">MIT License &middot; Built by Evan</div>
443
+ </div>
444
+ </footer>
445
+
446
+ <!-- Lightbox -->
447
+ <div class="lightbox" id="lightbox">
448
+ <button class="lb-close" id="lbClose">&times;</button>
449
+ <button class="lb-nav lb-prev" id="lbPrev">&#8249;</button>
450
+ <button class="lb-nav lb-next" id="lbNext">&#8250;</button>
451
+ <img id="lbImg" src="" alt="">
452
+ <div class="lb-caption" id="lbCaption"></div>
453
+ </div>
454
+
455
+ <script>
456
+ // Lightbox
457
+ !function(){
458
+ const slides=[
459
+ {src:'images/screenshots/search-active.png',cap:'Hybrid AI search — meaning, not just keywords'},
460
+ {src:'images/screenshots/graph-heatmap.png',cap:'Heatmap overlay — see where your knowledge lives'},
461
+ {src:'images/screenshots/graph-light-mode.png',cap:'Light mode — monochrome with color on interaction'},
462
+ ];
463
+ let cur=0;
464
+ const lb=document.getElementById('lightbox'),img=document.getElementById('lbImg'),cap=document.getElementById('lbCaption');
465
+ const show=i=>{cur=((i%slides.length)+slides.length)%slides.length;img.src=slides[cur].src;cap.textContent=slides[cur].cap;lb.classList.add('active');document.body.style.overflow='hidden'};
466
+ const hide=()=>{lb.classList.remove('active');document.body.style.overflow=''};
467
+ document.querySelectorAll('.screenshot-card[data-lb]').forEach(c=>c.addEventListener('click',()=>show(+c.dataset.lb)));
468
+ document.getElementById('lbClose').addEventListener('click',e=>{e.stopPropagation();hide()});
469
+ document.getElementById('lbPrev').addEventListener('click',e=>{e.stopPropagation();show(cur-1)});
470
+ document.getElementById('lbNext').addEventListener('click',e=>{e.stopPropagation();show(cur+1)});
471
+ lb.addEventListener('click',hide);
472
+ lb.querySelector('img').addEventListener('click',e=>e.stopPropagation());
473
+ document.addEventListener('keydown',e=>{if(!lb.classList.contains('active'))return;if(e.key==='Escape')hide();if(e.key==='ArrowLeft')show(cur-1);if(e.key==='ArrowRight')show(cur+1)});
474
+ }();
475
+
476
+ !function(){const c=document.getElementById('stars'),s=document.createDocumentFragment();for(let i=0;i<100;i++){const d=document.createElement('div');const sz=Math.random()*2+.5;d.style.cssText=`position:absolute;width:${sz}px;height:${sz}px;background:#fff;border-radius:50%;opacity:${Math.random()*.35+.05};left:${Math.random()*100}%;top:${Math.random()*100}%;animation:pulse ${Math.random()*5+3}s ${Math.random()*4}s ease-in-out infinite`;s.appendChild(d)}c.appendChild(s)}();
477
+ !function(){const g=document.getElementById('graphVisual'),nodes=[],cls=['v','c','e'];for(let i=0;i<24;i++){const d=document.createElement('div'),sz=Math.random()*10+3,x=Math.random()*100,y=Math.random()*100;d.className=`node ${cls[i%3]}`;d.style.cssText=`width:${sz}px;height:${sz}px;left:${x}%;top:${y}%;animation-delay:${Math.random()*3}s;animation-duration:${Math.random()*4+2}s`;g.appendChild(d);nodes.push({x,y})}for(let i=0;i<15;i++){const a=nodes[0|Math.random()*nodes.length],b=nodes[0|Math.random()*nodes.length];if(a===b)continue;const dx=b.x-a.x,dy=b.y-a.y,len=Math.sqrt(dx*dx+dy*dy);if(len>35)continue;const e=document.createElement('div');e.className='edge';e.style.cssText=`left:${a.x}%;top:${a.y}%;width:${len}%;transform:rotate(${Math.atan2(dy,dx)*180/Math.PI}deg);opacity:.25`;g.appendChild(e)}}();
478
+ document.getElementById('ctaInstall').addEventListener('click',function(){navigator.clipboard.writeText('npm install -g stellavault');this.classList.add('copied');this.querySelector('.copy-hint').textContent='copied!';setTimeout(()=>{this.classList.remove('copied');this.querySelector('.copy-hint').textContent='click to copy'},2000)});
479
+ const obs=new IntersectionObserver(e=>{e.forEach(e=>{if(e.isIntersecting){e.target.classList.add('visible');obs.unobserve(e.target)}})},{threshold:.1,rootMargin:'0px 0px -40px 0px'});document.querySelectorAll('.reveal').forEach(el=>obs.observe(el));
480
+ document.querySelectorAll('.mcp-tool').forEach((el,i)=>el.style.setProperty('transition-delay',i*25+'ms'));
481
+ </script>
482
+ </body>
483
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stellavault",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Self-compiling knowledge MCP server — ingest anything, auto-organize into Zettelkasten wiki, search with AI, and let Claude access your entire knowledge base.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,8 +29,11 @@ export async function draftCommand(topic, options) {
29
29
  console.log(chalk.dim(` Concepts: ${result.concepts.join(', ')}`));
30
30
  }
31
31
  console.log('');
32
+ console.log(chalk.dim(`Next steps:`));
33
+ console.log(chalk.dim(` Edit in Obsidian, then promote:`));
34
+ console.log(chalk.cyan(` stellavault promote ${result.filePath} --to literature`));
32
35
  if (!options.ai) {
33
- console.log(chalk.dim('Tip: Use --ai to generate with Claude API, or use MCP generate-draft tool in Claude Code.'));
36
+ console.log(chalk.dim(` Or use --ai for Claude-enhanced draft, or MCP generate-draft in Claude Code.`));
34
37
  }
35
38
  }
36
39
  catch (err) {
@@ -0,0 +1,2 @@
1
+ export declare function flushCommand(): Promise<void>;
2
+ //# sourceMappingURL=flush-cmd.d.ts.map
@@ -0,0 +1,58 @@
1
+ // stellavault flush — Daily Logs → Wiki 플러시 (카파시의 Flush Process)
2
+ // Raw daily logs에서 개념/연결 추출 → wiki 갱신
3
+ // "소스 코드를 실행 파일로 컴파일하듯, 원시 데이터를 구조화된 지식으로 변환"
4
+ import chalk from 'chalk';
5
+ import { loadConfig } from '@stellavault/core';
6
+ import { readdirSync, readFileSync, existsSync } from 'node:fs';
7
+ import { resolve, join } from 'node:path';
8
+ export async function flushCommand() {
9
+ const config = loadConfig();
10
+ if (!config.vaultPath) {
11
+ console.error(chalk.red('No vault configured. Run `stellavault init` first.'));
12
+ process.exit(1);
13
+ }
14
+ const folders = config.folders;
15
+ const logDir = resolve(config.vaultPath, folders.fleeting, '_daily-logs');
16
+ if (!existsSync(logDir)) {
17
+ console.log(chalk.yellow('No daily logs found. Use `stellavault session-save` or let Claude Code hooks capture sessions.'));
18
+ return;
19
+ }
20
+ // 1. Daily logs 수집
21
+ const logFiles = readdirSync(logDir).filter(f => f.startsWith('daily-log-') && f.endsWith('.md'));
22
+ if (logFiles.length === 0) {
23
+ console.log(chalk.yellow('No daily log files found.'));
24
+ return;
25
+ }
26
+ console.log(chalk.dim(`Found ${logFiles.length} daily logs`));
27
+ // 2. 모든 로그에서 세션 요약 추출
28
+ let totalSessions = 0;
29
+ const allContent = [];
30
+ for (const file of logFiles) {
31
+ const content = readFileSync(join(logDir, file), 'utf-8');
32
+ const sessions = content.split(/^## Session/m).slice(1);
33
+ totalSessions += sessions.length;
34
+ allContent.push(content);
35
+ }
36
+ console.log(chalk.dim(`Total sessions: ${totalSessions}`));
37
+ // 3. Compile: raw/ 전체 (daily-logs 포함) → wiki
38
+ try {
39
+ const { compileWiki } = await import('@stellavault/core/intelligence/wiki-compiler');
40
+ const rawDir = resolve(config.vaultPath, folders.fleeting);
41
+ const wikiDir = resolve(config.vaultPath, folders.wiki);
42
+ const result = compileWiki(rawDir, wikiDir);
43
+ console.log(chalk.green(`Flush complete!`));
44
+ console.log(chalk.dim(` Daily logs: ${logFiles.length} files, ${totalSessions} sessions`));
45
+ console.log(chalk.dim(` Wiki articles: ${result.wikiArticles.length}`));
46
+ console.log(chalk.dim(` Concepts extracted: ${result.concepts.length}`));
47
+ if (result.concepts.length > 0) {
48
+ console.log(chalk.dim(` Top concepts: ${result.concepts.slice(0, 8).join(', ')}`));
49
+ }
50
+ console.log(chalk.dim(` Index: ${result.indexFile}`));
51
+ }
52
+ catch (err) {
53
+ console.error(chalk.red(`Flush failed: ${err instanceof Error ? err.message : 'unknown'}`));
54
+ process.exit(1);
55
+ }
56
+ console.log(chalk.dim(' Tip: Run `stellavault lint` to check knowledge health'));
57
+ }
58
+ //# sourceMappingURL=flush-cmd.js.map
@@ -0,0 +1,7 @@
1
+ export declare function sessionSaveCommand(options: {
2
+ summary?: string;
3
+ decisions?: string;
4
+ lessons?: string;
5
+ actions?: string;
6
+ }): Promise<void>;
7
+ //# sourceMappingURL=session-cmd.d.ts.map
@@ -0,0 +1,95 @@
1
+ // stellavault session-save — 세션 요약을 daily log로 자동 저장
2
+ // 카파시 아키텍처: 세션 종료 시 대화의 핵심 결정/교훈/실행항목을 캡처
3
+ // Claude Code hooks의 pre-compact/session-end에서 호출됨
4
+ import chalk from 'chalk';
5
+ import { loadConfig } from '@stellavault/core';
6
+ import { writeFileSync, mkdirSync, existsSync, appendFileSync } from 'node:fs';
7
+ import { resolve, join } from 'node:path';
8
+ export async function sessionSaveCommand(options) {
9
+ const config = loadConfig();
10
+ if (!config.vaultPath) {
11
+ console.error(chalk.red('No vault configured. Run `stellavault init` first.'));
12
+ process.exit(1);
13
+ }
14
+ const folders = config.folders;
15
+ const logDir = resolve(config.vaultPath, folders.fleeting, '_daily-logs');
16
+ if (!existsSync(logDir))
17
+ mkdirSync(logDir, { recursive: true });
18
+ const now = new Date();
19
+ const dateStr = now.toISOString().split('T')[0];
20
+ const timeStr = now.toTimeString().split(' ')[0];
21
+ const logFile = join(logDir, `daily-log-${dateStr}.md`);
22
+ // stdin에서 요약 읽기 (Claude Code hook에서 파이프로 전달)
23
+ let summary = options.summary ?? '';
24
+ if (!summary && !process.stdin.isTTY) {
25
+ const chunks = [];
26
+ for await (const chunk of process.stdin) {
27
+ chunks.push(chunk);
28
+ }
29
+ summary = Buffer.concat(chunks).toString('utf-8').trim();
30
+ }
31
+ if (!summary) {
32
+ // 대화형: 직접 입력
33
+ console.log(chalk.dim('Enter session summary (Ctrl+D to finish):'));
34
+ const chunks = [];
35
+ for await (const chunk of process.stdin) {
36
+ chunks.push(chunk);
37
+ }
38
+ summary = Buffer.concat(chunks).toString('utf-8').trim();
39
+ }
40
+ if (!summary) {
41
+ console.error(chalk.yellow('No summary provided. Skipping.'));
42
+ return;
43
+ }
44
+ // daily log 엔트리 구성
45
+ const entry = [
46
+ '',
47
+ `## Session — ${timeStr}`,
48
+ '',
49
+ '### Summary',
50
+ summary,
51
+ '',
52
+ ];
53
+ if (options.decisions) {
54
+ entry.push('### Decisions', options.decisions, '');
55
+ }
56
+ if (options.lessons) {
57
+ entry.push('### Lessons Learned', options.lessons, '');
58
+ }
59
+ if (options.actions) {
60
+ entry.push('### Action Items', options.actions, '');
61
+ }
62
+ entry.push('---', '');
63
+ // 파일이 없으면 헤더 생성, 있으면 append
64
+ if (!existsSync(logFile)) {
65
+ const header = [
66
+ '---',
67
+ `title: "Daily Log — ${dateStr}"`,
68
+ 'type: fleeting',
69
+ 'tags: ["daily-log", "session"]',
70
+ `created: ${now.toISOString()}`,
71
+ '---',
72
+ '',
73
+ `# Daily Log — ${dateStr}`,
74
+ '',
75
+ ].join('\n');
76
+ writeFileSync(logFile, header + entry.join('\n'), 'utf-8');
77
+ }
78
+ else {
79
+ appendFileSync(logFile, entry.join('\n'), 'utf-8');
80
+ }
81
+ console.log(chalk.green(`Session saved to daily log: ${dateStr}`));
82
+ console.log(chalk.dim(` File: ${logFile}`));
83
+ console.log(chalk.dim(` Time: ${timeStr}`));
84
+ console.log(chalk.dim(` Words: ${summary.split(/\s+/).length}`));
85
+ // 자동 compile 트리거
86
+ try {
87
+ const { compileWiki } = await import('@stellavault/core/intelligence/wiki-compiler');
88
+ const rawDir = resolve(config.vaultPath, folders.fleeting);
89
+ const wikiDir = resolve(config.vaultPath, folders.wiki);
90
+ compileWiki(rawDir, wikiDir);
91
+ console.log(chalk.dim(' Wiki: auto-compiled'));
92
+ }
93
+ catch { /* compile 실패해도 저장은 성공 */ }
94
+ }
95
+ //# sourceMappingURL=session-cmd.js.map
@@ -24,6 +24,8 @@ import { captureCommand } from './commands/capture-cmd.js';
24
24
  import { askCommand } from './commands/ask-cmd.js';
25
25
  import { compileCommand } from './commands/compile-cmd.js';
26
26
  import { draftCommand } from './commands/draft-cmd.js';
27
+ import { sessionSaveCommand } from './commands/session-cmd.js';
28
+ import { flushCommand } from './commands/flush-cmd.js';
27
29
  import { lintCommand } from './commands/lint-cmd.js';
28
30
  import { fleetingCommand } from './commands/fleeting-cmd.js';
29
31
  import { ingestCommand, promoteCommand } from './commands/ingest-cmd.js';
@@ -146,6 +148,18 @@ program
146
148
  .option('--format <type>', 'Output format: blog, report, outline (default: blog)')
147
149
  .option('--ai', 'Use Claude API for AI-enhanced draft (requires ANTHROPIC_API_KEY)')
148
150
  .action((topic, opts) => draftCommand(topic, opts));
151
+ program
152
+ .command('session-save')
153
+ .description('Save session summary to daily log (used by Claude Code hooks)')
154
+ .option('-s, --summary <text>', 'Session summary text (or pipe via stdin)')
155
+ .option('-d, --decisions <text>', 'Key decisions made')
156
+ .option('-l, --lessons <text>', 'Lessons learned')
157
+ .option('-a, --actions <text>', 'Action items')
158
+ .action((opts) => sessionSaveCommand(opts));
159
+ program
160
+ .command('flush')
161
+ .description('Flush daily logs → wiki: extract concepts, rebuild connections (Karpathy compile)')
162
+ .action(() => flushCommand());
149
163
  program
150
164
  .command('lint')
151
165
  .description('Knowledge health check — find gaps, duplicates, contradictions, stale notes')
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,122 @@
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
+ for (const phrase of sortedPhrases) {
99
+ const targetTitle = phraseToTitle.get(phrase);
100
+ if (linkedPhrases.has(targetTitle))
101
+ continue; // 같은 노트에 여러 구문 매칭 방지
102
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
103
+ const regex = new RegExp(`(?<!\\[\\[)(?<!#\\s)\\b(${escaped})\\b(?!\\]\\])(?![^\\[]*\\]\\])`, 'gi');
104
+ let replaced = false;
105
+ content = content.replace(regex, (match) => {
106
+ if (replaced)
107
+ return match;
108
+ replaced = true;
109
+ linkedPhrases.add(targetTitle);
110
+ return `[[${targetTitle}|${match}]]`;
111
+ });
112
+ }
113
+ return frontmatter + content;
114
+ }
115
+ /**
116
+ * vault 스캔 + wikilink 삽입을 한 번에 수행하는 편의 함수.
117
+ */
118
+ export function autoLink(body, vaultPath, selfTitle, folders = DEFAULT_FOLDERS) {
119
+ const titles = collectVaultTitles(vaultPath, folders);
120
+ return insertWikilinks(body, titles, selfTitle);
121
+ }
122
+ //# sourceMappingURL=auto-linker.js.map
@@ -7,6 +7,7 @@ import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';
7
7
  import { join, resolve, basename } from 'node:path';
8
8
  import { scanFrontmatter, assignIndexCodes, archiveFile } from './zettelkasten.js';
9
9
  import { compileWiki } from './wiki-compiler.js';
10
+ import { autoLink } from './auto-linker.js';
10
11
  import { DEFAULT_FOLDERS } from '../config.js';
11
12
  /** YAML 값에서 위험한 문자를 이스케이프 */
12
13
  function sanitizeYaml(val) {
@@ -58,7 +59,7 @@ export function ingest(vaultPath, input, folders = DEFAULT_FOLDERS) {
58
59
  }
59
60
  catch { /* index code is optional */ }
60
61
  // Stellavault 표준 포맷으로 저장
61
- const md = buildStandardNote({
62
+ let md = buildStandardNote({
62
63
  title,
63
64
  body,
64
65
  tags,
@@ -68,6 +69,11 @@ export function ingest(vaultPath, input, folders = DEFAULT_FOLDERS) {
68
69
  created: now.toISOString(),
69
70
  inputType: input.type,
70
71
  });
72
+ // wikilink 자동 삽입: 기존 노트 제목과 매칭
73
+ try {
74
+ md = autoLink(md, vaultPath, title, folders);
75
+ }
76
+ catch { /* autoLink 실패해도 저장은 진행 */ }
71
77
  writeFileSync(fullPath, md, 'utf-8');
72
78
  // 자동 compile: fleeting → wiki (rule-based, <100ms)
73
79
  try {
@@ -21,6 +21,14 @@
21
21
  "./intelligence/draft-generator": {
22
22
  "import": "./dist/intelligence/draft-generator.js",
23
23
  "types": "./dist/intelligence/draft-generator.d.ts"
24
+ },
25
+ "./intelligence/wiki-compiler": {
26
+ "import": "./dist/intelligence/wiki-compiler.js",
27
+ "types": "./dist/intelligence/wiki-compiler.d.ts"
28
+ },
29
+ "./intelligence/knowledge-lint": {
30
+ "import": "./dist/intelligence/knowledge-lint.js",
31
+ "types": "./dist/intelligence/knowledge-lint.d.ts"
24
32
  }
25
33
  },
26
34
  "scripts": {