Skip to content

feat: add cursor activity attribution#20

Merged
Miss-you merged 6 commits into
mainfrom
you-12-cursor-activity-attribution
Mar 19, 2026
Merged

feat: add cursor activity attribution#20
Miss-you merged 6 commits into
mainfrom
you-12-cursor-activity-attribution

Conversation

@Miss-you

Copy link
Copy Markdown
Owner

Summary

  • add a dedicated Cursor activity reader for ~/.cursor/ai-tracking/ai-code-tracking.db
  • add codetok cursor activity with table and JSON output for separate composer and tab attribution metrics
  • add unit and e2e coverage to keep activity attribution out of provider.TokenUsage, daily, and session

Changes

  • cursor/activity.go: new activity model, default DB path resolution, SQLite reader, and Service.Activity
  • cmd/cursor.go: new cursor activity subcommand with --json and --db-path
  • cursor/activity_test.go, cmd/cursor_test.go, e2e/e2e_test.go: reader, command, and regression coverage
  • README.md, README_zh.md: document the separate local activity view and the explicit remote Cursor command boundary
  • openspec/changes/cursor-usage-support/tasks.md: mark 4.1-4.4, 5.1, and 5.4 complete

Validation

  • make fmt
  • make vet
  • make test
  • make lint (skipped by Makefile because golangci-lint is not installed)
  • make build
  • ./bin/codetok cursor activity --json
  • ./bin/codetok daily --json --all --cursor-dir $(pwd)/e2e/testdata/cursor --claude-dir <empty> --codex-dir <empty> --kimi-dir <empty>

Risks / Notes

  • activity attribution is line-based and intentionally separate from token accounting
  • I did not run openspec-verify-change or openspec-archive-change because the broader cursor-usage-support task list still appears to have unresolved items outside this ticket's scoped work

Copilot AI review requested due to automatic review settings March 16, 2026 10:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a dedicated codetok cursor activity subcommand that reads Cursor's local SQLite tracking database (~/.cursor/ai-tracking/ai-code-tracking.db) and reports composer and tab line-based activity metrics, intentionally kept separate from the existing token-based reporting in daily and session.

Changes:

  • New cursor/activity.go with activity model, SQLite reader, and Service.Activity method; new cursor activity Cobra subcommand in cmd/cursor.go with --json and --db-path flags
  • Unit, command, and e2e tests verifying activity reading, command output, and regression ensuring activity data doesn't pollute token reports
  • Documentation updates in both READMEs, design/plan docs, and OpenSpec task tracking

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cursor/activity.go New activity reader, model types, default DB path resolution, and Service.Activity
cmd/cursor.go New cursor activity subcommand with JSON and table output
cursor/activity_test.go Unit tests for SQLite reader with fixture DBs
cmd/cursor_test.go Command-level tests for activity subcommand
e2e/e2e_test.go E2e tests for activity JSON output and daily/session non-pollution
go.mod / go.sum Added modernc.org/sqlite and transitive dependencies
README.md / README_zh.md Documented cursor activity command and updated API boundary docs
docs/plans/*.md Design and implementation plan documents
openspec/changes/cursor-usage-support/tasks.md Marked completed tasks

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cursor/activity.go Outdated
if os.IsNotExist(err) || os.IsPermission(err) {
return result, nil
}
return result, nil

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated Read to keep only os.IsNotExist and os.IsPermission on the no-data path. Unexpected os.Stat failures now return an error with the DB path, and TestReadActivity_UnexpectedStatErrorReturnsError locks that in.

Comment thread cursor/activity.go Outdated
if strings.Contains(err.Error(), "no such table") {
return result, nil
}
return result, nil

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the reader to surface unexpected open/query failures with context while still treating no such table as no data. Coverage is in TestReadActivity_OpenDatabaseErrorReturnsError and TestReadActivity_UnexpectedQueryErrorReturnsError.

Comment thread e2e/e2e_test.go Outdated
Comment on lines +99 to +157
func writeCursorActivityDB(t *testing.T, rows []activityFixtureRow) string {
t.Helper()

dbPath := filepath.Join(t.TempDir(), "ai-code-tracking.db")
db, err := sql.Open("sqlite", dbPath)
if err != nil {
t.Fatalf("sql.Open returned error: %v", err)
}
defer db.Close()

_, err = db.Exec(`
CREATE TABLE scored_commits (
commitHash TEXT NOT NULL,
branchName TEXT NOT NULL,
scoredAt INTEGER NOT NULL,
linesAdded INTEGER,
linesDeleted INTEGER,
tabLinesAdded INTEGER,
tabLinesDeleted INTEGER,
composerLinesAdded INTEGER,
composerLinesDeleted INTEGER,
humanLinesAdded INTEGER,
humanLinesDeleted INTEGER,
blankLinesAdded INTEGER,
blankLinesDeleted INTEGER,
commitMessage TEXT,
commitDate TEXT,
v1AiPercentage TEXT,
v2AiPercentage TEXT,
PRIMARY KEY (commitHash, branchName)
);
`)
if err != nil {
t.Fatalf("creating scored_commits table: %v", err)
}

for i, row := range rows {
_, err := db.Exec(`
INSERT INTO scored_commits (
commitHash, branchName, scoredAt, linesAdded, linesDeleted,
tabLinesAdded, tabLinesDeleted, composerLinesAdded, composerLinesDeleted
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`,
"commit-"+string(rune('a'+i)),
"main",
1000+i,
row.composerAdded+row.tabAdded,
row.composerDeleted+row.tabDeleted,
row.tabAdded,
row.tabDeleted,
row.composerAdded,
row.composerDeleted,
)
if err != nil {
t.Fatalf("inserting fixture row %d: %v", i, err)
}
}

return dbPath

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted the shared SQLite fixture builder into internal/testutil/cursor_activity_db.go and switched both cursor/activity_test.go and e2e/e2e_test.go to use it, so the activity schema and insert logic now live in one place.

@Miss-you

Copy link
Copy Markdown
Owner Author

Conflict resolution update:

  • rebased the PR content onto current main and restored a clean PR diff
  • reran make test, make build, and ./bin/codetok cursor activity --json against the final workspace tree
  • the PR now reflects the resolved activity-attribution changes on top of current main

@Miss-you
Miss-you merged commit 2429195 into main Mar 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants