Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: go version

- name: Install dependencies
run: go install github.com/honganh1206/clue
run: go install github.com/honganh1206/tinker
#
# - name: Check formatting
# run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ AGENT.md

dist/
refs/
bin/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ list/conversations:
go run ./main.go conversation -l
build:
$(eval VERSION := $(shell cat VERSION))
go build -ldflags="-s -X 'github.com/honganh1206/clue/cmd.Version=$(VERSION)'" -o bin/clue main.go
go build -ldflags="-s -X 'github.com/honganh1206/tinker/cmd.Version=$(VERSION)'" -o bin/tinker main.go
coverage:
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out
Expand Down
32 changes: 11 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
[![justforfunnoreally.dev badge](https://img.shields.io/badge/justforfunnoreally-dev-9ff)](https://justforfunnoreally.dev)

<div class="title-block" style="text-align: center;" align="center">

# Clue - Simple AI Coding Agent in Go

<p><img title="clue logo" src="assets/images/clue-logo.svg" width="320" height="320"></p>

</div>

If this proves to be helpful to anyone, consider it my thanks to the open-source community :)

(Important) Read through this wonderful article on [how to build an agent by Thorsten Ball](https://ampcode.com/how-to-build-an-agent) and follow along if possible
# Tinker

## Dependencies

Expand All @@ -28,32 +18,32 @@ export GOOGLE_API_KEY="your-api-key-here"
2. Run the installation script for the latest version (Linux only at the moment):

```bash
curl -fsSL https://raw.githubusercontent.com/honganh1206/clue/main/scripts/install.sh | sudo -E bash
curl -fsSL https://raw.githubusercontent.com/honganh1206/tinker/main/scripts/install.sh | sudo -E bash
```

## MCP

To add MCP servers to clue:
To add MCP servers to tinker:

```sh
clue mcp --server-cmd "my-server:npx @modelcontextprotocol/server-everything"
tinker mcp --server-cmd "my-server:npx @modelcontextprotocol/server-everything"
```

## Breaking Changes

> **⚠️ WARNING**: If you have a running clue daemon from a previous version, you must purge it before installing the new version:
> **⚠️ WARNING**: If you have a running tinker daemon from a previous version, you must purge it before installing the new version:

1. Disable the systemd service:

```bash
sudo systemctl disable clue
sudo systemctl stop clue
sudo systemctl disable tinker
sudo systemctl stop tinker
```

2. Identify the clue process:
2. Identify the tinker process:

```bash
ps aux | grep clue
ps aux | grep tinker
```

3. Kill the process entirely (replace `<PID>` with the actual process ID):
Expand All @@ -65,11 +55,11 @@ kill -9 <PID>
4. Remove the service file:

```bash
sudo rm /etc/systemd/system/clue.service
sudo rm /etc/systemd/system/tinker.service
sudo systemctl daemon-reload
```

5. Move the existing `conversation.db` from `~/.local/.clue` to `~/.clue` and rename the database to `clue.db`
5. Move the existing `conversation.db` from `~/.local/.tinker` to `~/.tinker` and rename the database to `tinker.db`

## Development

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.3
0.2.4
14 changes: 7 additions & 7 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import (
"strings"
"sync"

"github.com/honganh1206/clue/inference"
"github.com/honganh1206/clue/mcp"
"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/server/api"
"github.com/honganh1206/clue/server/data"
"github.com/honganh1206/clue/tools"
"github.com/honganh1206/clue/ui"
"github.com/honganh1206/tinker/inference"
"github.com/honganh1206/tinker/mcp"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/server/api"
"github.com/honganh1206/tinker/server/data"
"github.com/honganh1206/tinker/tools"
"github.com/honganh1206/tinker/ui"
)

type PlanUpdateCallback func(*data.Plan)
Expand Down
10 changes: 5 additions & 5 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/honganh1206/clue/mcp"
"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/server/api"
"github.com/honganh1206/clue/server/data"
"github.com/honganh1206/clue/tools"
"github.com/honganh1206/tinker/mcp"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/server/api"
"github.com/honganh1206/tinker/server/data"
"github.com/honganh1206/tinker/tools"
)

// Mock implementations
Expand Down
4 changes: 2 additions & 2 deletions agent/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"fmt"
"os"

"github.com/honganh1206/clue/mcp"
"github.com/honganh1206/clue/tools"
"github.com/honganh1206/tinker/mcp"
"github.com/honganh1206/tinker/tools"
)

func (a *Agent) RegisterMCPServers() {
Expand Down
6 changes: 3 additions & 3 deletions agent/subagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"encoding/json"
"fmt"

"github.com/honganh1206/clue/inference"
"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/tools"
"github.com/honganh1206/tinker/inference"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/tools"
)

// Subagent is a lightweight agent for executing sub-tasks like finder
Expand Down
4 changes: 2 additions & 2 deletions agent/subagent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/tools"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/tools"
)

// Test helpers for subagent
Expand Down
File renamed without changes
6 changes: 3 additions & 3 deletions cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"os"
"strings"

"github.com/honganh1206/clue/agent"
"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/server/data"
"github.com/honganh1206/tinker/agent"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/server/data"
)

const (
Expand Down
32 changes: 16 additions & 16 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
"strings"
"time"

"github.com/honganh1206/clue/inference"
"github.com/honganh1206/clue/mcp"
"github.com/honganh1206/clue/server"
"github.com/honganh1206/clue/server/api"
"github.com/honganh1206/clue/utils"
"github.com/honganh1206/tinker/inference"
"github.com/honganh1206/tinker/mcp"
"github.com/honganh1206/tinker/server"
"github.com/honganh1206/tinker/server/api"
"github.com/honganh1206/tinker/utils"
"github.com/spf13/cobra"
)

Expand All @@ -35,9 +35,9 @@ var (
)

func HelpHandler(cmd *cobra.Command, args []string) error {
fmt.Println("clue - A simple CLI-based AI coding agent")
fmt.Println("tinker - A simple CLI-based AI coding agent")
fmt.Println("\nUsage:")
fmt.Println("\tclue -provider anthropic -model claude-4-sonnet")
fmt.Println("\ttinker -provider anthropic -model claude-4-sonnet")

return nil
}
Expand Down Expand Up @@ -248,15 +248,15 @@ func NewCLI() *cobra.Command {

versionCmd := &cobra.Command{
Use: "version",
Short: "Print the version number of clue",
Short: "Print the version number of tinker",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Clue version %s (commit: %s, built: %s)\n", Version, GitCommit, BuildTime)
fmt.Printf("Tinker version %s (commit: %s, built: %s)\n", Version, GitCommit, BuildTime)
},
}

serveCmd := &cobra.Command{
Use: "serve",
Short: "Start clue server",
Short: "Start tinker server",
Args: cobra.ExactArgs(0),
RunE: RunServer,
}
Expand All @@ -269,18 +269,18 @@ func NewCLI() *cobra.Command {
Server configurations must be in the format id:command.

Examples:
clue mcp --server-cmd "my-server:uvx mcp-server-fetch"
clue mcp "fetch-server:uvx mcp-server-fetch"
clue mcp "python-server:python my_mcp_server.py --port 8080"
clue mcp --verbose "node-server:node mcp-server.js"
clue mcp "server1:uvx mcp-server-fetch" "server2:python other_server.py"`,
tinker mcp --server-cmd "my-server:uvx mcp-server-fetch"
tinker mcp "fetch-server:uvx mcp-server-fetch"
tinker mcp "python-server:python my_mcp_server.py --port 8080"
tinker mcp --verbose "node-server:node mcp-server.js"
tinker mcp "server1:uvx mcp-server-fetch" "server2:python other_server.py"`,
RunE: MCPHandler,
}

mcpCmd.Flags().StringVar(&mcpServerCmd, "server-cmd", "", "Server configuration in format id:command (e.g., 'my-server:uvx mcp-server-fetch')")

rootCmd := &cobra.Command{
Use: "clue",
Use: "tinker",
Short: "An AI agent for code editing and assistance",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if configs, err := mcp.LoadConfigs(); err == nil {
Expand Down
14 changes: 7 additions & 7 deletions cmd/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"fmt"
"log"

"github.com/honganh1206/clue/agent"
"github.com/honganh1206/clue/inference"
"github.com/honganh1206/clue/mcp"
"github.com/honganh1206/clue/server/api"
"github.com/honganh1206/clue/server/data"
"github.com/honganh1206/clue/tools"
"github.com/honganh1206/clue/ui"
"github.com/honganh1206/tinker/agent"
"github.com/honganh1206/tinker/inference"
"github.com/honganh1206/tinker/mcp"
"github.com/honganh1206/tinker/server/api"
"github.com/honganh1206/tinker/server/data"
"github.com/honganh1206/tinker/tools"
"github.com/honganh1206/tinker/ui"
)

// TODO: All these parameters should go into a struct
Expand Down
14 changes: 7 additions & 7 deletions cmd/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
"time"

"github.com/gdamore/tcell/v2"
"github.com/honganh1206/clue/agent"
"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/server/data"
"github.com/honganh1206/clue/ui"
"github.com/honganh1206/clue/utils"
"github.com/honganh1206/tinker/agent"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/server/data"
"github.com/honganh1206/tinker/ui"
"github.com/honganh1206/tinker/utils"
"github.com/rivo/tview"
)

Expand Down Expand Up @@ -229,9 +229,9 @@ func formatMessage(msg *message.Message) string {

func formatWelcomeMessage() string {
return utils.RenderBox(
fmt.Sprintf("Clue v%s", Version),
fmt.Sprintf("Tinker v%s", Version),
[]string{
"Thank you for using Clue!",
"Thank you for using Tinker!",
"",
"Feel free to make a contribution - this app is open source",
"",
Expand Down
6 changes: 3 additions & 3 deletions devlogs/DevLogMay2025.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
The first dev log! The idea of clue came from [this wonderful on how to build an Agent by Thorsten Ball](https://ampcode.com/how-to-build-an-agent)
The first dev log! The idea of tinker came from [this wonderful on how to build an Agent by Thorsten Ball](https://ampcode.com/how-to-build-an-agent)

So what so special about clue? Hard to answer, since clue is pretty run-off-the-mill: jan, cline, serena... there are probably hundreds of them out there with thousand of users daily. So my answer would be:
So what so special about tinker? Hard to answer, since tinker is pretty run-off-the-mill: jan, cline, serena... there are probably hundreds of them out there with thousand of users daily. So my answer would be:

1. It's written in Go(not really special, I know) and
2. It's CLI-based (booooo think of better things to say)

That is why I do not intend to commercialize it, and clue would stay to be a for-fun thing (for now, contact me VCs if you see something potential)
That is why I do not intend to commercialize it, and tinker would stay to be a for-fun thing (for now, contact me VCs if you see something potential)

But I have a handful of ideas at the moment: Expanding the toolset, integrating different models into it, creating profiles, etc., and for a person who loves tinkering like me, this is like a kid walking into a candy store. I love having ideas for my work and try to make them come to live and become useful.

Expand Down
4 changes: 2 additions & 2 deletions devlogs/DevLogOct2025.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Clue can now delegate work to its subagents! Well, at least with search-related tasks.
Tinker can now delegate work to its subagents! Well, at least with search-related tasks.

The development behind this was quite funny:

I initially just wanted to write a bash tool for the agent to execute command, but I completed writing it faster than I expected :) So I thought of improving the search tool that can invoke a subagent.

I was so impressed by [Amp's subagents](https://ampcode.com/agents-for-the-agent) that I tried to copy it (TLDR: Subagents save your main agent's context window and can be invoked by including "subagent" in the prompt - How cool is that?). Think of it like mini-Clues: They should have their own context window and set of tools, and they should be reporting to the big agent in the room.
I was so impressed by [Amp's subagents](https://ampcode.com/agents-for-the-agent) that I tried to copy it (TLDR: Subagents save your main agent's context window and can be invoked by including "subagent" in the prompt - How cool is that?). Think of it like mini-Tinkers: They should have their own context window and set of tools, and they should be reporting to the big agent in the room.

My design is to have a smaller version of the Agent struct without the persisting conversation slice + MCP connects (will have in the future?), but AI suggests that I use a runner and re-use the same Agent struct so to avoid import cycle with the `tools` package.

Expand Down
2 changes: 1 addition & 1 deletion docs/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CLI, server and model engine are separate

Root `main.go` as stateless command-line interface and client operations. The CLI starts conversations and manages user I/O

The CLI client `clue serve` send HTTP request to `server/`
The CLI client `tinker serve` send HTTP request to `server/`

`app/main.go` as server daemon and API service (background processing). The server performs CRUD operations on conversations and messages (also inference routing?)

Expand Down
10 changes: 5 additions & 5 deletions docs/sub-agent-design-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,11 @@ import (
"fmt"
"strings"

"github.com/honganh1206/clue/agent"
"github.com/honganh1206/clue/inference"
"github.com/honganh1206/clue/message"
"github.com/honganh1206/clue/schema"
"github.com/honganh1206/clue/server/data/conversation"
"github.com/honganh1206/tinker/agent"
"github.com/honganh1206/tinker/inference"
"github.com/honganh1206/tinker/message"
"github.com/honganh1206/tinker/schema"
"github.com/honganh1206/tinker/server/data/conversation"
)

//go:embed codebase_search.md
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/honganh1206/clue
module github.com/honganh1206/tinker

go 1.24.4

Expand Down
5 changes: 0 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842Bg
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anthropics/anthropic-sdk-go v1.9.1 h1:raRhZKmayVSVZtLpLDd6IsMXvxLeeSU03/2IBTerWlg=
github.com/anthropics/anthropic-sdk-go v1.9.1/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
github.com/anthropics/anthropic-sdk-go v1.14.0 h1:EzNQvnZlaDHe2UPkoUySDz3ixRgNbwKdH8KtFpv7pi4=
github.com/anthropics/anthropic-sdk-go v1.14.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
Expand Down Expand Up @@ -111,7 +109,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
Expand Down Expand Up @@ -193,8 +190,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genai v1.21.0 h1:0olX8oJPFn0iXNV4cNwgdvc4NHGTZpUbhGhu6Y/zh7U=
google.golang.org/genai v1.21.0/go.mod h1:QPj5NGJw+3wEOHg+PrsWwJKvG6UC84ex5FR7qAYsN/M=
google.golang.org/genai v1.36.0 h1:sJCIjqTAmwrtAIaemtTiKkg2TO1RxnYEusTmEQ3nGxM=
google.golang.org/genai v1.36.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
Expand Down
Loading