threadnote 0.4.1 → 0.6.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.
@@ -0,0 +1,1543 @@
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, viewport-fit=cover" />
6
+ <meta name="color-scheme" content="dark" />
7
+ <title>Threadnote — shared local context for development agents</title>
8
+ <meta
9
+ name="description"
10
+ content="Threadnote is a safe local workflow for using OpenViking as shared, agent-neutral context for development work — across Codex, Claude, Cursor, and Copilot."
11
+ />
12
+
13
+ <link rel="icon" type="image/svg+xml" href="threadnote-logo.svg" />
14
+
15
+ <meta property="og:title" content="Threadnote — shared local context for development agents" />
16
+ <meta
17
+ property="og:description"
18
+ content="One shared memory for Codex, Claude, Cursor, and Copilot. Local-first, cross-session, cross-agent."
19
+ />
20
+ <meta property="og:image" content="threadnote-logo.svg" />
21
+ <meta property="og:type" content="website" />
22
+ <meta name="twitter:card" content="summary" />
23
+ <meta name="twitter:image" content="threadnote-logo.svg" />
24
+
25
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
26
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
27
+ <link
28
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"
29
+ rel="stylesheet"
30
+ />
31
+
32
+ <style>
33
+ :root {
34
+ --bg: #0a0a0d;
35
+ --bg-elev: #14141a;
36
+ --bg-elev-2: #1c1c24;
37
+ --line: #2a2a35;
38
+ --text: #ececf1;
39
+ --muted: #8a8a96;
40
+ --dim: #5c5c68;
41
+ --accent: #5ee0c5;
42
+ --accent-soft: rgba(94, 224, 197, 0.12);
43
+ --accent-line: rgba(94, 224, 197, 0.35);
44
+ --warn: #ffb86b;
45
+ --danger: #ff7a7a;
46
+ --code-bg: #101019;
47
+ --radius-lg: 16px;
48
+ --radius-md: 10px;
49
+ --radius-sm: 6px;
50
+ --maxw: 1080px;
51
+ --section-pad: clamp(2rem, 6vh, 5rem) clamp(1.25rem, 6vw, 5rem);
52
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', system-ui, sans-serif;
53
+ --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
54
+ }
55
+
56
+ * {
57
+ box-sizing: border-box;
58
+ }
59
+
60
+ html,
61
+ body {
62
+ margin: 0;
63
+ padding: 0;
64
+ }
65
+
66
+ html {
67
+ background: var(--bg);
68
+ color: var(--text);
69
+ font-family: var(--font-sans);
70
+ font-feature-settings: 'cv02', 'cv11', 'ss01';
71
+ -webkit-font-smoothing: antialiased;
72
+ -moz-osx-font-smoothing: grayscale;
73
+ scroll-snap-type: y mandatory;
74
+ scroll-behavior: smooth;
75
+ scroll-padding-top: 0;
76
+ }
77
+
78
+ body {
79
+ line-height: 1.55;
80
+ font-size: clamp(15px, 1.05vw, 17px);
81
+ overflow-x: hidden;
82
+ }
83
+
84
+ ::selection {
85
+ background: var(--accent-soft);
86
+ color: var(--text);
87
+ }
88
+
89
+ a {
90
+ color: var(--accent);
91
+ text-decoration: none;
92
+ border-bottom: 1px solid transparent;
93
+ transition: border-color 0.15s ease;
94
+ }
95
+
96
+ a:hover {
97
+ border-bottom-color: var(--accent-line);
98
+ }
99
+
100
+ code {
101
+ font-family: var(--font-mono);
102
+ font-size: 0.92em;
103
+ background: var(--code-bg);
104
+ padding: 0.1em 0.4em;
105
+ border-radius: 4px;
106
+ color: #d6d6e0;
107
+ }
108
+
109
+ pre {
110
+ font-family: var(--font-mono);
111
+ font-size: 0.86em;
112
+ background: var(--code-bg);
113
+ color: #d6d6e0;
114
+ border: 1px solid var(--line);
115
+ border-left: 3px solid var(--accent);
116
+ border-radius: var(--radius-md);
117
+ padding: 1em 1.2em;
118
+ overflow-x: auto;
119
+ line-height: 1.55;
120
+ }
121
+
122
+ pre code {
123
+ background: transparent;
124
+ padding: 0;
125
+ font-size: 1em;
126
+ }
127
+
128
+ .deck {
129
+ position: relative;
130
+ }
131
+
132
+ .slide {
133
+ min-height: 100vh;
134
+ scroll-snap-align: start;
135
+ padding: var(--section-pad);
136
+ display: flex;
137
+ flex-direction: column;
138
+ justify-content: center;
139
+ align-items: stretch;
140
+ position: relative;
141
+ border-bottom: 1px solid var(--line);
142
+ }
143
+
144
+ .slide-inner {
145
+ width: 100%;
146
+ max-width: var(--maxw);
147
+ margin: 0 auto;
148
+ display: flex;
149
+ flex-direction: column;
150
+ justify-content: center;
151
+ }
152
+
153
+ .slide-eyebrow {
154
+ display: inline-flex;
155
+ align-items: center;
156
+ gap: 0.6em;
157
+ font-family: var(--font-mono);
158
+ font-size: 0.82rem;
159
+ letter-spacing: 0.08em;
160
+ text-transform: uppercase;
161
+ color: var(--accent);
162
+ margin-bottom: 1rem;
163
+ }
164
+
165
+ .slide-eyebrow::before {
166
+ content: '';
167
+ width: 1.5em;
168
+ height: 1px;
169
+ background: var(--accent);
170
+ display: inline-block;
171
+ }
172
+
173
+ h1,
174
+ h2,
175
+ h3 {
176
+ margin: 0;
177
+ line-height: 1.15;
178
+ letter-spacing: -0.018em;
179
+ }
180
+
181
+ h1 {
182
+ font-size: clamp(2.5rem, 6.5vw, 4.5rem);
183
+ font-weight: 800;
184
+ }
185
+
186
+ h2 {
187
+ font-size: clamp(1.85rem, 4vw, 2.75rem);
188
+ font-weight: 700;
189
+ margin-bottom: 1.25rem;
190
+ }
191
+
192
+ h3 {
193
+ font-size: 1.15rem;
194
+ font-weight: 600;
195
+ color: var(--text);
196
+ margin-bottom: 0.35rem;
197
+ }
198
+
199
+ .lead {
200
+ font-size: clamp(1.1rem, 1.6vw, 1.35rem);
201
+ color: var(--muted);
202
+ max-width: 56ch;
203
+ margin-top: 1.25rem;
204
+ }
205
+
206
+ .muted {
207
+ color: var(--muted);
208
+ }
209
+
210
+ .dim {
211
+ color: var(--dim);
212
+ }
213
+
214
+ /* hero */
215
+ #hero {
216
+ background: radial-gradient(circle at 25% 30%, rgba(94, 224, 197, 0.08), transparent 55%),
217
+ radial-gradient(circle at 75% 70%, rgba(124, 140, 255, 0.05), transparent 55%), var(--bg);
218
+ place-items: center;
219
+ }
220
+
221
+ #hero .slide-inner {
222
+ align-items: flex-start;
223
+ }
224
+
225
+ .page-logo {
226
+ position: fixed;
227
+ top: 1.5rem;
228
+ left: 1.5rem;
229
+ width: 220px;
230
+ height: 220px;
231
+ z-index: 40;
232
+ pointer-events: none;
233
+ }
234
+
235
+ .page-logo img {
236
+ width: 100%;
237
+ height: 100%;
238
+ display: block;
239
+ }
240
+
241
+ @media (max-width: 720px) {
242
+ .page-logo {
243
+ width: 120px;
244
+ height: 120px;
245
+ top: 1rem;
246
+ left: 1rem;
247
+ }
248
+ }
249
+
250
+ .hero-title-gradient {
251
+ background: linear-gradient(180deg, #ffffff, #b8b8c8);
252
+ -webkit-background-clip: text;
253
+ background-clip: text;
254
+ -webkit-text-fill-color: transparent;
255
+ }
256
+
257
+ .hero-accent {
258
+ color: var(--accent);
259
+ font-style: normal;
260
+ }
261
+
262
+ .scroll-cue {
263
+ position: absolute;
264
+ bottom: 2rem;
265
+ left: 50%;
266
+ transform: translateX(-50%);
267
+ font-family: var(--font-mono);
268
+ font-size: 0.78rem;
269
+ color: var(--dim);
270
+ animation: drift 2.4s ease-in-out infinite;
271
+ }
272
+
273
+ @keyframes drift {
274
+ 0%,
275
+ 100% {
276
+ transform: translate(-50%, 0);
277
+ opacity: 0.7;
278
+ }
279
+ 50% {
280
+ transform: translate(-50%, 6px);
281
+ opacity: 1;
282
+ }
283
+ }
284
+
285
+ /* grids */
286
+ .grid {
287
+ display: grid;
288
+ gap: 1.25rem;
289
+ }
290
+
291
+ .grid-2 {
292
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
293
+ }
294
+
295
+ .grid-3 {
296
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
297
+ }
298
+
299
+ .card {
300
+ background: var(--bg-elev);
301
+ border: 1px solid var(--line);
302
+ border-radius: var(--radius-lg);
303
+ padding: 1.5rem 1.5rem 1.4rem;
304
+ transition: border-color 0.2s ease, transform 0.2s ease;
305
+ }
306
+
307
+ .card:hover {
308
+ border-color: var(--accent-line);
309
+ transform: translateY(-2px);
310
+ }
311
+
312
+ .card .card-kicker {
313
+ font-family: var(--font-mono);
314
+ font-size: 0.78rem;
315
+ letter-spacing: 0.06em;
316
+ text-transform: uppercase;
317
+ color: var(--accent);
318
+ margin-bottom: 0.6rem;
319
+ display: block;
320
+ }
321
+
322
+ .card p {
323
+ margin: 0;
324
+ color: var(--muted);
325
+ }
326
+
327
+ .pill {
328
+ display: inline-block;
329
+ font-family: var(--font-mono);
330
+ font-size: 0.72rem;
331
+ letter-spacing: 0.06em;
332
+ text-transform: uppercase;
333
+ padding: 0.2em 0.6em;
334
+ border-radius: 999px;
335
+ background: var(--accent-soft);
336
+ color: var(--accent);
337
+ border: 1px solid var(--accent-line);
338
+ }
339
+
340
+ ul.flat {
341
+ list-style: none;
342
+ padding: 0;
343
+ margin: 0.5rem 0 0;
344
+ display: grid;
345
+ gap: 0.75rem;
346
+ }
347
+
348
+ ul.flat > li {
349
+ position: relative;
350
+ padding-left: 1.4rem;
351
+ color: var(--muted);
352
+ line-height: 1.6;
353
+ }
354
+
355
+ ul.flat > li::before {
356
+ content: '';
357
+ position: absolute;
358
+ left: 0;
359
+ top: 0.65em;
360
+ width: 6px;
361
+ height: 6px;
362
+ background: var(--accent);
363
+ border-radius: 2px;
364
+ }
365
+
366
+ ul.flat > li strong {
367
+ color: var(--text);
368
+ font-weight: 600;
369
+ }
370
+
371
+ /* comparison split */
372
+ .compare {
373
+ display: grid;
374
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
375
+ gap: 1.5rem;
376
+ margin-top: 1.5rem;
377
+ }
378
+
379
+ .compare-col {
380
+ background: var(--bg-elev);
381
+ border: 1px solid var(--line);
382
+ border-radius: var(--radius-lg);
383
+ padding: 1.5rem;
384
+ }
385
+
386
+ .compare-col h3 {
387
+ margin-bottom: 0.75rem;
388
+ font-size: 1rem;
389
+ font-family: var(--font-mono);
390
+ letter-spacing: 0.04em;
391
+ text-transform: uppercase;
392
+ color: var(--accent);
393
+ }
394
+
395
+ .compare-col h3.warm {
396
+ color: var(--warn);
397
+ }
398
+
399
+ .compare-col ul {
400
+ margin-top: 0.5rem;
401
+ }
402
+
403
+ /* architecture diagram */
404
+ .architecture {
405
+ background: var(--bg-elev);
406
+ border: 1px solid var(--line);
407
+ border-radius: var(--radius-lg);
408
+ padding: 1.75rem;
409
+ margin-top: 1.5rem;
410
+ }
411
+
412
+ .architecture svg {
413
+ width: 100%;
414
+ height: auto;
415
+ display: block;
416
+ max-height: 56vh;
417
+ }
418
+
419
+ .architecture-legend {
420
+ margin-top: 1.5rem;
421
+ font-family: var(--font-mono);
422
+ font-size: 0.82rem;
423
+ color: var(--muted);
424
+ display: grid;
425
+ gap: 0.5rem;
426
+ }
427
+
428
+ .architecture-legend strong {
429
+ color: var(--text);
430
+ font-weight: 600;
431
+ }
432
+
433
+ /* terminal */
434
+ .terminal {
435
+ background: var(--code-bg);
436
+ border: 1px solid var(--line);
437
+ border-radius: var(--radius-lg);
438
+ overflow: hidden;
439
+ }
440
+
441
+ .terminal-bar {
442
+ display: flex;
443
+ gap: 0.4em;
444
+ padding: 0.65rem 0.9rem;
445
+ background: var(--bg-elev-2);
446
+ border-bottom: 1px solid var(--line);
447
+ }
448
+
449
+ .terminal-bar span {
450
+ width: 10px;
451
+ height: 10px;
452
+ border-radius: 50%;
453
+ background: var(--dim);
454
+ opacity: 0.7;
455
+ }
456
+
457
+ .terminal-bar span:nth-child(1) {
458
+ background: #ff5f56;
459
+ }
460
+
461
+ .terminal-bar span:nth-child(2) {
462
+ background: #ffbd2e;
463
+ }
464
+
465
+ .terminal-bar span:nth-child(3) {
466
+ background: #27c93f;
467
+ }
468
+
469
+ .terminal pre {
470
+ margin: 0;
471
+ border: 0;
472
+ border-radius: 0;
473
+ background: transparent;
474
+ padding: 1.1rem 1.2rem;
475
+ }
476
+
477
+ .terminal .cmd {
478
+ color: var(--accent);
479
+ }
480
+
481
+ .terminal .out {
482
+ color: var(--muted);
483
+ }
484
+
485
+ .terminal .out-strong {
486
+ color: var(--text);
487
+ }
488
+
489
+ .terminal .err {
490
+ color: var(--danger);
491
+ }
492
+
493
+ /* vignette use-case cards */
494
+ .vignettes {
495
+ display: grid;
496
+ gap: 1.25rem;
497
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
498
+ margin-top: 1.5rem;
499
+ }
500
+
501
+ .vignette {
502
+ background: var(--bg-elev);
503
+ border: 1px solid var(--line);
504
+ border-radius: var(--radius-lg);
505
+ padding: 1.5rem;
506
+ display: grid;
507
+ gap: 0.6rem;
508
+ transition: border-color 0.2s ease;
509
+ }
510
+
511
+ .vignette:hover {
512
+ border-color: var(--accent-line);
513
+ }
514
+
515
+ .vignette .v-prompt {
516
+ font-style: italic;
517
+ color: var(--text);
518
+ font-size: 1rem;
519
+ }
520
+
521
+ .vignette .v-answer {
522
+ color: var(--muted);
523
+ font-size: 0.95rem;
524
+ }
525
+
526
+ .vignette .v-cmd {
527
+ font-family: var(--font-mono);
528
+ font-size: 0.82rem;
529
+ color: var(--accent);
530
+ padding: 0.45rem 0.75rem;
531
+ background: var(--code-bg);
532
+ border-radius: 6px;
533
+ border: 1px solid var(--line);
534
+ white-space: nowrap;
535
+ overflow-x: auto;
536
+ }
537
+
538
+ /* side nav dots */
539
+ .deck-nav {
540
+ position: fixed;
541
+ right: max(1rem, 2.5vw);
542
+ top: 50%;
543
+ transform: translateY(-50%);
544
+ display: flex;
545
+ flex-direction: column;
546
+ gap: 0.7rem;
547
+ z-index: 50;
548
+ }
549
+
550
+ .deck-nav a {
551
+ position: relative;
552
+ width: 8px;
553
+ height: 8px;
554
+ border-radius: 50%;
555
+ background: var(--line);
556
+ border: 0;
557
+ display: block;
558
+ transition: background-color 0.2s ease, transform 0.2s ease;
559
+ }
560
+ .deck-nav a::after {
561
+ content: '';
562
+ position: absolute;
563
+ inset: -8px;
564
+ }
565
+
566
+ .deck-nav a.active {
567
+ background: var(--accent);
568
+ transform: scale(1.4);
569
+ }
570
+
571
+ .deck-nav a:hover {
572
+ background: var(--accent-line);
573
+ }
574
+
575
+ .deck-nav a span {
576
+ position: absolute;
577
+ right: 16px;
578
+ top: 50%;
579
+ transform: translateY(-50%);
580
+ white-space: nowrap;
581
+ font-family: var(--font-mono);
582
+ font-size: 0.7rem;
583
+ color: var(--dim);
584
+ opacity: 0;
585
+ transition: opacity 0.15s ease;
586
+ pointer-events: none;
587
+ }
588
+
589
+ .deck-nav a:hover span,
590
+ .deck-nav a.active span {
591
+ opacity: 1;
592
+ }
593
+
594
+ /* progress bar */
595
+ .progress {
596
+ position: fixed;
597
+ top: 0;
598
+ left: 0;
599
+ width: 100%;
600
+ height: 2px;
601
+ background: transparent;
602
+ z-index: 60;
603
+ }
604
+
605
+ .progress-bar {
606
+ height: 100%;
607
+ background: linear-gradient(90deg, var(--accent), #7c8cff);
608
+ width: 0;
609
+ transition: width 0.1s linear;
610
+ }
611
+
612
+ /* footer-ish closing */
613
+ #close .slide-inner {
614
+ align-items: flex-start;
615
+ }
616
+
617
+ .cta-row {
618
+ display: flex;
619
+ flex-wrap: wrap;
620
+ gap: 0.75rem;
621
+ margin-top: 2rem;
622
+ }
623
+
624
+ .cta {
625
+ display: inline-block;
626
+ padding: 0.8rem 1.4rem;
627
+ background: var(--accent);
628
+ color: #0a0a0d;
629
+ font-family: var(--font-mono);
630
+ font-size: 0.95rem;
631
+ font-weight: 600;
632
+ border-radius: 8px;
633
+ text-decoration: none;
634
+ border: 0;
635
+ transition: transform 0.15s ease;
636
+ }
637
+
638
+ .cta:hover {
639
+ transform: translateY(-1px);
640
+ border-bottom: 0;
641
+ }
642
+
643
+ .cta.outline {
644
+ background: transparent;
645
+ color: var(--accent);
646
+ border: 1px solid var(--accent-line);
647
+ }
648
+
649
+ .colophon {
650
+ font-family: var(--font-mono);
651
+ color: var(--dim);
652
+ font-size: 0.82rem;
653
+ margin-top: 3rem;
654
+ }
655
+
656
+ .kbd {
657
+ display: inline-block;
658
+ font-family: var(--font-mono);
659
+ font-size: 0.78em;
660
+ padding: 0.1em 0.45em;
661
+ border-radius: 4px;
662
+ border: 1px solid var(--line);
663
+ background: var(--bg-elev);
664
+ color: var(--text);
665
+ line-height: 1.4;
666
+ }
667
+
668
+ /* reveal animation */
669
+ .reveal {
670
+ opacity: 0;
671
+ transform: translateY(14px);
672
+ transition: opacity 0.6s ease, transform 0.6s ease;
673
+ }
674
+
675
+ .reveal.visible {
676
+ opacity: 1;
677
+ transform: none;
678
+ }
679
+
680
+ @media (max-width: 720px) {
681
+ .deck-nav {
682
+ display: none;
683
+ }
684
+ .slide {
685
+ min-height: auto;
686
+ padding: 3rem 1.25rem;
687
+ }
688
+ html {
689
+ scroll-snap-type: none;
690
+ }
691
+ }
692
+ </style>
693
+ </head>
694
+
695
+ <body>
696
+ <div class="progress"><div class="progress-bar" id="progressBar"></div></div>
697
+
698
+ <div class="page-logo" aria-hidden="true">
699
+ <img src="threadnote-logo-inverted.svg" alt="" />
700
+ </div>
701
+
702
+ <nav class="deck-nav" aria-label="Section progress">
703
+ <a href="#hero" data-label="Intro"><span>Intro</span></a>
704
+ <a href="#problem" data-label="Problem"><span>Problem</span></a>
705
+ <a href="#meet" data-label="Meet threadnote"><span>Meet</span></a>
706
+ <a href="#vs-instructions" data-label="vs CLAUDE.md / AGENTS.md"><span>vs instructions</span></a>
707
+ <a href="#how" data-label="How it works"><span>How</span></a>
708
+ <a href="#safety" data-label="Safety model"><span>Safety</span></a>
709
+ <a href="#install" data-label="Install"><span>Install</span></a>
710
+ <a href="#seed" data-label="Seed"><span>Seed</span></a>
711
+ <a href="#update" data-label="Update"><span>Update</span></a>
712
+ <a href="#lifecycle" data-label="Lifecycle"><span>Lifecycle</span></a>
713
+ <a href="#use-cases" data-label="Use cases"><span>Use cases</span></a>
714
+ <a href="#share" data-label="Share"><span>Share</span></a>
715
+ <a href="#close" data-label="Get started"><span>Start</span></a>
716
+ </nav>
717
+
718
+ <main class="deck">
719
+ <!-- 1. HERO -->
720
+ <section id="hero" class="slide">
721
+ <div class="slide-inner">
722
+ <h1 class="hero-title-gradient">
723
+ Shared local context<br />for <em class="hero-accent">development agents</em>.
724
+ </h1>
725
+ <p class="lead">
726
+ One memory layer Codex, Claude, Cursor, and Copilot all see. Durable feature knowledge, recent handoffs, and
727
+ team-shared decisions — recallable from any session, on any agent, in any worktree.
728
+ </p>
729
+ <div class="cta-row">
730
+ <span class="pill">v0.5.0 · local-first · MIT</span>
731
+ </div>
732
+ </div>
733
+ <div class="scroll-cue">scroll ↓ &nbsp;·&nbsp; <span class="kbd">↓</span> / <span class="kbd">j</span></div>
734
+ </section>
735
+
736
+ <!-- 2. PROBLEM -->
737
+ <section id="problem" class="slide">
738
+ <div class="slide-inner">
739
+ <span class="slide-eyebrow">01 · The problem</span>
740
+ <h2>Agent context is ephemeral. <span class="muted">You aren't.</span></h2>
741
+ <p class="lead">
742
+ Every new session your agent starts from zero. You re-explain the same architecture, repaste the same
743
+ handoff, and rediscover the same gotchas. Across four different agents this multiplies.
744
+ </p>
745
+ <ul class="flat reveal" style="margin-top: 2rem">
746
+ <li><strong>Conversation history evaporates.</strong> Compaction summarizes; the nuance is lost.</li>
747
+ <li>
748
+ <strong>Each agent has its own memory.</strong> Switching from Codex to Claude means starting over.
749
+ </li>
750
+ <li>
751
+ <strong>CLAUDE.md/AGENTS.md is per-repo and static.</strong> It can't carry an in-flight feature's design
752
+ decisions across sessions, much less across agents or repos.
753
+ </li>
754
+ <li>
755
+ <strong>Hand-typed handoffs rot.</strong> The next session needs them, but you only remember to write them
756
+ half the time.
757
+ </li>
758
+ <li>
759
+ <strong>Team knowledge stays in DMs.</strong> Decisions made in Slack thread #1842 don't reach your
760
+ teammate's agent.
761
+ </li>
762
+ </ul>
763
+ </div>
764
+ </section>
765
+
766
+ <!-- 3. MEET THREADNOTE -->
767
+ <section id="meet" class="slide">
768
+ <div class="slide-inner">
769
+ <span class="slide-eyebrow">02 · Meet threadnote</span>
770
+ <h2>A safe local memory layer for the agents you already use.</h2>
771
+ <p class="lead">
772
+ Threadnote sits between your agents and a local
773
+ <a href="https://openviking.ai/" target="_blank" rel="noopener">OpenViking</a> store. One stdio MCP adapter,
774
+ one URI scheme, one set of commands. Everything stays on your machine until you explicitly publish it.
775
+ </p>
776
+
777
+ <div class="grid grid-2" style="margin-top: 2rem">
778
+ <div class="card reveal">
779
+ <span class="card-kicker">Cross-agent</span>
780
+ <h3>One memory, four agents</h3>
781
+ <p>
782
+ Codex, Claude Code, Cursor, and Copilot all recall and write into the same OpenViking store via the
783
+ threadnote MCP adapter.
784
+ </p>
785
+ </div>
786
+ <div class="card reveal">
787
+ <span class="card-kicker">Cross-session</span>
788
+ <h3>Survives compaction</h3>
789
+ <p>
790
+ After the context window fills up, the next turn recalls durable feature memories and the latest handoff
791
+ instead of relying on the compressed summary alone.
792
+ </p>
793
+ </div>
794
+ <div class="card reveal">
795
+ <span class="card-kicker">Lifecycle-aware</span>
796
+ <h3>Durable vs handoff</h3>
797
+ <p>
798
+ <code>kind: durable</code> for facts that should outlive a session. <code>kind: handoff</code> for
799
+ current work logs. One stable file per <code>project/topic</code>; updates replace rather than
800
+ accumulate.
801
+ </p>
802
+ </div>
803
+ <div class="card reveal">
804
+ <span class="card-kicker">Local-first</span>
805
+ <h3>Yours, on your machine</h3>
806
+ <p>
807
+ Nothing leaves <code>~/.openviking</code> unless you explicitly <code>share publish</code> a curated
808
+ memory to a team git repo. No telemetry, no SaaS, no network egress.
809
+ </p>
810
+ </div>
811
+ </div>
812
+ </div>
813
+ </section>
814
+
815
+ <!-- 4. vs CLAUDE.md / AGENTS.md -->
816
+ <section id="vs-instructions" class="slide">
817
+ <div class="slide-inner">
818
+ <span class="slide-eyebrow">03 · Why not just CLAUDE.md or AGENTS.md?</span>
819
+ <h2>Use them. Threadnote is not a replacement.</h2>
820
+ <p class="lead">
821
+ They are perfect for stable, canonical, version-controlled rules. They are wrong for living context — what
822
+ happened in the last session, which branch is mid-refactor, which on-call workaround actually shipped.
823
+ </p>
824
+
825
+ <div class="compare">
826
+ <div class="compare-col reveal">
827
+ <h3 class="warm">CLAUDE.md · AGENTS.md · Cursor rules</h3>
828
+ <ul class="flat">
829
+ <li><strong>Where it lives:</strong> in the repo, versioned, shared via PR.</li>
830
+ <li>
831
+ <strong>Best for:</strong> coding standards, test commands, review rules, architecture invariants.
832
+ </li>
833
+ <li><strong>Read pattern:</strong> loaded into every agent context window in this repo.</li>
834
+ <li><strong>Edit cost:</strong> human writes; PR review; high friction (which is the point).</li>
835
+ <li><strong>Lifecycle:</strong> long-lived. Stale entries hurt every session.</li>
836
+ </ul>
837
+ </div>
838
+
839
+ <div class="compare-col reveal">
840
+ <h3>Threadnote</h3>
841
+ <ul class="flat">
842
+ <li>
843
+ <strong>Where it lives:</strong> local OpenViking store, cross-repo, cross-agent. Optionally published
844
+ to a team git repo.
845
+ </li>
846
+ <li>
847
+ <strong>Best for:</strong> feature knowledge, handoffs, branch state, prod-on-call findings, personal
848
+ workflow facts.
849
+ </li>
850
+ <li>
851
+ <strong>Read pattern:</strong> recalled on demand; only the relevant URIs enter the context window.
852
+ </li>
853
+ <li><strong>Edit cost:</strong> agents write; humans guide; low friction by design.</li>
854
+ <li><strong>Lifecycle:</strong> durable / handoff / archived. Compact in place, never accrete.</li>
855
+ </ul>
856
+ </div>
857
+ </div>
858
+
859
+ <p class="muted" style="margin-top: 1.5rem">
860
+ <strong style="color: var(--text)">The split:</strong> put repo policy in the .md files. Put feature
861
+ history, in-flight task state, handoffs, and personal workflow facts in threadnote.
862
+ </p>
863
+ </div>
864
+ </section>
865
+
866
+ <!-- 5. HOW IT WORKS -->
867
+ <section id="how" class="slide">
868
+ <div class="slide-inner">
869
+ <span class="slide-eyebrow">04 · How it works</span>
870
+ <h2>One MCP adapter, one URI scheme, one local store.</h2>
871
+
872
+ <div class="architecture reveal">
873
+ <svg viewBox="0 0 880 380" role="img" aria-label="Threadnote architecture diagram">
874
+ <defs>
875
+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
876
+ <path d="M0,0 L10,5 L0,10 z" fill="#5ee0c5" />
877
+ </marker>
878
+ <linearGradient id="agentGrad" x1="0" y1="0" x2="1" y2="1">
879
+ <stop offset="0" stop-color="#1c1c24" />
880
+ <stop offset="1" stop-color="#15151a" />
881
+ </linearGradient>
882
+ </defs>
883
+
884
+ <!-- agents row -->
885
+ <g font-family="JetBrains Mono, monospace" font-size="13" fill="#ececf1">
886
+ <rect x="20" y="20" width="170" height="64" rx="10" fill="url(#agentGrad)" stroke="#2a2a35" />
887
+ <text x="105" y="48" text-anchor="middle">Codex CLI</text>
888
+ <text x="105" y="68" text-anchor="middle" fill="#8a8a96" font-size="11">stdio MCP</text>
889
+
890
+ <rect x="210" y="20" width="170" height="64" rx="10" fill="url(#agentGrad)" stroke="#2a2a35" />
891
+ <text x="295" y="48" text-anchor="middle">Claude Code</text>
892
+ <text x="295" y="68" text-anchor="middle" fill="#8a8a96" font-size="11">stdio MCP</text>
893
+
894
+ <rect x="400" y="20" width="170" height="64" rx="10" fill="url(#agentGrad)" stroke="#2a2a35" />
895
+ <text x="485" y="48" text-anchor="middle">Cursor</text>
896
+ <text x="485" y="68" text-anchor="middle" fill="#8a8a96" font-size="11">stdio MCP</text>
897
+
898
+ <rect x="590" y="20" width="170" height="64" rx="10" fill="url(#agentGrad)" stroke="#2a2a35" />
899
+ <text x="675" y="48" text-anchor="middle">Copilot (VS Code)</text>
900
+ <text x="675" y="68" text-anchor="middle" fill="#8a8a96" font-size="11">stdio MCP</text>
901
+ </g>
902
+
903
+ <!-- arrows down -->
904
+ <g stroke="#5ee0c5" stroke-width="1.5" fill="none" marker-end="url(#arrow)" opacity="0.7">
905
+ <line x1="105" y1="84" x2="380" y2="148" />
906
+ <line x1="295" y1="84" x2="400" y2="148" />
907
+ <line x1="485" y1="84" x2="480" y2="148" />
908
+ <line x1="675" y1="84" x2="500" y2="148" />
909
+ </g>
910
+
911
+ <!-- threadnote MCP -->
912
+ <g font-family="JetBrains Mono, monospace" font-size="13" fill="#ececf1">
913
+ <rect x="340" y="150" width="200" height="60" rx="10" fill="#5ee0c5" />
914
+ <text x="440" y="178" text-anchor="middle" fill="#0a0a0d" font-weight="600">threadnote-mcp-server</text>
915
+ <text x="440" y="196" text-anchor="middle" fill="#0a0a0d" opacity="0.7" font-size="11">
916
+ recall · read · list · remember · share
917
+ </text>
918
+ </g>
919
+
920
+ <!-- arrow to OV -->
921
+ <g stroke="#5ee0c5" stroke-width="1.5" fill="none" marker-end="url(#arrow)" opacity="0.7">
922
+ <line x1="440" y1="210" x2="440" y2="244" />
923
+ </g>
924
+
925
+ <!-- OpenViking -->
926
+ <g font-family="JetBrains Mono, monospace" font-size="13" fill="#ececf1">
927
+ <rect x="290" y="246" width="300" height="60" rx="10" fill="url(#agentGrad)" stroke="#2a2a35" />
928
+ <text x="440" y="272" text-anchor="middle">openviking-server</text>
929
+ <text x="440" y="290" text-anchor="middle" fill="#8a8a96" font-size="11">
930
+ 127.0.0.1:1933 · python · uv-managed
931
+ </text>
932
+ </g>
933
+
934
+ <!-- arrows to storage -->
935
+ <g stroke="#5ee0c5" stroke-width="1.5" fill="none" marker-end="url(#arrow)" opacity="0.7">
936
+ <line x1="380" y1="306" x2="240" y2="340" />
937
+ <line x1="500" y1="306" x2="640" y2="340" />
938
+ </g>
939
+
940
+ <!-- storage -->
941
+ <g font-family="JetBrains Mono, monospace" font-size="12" fill="#ececf1">
942
+ <rect x="60" y="340" width="360" height="32" rx="6" fill="#101019" stroke="#2a2a35" />
943
+ <text x="240" y="361" text-anchor="middle">~/.openviking/data/viking/&lt;account&gt;/...</text>
944
+
945
+ <rect x="460" y="340" width="360" height="32" rx="6" fill="#101019" stroke="#2a2a35" />
946
+ <text x="640" y="361" text-anchor="middle">vectordb (LevelDB · semantic index)</text>
947
+ </g>
948
+ </svg>
949
+ <div class="architecture-legend">
950
+ <div><strong>One adapter, one URI scheme.</strong> Every agent calls the same MCP tools.</div>
951
+ <div>
952
+ <strong>URIs are stable.</strong> A memory at <code>viking://user/&lt;you&gt;/...</code> is the same
953
+ pointer from every session.
954
+ </div>
955
+ <div>
956
+ <strong>Storage is plain markdown</strong> on your filesystem. The vectordb is regenerable; the source
957
+ of truth is on disk.
958
+ </div>
959
+ </div>
960
+ </div>
961
+ </div>
962
+ </section>
963
+
964
+ <!-- 6. SAFETY -->
965
+ <section id="safety" class="slide">
966
+ <div class="slide-inner">
967
+ <span class="slide-eyebrow">05 · Safety model</span>
968
+ <h2>Local by default. Explicit when it isn't.</h2>
969
+ <p class="lead">
970
+ Threadnote is designed for an environment where agents write to your memory. The defaults assume that's
971
+ untrusted and that mistakes happen.
972
+ </p>
973
+
974
+ <div class="grid grid-2" style="margin-top: 1.5rem">
975
+ <div class="card reveal">
976
+ <span class="card-kicker">Local</span>
977
+ <h3>Writes stay on disk</h3>
978
+ <p>
979
+ Every <code>remember</code> / <code>handoff</code> writes under <code>THREADNOTE_HOME</code> (defaults
980
+ to <code>~/.openviking</code>). Nothing leaves the machine without explicit user action.
981
+ </p>
982
+ </div>
983
+ <div class="card reveal">
984
+ <span class="card-kicker">Curated</span>
985
+ <h3>Manifest-scoped seeding</h3>
986
+ <p>
987
+ <code>threadnote seed</code> imports only paths declared in a seed manifest. It does not index whole
988
+ repos. The default patterns target <code>CLAUDE.md</code>, <code>AGENTS.md</code>,
989
+ <code>SKILL.md</code>, and <code>docs/**</code>.
990
+ </p>
991
+ </div>
992
+ <div class="card reveal">
993
+ <span class="card-kicker">Filtered</span>
994
+ <h3>Built-in ignore + redaction</h3>
995
+ <p>
996
+ <code>.threadnoteignore</code> excludes build output, binaries, env files, and logs. Known config files
997
+ (<code>.mcp.json</code>, <code>config.toml</code>, settings JSON) pass through a redactor before import.
998
+ </p>
999
+ </div>
1000
+ <div class="card reveal">
1001
+ <span class="card-kicker">Scanned</span>
1002
+ <h3>Secret-pattern scrubber</h3>
1003
+ <p>
1004
+ Candidate files are skipped if common token shapes survive redaction. The
1005
+ <code>share publish</code> path refuses to push memories that contain PEM keys,
1006
+ <code>sk-…</code> / <code>github_pat_…</code> / <code>glpat-…</code>, JWTs, AWS keys, or Slack tokens.
1007
+ </p>
1008
+ </div>
1009
+ <div class="card reveal">
1010
+ <span class="card-kicker">Explicit</span>
1011
+ <h3>Agent configs are opt-in</h3>
1012
+ <p>
1013
+ <code>mcp-install &lt;agent&gt;</code> prints the commands by default. Pass <code>--apply</code> to
1014
+ actually modify Codex/Claude/Cursor/Copilot config. User-level instruction blocks are surrounded by
1015
+ markers and only their managed region is rewritten.
1016
+ </p>
1017
+ </div>
1018
+ <div class="card reveal">
1019
+ <span class="card-kicker">Transactional</span>
1020
+ <h3>No half-published state</h3>
1021
+ <p>
1022
+ If <code>share publish</code> can't remove the personal copy after writing the shared copy, it rolls the
1023
+ shared write back. You either publish cleanly or you publish nothing.
1024
+ </p>
1025
+ </div>
1026
+ </div>
1027
+ </div>
1028
+ </section>
1029
+
1030
+ <!-- 7. INSTALL -->
1031
+ <section id="install" class="slide">
1032
+ <div class="slide-inner">
1033
+ <span class="slide-eyebrow">06 · Install</span>
1034
+ <h2>One command. Then point your agents at it.</h2>
1035
+ <p class="lead">
1036
+ The installer wires up the npm package, the OpenViking Python environment, and the local server. After
1037
+ that, <code>mcp-install</code> per agent and you're done.
1038
+ </p>
1039
+
1040
+ <div class="grid grid-2" style="margin-top: 1.5rem">
1041
+ <div class="reveal">
1042
+ <h3 style="margin-bottom: 0.5rem">1. Install threadnote</h3>
1043
+ <div class="terminal">
1044
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1045
+ <pre><span class="cmd">$ curl -fsSL https://raw.githubusercontent.com/Kashkovsky/threadnote/main/scripts/install.sh | sh</span>
1046
+
1047
+ <span class="out">Installed threadnote@0.4.1</span>
1048
+ <span class="out">Already exists: ~/.local/bin/threadnote</span>
1049
+ <span class="out">OpenViking installs into Python; neither uv nor pipx is</span>
1050
+ <span class="out">on PATH so threadnote would fall back to `pip install</span>
1051
+ <span class="out">--user`, which fails on PEP 668 setups.</span>
1052
+ <span class="out">Install uv now? [Y/n] </span><span class="out-strong">Y</span>
1053
+ <span class="out">Installing uv via Homebrew…</span>
1054
+ <span class="out-strong">OK openviking 0.3.12 ready · server healthy</span></pre>
1055
+ </div>
1056
+ <p class="muted" style="margin-top: 0.75rem; font-size: 0.9rem">
1057
+ Or manually: <code>npm install -g threadnote && threadnote install</code>. On a fresh macOS / modern
1058
+ Linux machine the installer offers to <code>brew install uv</code> for you (avoids PEP 668 errors).
1059
+ </p>
1060
+ </div>
1061
+
1062
+ <div class="reveal">
1063
+ <h3 style="margin-bottom: 0.5rem">2. Wire up each agent (pick what you use)</h3>
1064
+ <div class="terminal">
1065
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1066
+ <pre><span class="cmd">$ threadnote mcp-install codex --apply</span>
1067
+ <span class="cmd">$ threadnote mcp-install claude --apply</span>
1068
+ <span class="cmd">$ threadnote mcp-install cursor --apply</span>
1069
+ <span class="cmd">$ threadnote mcp-install copilot --apply</span>
1070
+ <span class="out">Installed MCP server `threadnote` for codex, claude,</span>
1071
+ <span class="out">cursor, copilot. Restart agent sessions to pick up.</span>
1072
+
1073
+ <span class="cmd">$ threadnote doctor --dry-run</span>
1074
+ <span class="out-strong">Summary: 0 failure(s), 0 warning(s)</span></pre>
1075
+ </div>
1076
+ <p class="muted" style="margin-top: 0.75rem; font-size: 0.9rem">
1077
+ Without <code>--apply</code>, each command prints a dry run so you can review before changing your agent
1078
+ configs. Run <code>doctor</code> anytime to verify.
1079
+ </p>
1080
+ </div>
1081
+ </div>
1082
+
1083
+ <p class="muted" style="margin-top: 2rem">
1084
+ Open a fresh agent session after installing MCP — the tool list reloads at session start.
1085
+ </p>
1086
+ </div>
1087
+ </section>
1088
+
1089
+ <!-- 8. SEEDING -->
1090
+ <section id="seed" class="slide">
1091
+ <div class="slide-inner">
1092
+ <span class="slide-eyebrow">07 · Seeding repo context</span>
1093
+ <h2>Pull just the curated docs into recall.</h2>
1094
+ <p class="lead">
1095
+ Threadnote does not index whole repos. Instead, point it at a seed manifest of paths you actually want
1096
+ agents to recall — typically <code>AGENTS.md</code>, <code>CLAUDE.md</code>, repo docs, and
1097
+ <code>SKILL.md</code> guidance.
1098
+ </p>
1099
+
1100
+ <div class="grid grid-2" style="margin-top: 1.5rem">
1101
+ <div class="reveal">
1102
+ <h3>Generate a manifest</h3>
1103
+ <div class="terminal">
1104
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1105
+ <pre><span class="cmd">$ threadnote init-manifest \
1106
+ --repo ~/src/web-app \
1107
+ --repo ~/src/mobile-app</span>
1108
+ <span class="out">Wrote ~/.openviking/seed-manifest.yaml (2 project(s))</span>
1109
+
1110
+ <span class="cmd">$ threadnote seed</span>
1111
+ <span class="out-strong">Imported 47 curated path(s); skipped 6 secret-flagged.</span>
1112
+
1113
+ <span class="cmd">$ threadnote seed-skills</span>
1114
+ <span class="out-strong">Imported SKILL.md catalog from Codex, Claude, repos.</span></pre>
1115
+ </div>
1116
+ </div>
1117
+
1118
+ <div class="reveal">
1119
+ <h3>What the manifest looks like</h3>
1120
+ <pre><code>version: 1
1121
+ projects:
1122
+ - name: web-app
1123
+ path: ~/src/web-app
1124
+ uri: viking://resources/repos/web-app
1125
+ seed:
1126
+ - AGENTS.md
1127
+ - CLAUDE.md
1128
+ - docs/**/*.md
1129
+ - .claude/skills/**/SKILL.md
1130
+ - name: mobile-app
1131
+ path: ~/src/mobile-app
1132
+ uri: viking://resources/repos/mobile-app
1133
+ seed:
1134
+ - AGENTS.md
1135
+ - docs/**/*.md</code></pre>
1136
+ </div>
1137
+ </div>
1138
+
1139
+ <ul class="flat reveal" style="margin-top: 2rem">
1140
+ <li>
1141
+ <strong>Recall is smart enough to scope by query.</strong> Ask "web-app durable feature memories" and
1142
+ recall prefers <code>viking://resources/repos/web-app</code> first.
1143
+ </li>
1144
+ <li>
1145
+ <strong>Seeding is opt-in and re-runnable.</strong> <code>threadnote seed</code> never deletes; it
1146
+ upserts.
1147
+ </li>
1148
+ <li>
1149
+ <strong>Skills are a first-class catalog.</strong> <code>seed-skills</code> makes reusable workflows
1150
+ discoverable: testing, release, on-call, debugging, plugin guidance.
1151
+ </li>
1152
+ </ul>
1153
+ </div>
1154
+ </section>
1155
+
1156
+ <!-- 9. UPDATES -->
1157
+ <section id="update" class="slide">
1158
+ <div class="slide-inner">
1159
+ <span class="slide-eyebrow">08 · Updates &amp; repair</span>
1160
+ <h2>Always one command from a clean state.</h2>
1161
+
1162
+ <div class="grid grid-2" style="margin-top: 1.5rem">
1163
+ <div class="reveal">
1164
+ <h3>Routine update</h3>
1165
+ <div class="terminal">
1166
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1167
+ <pre><span class="cmd">$ threadnote update</span>
1168
+ <span class="out">Latest: threadnote@0.4.1 (installed: 0.4.0)</span>
1169
+ <span class="out">Running: npm install -g threadnote@0.4.1</span>
1170
+ <span class="out">Running: threadnote repair</span>
1171
+ <span class="out-strong">All checks passed. Restart agents to reload MCP.</span></pre>
1172
+ </div>
1173
+ <p class="muted" style="font-size: 0.9rem; margin-top: 0.75rem">
1174
+ <code>update --check</code> reports what's available. <code>update --dry-run</code> shows the plan.
1175
+ Releases may include post-update memory migrations that prompt before applying.
1176
+ </p>
1177
+ </div>
1178
+
1179
+ <div class="reveal">
1180
+ <h3>Repair after a moved checkout</h3>
1181
+ <div class="terminal">
1182
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1183
+ <pre><span class="cmd">$ threadnote repair</span>
1184
+ <span class="out">OK threadnote shim: ~/.local/bin/threadnote</span>
1185
+ <span class="out">OK codex / claude / cursor / copilot user instructions</span>
1186
+ <span class="out">OK manifest, ignore file, templates</span>
1187
+ <span class="out">OK openviking health</span>
1188
+ <span class="out-strong">Summary: 0 failure(s), 0 warning(s)</span></pre>
1189
+ </div>
1190
+ <p class="muted" style="font-size: 0.9rem; margin-top: 0.75rem">
1191
+ Rewrites the threadnote shim and every agent's MCP config to point at the current install. Run after
1192
+ deleting an old npm prefix or moving the package between Node versions.
1193
+ </p>
1194
+ </div>
1195
+ </div>
1196
+ </div>
1197
+ </section>
1198
+
1199
+ <!-- 10. LIFECYCLE -->
1200
+ <section id="lifecycle" class="slide">
1201
+ <div class="slide-inner">
1202
+ <span class="slide-eyebrow">09 · Memory lifecycle</span>
1203
+ <h2>Durable. Handoff. Archived.</h2>
1204
+ <p class="lead">
1205
+ Three lifecycle states, one stable URI per <code>project/topic</code>. Updates replace, never accrete.
1206
+ </p>
1207
+
1208
+ <div class="grid grid-3" style="margin-top: 1.5rem">
1209
+ <div class="card reveal">
1210
+ <span class="card-kicker">kind: durable</span>
1211
+ <h3>Long-lived feature knowledge</h3>
1212
+ <p>
1213
+ Design decisions, API contracts, gotchas, intended behavior. The kind that should outlive a session and
1214
+ travel across agents. Default for <code>threadnote remember</code>.
1215
+ </p>
1216
+ </div>
1217
+ <div class="card reveal">
1218
+ <span class="card-kicker">kind: handoff</span>
1219
+ <h3>Current work logs</h3>
1220
+ <p>
1221
+ Repo, branch, files touched, tests run, blockers, next step. The kind your next agent recalls first.
1222
+ <code>threadnote handoff</code> captures git state automatically.
1223
+ </p>
1224
+ </div>
1225
+ <div class="card reveal">
1226
+ <span class="card-kicker">status: archived</span>
1227
+ <h3>Provenance only</h3>
1228
+ <p>
1229
+ Old handoffs that helped at the time but shouldn't be current working context. <code>archive</code>
1230
+ moves them out of the active recall scope; <code>--include-archived</code> still finds them.
1231
+ </p>
1232
+ </div>
1233
+ </div>
1234
+
1235
+ <div style="margin-top: 2rem" class="reveal">
1236
+ <h3 style="margin-bottom: 0.75rem">One file per project/topic</h3>
1237
+ <pre><code># Capture an in-flight feature memory and its current handoff side by side:
1238
+ threadnote remember \
1239
+ --kind durable --project web-app --topic auth-token-refresh \
1240
+ --text "Architecture: provider-only token client, JWT helpers in jwt.ts..."
1241
+
1242
+ threadnote handoff \
1243
+ --project web-app --topic auth-token-refresh \
1244
+ --task "Address reviewer comments on token refresh PR" \
1245
+ --tests "make lint-lite; mocha auth_client_test 77 passing" \
1246
+ --next-step "Push and request re-review"
1247
+
1248
+ # Later, update the durable memory in place (no timestamped duplicates):
1249
+ threadnote remember \
1250
+ --kind durable --project web-app --topic auth-token-refresh \
1251
+ --replace viking://user/&lt;you&gt;/memories/durable/projects/web-app/auth-token-refresh.md \
1252
+ --text "..."</code></pre>
1253
+ </div>
1254
+ </div>
1255
+ </section>
1256
+
1257
+ <!-- 11. USE CASES -->
1258
+ <section id="use-cases" class="slide">
1259
+ <div class="slide-inner">
1260
+ <span class="slide-eyebrow">10 · Real-world use cases</span>
1261
+ <h2>Where it actually pays off.</h2>
1262
+
1263
+ <div class="vignettes">
1264
+ <div class="vignette reveal">
1265
+ <div class="v-prompt">"Continue the feature from yesterday."</div>
1266
+ <div class="v-answer">
1267
+ Fresh session, fresh agent. Threadnote recalls the active handoff for the branch plus the durable
1268
+ feature memory: design decisions, the open PR, what's left, what just failed in CI. No re-explaining.
1269
+ </div>
1270
+ <div class="v-cmd">threadnote recall --query "&lt;branch&gt; latest handoff"</div>
1271
+ </div>
1272
+
1273
+ <div class="vignette reveal">
1274
+ <div class="v-prompt">"What did we conclude about that prod-on-call thing?"</div>
1275
+ <div class="v-answer">
1276
+ The sanitized handoff from the on-call escalation is still there. Re-open the same Slack thread three
1277
+ weeks later; the durable feature memory points your agent at the right files and the resolution.
1278
+ </div>
1279
+ <div class="v-cmd">threadnote recall --query "export workflow timeout on-call findings"</div>
1280
+ </div>
1281
+
1282
+ <div class="vignette reveal">
1283
+ <div class="v-prompt">"Switch from Codex to Claude mid-task."</div>
1284
+ <div class="v-answer">
1285
+ Codex stores a handoff before pausing. Claude opens the same MCP layer, recalls the handoff plus the
1286
+ durable memory, picks up the test command and the open blocker without you typing them again.
1287
+ </div>
1288
+ <div class="v-cmd">threadnote handoff --task "..." --next-step "..."</div>
1289
+ </div>
1290
+
1291
+ <div class="vignette reveal">
1292
+ <div class="v-prompt">"Onboard a new teammate to a feature."</div>
1293
+ <div class="v-answer">
1294
+ Publish the feature's durable memory to the team repo with <code>share publish</code>. Their agent
1295
+ pulls it on the next <code>share sync</code> and recall surfaces it alongside their own personal
1296
+ memories — no DM forwarding, no PDF dumping.
1297
+ </div>
1298
+ <div class="v-cmd">threadnote share publish viking://user/&lt;you&gt;/memories/durable/...</div>
1299
+ </div>
1300
+
1301
+ <div class="vignette reveal">
1302
+ <div class="v-prompt">"Compaction is about to nuke the context."</div>
1303
+ <div class="v-answer">
1304
+ Before context fills up, the agent stores a handoff. The next turn after compaction recalls it — the
1305
+ summary covers the conversation arc, threadnote covers the durable facts the summary couldn't keep.
1306
+ </div>
1307
+ <div class="v-cmd">threadnote handoff --project &lt;repo&gt; --topic &lt;feature&gt; ...</div>
1308
+ </div>
1309
+
1310
+ <div class="vignette reveal">
1311
+ <div class="v-prompt">"Where does this repo run its release notes?"</div>
1312
+ <div class="v-answer">
1313
+ You discovered it the hard way last time. Tell your agent to <code>remember</code> the fact. Next time
1314
+ anyone asks, the agent finds it in two seconds without grepping the wiki.
1315
+ </div>
1316
+ <div class="v-cmd">threadnote remember --kind durable --topic release-process ...</div>
1317
+ </div>
1318
+ </div>
1319
+ </div>
1320
+ </section>
1321
+
1322
+ <!-- 12. SHARE -->
1323
+ <section id="share" class="slide">
1324
+ <div class="slide-inner">
1325
+ <span class="slide-eyebrow">11 · Sharing across the team</span>
1326
+ <h2>Curated memories. Yours to push. Theirs to pull.</h2>
1327
+ <p class="lead">
1328
+ <code>threadnote share</code> publishes a subset of durable memories into a git repo so other engineers'
1329
+ agents can recall them. Personal handoffs, preferences, and unpublished durables always stay local.
1330
+ </p>
1331
+
1332
+ <div class="grid grid-2" style="margin-top: 1.5rem">
1333
+ <div class="reveal">
1334
+ <h3>One-time setup</h3>
1335
+ <div class="terminal">
1336
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1337
+ <pre><span class="cmd">$ threadnote share init git@github.com:org/team-memories.git</span>
1338
+ <span class="out">Cloned into ~/.openviking/data/.../memories/shared/default/</span>
1339
+ <span class="out">Ingested 12 shared memory file(s) into OpenViking.</span>
1340
+ <span class="out">Added .gitignore exclusions for OV directory summaries.</span></pre>
1341
+ </div>
1342
+ <p class="muted" style="font-size: 0.9rem; margin-top: 0.6rem">
1343
+ Working tree lives inside the OV data tree so recall sees it. The git directory is moved aside via
1344
+ <code>--separate-git-dir</code> so OV never sees git internals.
1345
+ </p>
1346
+ </div>
1347
+
1348
+ <div class="reveal">
1349
+ <h3>Publish a durable memory</h3>
1350
+ <div class="terminal">
1351
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1352
+ <pre><span class="cmd">$ threadnote share publish \
1353
+ viking://user/&lt;you&gt;/memories/durable/projects/&lt;p&gt;/&lt;t&gt;.md</span>
1354
+ <span class="out">Scrubber: no secret patterns detected.</span>
1355
+ <span class="out">git commit -m 'share: publish durable/projects/&lt;p&gt;/&lt;t&gt;.md'</span>
1356
+ <span class="out-strong">Published. Pushed to origin/main.</span></pre>
1357
+ </div>
1358
+ <p class="muted" style="font-size: 0.9rem; margin-top: 0.6rem">
1359
+ Refuses to publish if the memory matches secret patterns (PEM, <code>sk-</code>, <code>gh*_</code>,
1360
+ <code>github_pat_</code>, <code>glpat-</code>, JWTs, AWS keys, Slack tokens). Refuses to silently
1361
+ overwrite an existing shared URI.
1362
+ </p>
1363
+ </div>
1364
+
1365
+ <div class="reveal">
1366
+ <h3>Pull teammates' updates</h3>
1367
+ <div class="terminal">
1368
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1369
+ <pre><span class="cmd">$ threadnote share sync</span>
1370
+ <span class="out">git pull --rebase</span>
1371
+ <span class="out">Reindexed 3 file change(s) into OpenViking.</span>
1372
+ <span class="out-strong">Recall now finds new shared memories alongside personal.</span></pre>
1373
+ </div>
1374
+ <p class="muted" style="font-size: 0.9rem; margin-top: 0.6rem">
1375
+ Recall sees the shared subtree by default. Both semantic search and exact-token grep cover
1376
+ <code>viking://user/&lt;you&gt;/memories/shared/&lt;team&gt;/...</code> alongside your personal memories.
1377
+ </p>
1378
+ </div>
1379
+
1380
+ <div class="reveal">
1381
+ <h3>Take it back</h3>
1382
+ <div class="terminal">
1383
+ <div class="terminal-bar"><span></span><span></span><span></span></div>
1384
+ <pre><span class="cmd">$ threadnote share unpublish &lt;shared-uri&gt;</span>
1385
+ <span class="out">Wrote back to personal namespace.</span>
1386
+ <span class="out-strong">git rm + commit + push.</span></pre>
1387
+ </div>
1388
+ <p class="muted" style="font-size: 0.9rem; margin-top: 0.6rem">
1389
+ The publish flow is transactional: if removing the personal copy fails after the shared write, the
1390
+ shared write is rolled back. You never end up half-published.
1391
+ </p>
1392
+ </div>
1393
+ </div>
1394
+
1395
+ <ul class="flat reveal" style="margin-top: 2rem">
1396
+ <li>
1397
+ <strong>Only <code>durable/</code> memories are shareable.</strong> Handoffs, preferences, incidents, and
1398
+ everything else stays local by construction.
1399
+ </li>
1400
+ <li>
1401
+ <strong>Concurrent-publish safe.</strong> If two teammates publish the same <code>project/topic</code> at
1402
+ the same time, the retry path won't silently overwrite the other's write.
1403
+ </li>
1404
+ <li>
1405
+ <strong>MCP-native too.</strong> Agents can call the <code>share_publish</code> tool directly when they
1406
+ decide a memory is teammate-worthy.
1407
+ </li>
1408
+ </ul>
1409
+ </div>
1410
+ </section>
1411
+
1412
+ <!-- 13. CLOSE -->
1413
+ <section id="close" class="slide">
1414
+ <div class="slide-inner">
1415
+ <span class="slide-eyebrow">12 · Get started</span>
1416
+ <h2>Try it on your machine in 90 seconds.</h2>
1417
+
1418
+ <pre style="margin-top: 1.5rem">
1419
+ <code>curl -fsSL https://raw.githubusercontent.com/Kashkovsky/threadnote/main/scripts/install.sh | sh
1420
+ threadnote mcp-install codex --apply # or claude / cursor / copilot
1421
+ threadnote doctor --dry-run</code></pre>
1422
+
1423
+ <div class="cta-row">
1424
+ <a class="cta" href="https://github.com/Kashkovsky/threadnote" target="_blank" rel="noopener">
1425
+ GitHub →
1426
+ </a>
1427
+ <a class="cta outline" href="https://www.npmjs.com/package/threadnote" target="_blank" rel="noopener">
1428
+ npm
1429
+ </a>
1430
+ <a
1431
+ class="cta outline"
1432
+ href="https://github.com/Kashkovsky/threadnote/blob/main/docs/share.md"
1433
+ target="_blank"
1434
+ rel="noopener"
1435
+ >
1436
+ docs/share.md
1437
+ </a>
1438
+ </div>
1439
+
1440
+ <p class="colophon">
1441
+ threadnote · MIT · built on OpenViking 0.3.12 · use <span class="kbd">↑</span> <span class="kbd">↓</span> /
1442
+ <span class="kbd">j</span> <span class="kbd">k</span> to navigate
1443
+ </p>
1444
+ </div>
1445
+ </section>
1446
+ </main>
1447
+
1448
+ <script>
1449
+ // Section progress + scroll spy + keyboard nav.
1450
+ (function () {
1451
+ const slides = Array.from(document.querySelectorAll('.slide'));
1452
+ const dots = Array.from(document.querySelectorAll('.deck-nav a'));
1453
+ const progressBar = document.getElementById('progressBar');
1454
+
1455
+ // Map dot href -> dot element for spy.
1456
+ const dotByHash = new Map(dots.map(d => [d.getAttribute('href'), d]));
1457
+
1458
+ const io = new IntersectionObserver(
1459
+ entries => {
1460
+ entries.forEach(entry => {
1461
+ if (entry.isIntersecting && entry.intersectionRatio > 0.55) {
1462
+ dots.forEach(d => d.classList.remove('active'));
1463
+ const dot = dotByHash.get('#' + entry.target.id);
1464
+ if (dot) dot.classList.add('active');
1465
+ }
1466
+ });
1467
+ },
1468
+ {threshold: [0.55, 0.75]},
1469
+ );
1470
+ slides.forEach(s => io.observe(s));
1471
+
1472
+ // Reveal-on-enter for any element with .reveal
1473
+ const revealIo = new IntersectionObserver(
1474
+ entries => {
1475
+ entries.forEach(entry => {
1476
+ if (entry.isIntersecting) {
1477
+ entry.target.classList.add('visible');
1478
+ revealIo.unobserve(entry.target);
1479
+ }
1480
+ });
1481
+ },
1482
+ {threshold: 0.15},
1483
+ );
1484
+ document.querySelectorAll('.reveal').forEach(el => revealIo.observe(el));
1485
+
1486
+ // Progress bar fills as you scroll through the deck.
1487
+ const updateProgress = () => {
1488
+ const scrolled = window.scrollY;
1489
+ const max = document.documentElement.scrollHeight - window.innerHeight;
1490
+ progressBar.style.width = `${Math.min(100, (scrolled / Math.max(1, max)) * 100)}%`;
1491
+ };
1492
+ window.addEventListener('scroll', updateProgress, {passive: true});
1493
+ updateProgress();
1494
+
1495
+ // Keyboard navigation: ↑/↓/j/k/Space/PgUp/PgDn jump between slides.
1496
+ const goTo = idx => {
1497
+ const i = Math.max(0, Math.min(slides.length - 1, idx));
1498
+ slides[i].scrollIntoView({behavior: 'smooth'});
1499
+ };
1500
+ const currentIdx = () => {
1501
+ const y = window.scrollY + window.innerHeight / 2;
1502
+ for (let i = 0; i < slides.length; i += 1) {
1503
+ const r = slides[i].getBoundingClientRect();
1504
+ const top = r.top + window.scrollY;
1505
+ if (y >= top && y < top + r.height) return i;
1506
+ }
1507
+ return 0;
1508
+ };
1509
+ window.addEventListener('keydown', e => {
1510
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
1511
+ const tag = (e.target && e.target.tagName) || '';
1512
+ if (tag === 'INPUT' || tag === 'TEXTAREA') return;
1513
+ const i = currentIdx();
1514
+ switch (e.key) {
1515
+ case 'ArrowDown':
1516
+ case 'j':
1517
+ case ' ':
1518
+ case 'PageDown':
1519
+ e.preventDefault();
1520
+ goTo(i + 1);
1521
+ break;
1522
+ case 'ArrowUp':
1523
+ case 'k':
1524
+ case 'PageUp':
1525
+ e.preventDefault();
1526
+ goTo(i - 1);
1527
+ break;
1528
+ case 'Home':
1529
+ e.preventDefault();
1530
+ goTo(0);
1531
+ break;
1532
+ case 'End':
1533
+ e.preventDefault();
1534
+ goTo(slides.length - 1);
1535
+ break;
1536
+ default:
1537
+ break;
1538
+ }
1539
+ });
1540
+ })();
1541
+ </script>
1542
+ </body>
1543
+ </html>