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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Load the analytics discovery receipt beside the resolved config, including `GITCRAWL_CONFIG`, instead of the working directory. Thanks @SebTardif.

- Speed up `embed --include-closed` on large archives with indexed candidate pagination, preserving ordering and limits and adding the index automatically without a schema version bump. Thanks @masatohoshino.

- Use one shared autoreview installation from `openclaw/agent-skills`; repository entrypoints now receive upstream fixes without copied helpers or test suites.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Direct search supports keyword, semantic, and hybrid modes over one local reposi

`gitcrawl embed owner/repo --include-closed` includes historical threads. Candidate collection uses an ordering index added automatically when the archive opens for writing, so large archives do not need a full sort for every page.

For `analytics --once` and `analytics --watch`, keep the historical discovery receipt (`status.json`) beside the selected config file, including when selecting it through `GITCRAWL_CONFIG`. Analytics uses that location regardless of the working directory.

Commands support structured output with `--json`. Gitcrawl reserves stdout for results and sends diagnostics to stderr, so its output can feed `jq` or another process directly. See [Search](docs/search.md) and [Automation](docs/automation.md) for the supported shapes.

## Cluster and review
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (a *App) runAnalytics(ctx context.Context, args []string) error {
return e
}
if existing == 0 {
b, err := os.ReadFile(filepath.Join(filepath.Dir(a.configPath), "status.json"))
b, err := os.ReadFile(filepath.Join(filepath.Dir(config.ResolvePath(a.configPath)), "status.json"))
if err == nil {
var status struct {
Repository string `json:"repository"`
Expand Down
59 changes: 59 additions & 0 deletions internal/cli/analytics_config_path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cli

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"

"github.com/openclaw/gitcrawl/internal/config"
"github.com/openclaw/gitcrawl/internal/store"
)

func TestAnalyticsReadsReceiptBesideResolvedConfig(t *testing.T) {
ctx := context.Background()
dir := t.TempDir()
path := filepath.Join(dir, "source.db")
st, err := store.Open(ctx, path)
if err != nil {
t.Fatal(err)
}
defer st.Close()
cfg := writeDoctorTestConfig(t, dir, path)
receipt := `{"repository":"fixture/repo","phase":"complete","discovery":[{"kind":"issues","done":1,"total":0,"updated_at":"2026-01-01T00:00:00Z"},{"kind":"pullRequests","done":1,"total":0,"updated_at":"2026-01-01T00:00:00Z"}]}`
if err := os.WriteFile(filepath.Join(dir, "status.json"), []byte(receipt), 0600); err != nil {
t.Fatal(err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/rate_limit" {
fmt.Fprint(w, `{"resources":{"graphql":{"limit":20000,"remaining":19000,"reset":4102444800},"core":{"limit":20000,"remaining":19000,"reset":4102444800}}}`)
return
}
conn := map[string]any{"totalCount": 0, "nodes": []any{}, "pageInfo": map[string]any{"hasNextPage": false, "endCursor": nil}}
json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{"rateLimit": map[string]any{"cost": 1, "remaining": 19000, "resetAt": "2099-01-01T00:00:00Z"}, "repository": map[string]any{"issues": conn, "pullRequests": conn}}})
}))
defer server.Close()
t.Setenv(config.DefaultConfigEnv, cfg)
t.Setenv("GITCRAWL_GITHUB_BASE_URL", server.URL)
t.Setenv("GITHUB_TOKEN", "test-token-placeholder")
cwd := t.TempDir()
if err := os.WriteFile(filepath.Join(cwd, "status.json"), []byte(`{"repository":"other/repo","phase":"complete"}`), 0600); err != nil {
t.Fatal(err)
}
t.Chdir(cwd)

app := New()
app.Stdout, app.Stderr = io.Discard, io.Discard
if err := app.Run(ctx, []string{"analytics", "fixture/repo", "--once"}); err != nil {
t.Fatal(err)
}
var complete, issues, prs int
if err := st.DB().QueryRow(`SELECT complete,issues,pull_requests FROM analytics_coverage WHERE repository='fixture/repo'`).Scan(&complete, &issues, &prs); err != nil || complete != 1 || issues != 0 || prs != 0 {
t.Fatalf("resolved-config baseline coverage: %d %d %d %v", complete, issues, prs, err)
}
}
Loading