Skip to content

Commit 79332b0

Browse files
committed
feat: improve gc command and docs
Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
1 parent 83971e5 commit 79332b0

File tree

4 files changed

+36
-9
lines changed

4 files changed

+36
-9
lines changed

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ Bridge build/dev process output to AI coding sessions automatically.
1414

1515
In vibe coding workflows, you run an AI coding tool in one terminal and build commands in another. When errors occur, you manually copy-paste logs into the coding session. `devtap` automates this feedback loop.
1616

17-
Two common cases make this especially painful:
17+
Three common cases make this especially painful:
1818

1919
1. Multiple local dev processes (frontend + backend + worker) that you need to keep an eye on and fix quickly.
2020
2. Multiple coding agents working on the same project, where you want them to analyze the same failures in parallel and compare findings.
21+
3. Build/test runs on remote machines (CI, dev boxes) whose output you need to feed back to a local coding agent.
2122

2223
## Quick Start
2324

@@ -119,7 +120,7 @@ Zero-dependency JSONL files at `~/.devtap/<session>/<adapter>/pending.jsonl`. Ea
119120

120121
### [GreptimeDB](https://github.com/GreptimeTeam/greptimedb) (optional)
121122

122-
For persistent history, SQL-based filtering, and richer statistics. With a remote GreptimeDB instance, Terminal A and Terminal B don't even need to be on the same machine — capture build output from a CI server or remote dev box and consume it from your local AI coding session.
123+
For persistent history, SQL-based filtering, and richer statistics. With a remote GreptimeDB instance, the build and the AI tool don't even need to be on the same machine — see [Cross-machine builds](#advanced-usage) for details.
123124

124125
See [GreptimeDB installation guide](https://docs.greptime.com/getting-started/installation/greptimedb-standalone/) for more options.
125126

@@ -184,6 +185,18 @@ devtap --tag cargo-test --debounce 5s -- cargo watch -x test
184185

185186
**Multi-adapter fan-out** — when multiple AI tools are installed, build output is automatically delivered to all of them. Each tool independently consumes its own copy.
186187

188+
**Cross-machine builds** — with a shared [GreptimeDB](#greptimedb-optional) instance, the build and the AI tool can run on different machines. Use `--session` to give both sides the same logical session name:
189+
190+
```bash
191+
# Machine A (CI / remote build server)
192+
devtap --store greptimedb --session myproject -- make
193+
194+
# Machine B (your laptop, running the AI tool)
195+
devtap mcp-serve --store greptimedb --session myproject
196+
```
197+
198+
Multiple build machines can write to the same session simultaneously — each entry is tagged with its source, and the AI tool drains them all.
199+
187200
**Session auto-detection** — when `--session auto` (default), devtap resolves the project directory like this:
188201
1. Git root (nearest parent with `.git`)
189202
2. Project marker files (nearest parent with one of: `go.mod`, `package.json`, `pyproject.toml`, `Cargo.toml`, `pom.xml`, `build.gradle`, `build.gradle.kts`, `composer.json`, `Gemfile`, `setup.py`)
@@ -209,7 +222,7 @@ devtap [flags] -- <command> [args...]
209222
210223
Flags:
211224
-a, --adapter <name> AI tool adapter (default "claude-code")
212-
-s, --session <id> Target session ("auto", "pick", or UUID)
225+
-s, --session <id> Target session ("auto", "pick", or explicit name)
213226
--store <backend> Storage backend ("file" or "greptimedb")
214227
--filter-regex <pat> Regex filter for output lines
215228
--filter-invert Invert filter (exclude matching lines)

cmd/devtap/gc.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,19 @@ func gcStoreDir(storeDir string, cutoff time.Time) error {
100100
}
101101

102102
// allFilesOlderThan returns true if every file in dir (recursively) has a
103-
// modification time before cutoff. Returns true for empty directories.
103+
// modification time before cutoff. Empty directories are removed only if the
104+
// directory mtime is before cutoff.
104105
func allFilesOlderThan(dir string, cutoff time.Time) bool {
105106
entries, err := os.ReadDir(dir)
106107
if err != nil {
107108
return false
108109
}
109110
if len(entries) == 0 {
110-
return true
111+
info, err := os.Stat(dir)
112+
if err != nil {
113+
return false
114+
}
115+
return info.ModTime().Before(cutoff)
111116
}
112117
for _, e := range entries {
113118
path := filepath.Join(dir, e.Name())

cmd/devtap/gc_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@ func TestAllFilesOlderThan(t *testing.T) {
1111
dir := t.TempDir()
1212
cutoff := time.Now().Add(-1 * time.Hour)
1313

14-
// Empty directory — should return true.
14+
// Empty directory with recent mtime — should return false.
15+
if allFilesOlderThan(dir, cutoff) {
16+
t.Error("empty dir with recent mtime should return false")
17+
}
18+
19+
// Empty directory with old mtime — should return true.
20+
old := time.Now().Add(-2 * time.Hour)
21+
if err := os.Chtimes(dir, old, old); err != nil {
22+
t.Fatal(err)
23+
}
1524
if !allFilesOlderThan(dir, cutoff) {
16-
t.Error("empty dir should be considered all-older")
25+
t.Error("empty dir with old mtime should be considered all-older")
1726
}
1827

1928
// Create a fresh file — should return false.
@@ -25,7 +34,7 @@ func TestAllFilesOlderThan(t *testing.T) {
2534
}
2635

2736
// Set file mtime to 2 hours ago — should return true.
28-
old := time.Now().Add(-2 * time.Hour)
37+
old = time.Now().Add(-2 * time.Hour)
2938
if err := os.Chtimes(filepath.Join(dir, "recent.txt"), old, old); err != nil {
3039
t.Fatal(err)
3140
}

cmd/devtap/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Homepage: https://github.com/killme2008/devtap`,
2828

2929
// Global flags
3030
cmd.PersistentFlags().StringP("adapter", "a", "claude-code", "AI tool adapter (claude-code, codex, opencode, gemini, aider)")
31-
cmd.PersistentFlags().StringP("session", "s", "auto", `target session ("auto", "pick", or UUID)`)
31+
cmd.PersistentFlags().StringP("session", "s", "auto", `target session ("auto", "pick", or explicit name for cross-machine use)`)
3232
cmd.PersistentFlags().String("store", "", `storage backend override ("file" or "greptimedb", default from config)`)
3333

3434
// Run-specific flags

0 commit comments

Comments
 (0)