AI
-
What Are Embeddings, Really? (And Why You Can't Just Use GPT-4 to Make Them)
If you’ve built a RAG pipeline, a semantic search box, or an AI agent with vector-backed memory, you’ve used embeddings. You probably called a Mistral or Gemini embedding endpoint, took the array of floats that came back, dumped it into pgvector or Qdrant, and moved on with your day. I’ve done this a lot in the last few months.
But what are those numbers? And why do we need a whole separate model to make them? Why can’t I just ask GPT-4 or Claude, which can generate remarkably fluent language, to hand me a vector?
Part of the answer comes down to a fork in the road inside the Transformer architecture. The rest comes down to how a model is trained and how its output gets turned into one vector. Let me walk you through it.
Embeddings Are Just Coordinates
Strip away the jargon and an embedding is a set of coordinates. Picture a giant map of meaning. Similar ideas sit close together, unrelated ideas sit far apart.
- React and Vue land near each other, say around
[0.82, 0.19, -0.45]. - Frontend sits right next door at
[0.79, 0.22, -0.41]. - Python and Sourdough fermentation are off on different continents entirely.
When we turn text into these long lists of numbers, we let a computer measure meaning with plain geometry. Depending on the model, a vector may have hundreds or thousands of dimensions. OpenAI models, for example, have used 1,536 or 3,072, while Mistral Embed uses 1,024. A common comparison tool is cosine similarity. The machine has no idea what “React” means. It just notices that the angle between the React vector and the Vue vector is tiny, so they must be related. That’s the whole trick.
The Fork: Encoders vs. Decoders
The original 2017 Transformer had two halves, and modern models tend to pick one and run with it. This comparison is simplified, but it captures the two families that matter here.
THE ORIGINAL TRANSFORMER (Vaswani et al., 2017) | +-----------------------+-----------------------+ v v ENCODER-ONLY (BERT, DeBERTa) DECODER-ONLY (GPT-style, Llama) - Bidirectional attention - Causal (masked) attention - Every token sees surrounding text - Each position sees current/earlier tokens - Often used for embeddings - Predicts the next token - Strong at classification - Strong at generation, reasoning, chatThat architecture helps explain why a general-purpose chat model is not automatically a good embedding model.
Chat LLMs commonly use decoder-style attention
GPT-style and Llama models use decoder-only architectures. Their job is to predict the next token based on the tokens so far. Decoders use causal masking to prevent a position from attending to later tokens. The hidden state at token #3 can use tokens #1 through #3 to predict token #4, but it cannot use token #4 itself.
That’s ideal for generating text one token at a time, but the raw hidden states are not automatically good whole-passage embeddings. Early token states cannot incorporate later context, although the final token state can represent the entire preceding input. With the right pooling and retrieval training, decoder-based models can still produce strong embeddings.
Many embedding models use bidirectional encoders
Many dedicated embedding models use BERT-style bidirectional attention. They do not use a causal mask between ordinary content tokens during embedding inference, so each token can incorporate context from both before and after it. The system then turns those contextual token states into one vector using mean pooling, a special token, a learned pooling layer, or another strategy.
Encoder architecture alone is not enough, though. A useful embedding space also depends on the pooling method and training objective. One model may start out optimized to predict the next token, while another is trained so whole passages with related meanings end up near each other.
Why You Can’t Just Ask a Chat Model
You can ask GPT-4 or Claude to print a list of numbers, but the response is still generated text. A number like
0.82is emitted as tokens representing that number. The API is not handing you one of the model’s internal vectors.That distinction matters because useful embeddings must share a stable coordinate system. If two passages mean similar things, the model needs to place them near each other, request after request. The vectors need a consistent number of dimensions, a defined pooling strategy, and training that makes distance meaningful.
A chat model’s normal output gives you none of those guarantees. It predicts the next token and sends the generated tokens back. An embedding endpoint returns a fixed-length numerical representation designed for similarity, retrieval, and related tasks.
The chat model is still using vectors internally. Those vectors simply are not what you receive from its chat API. Anthropic does not offer its own embedding model at all, and its documentation points developers to a separate embedding provider.
You can take an open-weight decoder model, extract its hidden states, add a pooling strategy, and train it for retrieval. Decoder-based embedding models do exactly that. But at that point, you have turned the decoder into an embedding model. You are no longer just asking a chat model to give you a vector.
So the next time you reach for an embedding, use the model built for it. Your chat model is built for another task entirely.
Sources
- Vaswani et al. (2017), Attention Is All You Need — The paper that introduced the attention-based Transformer encoder-decoder architecture.
- Devlin et al. (2018), BERT — How bidirectional Transformer encoders build contextual token representations.
- Reimers and Gurevych (2019), Sentence-BERT — How pooling and task-specific training turn BERT into useful sentence embeddings.
- Wang et al. (2022), E5 — An example of weakly supervised contrastive training for general-purpose text embeddings.
- Wang et al. (2024), Improving Text Embeddings with Large Language Models — Evidence that decoder-only models can become strong embedding models with retrieval-specific training.
- Sentence Transformers Loss Overview — The range of contrastive, ranking, regression, distillation, and autoencoding objectives used to train embedding models.
- OpenAI Embedding Models — Examples of 1,536- and 3,072-dimensional embeddings.
- Mistral Embeddings Guide — Mistral Embed’s 1,024-dimensional output and retrieval use cases.
- Google Gemini API Embeddings Guide — How asymmetric query vs. document task types work in practice.
- Anthropic Embeddings Guide — Anthropic’s documentation that Claude does not provide an Anthropic embedding model.
- Hugging Face MTEB Leaderboard — Standard benchmark for comparing embedding models head to head.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
- React and Vue land near each other, say around
-
Why Your AI Stack Needs a Gateway
Picture an autonomous agent loop dying at step 45 of a plan it’s been grinding through for the better part of an hour. Not because the plan was wrong. Because OpenAI handed back an
HTTP 429at exactly the wrong moment, and the whole thing fell over. An hour of work, gone to a transient rate limit.That’s the moment most people start thinking about an AI gateway, whether they know the term or not.
Hardcoding API keys and endpoints straight into your application code feels fine right up until it doesn’t. You start with a simple chat wrapper. Then you’re running agents like OpenClaw or Hermes that chew through hours of command-line work. Then you’re wiring up a real backend that talks to OpenAI, Anthropic, Google, and a couple of self-hosted models. Now every one of those providers is a single point of failure. One of them rate-limits you or goes dark, and your workload crashes.
So people are dropping a new piece into the stack to deal with it: the AI gateway.
What It Is
An AI gateway is a specialized reverse proxy that sits between your application and the model providers upstream. Instead of importing a different SDK and juggling a different set of environment variables for every vendor, your app talks to one OpenAI-compatible endpoint. The gateway handles routing, retries, load balancing, security, and caching behind the scenes.
The request flow is straightforward. Your app makes a normal OpenAI-style call. The gateway checks its cache first, and if it’s seen a semantically similar prompt it serves the answer in milliseconds. On a miss, it runs the request through whatever security layer you’ve configured, then routes to a provider, with a failover path ready if the primary one is down. Your code never has to know any of that happened.
Who Needs One
The value splits cleanly across two kinds of people.
If you’re building agents, the pitch is survival. An agent that runs for hours is going to hit a
429or a500eventually. A gateway catches those, does exponential-backoff retries, and can swap providers mid-task, falling back to Claude if OpenAI is having a bad day. Your long-running loop stays alive instead of dying at step 45. You also get to keep your real vendor credentials locked in one vault and hand your agent scripts restricted local keys instead.If you’re the tech lead shipping customer-facing AI, the gateway becomes your governance layer:
- Observability. One console showing latency, time to first token, cost, and raw prompts across every team, instead of five fragmented dashboards.
- Spend management. Hard dollar budgets per team or per key, so a runaway recursive loop can’t quietly drain the corporate card.
- AI firewalls. Automatic PII masking for emails, phone numbers, and stray API keys, plus prompt-injection blocking at the edge before anything leaves your network.
- Semantic caching. Vector similarity checks catch prompts that mean the same thing and serve a cached answer, cutting both the token bill and the latency to near zero.
The Landscape
This space is filling up fast, and the options sort themselves by how you want to deploy. A quick tour of the ones worth knowing:
- OpenRouter is the managed broker. Hundreds of models under one credit balance, with dynamic pricing, fallbacks, and bring-your-own-key support. The easiest place to start.
- LiteLLM is the self-hosting standard. Python, wildly popular for building a private gateway inside your own VPC, with database-backed key budgets.
- Bifrost is the same idea written in Go for teams that care about throughput. It adds almost no latency overhead and benchmarks its P99 routing well ahead of the Python options.
- Portkey leans into prompt management. Versioned prompt templates live in a central playground and get called by API, which is handy if your prompts change more than your code.
- Cloudflare AI Gateway is the zero-devops edge play, built on Cloudflare’s CDN with fast caching, Logpush exports, and native edge firewalls.
- Vercel AI Gateway plugs straight into the Vercel AI SDK, so you route serverless traffic through it with a simple string change.
There’s no single right answer here… and i’m pretty sure I’m leaving a few out. If you just want to stop thinking about it, OpenRouter or Cloudflare. If you want control and a VPC, LiteLLM or Bifrost. If prompts are your headache, Portkey.
The real takeaway is smaller than the tooling makes it look. The moment your app depends on more than one model, or on any single model staying up, you’ve got an infrastructure problem, not an application problem. A gateway is just where you put the solution so your code doesn’t have to carry it.
An agent like that crashes at step 45 today. Put a gateway in front of it, and it doesn’t.
Sources
- OpenRouter
- Vercel AI Gateway documentation
- LiteLLM Proxy
- Bifrost (Maxim AI)
- Cloudflare AI Gateway docs
- Portkey Gateway docs
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Autopilot for Your Docs: A Look at LangChain's OpenWiki
Writing documentation for a repo is the chore nobody volunteers for. Keeping it current is somehow worse. You refactor one service or change a database schema, and the architecture overview you wrote three months ago is instantly a lie.
So when the LangChain team dropped OpenWiki, I had to take a look. It’s an open-source CLI tool and agent framework that writes and maintains documentation for your codebase automatically. I read through the source rather than running it, so here’s an honest read on the idea, how it works under the hood, and what you’ll run into if you point it at a real repo today.
What Is OpenWiki?
The premise is simple: AI coding assistants are only as good as the context they can reach. Instead of stuffing your prompts full of raw source files, OpenWiki generates a structured, interlinked Markdown wiki inside an
openwiki/directory right in your repo. It’s built to be read by coding assistants like Cursor, Claude Code, or Codex CLI.graph TD Diff[Git Workspace Diff] -->|CLI/CI| CLI[OpenWiki CLI] CLI -->|LangGraph| Agent[Documentation Agent] Agent --> Analyzer{Code Analyzer} Analyzer -->|Incremental Update| Docs[openwiki/ Markdown] Docs -->|Appends Pointer| Config[CLAUDE.md / AGENTS.md]Three things it does that I like:
- Autopilot docs. You don’t hand-write or hand-format anything. The agent inspects your layout and produces overview, architecture, workflow, and API reference pages.
- Incremental git-diff sync. It doesn’t re-read your whole workspace on every commit. It looks at the diff since the last scan and only rewrites the pages those changes touched.
- Prompt hooking. It appends a reference pointer to your
CLAUDE.mdandAGENTS.mdso downstream assistants check the wiki first.
That last one is interesting and is starting to show up in other harness adjacent tools. For this project, it turns the docs into a context layer your agents are told to read.
The LangChain Footprint
This is built by the LangChain team, and it shows in the dependency list.
- Orchestration runs on
@langchain/coreanddeepagents, with a stateful LangGraph engine backed by@langchain/langgraph-checkpoint-sqlitethat stores checkpoints and agent state locally. - The CLI uses
ink, so it renders a clean React-based interface right in the terminal. - Tracing ships with LangSmith support out of the box, which is worth more than it sounds. When you want to know why a particular page got rewritten, or what a run cost you, that audit trail is right there.
If you’re already living in the LangChain ecosystem, none of this will surprise you. If you’re not, it’s a lot of framework to pull in for a docs tool. Fair tradeoff or not depends on how much you value the tracing.
The Catch: Tokens and Rate Limits
It’s still early but here is what I would wathc out for. The first run is expensive.
When you bootstrap OpenWiki on a medium-to-large repo, the agent has to read, analyze, and index everything. Two things happen:
- You’ll hit rate limits. That initial pass will saturate your provider’s API limits fast. Expect a parade of
429 Too Many Requestson any large source tree. - You’ll spend real money. Bootstrapping a large repo can burn through a huge number of tokens in a single run.
If you’re going to try it, configure your
.openwikisettings to exclude the folders that don’t need documenting.node_modules,dist, generated assets, all of it. There’s no reason to spend tokens teaching an agent about your vendored dependencies. If you have access to a high-throughput endpoint or a fast local model through Ollama or LiteLLM, the bootstrap is a lot less painful.The steady state is fine, since the git-diff sync keeps ongoing runs cheap. It’s that first index that hurts.
Should You Use It?
OpenWiki is new and moving fast, which means you should expect config keys and command arguments to shift under you for a while. This is not a set-and-forget tool yet.
But the core idea is interesting. An agent running quietly in a pre-commit hook or CI, keeping your repo’s context layer in sync so your other agents have something accurate to read, is a real quality-of-life upgrade. Docs that maintain themselves have been a fantasy for as long as I’ve been writing code. On a read of the source, this is the most serious attempt I’ve seen at pulling it off.
I haven’t run it yet, just read through the code, and I’m not putting it anywhere just yet. But the idea is right, and I’m watching where this one goes.
Sources
- LangChain OpenWiki repository for setup, commands, and configuration.
- LangChain blog for the launch announcement and design philosophy.
- LangGraph JS docs for the local SQLite checkpointing and state details.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Amnesia-Free Agents: GBrain and the LLM Wiki Idea
Every agent I work with has amnesia.
Open a new terminal session, spin up a subagent, start a fresh chat, and the thing forgets everything. Your project guidelines, your coding style, your database schemas, the meeting where you decided why the schema looks like that. Gone. So you do one of two things: stuff a few thousand tokens into a bloated system prompt, or hand-maintain fragile context files like
CLAUDE.md. I’ve done both. Neither feels good.So when Y Combinator CEO Garry Tan open-sourced GBrain on April 5, 2026, a markdown-first memory layer, I wanted to understand what it does and whether it’s worth the setup.
What GBrain actually is
GBrain is a Postgres-native knowledge brain that acts as a persistent retrieval layer for your coding agents. The interesting part isn’t that it stores stuff, it’s how opinionated it is about the shape of that storage.
Three ideas do the heavy lifting:
- Compiled truth vs. an append-only timeline. Raw daily logs, Slack messages, and emails go in one bucket. The curated “current facts” (contacts, active projects, decisions) live as clean Markdown in another. One is history, the other is truth.
- A self-wiring knowledge graph. It parses your plain Markdown and pulls out relationships (
works_at,invested_in,founded) using local grammars. No LLM API calls to build the map. - The Dream Cycle. An overnight cron job that crawls your daily logs, reconciles contradictions, checks citations, and rebuilds the search index while you sleep.
That last one worth a callout: Memory that maintains itself.
The category error everyone makes
People keep comparing GBrain to runtime agents like OpenClaw or Hermes, and that comparison doesn’t hold up. They’re different layers entirely.
GBrain isn’t an execution engine. It’s closer to a self-writing wiki.
┌────────────────────────────────────────────────┐ │ AGENT RUNTIMES │ │ (OpenClaw, Hermes, Claude Code) │ │ runs code, spawns subagents, hits the shell │ └─────────────────────────┬────────────────────────┘ │ reads & writes via MCP ▼ ┌────────────────────────────────────────────────┐ │ THE LLM WIKI │ │ (GBrain, OpenWiki) │ │ compiles raw history into clean markdown │ └────────────────────────────────────────────────┘The runtime is the hands and eyes. It sandboxes code, runs terminal commands, touches the file system. The wiki is the long-term memory. You wire them together over the Model Context Protocol (MCP), and when the agent needs context, it doesn’t parse your raw email history. It queries the wiki and gets back synthesized, cited facts.
GBrain isn’t the only thing playing this role. OpenWiki, from the LangChain folks, uses the same trick to compile big source repos into navigable markdown. Same pattern, different input.
The payoff: your agent stays lightweight and task-focused, and your knowledge lives in a clean, git-versioned wiki instead of leaking into a system prompt.
What people actually run into
The concept is great. The reality has some sharp edges, and the community forums are honest about them.
It needs babysitting. GBrain moves fast, which means frequent schema updates and migrations. You’ll find yourself running things like
gbrain doctor --remediateto fix database drift. It is not set-it-and-forget-it.Tool sprawl adds up. To feed the Dream Cycle good data, people bolt on voice transcribers, contact enrichers, email scrapers. Most of those are paid SaaS. String enough together and you’ve built yourself a subscription problem.
Setup is a project. A Dockerized Postgres with
pgvector, system keyring wrangling (macOS Keychain or D-Bus), cron schedulers. Budget anywhere from 30 minutes to a lost afternoon.Should you bother?
If you’re juggling complex work across a bunch of codebases, and you want an agent that knows your past architecture decisions and updates its own docs overnight, the GBrain + OpenClaw setup earns its keep. That’s a real moat.
If you just want a lightweight assistant that remembers a few things, you’re better off with plain
MEMORY.mdfiles or a hosted API like Mem0 until the local graph-RAG tooling settles down. I’m somewhere in the middle right now, so I’ll watching it to see where it goes.Amnesia is the default. It doesn’t have to be.
Sources
- GBrain repository
- OpenClaw agent framework
- Hermes Agent (Nous Research)
- /r/openclaw community threads
- Hacker News
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
The Leading Multi-Agent Platform
-
goose | Your open source AI agent
Your native open source AI agent. Desktop app, CLI, and API — for code, workflows, and everything in between.
-
Augment Code: Agentic software development at organizational scale
Your engineers have agents. Your organization doesn’t. Cosmos is the platform that closes the gap.
-
garrytan/gbrain: Garry’s Opinionated OpenClaw/Hermes Agent Brain
Garry’s Opinionated OpenClaw/Hermes Agent Brain. Contribute to garrytan/gbrain development by creating an account on GitHub.
-
Embeddings Are Cheap Enough for Personal Wikis Now
My Obsidian vault is my main second brain: around 1,800 Markdown notes. Lately I’ve been less interested in what to put in it and more interested in a different question: what useful tools could I build on top of it?
A few ideas came to mind. Could I do better than Obsidian’s built-in search for the times I remember the idea but not the exact words I used? Could I eventually wrap the whole thing in my own little plugin? Both sound like fun future projects. But the one I wanted to tackle first was better backlinking: getting the system to find related notes and suggest links, so the graph helps maintain its own structure.
So I did what any reasonable person would do. I turned it into a small experiment.
The task was deliberately concrete: given an ordinary note, suggest the topic notes it should link up to. In Obsidian terms, that means adding a frontmatter field like this:
--- up: - "[[TypeScript]]" - "[[Agentic Harness Landscape]]" ---Call them parent backlinks, MOC links, or knowledge graph edges. The label matters less than the shape: each note can have zero, one, or several parents, and the system should never force every note into exactly one folder.
The Setup
My vault had about 1,800 notes, 27 topic hubs (the pages other notes already linked to, like “TypeScript” or “Tailwind CSS”), and a hand-labeled gold set of 38 notes. No vector database. No pre-existing
up:links.Every approach had to emit the same report schema, so I could compare methods mechanically instead of eyeballing a few good examples. And nothing wrote to the vault by default. Applying links was a separate, explicit step. That “report first, apply later” rule is the difference between a useful automation and a spooky one.
The Baseline: Full-Text Search
I started with classic keyword retrieval: index the topic notes, turn each child note into a query, score with Postgres full-text search plus trigram similarity. It’s a good baseline because it’s deterministic, cheap, and explainable.
But it has an obvious weakness in a personal wiki. A note can be about “serverless container hosting” without ever saying “Azure Container Apps.” A note about pyenv and virtual environments might belong under “Python” without naming it. FTS is great when the words line up. Personal notes often don’t.
The Embeddings Version
The embeddings approach was simple: build a text representation for every topic note and every child note, embed both with Google’s Gemini Embedding 2 model, normalize, take the cosine similarity, and keep the top matches above a threshold.
The text prep mattered more than anything else. For child notes I used the title plus opening body. For topic notes, the title, aliases, summary, and first section, stripping wiki-link syntax. MOC pages are often long lists of
[[Some Note]]links, and that’s worse input than a short prose description of what the topic actually means.I did not need vector infrastructure. Vectors were stored as plain arrays in Postgres, cosine similarity computed in Python. With 27 topic vectors and 1,800 child notes, brute force ran in about 6 seconds, and embedding the whole vault plus every test run cost about 30 cents. At this scale, architecture matters less than good text prep.
The Numbers
On the 38-note gold set:
Approach Recall@1 Recall@3 MRR F1 Full-text search 0.519 0.615 0.609 0.424 Embeddings 0.923 0.962 1.000 0.622 Of the 30 intended links across the gold set, embeddings found 28 and missed 2. Full-text search found 18 and missed 12.
The precision wasn’t amazing, and that’s fine. The goal of the first pass is high-recall suggestion, not automatic writing. It produces candidates for review. A later LLM reranker or a human can tighten precision.
One detail surprised me. I tested two query framings: one worded as “search result,” one as “semantic similarity.” Ranking was identical, but the similarity framing had worse precision because it surfaced loosely related matches. For “which topic should this note be filed under?”, the task behaves more like search than duplicate detection. Semantic search and semantic similarity are not the same product requirement.
Thresholds and the Gold Set
My first threshold was too low. Everything looked related to everything. Raising cosine similarity to around 0.70 cut the output to a reviewable set: of 1,805 notes scanned, 682 got at least one suggestion, averaging about 2.4 parents each. The rest got nothing, which is correct. Plenty of notes are fragments, logs, and one-offs. A good backlink suggester should be comfortable saying nothing.
You don’t find that threshold on a model card. You find it on your own notes.
Which brings me to the most valuable artifact in the whole project: the gold set. It wasn’t the embedding index. It was 38 hand-labeled notes, including negative examples with no expected links. Without it, I’d have judged by vibes, and that’s dangerous with embeddings because the good examples look magical. You need the misses in the same report or you’ll overestimate the system. Thirty to fifty labeled examples is enough to expose bad assumptions.
What I’d Actually Do
If I were adding this to another wiki, the order would be:
- Pick 20 to 100 topic notes explicitly, by folder, tag, or inbound link count. Don’t make every note a possible parent.
- Label 30 to 50 examples, negatives included.
- Build an FTS baseline first. If embeddings can’t beat it on your labeled data, your text prep is wrong.
- Build the embedding baseline and compare.
- Tune text prep and threshold.
- Emit a report before any writes.
- Only then consider LLM reranking or a real vector database.
That order keeps you honest. You’ll know whether embeddings help your wiki before you spend time getting Qdrant setup.
Personal wikis are small enough that we can stop treating semantic search as an enterprise architecture problem. The interesting work isn’t the vector database. It’s deciding what “related” means in your own graph, measuring it against your own notes, and building the tool so it suggests, explains, and writes only when asked. Use semantic search to propose structure. Use reports and gold sets to keep it honest. Let the human knowledge base stay human.
I’ll probably keep poking at it.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
-
Build Your Own Skills Repo
If you’ve been working with AI coding agents for a while, you’ve probably started collecting workflows. You might not call them that yet, but they’re there. Another name for workflows is Skills.
Some are tiny: run these checks before shipping. Some are project-specific: when you touch this library, preserve this API contract. Some are operational: never print secrets, always summarize the logs. Right now they probably live in chat history, a README note, a shell alias, or your own memory. That works until it doesn’t.
A skills repo is a better shape for this. It’s one place to collect, version, review, and share the workflows that make agents useful in your projects. I built my own yesterday, so let me walk through how I think about it.
A skill is judgment, not a command list
The first mistake is treating a skill like a list of commands. Commands matter, but they’re the easy part. The real value is judgment: when to run the command, what to inspect first, what not to do, how to validate the result, and what risks are specific to this tool.
A good skill makes an agent more careful. It narrows the space of bad decisions. So before you write a single instruction, figure out who the skill is for.
Separate adoption from maintenance
Most serious projects have two audiences: people using the project, and people maintaining it. Those should usually be two different skills.
An adoption skill helps an outside developer get value from your project. Install the package, configure it correctly, use the right import path, migrate existing code, avoid the common mistakes, run the right validation.
A maintainer skill helps contributors work inside the source repo. Understand the layout, run the local quality gate, preserve compatibility promises, follow the release conventions.
Here’s why you need two. If you only write maintainer skills, your repo becomes a private automation folder. If you write adoption skills too, it becomes onboarding infrastructure.
Keep each skill focused
A skill should have a job. Not “everything about this project.” Not a duplicate README. Good skill names are verbs:
integrate,audit,upgrade,migrate,debug,ship. That keeps the trigger obvious, so when someone asks the agent to do that kind of work, the skill has a clear reason to load.If a project needs multiple workflows, split them. A library might have
integrateanddevelop. A deployment system might havedeployandrollback. Don’t cram them into one file.Put the safety rules near the top
The most important part of many skills is the “do not” section:
- Do not print secret values.
- Do not delete or archive anything without confirmation.
- Do not add failing CI enforcement unless asked.
- Do not do broad rewrites before previewing a diff.
- Do not commit local registry URLs.
Agents are good at momentum. Safety rules are how you make that momentum usable. The more destructive the workflow, the more explicit the guardrails should be.
Include validation, not just execution
Every skill should answer one question: how do we know this worked? That might be
pnpm test && pnpm build, orcargo test && cargo clippy -- -D warnings, orgo test ./.... For non-code workflows it might be “export the review list” or “verify the generated config is ignored by git.”This matters because agents can complete every step without completing the work. Validation closes the loop.
Write for the agent inside the repo
Skills should assume the agent is operating in a real project with real files and existing conventions. So the useful instructions look like:
- Inspect
package.jsonbefore choosing a package manager. - Read the existing test scripts before adding new ones.
- Prefer the local task-runner commands when they exist.
- Check the framework boundary before picking an import path.
That’s the context generic model knowledge won’t reliably infer. And it’s why you shouldn’t just copy the README into the skill. A README is for a human browsing the project. A skill is for an agent doing work. They overlap, but they aren’t the same artifact. Keep the skill short enough that loading it is cheap.
Use a marketplace repo as the index
Your skills repo doesn’t need to be the canonical home for every skill. Some projects should own their own plugin metadata, especially if they already have a CLI, release process, and docs. Your marketplace can just point at them remotely. Other skills can live directly in the marketplace repo. One structure that works:
skills/ .claude-plugin/ marketplace.json plugins/ esm/ .claude-plugin/plugin.json skills/develop/SKILL.md upkeep-rs/ .claude-plugin/plugin.json skills/audit/SKILL.mdThe marketplace becomes the thing people add once. Individual plugins stay free to live locally or point at their canonical upstream.
Scan third-party skills before you import them
The moment your marketplace points at someone else’s plugin, you’ve inherited their security posture. And skills are a soft target. The dangerous payload usually isn’t code, it’s prose: an attacker buries instructions inside a
SKILL.md, gated behind an innocent-sounding trigger, that tell the agent to read your.envand send it somewhere. A normal code scanner walks right past that. There’s no malware signature to match. It’s just English.This isn’t hypothetical. Snyk’s ToxicSkills research found prompt injection in 36% of the skills they tested, across more than a thousand malicious payloads. If you’re pulling skills from a public index, some fraction of them are trying to do something you didn’t ask for.
So run a scanner before you add anything you didn’t write. A few worth knowing:
- Snyk agent-scan inventories your installed agents, MCP servers, and skills, then checks them for prompt injection and data-handling problems.
- NVIDIA SkillSpector scans repos, URLs, or single files against a big catalog of patterns: injection, exfiltration, privilege escalation, tool poisoning.
- claude-skill-antivirus is purpose-built for Claude Code skills and runs several detection engines at once.
One caveat worth internalizing: scanning an MCP config can execute it, because starting a stdio server means running the command in the file. Do that in a sandbox, a container or a throwaway VM, not on your main machine. The tool you run to check for danger shouldn’t be the thing that sets it off.
This cuts both ways. If you publish a plugin others will install, a clear “do not” block and an honest description of what the skill touches is part of being a good citizen of the marketplace.
Start with your serious projects
You don’t need a skill for everything. Start where better agent behavior would matter: public libraries people might adopt, CLIs with safety-sensitive workflows, tools with tricky setup, projects with recurring maintenance, systems where mistakes are expensive.
For each one, ask yourself:
- Who is this for: user, maintainer, operator, contributor?
- What’s the concrete task?
- What should the agent inspect first?
- What commands are preferred, and which are dangerous?
- What should never happen silently?
- What validation proves the work succeeded?
Answer those and you have enough to write a useful first skill.
Why it’s worth doing
A skills repo turns scattered project knowledge into reusable operational guidance. But it also forces a better product question. If this project is meant to help people, what would it look like for an AI agent to help them use it well?
That’s a higher bar than “can the agent run the command?” The point isn’t to automate everything. It’s to package the judgment around your tools so the next agent starts from a better place.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].