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
26 changes: 5 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,31 +110,15 @@ Manages `.graph.*` shards written next to each source file. Agents read these wi

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

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

For best results, use the `--three-file` flag to generate separate `.calls`, `.deps`, and `.impact` files instead of a single `.graph` file:

```bash
supermodel analyze --three-file
```

This produces three files per source file:

```
src/cache.go → src/cache.calls.go # who calls what, with file:line
→ src/cache.deps.go # imports and imported-by
→ src/cache.impact.go # risk level, domains, blast radius
```

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.

**Tell your agent about the files** by adding this to `CLAUDE.md` or `AGENTS.md`:
Add the Supermodel graph-file prompt to `CLAUDE.md`, `AGENTS.md`, or your agent's instruction file:

```bash
supermodel skill >> CLAUDE.md
Expand Down
9 changes: 1 addition & 8 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"

"github.com/supermodeltools/cli/internal/analyze"
Expand All @@ -13,7 +11,6 @@ import (
func init() {
var opts analyze.Options
var noShards bool
var threeFile bool

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

rootCmd.AddCommand(c)
}
6 changes: 3 additions & 3 deletions cmd/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ func init() {

c := &cobra.Command{
Use: "hook",
Short: "Forward Claude Code file-change events to the watch daemon",
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.`,
Short: "Forward Claude Code file-change events to the Supermodel daemon",
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.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return shards.Hook(port)
},
}

c.Flags().IntVar(&port, "port", 7734, "UDP port of the watch daemon")
c.Flags().IntVar(&port, "port", 7734, "UDP port of the Supermodel daemon")
rootCmd.AddCommand(c)
}
2 changes: 1 addition & 1 deletion cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func init() {
For CI or headless environments, pass the key directly:
supermodel login --token smsk_live_...`,
RunE: func(cmd *cobra.Command, _ []string) error {
if token != "" {
if cmd.Flags().Changed("token") {
return auth.LoginWithToken(token)
}
return auth.Login(cmd.Context())
Expand Down
16 changes: 16 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cmd

import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"

Expand Down Expand Up @@ -176,8 +178,22 @@ func init() {

// Execute is the entry point called by main.
func Execute() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
done := make(chan struct{})
go func() {
select {
case <-ctx.Done():
stop()
case <-done:
}
}()
rootCmd.SetContext(ctx)
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
close(done)
stop()
os.Exit(1)
}
close(done)
stop()
}
Loading
Loading