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
ソースの最終更新活動
2026年9月3日 11:52
検出された SKILL.md の言語
英語
スター
8
フォーク
1

インストール方法

デフォルトでは、最初にソースを確認する 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で見る