diff --git a/internal/analyze/handler.go b/internal/analyze/handler.go index 12b8663..83d5753 100644 --- a/internal/analyze/handler.go +++ b/internal/analyze/handler.go @@ -73,7 +73,7 @@ func GetGraph(ctx context.Context, cfg *config.Config, dir string, force bool) ( client := api.New(cfg) spin = ui.Start("Uploading and analyzing repository…") - ir, err := client.AnalyzeShards(ctx, zipPath, "analyze-"+hash[:16]) + ir, err := client.AnalyzeShards(ctx, zipPath, "analyze-"+hash[:16], nil) spin.Stop() if err != nil { return nil, hash, err diff --git a/internal/api/client.go b/internal/api/client.go index cf2206c..b88a3ae 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -8,6 +8,7 @@ import ( "io" "mime/multipart" "net/http" + "net/url" "os" "path/filepath" "time" @@ -115,11 +116,26 @@ func (c *Client) pollLoop(ctx context.Context, post func() (*JobResponse, error) return job, nil } +// PreviousDomain holds domain name + subdomain count for seeding the LLM prompt. +type PreviousDomain struct { + Name string `json:"name"` + SubdomainCount int `json:"subdomainCount"` +} + // AnalyzeShards uploads a repository ZIP and runs the full analysis pipeline, // returning the complete ShardIR response with full Node/Relationship data // required for sidecar rendering (IDs, labels, properties preserved). -func (c *Client) AnalyzeShards(ctx context.Context, zipPath, idempotencyKey string) (*ShardIR, error) { - job, err := c.pollUntilComplete(ctx, zipPath, idempotencyKey) +// If previousDomains is non-nil, passes them as a query param to seed domain names. +func (c *Client) AnalyzeShards(ctx context.Context, zipPath, idempotencyKey string, previousDomains []PreviousDomain) (*ShardIR, error) { + endpoint := analyzeEndpoint + if len(previousDomains) > 0 { + pd, err := json.Marshal(previousDomains) + if err == nil { + endpoint += "?previousDomains=" + url.QueryEscape(string(pd)) + } + } + post := func() (*JobResponse, error) { return c.postZipTo(ctx, zipPath, idempotencyKey, endpoint) } + job, err := c.pollLoop(ctx, post) if err != nil { return nil, err } diff --git a/internal/shards/daemon.go b/internal/shards/daemon.go index 1f5e425..619a33a 100644 --- a/internal/shards/daemon.go +++ b/internal/shards/daemon.go @@ -213,7 +213,7 @@ func (d *Daemon) fullGenerate(ctx context.Context) error { } defer os.Remove(zipPath) - ir, err := d.client.AnalyzeShards(ctx, zipPath, idemKey) + ir, err := d.client.AnalyzeShards(ctx, zipPath, idemKey, nil) if err != nil { return err } @@ -251,7 +251,7 @@ func (d *Daemon) incrementalUpdate(ctx context.Context, changedFiles []string) { } defer os.Remove(zipPath) - ir, err := d.client.AnalyzeShards(ctx, zipPath, "incremental-"+idemKey[:8]) + ir, err := d.client.AnalyzeShards(ctx, zipPath, "incremental-"+idemKey[:8], nil) if err != nil { d.logf("Incremental API error: %v", err) return diff --git a/internal/shards/handler.go b/internal/shards/handler.go index 9fd73fd..3168899 100644 --- a/internal/shards/handler.go +++ b/internal/shards/handler.go @@ -93,11 +93,25 @@ func Generate(ctx context.Context, cfg *config.Config, dir string, opts Generate } defer os.Remove(zipPath) + // Read previous domains from cache for LLM seeding (stabilizes names across refreshes) + var prevDomains []api.PreviousDomain + if data, readErr := os.ReadFile(cacheFile); readErr == nil { + var prev api.ShardIR + if json.Unmarshal(data, &prev) == nil && len(prev.Domains) > 0 { + for _, d := range prev.Domains { + prevDomains = append(prevDomains, api.PreviousDomain{ + Name: d.Name, + SubdomainCount: len(d.Subdomains), + }) + } + } + } + client := api.New(cfg) idemKey := newUUID() spin = ui.Start("Uploading and analyzing repository…") - ir, err := client.AnalyzeShards(ctx, zipPath, "shards-"+idemKey[:8]) + ir, err := client.AnalyzeShards(ctx, zipPath, "shards-"+idemKey[:8], prevDomains) spin.Stop() if err != nil { return err