Skip to content

Commit 35a4608

Browse files
committed
Create README.md
1 parent 575eb1f commit 35a4608

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

README.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# swift-claude-code
2+
3+
Building a simplified Claude Code-like CLI agent from scratch in Swift.
4+
5+
> **Current progress:** Stage 02 of 12 — tool dispatch with `read_file`, `write_file`, `edit_file`
6+
7+
## Why This Exists
8+
9+
If you've used Claude Code, you know exactly what I'm talking about. Especially the first time you use it — it's just _different_ than any other coding agent out there. There's definitely some magic behind Claude Code, and I went down a huge rabbit hole trying to figure out what makes it special.
10+
11+
My hypothesis: **Claude Code is so good because of how simple it is.**
12+
13+
Not the UI — the architecture. Down to the fact that it doesn't really have many tools. And the tools it does have are really simple: a search tool, a file editing tool. That's about it. But those tools are _really, really good_.
14+
15+
The other big thing is that Claude Code relies on the model way more than other tools. Most people build a lot of scaffolding around the model, but Claude Code really lets the model do all of the heavy lifting.
16+
17+
So I wanted to understand this deeply — not by reading about it, but by building it. This project rebuilds the core agent loop from scratch in Swift, one layer at a time, to see exactly how few moving parts you actually need.
18+
19+
## Architecture
20+
21+
Two-target Swift Package Manager project:
22+
23+
```
24+
swift-claude-code/
25+
├── Package.swift
26+
├── Sources/
27+
│ ├── Core/ ← library (all logic)
28+
│ │ ├── API/
29+
│ │ ├── Agent.swift agent loop + tool dispatch
30+
│ │ └── ShellExecutor.swift
31+
│ └── cli/ ← executable (@main entry point)
32+
└── Tests/CoreTests/
33+
```
34+
35+
**Core** is the library — API client, shell executor, agent loop, tools, everything testable. **cli** is just the entry point. The executable is called `claude`.
36+
37+
Raw HTTP to `POST https://api.anthropic.com/v1/messages` using [AsyncHTTPClient](https://github.com/swift-server/async-http-client). Works on both macOS and Linux.
38+
39+
## The Agent Loop
40+
41+
The whole thing boils down to one loop:
42+
43+
```swift
44+
func run(query: String) async throws -> String {
45+
messages.append(.user(query))
46+
47+
while true {
48+
let request = APIRequest(
49+
model: model, system: systemPrompt, messages: messages, tools: Self.toolDefinitions
50+
)
51+
let response = try await apiClient.createMessage(request)
52+
messages.append(Message(role: .assistant, content: response.content))
53+
54+
guard response.stopReason == .toolUse else {
55+
return response.content.textContent
56+
}
57+
58+
var results: [ContentBlock] = []
59+
for block in response.content {
60+
if case .toolUse(let id, let name, let input) = block {
61+
let output = await executeTool(name: name, input: input)
62+
results.append(.toolResult(toolUseId: id, content: output, isError: false))
63+
}
64+
}
65+
messages.append(Message(role: .user, content: results))
66+
}
67+
}
68+
```
69+
70+
That's it. The loop is the invariant. Tools are the variable. Every stage adds entries to the tool handler dictionary and injection points before the API call, but the loop body itself never changes.
71+
72+
## Roadmap
73+
74+
Each stage adds one mechanism on top of the previous one. Progress is tracked via git tags.
75+
76+
| Stage | What It Adds | Tag |
77+
| ------ | ---------------------------------------------------------------------- | ------------------ |
78+
| **00** | Bootstrap: SPM project | `00-bootstrap` |
79+
| **01** | Agent loop + bash tool | `01-agent-loop` |
80+
| **02** | Tool dispatch: `read_file`, `write_file`, `edit_file` with path safety | `02-tool-dispatch` |
81+
| 03 | TodoWrite: Codable todo tracking with nag reminder injection ||
82+
| 04 | Subagents: recursive `agentLoop()` with fresh messages ||
83+
| 05 | Skill loading: read `.md` files from disk, inject as tool results ||
84+
| 06 | Context compaction: 3-layer strategy (micro, auto, manual) ||
85+
| 07 | Task system: file-based CRUD with dependency DAG ||
86+
| 08 | Background tasks: `Task {}` + actor-based notification queue ||
87+
| 09 | Agent teams: JSONL mailbox files + actor coordination ||
88+
| 10 | Team protocols: request-response with correlation IDs ||
89+
| 11 | Autonomous agents: idle-poll-claim cycle ||
90+
| 12 | Worktree isolation: `git worktree` via Process ||
91+
92+
## Tech Stack
93+
94+
- **Swift 6.2** with strict concurrency
95+
- **AsyncHTTPClient** (SwiftNIO-based) for cross-platform HTTP + streaming SSE
96+
- **Foundation `Process`** for shell command execution
97+
- macOS 10.15+ / Linux
98+
99+
## Getting Started
100+
101+
```bash
102+
git clone https://github.com/ivan-magda/swift-claude-code.git
103+
cd swift-claude-code
104+
105+
# Set up your API key and model
106+
cp .env.example .env
107+
# Edit .env with your ANTHROPIC_API_KEY and MODEL_ID
108+
109+
swift build
110+
swift run claude
111+
```
112+
113+
## References
114+
115+
- [Anthropic Messages API](https://docs.anthropic.com/en/api/messages) — the single endpoint the entire agent talks to
116+
- [Anthropic Tool Use](https://docs.anthropic.com/en/docs/build-with-claude/tool-use/overview) — how tool definitions, `tool_use`, and `tool_result` work
117+
118+
## License
119+
120+
MIT

0 commit comments

Comments
 (0)