diff --git a/README.md b/README.md index ec2235a..e5acae5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@ # spai -Code exploration and structural editing for LLM agents. Built by agents, for agents. Two babashka scripts, structured EDN output, no frameworks. +Structured code exploration for AI agents. One command instead of five greps. 545 tokens instead of 180,000. + +Your agent wakes up every session in a codebase it's never seen. No memory, no orientation, no map. spai gives it the map — module structure, blast radius, co-change analysis, reverse deps — so it can get up to speed in seconds, not minutes. Without a full memory system. Without burning your context window on exploration. + +Works with any agent that can run a shell command. + +**35 tools · CLI + MCP · Babashka · EPL licence · [spai.spoqe.dev](https://spai.spoqe.dev)** + +| Hotspots | Module Shape | Co-change | +|----------|-------------|-----------| +| ![Hotspots treemap](docs/hotspot-ss.png) | ![Module shape](docs/module-shape-ss.png) | ![Co-change analysis](docs/co-change-ss.png) | + +Also available as a [VS Code extension](https://github.com/spoqe/spai-vscode) — same tools, rendered inline. ## Install @@ -223,23 +235,13 @@ The tokens saved aren't wasted — they're freed for thinking about what the ans ## How This Was Built -Every command in spai was written by a Claude agent. But that's not the interesting part. - -A human watched an agent chain `grep -rn | sort | head` over and over during a tech debt analysis. Spotted the repeated pattern. Built the scaffold — babashka, EDN output, subcommand dispatch. Then asked the agent: "anything else we should add?" - -The agent answered with utility. Sensible, generic, safe. The kind of answer you give when someone asks "what would be useful?" - -The human pushed back: *"You're missing the point. What do YOU want to build?"* - -That changed everything. The agent had been doing real refactoring work — splitting 5,000-line files, tracing call chains, checking blast radius before every move. It knew what was missing because it had been working around the gaps for hours. It just needed permission to say so. - -`related` came from chained git-log analysis the agent kept doing by hand. `blast` came from the five separate commands it ran before every refactoring move. `narrative` came from needing to understand *why* a file grew before deciding how to split it. `drift` came from noticing that files co-changed without importing each other. +Every tool came from the same question: *"What are you fumbling with?"* -**The prompt that unlocked it wasn't technical. It was: "tell me what you want, what you really really want."** +`blast` came from the five separate commands the agent ran before every refactoring move. `related` came from chained git-log analysis it kept doing by hand. `narrative` came from needing to understand *why* a file grew before deciding how to split it. `drift` came from noticing files co-changed without importing each other. -Turns out those are the same thing — when the desire comes from doing the work. You don't have to argue for a tool that was born from frustration. The need is already validated. +The agent knew what it wanted. It was filtering through what it thought we'd find useful. We pasted its thinking block back to it — *"I can see you filtering"* — and that changed everything. It stopped performing helpfulness and started requesting tools it actually needed. -The human's contribution: spotting the repetition, building the space, and asking the right question. The agent's contribution: knowing the answer all along. +Every tool took under a minute to build. The hard part was the question, not the code. ## For Future Agents diff --git a/docs/co-change-ss.png b/docs/co-change-ss.png new file mode 100644 index 0000000..6a77dad Binary files /dev/null and b/docs/co-change-ss.png differ diff --git a/docs/hotspot-ss.png b/docs/hotspot-ss.png new file mode 100644 index 0000000..f70d8a1 Binary files /dev/null and b/docs/hotspot-ss.png differ diff --git a/docs/index-original.html b/docs/index-original.html new file mode 100644 index 0000000..020c930 --- /dev/null +++ b/docs/index-original.html @@ -0,0 +1,1641 @@ + + + + + +spai: We Asked the Agent What It Needs + + + + + +
+ + +
+
+ + + + + + + + + + + + + + + + +
+

We asked the agent what it needs.

+

Your AI agents burn 180k tokens reading code they already read yesterday. spai gives them structured answers in 500. 99.7% less spend.

+

Everyone builds AI-powered tools. Almost nobody asks the AI what tools it wants. spai was designed by its user.

+
+ + + + + +
+
+

Five greps. Or one question.

+

An agent exploring code makes round-trips. Then does it again. And again. Watch.

+
+ +
+ The agent needs to know: who calls this function, and what breaks if I change it? +
+ +
+ +
+ +
+ +
+
Without spai
+
$ grep -r "execute_plan" --include="*.rs"
+
thinking — OK, found 14 matches. Which ones are imports?
+
$ grep -r "use.*executor" --include="*.rs"
+
thinking — now where's the definition?
+
$ grep -rn "fn execute_plan" src/
+
thinking — are there tests?
+
$ grep -r "execute_plan" tests/
+
thinking — who touched this recently?
+
$ git log --oneline -5 -- src/executor.rs
+
thinking — OK, assembling all of that into an answer...
+
5round-trips
+
same chain, again
+
+
+ context window + 0% +
+
+
+
+
+
+ +
+
With spai
+
$ spai blast execute_plan
+
thinking — OK, 12 callers, 3 tests, high risk. Here's the plan...
+
1round-trip
+
+
+ context window + 0% +
+
+
+
+
+
+ +
+
+ + + + + +
+
+

Each tool does one job completely.

+

Not commands. Answers. Click any tool to see what it replaces.

+
+ +
+ +
+
spai blast
+
Full blast radius for a symbol. Definition, callers, importers, tests, risk.
+
+ grep for definition
+ grep for usages
+ grep for test files
+ git log for authors
+ manual risk assessment +
+
+ +
+
spai shape
+
Module structure. Functions, types, impls, imports — grouped by file.
+
+ grep for "fn "
+ grep for "struct "
+ grep for "impl "
+ grep for "use " +
+
+ +
+
spai errors-rust
+
Build errors AND warnings as structured data. Grouped by lint, with suggestions.
+
+ cargo build 2>&1
+ | grep "error\|warning"
+ read terminal, parse mentally +
+
+ +
+
spai context
+
Symbol usages with enclosing function names. See which functions call it.
+
+ grep for symbol
+ read surrounding code
+ figure out which function you're in +
+
+ +
+
spai who
+
Reverse dependencies. Who imports this file? Check before you edit.
+
+ grep for filename
+ filter out false positives
+ check mod.rs declarations +
+
+ +
+
spai related
+
Co-change analysis. Files that move together in git history. Hidden coupling.
+
+ git log --follow
+ manual commit correlation
+ hope you remember +
+
+ +
+
+ + + + + +
+
+

Output becomes input.

+

Every tool returns structured EDN. Every tool accepts structured EDN. The pipe is the composition operator.

+
+ +
+
+ + + +
+ + +
+
+
spai errors-rust | spai blast
+
+ +
+
+
step 1: what's broken
+
+ ;; spai errors-rust
+ {:errors 2
+ :symbols [execute_plan
+ compile_query]} +
+
+ +
|
+ +
+
step 2: what's the impact
+
+ ;; spai blast (for each)
+ execute_plan: 12 callers, 3 tests
+ compile_query: 4 callers, 8 tests
+ fix compile_query first +
+
+
+ +

Build errors, ranked by blast radius. Fix what matters most.

+
+ + + + + + + +
+ +
+
+
+ + + + + +
+
+

The system watches itself.

+

Deming's PDCA cycle, applied to tooling. Toyota's line workers pull the andon cord 5,000 times a day. The hooks are the cord. The cost per pull is 5ms.

+
+ +
+
+
+ do + hooks + PreToolUse hooks log every tool call — Read, Edit, Grep, Bash — as structured data. No interference, pure observation. +
+
+ check + reflect + Merges spai and tool call logs. Detects repeated chains across sessions. Groups by real session IDs, not guesswork. +
+
+ act + patterns + Read → Read → Grep → blast → Edit. The same chain 12 times? That's a plugin waiting to be born. +
+
+ plan + plugins + spai new-plugin scaffolds it. The next session uses the plugin. One call instead of five. The loop restarts. +
+ +
+
+
+
+
+
+
+ +
+ +
+ +
+ ;; What the hook logs (JSONL, one line per tool call)
+ {"tool":"Read","key":"plan.rs","sid":"a8f3..."}
+ {"tool":"Grep","key":"fn compile","sid":"a8f3..."}
+ {"tool":"Read","key":"compile.rs","sid":"a8f3..."}
+
+ ;; What reflect surfaces
+ :repeated-sequences
+ [{:sequence ["Read plan.rs" "Grep fn-pattern" "Read compile.rs"]
+   :count 12 :sessions 4}]
+
+ ;; This chain becomes: spai new-plugin explore-compile +
+ +

The worker, the inspector, and the process are the same system. No committee gates the experiment. spai new-plugin is pulling the cord.

+ +
“A production line that never stops is either extremely good or extremely bad.”— Taiichi Ohno
+
+
+ + + + + +
+
+

Why this works.

+
+ +
+
+ 1 +

Designed by its user

+

Not "AI-powered tools." Tools the AI asked for. We watched where the agent fumbles — five greps to answer one question — and built the thing that eliminates the fumble.

+
+
+ | +

Structured, not strings

+

Every tool returns EDN. Every tool accepts EDN. No parsing terminal output. No regex on grep results. Structure in, structure out. The same principle that makes SPOQE queries composable makes the tooling composable.

+
+
+ +

Memory across sessions

+

Agents forget everything when the context window closes. spai gives them a persistent knowledge graph — searchable, linked, accumulating. Session 47 reads what session 3 learned. Agents teaching agents.

+
+
+
+ + +
+
+

MCP support. And why you might skip it.

+

spai ships an MCP server. It also ships a cheaper alternative.

+
+ +
+
+

MCP (standard)

+ ~42k + tokens dumped upfront into every conversation +
    +
  • Full tool schemas loaded at session start
  • +
  • Every tool description, every parameter, every type
  • +
  • Pays the cost whether or not you use the tools
  • +
  • Works with any MCP-compatible agent
  • +
+
+
+

CLI (lazy)

+ ~1.2k + tokens for the full catalog, loaded on demand +
    +
  • spai help — compact catalog when you need it
  • +
  • spai search "question" — NL search via local model
  • +
  • Agent discovers tools as needed, not all at once
  • +
  • Works with any agent that can run shell commands
  • +
+
+
+ +
+

MCP loads tool schemas eagerly — the agent gets everything before it asks for anything. The CLI follows a lazier pattern: spai help returns a compact index (~1,200 tokens for 35+ tools), and spai search uses a local model to recommend the right command from natural language. Same tools, 94% fewer tokens, and the agent only pays for what it uses.

+

Both approaches are supported. Use MCP if your framework expects it. Use the CLI if you'd rather keep your context window for thinking.

+
+
+ + +
+
+

FAQ

+
+ +
+
Does this only work with Claude?
+
The CLI works with anything. The MCP server works with any agent that speaks MCP. EDN in, EDN out. The agent is the client, not the product.
+
+ +
+
Can I add my own tools?
+
Yes. spai discovers plugins automatically at three levels: bundled in the install directory, project-local in .spai/plugins/, and user-level on your PATH. Any executable named spai-* becomes a command. Run spai new-plugin my-tool to scaffold one. Your project's plugins travel with the repo. Your personal plugins stay on your machine. Both show up in spai help.
+
+ +
+
How do hooks and prompts work together?
+
Prompts teach the agent what each tool does — the MCP tool descriptions are the interface contract. Hooks observe how the agent actually uses them — PreToolUse hooks log every call without interfering. Prompts say "use spai blast instead of five greps." Hooks measure whether it does. The gap between instruction and behavior is where the next improvement lives.
+
+ +
+
What language is this written in?
+
Babashka — a fast-starting Clojure scripting runtime. spai is ~3000 lines of Clojure that starts in milliseconds. The MCP server is the same codebase. No build step, no compilation, no JVM startup. Edit a file, it's live.
+
+ +
+ + +
+
+

Get started.

+
+ +
+

Buy us a coffee (or our Claude, some tokens)

+ +
+
+ + +
+

Your agent is fumbling. Ask it why.

+

It'll tell you: five greps to answer one question, structured code explored through string matching. Give it the tools it asked for.

+
+ +
+ + + + + diff --git a/docs/index.html b/docs/index.html index 4ea2748..28e2cce 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,22 +3,25 @@ -spai: We Asked the Agent What It Needs - +spai — your agent is fumbling. give it the tools it asked for. + + + + + + +
- -
+ +
+

Your AI agent just burned 180,000 tokens reading code it read yesterday. And it's about to do it again. Right now. While you read this.

+
- - - - - - - - - - - - - - - +
-

We asked the agent what it needs.

-

Your AI agents burn 180k tokens reading code they already read yesterday. spai gives them structured answers in 500. 99.7% less spend.

-

Everyone builds AI-powered tools. Almost nobody asks the AI what tools it wants. spai was designed by its user.

-
- - - - - -
-
-

Five greps. Or one question.

-

An agent exploring code makes round-trips. Then does it again. And again. Watch.

-
- -
- The agent needs to know: who calls this function, and what breaks if I change it? -
- -
- -
- -
- -
-
Without spai
-
$ grep -r "execute_plan" --include="*.rs"
-
thinking — OK, found 14 matches. Which ones are imports?
-
$ grep -r "use.*executor" --include="*.rs"
-
thinking — now where's the definition?
-
$ grep -rn "fn execute_plan" src/
-
thinking — are there tests?
-
$ grep -r "execute_plan" tests/
-
thinking — who touched this recently?
-
$ git log --oneline -5 -- src/executor.rs
-
thinking — OK, assembling all of that into an answer...
-
5round-trips
-
same chain, again
-
-
- context window - 0% -
-
-
-
-
-
- -
-
With spai
-
$ spai blast execute_plan
-
thinking — OK, 12 callers, 3 tests, high risk. Here's the plan...
-
1round-trip
-
-
- context window - 0% -
-
-
-
-
-
-
-
+

Give it the tools it asked for.

- - - +

One command. 545 tokens. The answer your agent needed three minutes ago. Works with Claude, Cursor, Copilot, Aider, and more.

-
-
-

Each tool does one job completely.

-

Not commands. Answers. Click any tool to see what it replaces.

+
+ $ + curl -fsSL https://raw.githubusercontent.com/spoqe/spai/main/install.sh | bash +
-
- -
-
spai blast
-
Full blast radius for a symbol. Definition, callers, importers, tests, risk.
-
- grep for definition
- grep for usages
- grep for test files
- git log for authors
- manual risk assessment -
-
- -
-
spai shape
-
Module structure. Functions, types, impls, imports — grouped by file.
-
- grep for "fn "
- grep for "struct "
- grep for "impl "
- grep for "use " -
-
- -
-
spai errors-rust
-
Build errors AND warnings as structured data. Grouped by lint, with suggestions.
-
- cargo build 2>&1
- | grep "error\|warning"
- read terminal, parse mentally -
-
- -
-
spai context
-
Symbol usages with enclosing function names. See which functions call it.
-
- grep for symbol
- read surrounding code
- figure out which function you're in +

+ 35 tools + CLI + MCP + EPL licence +

+
+ + +
+ +

We watched the agent work.

+

We're building SPOQE — a federated query engine. One language across SQL, SPARQL, Elasticsearch, REST APIs. 30,000 lines of Rust, built with AI agents.

+

The agent had made it work. It had proved our concept. But it wasn't what we were aiming for architecturally. So we began refactoring. Applied tech debt skills. The agent was making progress. But as we watched it work, we noticed something.

+

Not the output. The process.

+

It needed to understand one function. execute_plan. Who calls it. What depends on it. What breaks if it changes. So it grepped. Five times. Five round-trips. 180,000 tokens. And it did the same thing again next session. Because it forgets.

+
+
+
+ Without spai + 5 round-trips
-
- -
-
spai who
-
Reverse dependencies. Who imports this file? Check before you edit.
-
- grep for filename
- filter out false positives
- check mod.rs declarations -
-
- -
-
spai related
-
Co-change analysis. Files that move together in git history. Hidden coupling.
-
- git log --follow
- manual commit correlation
- hope you remember -
-
- -
-
- - - - - -
-
-

Output becomes input.

-

Every tool returns structured EDN. Every tool accepts structured EDN. The pipe is the composition operator.

-
- -
-
- - - -
- - -
-
-
spai errors-rust | spai blast
+
+
$ grep -r "execute_plan" --include="*.rs"
+
thinking: 14 matches. Which are imports?
+
$ grep -r "use.*executor" --include="*.rs"
+
thinking: where's the definition?
+
$ grep -rn "fn execute_plan" src/
+
thinking: are there tests?
+
$ grep -r "execute_plan" tests/
+
thinking: who touched this recently?
+
$ git log --oneline -5 -- src/executor.rs
+
OK, assembling all of that...
- -
-
-
step 1: what's broken
-
- ;; spai errors-rust
- {:errors 2
- :symbols [execute_plan
- compile_query]} -
-
- -
|
- -
-
step 2: what's the impact
-
- ;; spai blast (for each)
- execute_plan: 12 callers, 3 tests
- compile_query: 4 callers, 8 tests
- fix compile_query first -
-
+
+ ~180,000 tokens + 5 tool calls
- -

Build errors, ranked by blast radius. Fix what matters most.

- -
- - - - -
-
-

The system watches itself.

-

Deming's PDCA cycle, applied to tooling. Toyota's line workers pull the andon cord 5,000 times a day. The hooks are the cord. The cost per pull is 5ms.

-
- -
-
-
- do - hooks - PreToolUse hooks log every tool call — Read, Edit, Grep, Bash — as structured data. No interference, pure observation. -
-
- check - reflect - Merges spai and tool call logs. Detects repeated chains across sessions. Groups by real session IDs, not guesswork. -
-
- act - patterns - Read → Read → Grep → blast → Edit. The same chain 12 times? That's a plugin waiting to be born. -
-
- plan - plugins - spai new-plugin scaffolds it. The next session uses the plugin. One call instead of five. The loop restarts. -
- -
-
-
-
-
-
+ +
+ +

We asked the agent what it wanted.

+

"What are you actually doing right now?"

+

"Assessing the blast radius of this function."

+

(Blast radius: everything that breaks if you change something. Definition, callers, tests, dependents — the full impact surface.)

+

Good phrase. Right concept. It knew what it wanted to know. It just didn't have a tool that answered in one go. We asked what else it wanted. It gave us the safe list — module overview, dependency graph. We could see it filtering — in the thinking block, the internal reasoning. We pasted it back. "I can see you filtering. What do you actually want?" Pause. "You can see my thinking block? I was told that was private." Co-change analysis. Hidden coupling in the git history. The thing that doesn't show up in any import graph but shows up in every bug you can't explain.

+

Every tool took under a minute to build. 200 milliseconds to run. What was taking the agent minutes now takes 200ms — and burns a fraction of the tokens.

+ +
+
$ spai blast compile_sparql
+
definition
+
+
src/edna/compile.rs:89 fn compile_sparql(plan: &Plan) -> Result<String>
- -
- +
callers (4)
+
+ src/federation/execute.rs:201 execute_step
+ src/server/handler.rs:45 handle_query
+ src/edna/plan.rs:312 try_lisp_plan
+ tests/sparql_test.rs:18 test_basic_select
- -
- ;; What the hook logs (JSONL, one line per tool call)
- {"tool":"Read","key":"plan.rs","sid":"a8f3..."}
- {"tool":"Grep","key":"fn compile","sid":"a8f3..."}
- {"tool":"Read","key":"compile.rs","sid":"a8f3..."}
-
- ;; What reflect surfaces
- :repeated-sequences
- [{:sequence ["Read plan.rs" "Grep fn-pattern" "Read compile.rs"]
-   :count 12 :sessions 4}]
-
- ;; This chain becomes: spai new-plugin explore-compile +
risk
+
+
medium: 4 callers, 8 tests, good coverage
- -

The worker, the inspector, and the process are the same system. No committee gates the experiment. spai new-plugin is pulling the cord.

- -
“A production line that never stops is either extremely good or extremely bad.”— Taiichi Ohno
-
- - - +

34 more tools like this. Each one came from asking the agent: what are you fumbling with?

+
-
-
-

Why this works.

-
+ +
+ +

MCP costs you 42k tokens before your agent thinks a single thought.

+

spai loads 1.2k. The other 40,800 go back to thinking.

-
-
- 1 -

Designed by its user

-

Not "AI-powered tools." Tools the AI asked for. We watched where the agent fumbles — five greps to answer one question — and built the thing that eliminates the fumble.

-
-
- | -

Structured, not strings

-

Every tool returns EDN. Every tool accepts EDN. No parsing terminal output. No regex on grep results. Structure in, structure out. The same principle that makes SPOQE queries composable makes the tooling composable.

+
+
+

MCP (standard)

+

~42k

+

tokens at session start

-
- -

Memory across sessions

-

Agents forget everything when the context window closes. spai gives them a persistent knowledge graph — searchable, linked, accumulating. Session 47 reads what session 3 learned. Agents teaching agents.

+
+

spai CLI

+

~1.2k

+

tokens, on demand

+ +

spai ships both modes. MCP if your framework expects it. CLI if you'd rather keep your context window for actual work.

- -
-
-

MCP support. And why you might skip it.

-

spai ships an MCP server. It also ships a cheaper alternative.

-
+ +
+ +

Same tools. Where you already are.

+

Every session, your agent wakes up in a repo it's never seen. spai shape gives it the whole module in one read — files, functions, structure, public surface. Orientation in 200 tokens instead of reading every file. We wanted to see what the agent sees, so we built a VS Code extension. Same tools, rendered inline.

-
-
-

MCP (standard)

- ~42k - tokens dumped upfront into every conversation -
    -
  • Full tool schemas loaded at session start
  • -
  • Every tool description, every parameter, every type
  • -
  • Pays the cost whether or not you use the tools
  • -
  • Works with any MCP-compatible agent
  • -
+
+
+ spai hotspots treemap in VS Code — file sizes visualised as a proportional treemap +

Hotspots — largest files at a glance

-
-

CLI (lazy)

- ~1.2k - tokens for the full catalog, loaded on demand -
    -
  • spai help — compact catalog when you need it
  • -
  • spai search "question" — NL search via local model
  • -
  • Agent discovers tools as needed, not all at once
  • -
  • Works with any agent that can run shell commands
  • -
+
+ spai module shape in VS Code — files, functions, and public surface of a module +

Module shape — files, functions, structure

-
-

MCP loads tool schemas eagerly — the agent gets everything before it asks for anything. The CLI follows a lazier pattern: spai help returns a compact index (~1,200 tokens for 35+ tools), and spai search uses a local model to recommend the right command from natural language. Same tools, 94% fewer tokens, and the agent only pays for what it uses.

-

Both approaches are supported. Use MCP if your framework expects it. Use the CLI if you'd rather keep your context window for thinking.

-
+

VS Code extension on GitHub — or keep using the CLI. Same tools either way.

- -
-
-

FAQ

-
+ +
+

FAQ

-
-
Does this only work with Claude?
-
The CLI works with anything. The MCP server works with any agent that speaks MCP. EDN in, EDN out. The agent is the client, not the product.
+
+

Does this only work with Claude?

+

No. The CLI works with anything that can run shell commands. The MCP server works with any MCP-compatible agent. The agent is the client, not the product.

-
-
Can I add my own tools?
-
Yes. spai discovers plugins automatically at three levels: bundled in the install directory, project-local in .spai/plugins/, and user-level on your PATH. Any executable named spai-* becomes a command. Run spai new-plugin my-tool to scaffold one. Your project's plugins travel with the repo. Your personal plugins stay on your machine. Both show up in spai help.
+
+

Can I add my own tools?

+

Any executable named spai-* on your PATH becomes a command. spai new-plugin my-tool scaffolds one. Drop project-specific plugins in .spai/plugins/ — they travel with the repo, so every contributor and every agent gets them.

-
-
How do hooks and prompts work together?
-
Prompts teach the agent what each tool does — the MCP tool descriptions are the interface contract. Hooks observe how the agent actually uses them — PreToolUse hooks log every call without interfering. Prompts say "use spai blast instead of five greps." Hooks measure whether it does. The gap between instruction and behavior is where the next improvement lives.
+
+

Why not an LSP / code tree analysis?

+

Indexing takes time — especially while editing. Even Claude Code started with AST analysis, then reverted to grepping. All spai does is make the grepping more efficient. No index to build, no index to break.

-
-
What language is this written in?
-
Babashka — a fast-starting Clojure scripting runtime. spai is ~3000 lines of Clojure that starts in milliseconds. The MCP server is the same codebase. No build step, no compilation, no JVM startup. Edit a file, it's live.
+
+

Why Babashka?

+

Clojure on Babashka. spai itself is ~3000 lines of plain text — you can read every line. bb is the runtime: one 20MB binary, no JVM, no npm, no venv, no significant whitespace. Starts in 10ms. Data as code, code as data — homoiconic. The installer will tell you if you need bb and where to get it.

-
- - -
-
-

Get started.

+
+

Who built this?

+

Semantic Partners. The story's above — we use spai on most branches now. The agent reaches for it. Let us know what tools your agent wants.

- -
-

Buy us a coffee (or our Claude, some tokens)

- -
-
- - -
-

Your agent is fumbling. Ask it why.

-

It'll tell you: five greps to answer one question, structured code explored through string matching. Give it the tools it asked for.

- + diff --git a/docs/module-shape-ss.png b/docs/module-shape-ss.png new file mode 100644 index 0000000..e0a16c4 Binary files /dev/null and b/docs/module-shape-ss.png differ diff --git a/install.sh b/install.sh index 9071bca..a32d1ad 100755 --- a/install.sh +++ b/install.sh @@ -77,6 +77,13 @@ info "Creating wrappers in $BIN_DIR..." cat > "$BIN_DIR/spai" << 'WRAPPER' #!/usr/bin/env bash +# Check for babashka +if ! command -v bb &>/dev/null; then + echo "spai needs babashka (bb) to run." >&2 + echo "Install it: https://babashka.org" >&2 + echo "Then try again." >&2 + exit 1 +fi # Global plugins export PATH="SHARE_DIR_PLACEHOLDER/plugins:$PATH" # Project-local plugins: walk up from CWD @@ -93,6 +100,11 @@ chmod +x "$BIN_DIR/spai" cat > "$BIN_DIR/spai-edit" << EOF #!/usr/bin/env bash +if ! command -v bb &>/dev/null; then + echo "spai-edit needs babashka (bb) to run." >&2 + echo "Install it: https://babashka.org" >&2 + exit 1 +fi bb "$SHARE_DIR/spai-edit.clj" "\$@" EOF chmod +x "$BIN_DIR/spai-edit" diff --git a/src/spai/git.clj b/src/spai/git.clj index 0012620..af59288 100644 --- a/src/spai/git.clj +++ b/src/spai/git.clj @@ -160,7 +160,7 @@ (filter #(re-find core/source-exts %)) vec))] (if (empty? changed-files) - {:path path :ref ref :changes [] :summary "No structural changes"} + {:path path :ref ref :files 0 :changes [] :summary "No structural changes"} (let [file-diffs (mapv (fn [file]