Skip to content

Commit 8e758dd

Browse files
CLI cleanup: production-ready file-mode workflow
Production-readiness cleanup for the local file-mode CLI workflow. Removes legacy --three-file support, fixes daemon Ctrl-C cleanup, hardens shard cache correctness, fixes restore --local counting/exclusions, and verifies update/setup/file-mode command surface.
1 parent 6516d61 commit 8e758dd

18 files changed

Lines changed: 875 additions & 303 deletions

README.md

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,31 +110,15 @@ Manages `.graph.*` shards written next to each source file. Agents read these wi
110110

111111
| Command | Description |
112112
|---|---|
113-
| `analyze [path]` | Upload repo, run full analysis, write graph files (use `--three-file` for best results, `--no-shards` to skip) |
113+
| `analyze [path]` | Upload repo, run full analysis, write `.graph.*` files (`--no-shards` skips file writing) |
114114
| `skill` | Print agent awareness prompt — pipe to `CLAUDE.md` or `AGENTS.md` |
115-
| `watch [path]` | Generate graph files on startup, then keep them updated incrementally |
115+
| `supermodel` | Generate graph files on startup, then keep them updated incrementally |
116116
| `clean [path]` | Remove all `.graph.*` files from the repository |
117-
| `hook` | Claude Code `PostToolUse` hook — forward file-change events to the `watch` daemon |
117+
| `hook` | Claude Code `PostToolUse` hook — forward file-change events to the `supermodel` daemon |
118118

119-
### Three-file shard format (recommended)
119+
### Tell your agent about graph files
120120

121-
For best results, use the `--three-file` flag to generate separate `.calls`, `.deps`, and `.impact` files instead of a single `.graph` file:
122-
123-
```bash
124-
supermodel analyze --three-file
125-
```
126-
127-
This produces three files per source file:
128-
129-
```
130-
src/cache.go → src/cache.calls.go # who calls what, with file:line
131-
→ src/cache.deps.go # imports and imported-by
132-
→ src/cache.impact.go # risk level, domains, blast radius
133-
```
134-
135-
The three-file format is **68% faster** in benchmarks because grep hits are more targeted — searching for a function name hits only the `.calls` file with caller/callee data, not a combined blob.
136-
137-
**Tell your agent about the files** by adding this to `CLAUDE.md` or `AGENTS.md`:
121+
Add the Supermodel graph-file prompt to `CLAUDE.md`, `AGENTS.md`, or your agent's instruction file:
138122

139123
```bash
140124
supermodel skill >> CLAUDE.md

cmd/analyze.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package cmd
22

33
import (
4-
"fmt"
5-
64
"github.com/spf13/cobra"
75

86
"github.com/supermodeltools/cli/internal/analyze"
@@ -13,7 +11,6 @@ import (
1311
func init() {
1412
var opts analyze.Options
1513
var noShards bool
16-
var threeFile bool
1714

1815
c := &cobra.Command{
1916
Use: "analyze [path]",
@@ -35,9 +32,6 @@ Use --no-shards to skip writing graph files.`,
3532
if err := cfg.RequireAPIKey(); err != nil {
3633
return err
3734
}
38-
if noShards && threeFile {
39-
return fmt.Errorf("--three-file cannot be used with --no-shards")
40-
}
4135
dir := "."
4236
if len(args) > 0 {
4337
dir = args[0]
@@ -46,7 +40,7 @@ Use --no-shards to skip writing graph files.`,
4640
// Shard mode: Generate handles the full pipeline (API call +
4741
// cache + shards) in a single upload. Running analyze.Run
4842
// first would duplicate the API call.
49-
return shards.Generate(cmd.Context(), cfg, dir, shards.GenerateOptions{Force: opts.Force, ThreeFile: threeFile})
43+
return shards.Generate(cmd.Context(), cfg, dir, shards.GenerateOptions{Force: opts.Force})
5044
}
5145
return analyze.Run(cmd.Context(), cfg, dir, opts)
5246
},
@@ -55,7 +49,6 @@ Use --no-shards to skip writing graph files.`,
5549
c.Flags().BoolVar(&opts.Force, "force", false, "re-analyze even if a cached result exists")
5650
c.Flags().StringVarP(&opts.Output, "output", "o", "", "output format: human|json")
5751
c.Flags().BoolVar(&noShards, "no-shards", false, "skip writing .graph.* shard files")
58-
c.Flags().BoolVar(&threeFile, "three-file", false, "generate .calls/.deps/.impact files instead of single .graph")
5952

6053
rootCmd.AddCommand(c)
6154
}

cmd/hook.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ func init() {
1111

1212
c := &cobra.Command{
1313
Use: "hook",
14-
Short: "Forward Claude Code file-change events to the watch daemon",
15-
Long: `Reads a Claude Code PostToolUse JSON payload from stdin and forwards the file path to the running watch daemon via UDP. Install as a PostToolUse hook in .claude/settings.json.`,
14+
Short: "Forward Claude Code file-change events to the Supermodel daemon",
15+
Long: `Reads a Claude Code PostToolUse JSON payload from stdin and forwards the file path to the running Supermodel daemon via UDP. Install as a PostToolUse hook in .claude/settings.json.`,
1616
Args: cobra.NoArgs,
1717
RunE: func(cmd *cobra.Command, args []string) error {
1818
return shards.Hook(port)
1919
},
2020
}
2121

22-
c.Flags().IntVar(&port, "port", 7734, "UDP port of the watch daemon")
22+
c.Flags().IntVar(&port, "port", 7734, "UDP port of the Supermodel daemon")
2323
rootCmd.AddCommand(c)
2424
}

cmd/login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func init() {
1717
For CI or headless environments, pass the key directly:
1818
supermodel login --token smsk_live_...`,
1919
RunE: func(cmd *cobra.Command, _ []string) error {
20-
if token != "" {
20+
if cmd.Flags().Changed("token") {
2121
return auth.LoginWithToken(token)
2222
}
2323
return auth.Login(cmd.Context())

cmd/root.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package cmd
22

33
import (
4+
"context"
45
"fmt"
56
"os"
7+
"os/signal"
68
"syscall"
79
"time"
810

@@ -176,8 +178,22 @@ func init() {
176178

177179
// Execute is the entry point called by main.
178180
func Execute() {
181+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
182+
done := make(chan struct{})
183+
go func() {
184+
select {
185+
case <-ctx.Done():
186+
stop()
187+
case <-done:
188+
}
189+
}()
190+
rootCmd.SetContext(ctx)
179191
if err := rootCmd.Execute(); err != nil {
180192
fmt.Fprintln(os.Stderr, err)
193+
close(done)
194+
stop()
181195
os.Exit(1)
182196
}
197+
close(done)
198+
stop()
183199
}

0 commit comments

Comments
 (0)