Skip to main content

time-lens

Analyze and visualize time spent on software projects by combining data from multiple sources: WakaTime coding time, git commit session detection, Claude Code usage, Codex CLI usage, and Cursor IDE usage. Produces both an interactive HTML dashboard (dark-themed, Chart.js) and a Markdown report with ASCII charts. Use when the user asks to: analyze work hours, calculate time spent on a project, generate a work hours report, visualize coding activity, create a project time breakdown, or summarize development effort across date ranges.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
vladmdgolam/agent-skills
آخر نشاط في المصدر
٣ سبتمبر ٢٠٢٦ في ١١:٥٢
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٨
التفرعات
١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
10 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
time-lens
description
Analyze and visualize time spent on software projects by combining data from multiple sources: WakaTime coding time, git commit session detection, Claude Code usage, Codex CLI usage, and Cursor IDE usage. Produces both an interactive HTML dashboard (dark-themed, Chart.js) and a Markdown report with ASCII charts. Use when the user asks to: analyze work hours, calculate time spent on a project, generate a work hours report, visualize coding activity, create a project time breakdown, or summarize development effort across date ranges.
# Project Time Tracker Combines five data sources → reconciles → produces HTML dashboard + Markdown report. ## Scripts All scripts live in `scripts/` next to this SKILL.md. Run them with `python3`: | Script | Purpose | Key flags | |--------|---------|-----------| | `git_sessions.py` | Parse git history → sessions → hours | `<repo> --since YYYY-MM-DD --until YYYY-MM-DD [--raw]` | | `wakatime_fetch.py` | WakaTime API → daily hours, filtered to project | `--start YYYY-MM-DD --end YYYY-MM-DD --project name` | | `claude_messages.py` | Claude Code user prompts + activity timestamps per day | `--project-path /abs/path` or `--filter name` | | `codex_messages.py` | Codex CLI user prompts + activity timestamps per day | `--project-path /abs/path` or `--filter name` | | `cursor_messages.py` | Cursor IDE user prompts + activity timestamps per day | `--project-path /abs/path` or `--filter name` | | `reconcile.py` | Merges the JSON output of the scripts above into a single hour estimate, with an optional supervised-vs-unattended split | `--git f.json --claude f.json --codex f.json --since Y-M-D --until Y-M-D [--gap-hours 0.5] [--presence-window-min 10] [--tz Area/City] [--daily]` | `claude_messages.py`, `codex_messages.py`, and `cursor_messages.py` each output **two** timestamp arrays: `timestamps` (user prompts only — for prompt-count metrics) and `activity_timestamps` (every timestamped event, including tool-call/tool-result cycles and assistant replies — use this one for reconciliation). See "Use `activity_timestamps`, not `timestamps`" in [references/reconciliation.md](references/reconciliation.md) for why this distinction matters — using prompt-only timestamps for hour reconciliation silently drops real work time. **Use `reconcile.py` instead of hand-rolling the merge in Python.** It's a deterministic implementation of the algorithm below — run it on the JSON files the other scripts already produced rather than re-deriving the merge/sessionize/intersect logic inline each time. Hand-rolling it live is exactly how a real bug happened once: two different gap-threshold runs (15min vs 30min "engine time") got compared against each other as if they were the same base, producing a nonsensical number. `reconcile.py` fixes the gap threshold and presence window as explicit flags so every run is reproducible and comparable. ## Workflow ### 1. Determine scope Ask for or infer: - Project directory/directories (git repos) - Date range (first commit → last commit, or user-specified) - Output location for HTML + markdown files **Auto-discover sub-repos:** By default, scan the project directory for `.git` folders in subdirectories (not just the root). Each parent of a `.git` directory is a sub-repo to analyze. ```bash # Find all git repos under the project directory find /path/to/project -name ".git" -type d 2>/dev/null | sort ``` This produces a list like: ``` /path/to/project/frontend/.git /path/to/project/backend/.git /path/to/project/libs/shared/.git ``` Each of these (minus the `/.git` suffix) is a repo to run `git_sessions.py`, `claude_messages.py`, and `codex_messages.py` on. Also run these scripts on the root project directory itself (for Claude/Codex messages sent from the root, which is common when using monorepo-style workflows). ### 2. Extract data Run all five scripts on every discovered repo. For git, Claude, Codex, and Cursor, run per sub-repo. For WakaTime, use the multi-project discovery approach described below. ```bash # Git sessions — run per sub-repo python3 git_sessions.py /path/to/project/frontend --since 2026-01-15 --until 2026-02-02 python3 git_sessions.py /path/to/project/backend --since 2026-01-15 --until 2026-02-02 # Claude Code — run per sub-repo AND the root directory python3 claude_messages.py --project-path /path/to/project python3 claude_messages.py --project-path /path/to/project/frontend python3 claude_messages.py --project-path /path/to/project/backend # Codex CLI — same as Claude python3 codex_messages.py --project-path /path/to/project python3 codex_messages.py --project-path /path/to/project/frontend python3 codex_messages.py --project-path /path/to/project/backend # Cursor IDE — same as Claude/Codex python3 cursor_messages.py --project-path /path/to/project python3 cursor_messages.py --project-path /path/to/project/frontend python3 cursor_messages.py --project-path /path/to/project/backend ``` **WakaTime multi-project discovery:** WakaTime often tracks sub-directories as separate projects (e.g., a monorepo at `my-project/` may have WakaTime projects named `my-project`, `frontend`, `backend`, `shared`). A single `--project` query will miss the others. 1. First, run `wakatime_fetch.py` **without** `--project` to get the full project list for the date range: ```bash python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 # Returns: { "projects": [{"project": "my-project", "hours": 9.2}, {"project": "frontend", "hours": 5.1}, ...] } ``` 2. Filter the returned `projects` list for names matching any of: - The root project directory basename (e.g., `my-project`) - Any sub-repo directory basename (e.g., `frontend`, `backend`) - Any intermediate directory basename that contains a sub-repo (e.g., `libs`) 3. Fetch intervals for each matching project: ```bash python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 --project my-project python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 --project frontend python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 --project backend ``` 4. Combine all intervals from all matching WakaTime projects into a single list for reconciliation. **Why this matters:** In a project with 4 sub-repos, a single `--project` query captured only 9h of the actual 26.5h of WakaTime data. The other 17.5h was tracked under sub-directory project names. **Folder move detection:** If `claude_messages.py`, `codex_messages.py`, or `cursor_messages.py` return 0 results, check the output for `alternate_paths`. If present, ask the user: > "No Claude/Codex history found at `/current/path`, but found sessions for `project-name` at `/old/path`. Was this project moved? Should I include that history too?" If confirmed, re-run with `--project-path /old/path` and merge timestamps from both paths. See [references/folder-move-detection.md](references/folder-move-detection.md) for full detection logic and edge cases. ### 3. Reconcile hours **Merged total = best estimate** (git ∪ Claude ∪ Codex ∪ Cursor ∪ WakaTime intervals, no double-counting). Run `reconcile.py` on the JSON files from step 2 rather than hand-rolling the merge: ```bash python3 reconcile.py \ --git git_backend.json --git git_frontend.json \ --claude claude_backend.json --claude claude_frontend.json \ --codex codex_backend.json --codex codex_frontend.json \ --cursor cursor_backend.json \ --waka waka.json \ --since 2026-06-29 --until 2026-08-19 \ --tz Europe/Berlin --daily ``` (`--git`/`--claude`/`--codex`/`--cursor`/`--waka` are all repeatable — pass one per sub-repo for multi-repo projects; `reconcile.py` merges them together.) This does, deterministically: 1. Take git's raw commit `timestamps`, and Claude/Codex/Cursor's `activity_timestamps` (**not** `timestamps` — see below) as point events. 2. Sessionize each source independently via the gap threshold (`--gap-hours`, default 0.5h; use 0.25 for a stricter/conservative estimate). 3. Union WakaTime's ready-made `[start, end]` intervals in alongside the sessionized point sources. 4. Merge everything that overlaps or sits within the gap threshold into final sessions. 5. Sum raw durations — **no `+0.5h` buffer, no `0.5h` floor** (legacy behavior; see reconciliation.md if you need it, e.g. for a `timestamps`-only source with no activity-level granularity). 6. If `--presence-window-min > 0` (default 10): also compute a **supervised vs. unattended split** — how much of the merged engine time falls within that window of an actual user prompt or git commit, vs. how much is the agent running with no human check-in nearby. See "Supervised vs. unattended time" below. 7. If `--daily`: bucket per day using an 08:00 local-time boundary (`--day-boundary-hour`, default 8) so an overnight session doesn't get split across two days in the table. **Use `activity_timestamps`, never `timestamps`, for engine-time reconciliation** (this is what `reconcile.py` does automatically). `timestamps` is prompt-only and will make an actively-worked session look like a single 0-duration point whenever the agent kept running after the last recorded prompt. `activity_timestamps` includes tool-call/tool-result cycles and assistant replies, so it reflects when the agent was actually still working. See [references/reconciliation.md](references/reconciliation.md) for the concrete example (a "0-duration" session that turned out to be ~50 minutes of continuous work) and why the gap threshold dropped from the old 1.5h default to 0.5h. **Don't compare `engine_hours` across different `--gap-hours` runs as if they're the same measurement** — a 15min-threshold total and a 30min-threshold total are two different quantities. If you need both, run `reconcile.py` twice and label each result with its threshold. #### Supervised vs. unattended time `engine_hours` measures when the *agent* was active — not necessarily when the human was watching. If a task ran autonomously for 20+ minutes with no new prompt, that time still counts as engine time but may not reflect real hands-on-keyboard effort. `reconcile.py`'s supervised split addresses this: it builds a "presence window" of `± --presence-window-min` minutes around every real user prompt or git commit, and reports how much of `engine_hours` falls inside vs. outside that window. The window size is a real sensitivity knob, not a solved parameter — on one real project, ±5min gave 72% supervised, ±30min gave 99%. **±10min is the recommended default**: long enough to cover a normal "read the output, think, reply" pause, tight enough that a genuine away-from-keyboard stretch still shows up as unattended. Always report which window was used alongside the number. Why merge matters: AI agent prompts (Claude/Codex/Cursor) often appear minutes before/after git commits in the same work session. WakaTime captures IDE keystrokes that may fall between commits. A user might research with Claude, use Cursor's AI, write code (WakaTime), then commit (git) — all one session. Union of all five sources captures the true session boundaries without double-counting. - The merged total replaces "git-only" as the primary estimate - WakaTime hours shown for reference (active keystrokes only, always lower) See [references/reconciliation.md](references/reconciliation.md) for the full underlying algorithm `reconcile.py` implements, the day-boundary bucketing rationale, and the legacy buffer/floor formula. ### 4. Generate HTML dashboard Write a single-file HTML with inline Chart.js (CDN). Dark theme (`#0a0a0a` bg, `#1a1a1a` cards). Required sections: 1. **Stat cards** — Merged total (git∪claude∪codex∪cursor∪waka), Git estimate, WakaTime, Sessions, Commits, Claude prompts, Codex prompts, Cursor prompts 2. **Daily activity chart** — Overlapping bars (git + WakaTime + merged) + AI prompts line on secondary axis 3. **Gantt timeline** — UTC horizontal bars; git, Claude, Codex, and Cursor as separate colored datasets on same chart (separate swimlane rows when they overlap on same day) 4. **Data table** — Session | Time (UTC) | Active | Est. | WakaTime | Claude | Codex | Cursor | Commits Chart.js essentials: ```javascript Chart.defaults.color = '#888'; Chart.defaults.borderColor = '#2a2a2a'; // Overlapping bars: same barPercentage/categoryPercentage on both datasets, different opacity // Mixed chart: type:'bar' on container, each dataset has its own type + yAxisID // Gantt floating bars: data: [[startH, endH]], indexAxis: 'y' // Claude as line on secondary axis: { type: 'line', yAxisID: 'yClaude', // right-side axis, max ~100, different color } // All charts: responsive: true, maintainAspectRatio: false ``` Save as `<project-dir>/work-hours-analysis.html`. ### 5. Generate Markdown report ```markdown # [Project] - Work Hours Analysis ## Summary **Estimated Total Working Hours: Xh** (based on commit timing analysis) **Supervised: Xh (Y%) · Unattended: Xh** (±10min presence window — see Methodology) | Date | Sessions | Time Range | Git Est. | WakaTime | Commits | Project | ... ## Timeline - Start: [date], End: [date], Duration: N days ## Per-Project Breakdown [sub-project sections with commit/session counts] ## Charts ### Daily Activity (ASCII) Jan 15 ██░░░░░░░░░░░░░░░░░░ 0.5h ... ### Project Distribution project-a ████████████████████ 45% (~18h) ... ## Methodology - Session Detection: events within 0.5h gap = same session (git commits + Claude/Codex/Cursor activity_timestamps, not just prompts) - Hour Estimate: Σ(session_duration), no buffer, no floor — activity_timestamps already captures real start/end - Supervised vs. Unattended: hours within ±10min of a real user prompt/commit count as supervised; the rest is the agent running with no recent human check-in - Why Git > WakaTime: WakaTime only tracks active IDE typing; git includes thinking/research/AI prompting ``` ASCII bar: `blocks = round(hours / max_hours * 20)`, `█` filled, `░` empty, 20 cols wide. Save as `<project-dir>/total_hours.md`. --- ## Validation Checklist ### Before running - [ ] **WakaTime API key** — `~/.wakatime.cfg` exists and contains `api_key = waka_...` under `[settings]`. Run `cat ~/.wakatime.cfg` to verify. If missing, the WakaTime script will fail silently or with an auth error. - [ ] **Git repo accessible** — the project directory is a git repo with commits (`git log --oneline -5 /path/to/repo` returns results). If not, `git_sessions.py` will return 0 sessions. - [ ] **Claude history exists** — `~/.claude/history.jsonl` is present and non-empty, OR `~/.claude/projects/` contains session files for the project. If both are missing, Claude hours will be 0. - [ ] **Cursor data accessible** — the Cursor state database exists at the platform-specific path (macOS: `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb`, Windows: `%APPDATA%\Cursor\User\globalStorage\state.vscdb`, Linux: `~/.config/Cursor/User/globalStorage/state.vscdb`). If missing, Cursor hours will be 0. The script auto-detects the platform and reads SQLite databases in read-only mode. - [ ] **Date range is valid** — `--since` is before `--until`; the range covers dates when work actually happened. ### After generation - [ ] **HTML loads without errors** — open `work-hours-analysis.html` in a browser; all charts render; no JS console errors. - [ ] **Total hours match** — the "Merged total" stat card in HTML equals the "Estimated Total Working Hours" in `total_hours.md` (allow ±0.01h for rounding). - [ ] **Date range matches input** — the first and last dates in the data table and the Timeline section match the requested `--since`/`--until` values. - [ ] **Session count non-zero** — at least one source contributed sessions. If all sources return 0, something is wrong (wrong path, wrong project name, date range outside project history). --- ## Examples ### Example 1: Single repo, standard report **Trigger phrase:** "How many hours did I spend on the api-server project this month? Generate the full report." **Actions:** 1. Determine scope: project at `/Users/alice/code/api-server`, date range inferred as 2026-02-01 → 2026-02-23 (current month to today).
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub