Linus Lee's Custom AI Tools

Table of content
Linus Lee's Custom AI Tools

Linus Lee is a researcher and engineer who has built over 100 side projects, including two programming languages. He worked as a research engineer at Notion on their AI team from 2023-2024, and now leads AI at Thrive Capital. His blog has half a million words on software, interfaces, and tools for thought.

Lee (@thesephist) builds personal tools to shape how he thinks. His approach: own your entire stack, use AI to extend cognition, and build what doesn’t exist.

The Philosophy

Tools shape thought. The tools you use determine what’s easy to think and what’s hard.

Commodity ToolLee’s Custom ToolDifference
Google SearchMonocleSearch your own data, not the web
Notes appNotationML highlights important words
Browser bookmarksReverySemantic search across saved pages
Task managerPolyxBidirectional links to people and notes

Monocle: Personal Search Engine

Monocle indexes Lee’s entire digital life. See Personal Search for how to build your own.

Query: "that conversation about compilers with Alex"

Results:
1. Journal entry, March 2024: "Met with Alex, discussed..."
2. Note: "Compiler optimization ideas from Alex"
3. Tweet reply to @alex: "Good point about parsing..."

Architecture:

monocle/
├── indexer/
│   ├── markdown.ink     # Index markdown files
│   ├── tweets.ink       # Twitter archive
│   ├── bookmarks.ink    # Pocket, browser
│   └── contacts.ink     # People database
├── search/
│   ├── tokenizer.ink    # Text processing
│   ├── stemmer.ink      # English stemming
│   └── ranking.ink      # Relevance scoring
└── web/
    └── static/          # Search interface

Design:

Building Your Own

# Simplified personal search concept
import sqlite3
from sentence_transformers import SentenceTransformer

class PersonalSearch:
    def __init__(self, db_path):
        self.db = sqlite3.connect(db_path)
        self.model = SentenceTransformer('all-MiniLM-L6-v2')

    def index_document(self, title, content, source):
        embedding = self.model.encode(content)
        self.db.execute("""
            INSERT INTO documents (title, content, source, embedding)
            VALUES (?, ?, ?, ?)
        """, (title, content, source, embedding.tobytes()))

    def search(self, query, limit=10):
        query_embedding = self.model.encode(query)
        # Vector similarity search
        results = self.db.execute("""
            SELECT title, content, source,
                   cosine_similarity(embedding, ?) as score
            FROM documents
            ORDER BY score DESC
            LIMIT ?
        """, (query_embedding.tobytes(), limit))
        return results.fetchall()

Notation: ML-Enhanced Notes

Notation highlights important words in your notes:

Input:
"Meeting with Sarah about the Q3 roadmap.
She mentioned concerns about timeline and
suggested we might need to hire another engineer."

Output:
"Meeting with [Sarah] about the [Q3 roadmap].
She mentioned [concerns] about [timeline] and
suggested we might need to [hire] another [engineer]."

The model learns importance from your writing patterns, later searches, and cross-note references.

Benefit: scan notes 3x faster with key information highlighted.

Revery searches bookmarks by meaning, not tags.

Query: "remote work productivity"

Traditional bookmarks: Hope you tagged it correctly

Revery: Returns semantically similar articles
        across all saved content

Indexes: browser bookmarks, Pocket saves, URLs in journal, external links in notes.

Tools for Reading

CoStructure

Visualizes information density in articles:

┌─────────────────────────────────────────┐
│ ████ Introduction (low)                 │
│ ████████████ Key argument (high)        │
│ ██████ Evidence (medium)                │
│ ████████████████ Conclusion (highest)   │
└─────────────────────────────────────────┘

Click to expand any section. Hover to highlight related sentences.

Academic Paper Workflow

  1. Find via Elicit
  2. Dense sections via ExplainPaper
  3. Overview via CoStructure
  4. Take notes in Notation

The Ink Programming Language

Lee built his own programming language for these tools. He later created Oak, a successor with a broader standard library:

` Ink syntax example - simple HTTP server `

std := load('std')
http := load('http')

server := http.new()

server.route('/search', params => (
    query := params.q
    results := search(query)
    http.json(results)
))

server.start(8080)

Why: custom thinking style, full stack control, understanding compilers, and learning.

The point: if a tool doesn’t exist, you can build it. You don’t need a custom language.

AI as Thought Calculator

AI isn’t replacement for thinking. It’s a calculator for thought: handles language manipulation so you focus on ideas.

Use cases:

Personal CRM: Polyx

Polyx is Lee’s personal productivity suite, including a CRM, task manager, and notes app:

Person: Sarah Chen
Last contact: 2026-01-15
Context: Q3 roadmap discussion
- Concerned about hiring timeline
- Interested in AI tooling
- Prefers async communication

Connected:
- Project: Q3 Planning
- Note: "Roadmap meeting notes"
- Email: "Re: Engineering capacity"

Bidirectional links connect people to projects, notes, and emails.

The Custom Tool Mindset

When to Build Custom

Build Custom WhenUse Existing When
Commodity tools don’t fit your workflowGood enough exists
You’ll use it daily for yearsOne-time need
Learning value is highTime is more valuable
Privacy is criticalCollaboration is needed
You enjoy buildingYou don’t enjoy building

4-Week Build

Week 1: Identify friction. What’s awkward? What do you look up repeatedly? What doesn’t exist?

Week 2: Prototype. Build the smallest version. Use any language. Don’t polish.

Week 3: Use daily. Note what’s missing. Add features only when needed.

Week 4: Decide. Worth maintaining? Rebuild properly? Or learning exercise?

Example: Daily Note Tool

#!/usr/bin/env python3
"""
Minimal daily notes tool.
Creates dated markdown file, opens in editor.
"""
import os
import subprocess
from datetime import date

NOTES_DIR = os.path.expanduser("~/notes/daily")
EDITOR = os.environ.get("EDITOR", "vim")

def main():
    today = date.today().isoformat()
    filepath = f"{NOTES_DIR}/{today}.md"

    if not os.path.exists(filepath):
        with open(filepath, "w") as f:
            f.write(f"# {today}\n\n## Morning\n\n## Tasks\n\n## Notes\n")

    subprocess.run([EDITOR, filepath])

if __name__ == "__main__":
    main()

20 lines. One function. Build from there.

Getting Started

Start with: What’s hard to think about? What information is scattered? What cognitive work repeats?

Then build the smallest useful tool. Weekend project. Use for a month. Iterate or abandon.

Resources:


Next: Alex Albert’s Claude Prompting System

Topics: knowledge-management open-source local-first ai-coding