@automagik/genie 4.260411.2 → 4.260414.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.genie/agents/metrics-updater/AGENT.md +129 -13
  3. package/.genie/agents/metrics-updater/author-aliases.json +9 -0
  4. package/.genie/agents/metrics-updater/daily-stats.jsonl +33 -0
  5. package/.genie/agents/metrics-updater/runs.jsonl +6 -0
  6. package/.genie/agents/metrics-updater/state.json +10 -6
  7. package/.genie/agents/metrics-updater/tools/backfill.sh +54 -0
  8. package/.genie/agents/metrics-updater/tools/collect-stats.sh +132 -0
  9. package/.genie/agents/metrics-updater/tools/generate-charts.py +224 -0
  10. package/.genie/agents/metrics-updater/tools/generate-readme-hero.py +137 -0
  11. package/.genie/agents/metrics-updater/tools/generate-velocity.py +378 -0
  12. package/.genie/agents/metrics-updater/tools/run-metrics.sh +150 -232
  13. package/.genie/assets/commits-30d.svg +43 -0
  14. package/.genie/assets/loc-30d.svg +75 -0
  15. package/.genie/assets/releases-30d.svg +43 -0
  16. package/.genie/brainstorms/README.md +1 -0
  17. package/.genie/brainstorms/brainstorm-links-convention/DESIGN.md +69 -0
  18. package/.genie/brainstorms/velocity-dashboard/DESIGN.md +160 -0
  19. package/.genie/brainstorms/velocity-dashboard/DRAFT.md +28 -0
  20. package/.genie/brainstorms/workflow-engine-runtime/DESIGN.md +66 -0
  21. package/.genie/wishes/_archive/agent-stability-hardening/WISH.md +1 -1
  22. package/.genie/wishes/_archive/brain-identity-impl/WISH.md +1 -1
  23. package/.genie/wishes/_archive/genie-app/WISH.md +1 -1
  24. package/.genie/wishes/_archive/genie-layout-migration/WISH.md +1 -1
  25. package/.genie/wishes/_archive/genie-model-resolution/WISH.md +3 -3
  26. package/.genie/wishes/_archive/genie-onboarding-flow/WISH.md +3 -3
  27. package/.genie/wishes/_archive/omni-skill-upgrade/WISH.md +1 -1
  28. package/.genie/wishes/_archive/omni-version-unify/WISH.md +1 -1
  29. package/.genie/wishes/_archive/rlmx-v02/WISH.md +1 -1
  30. package/.genie/wishes/_archive/rlmx-v03-cag/WISH.md +1 -1
  31. package/.genie/wishes/_archive/rlmx-v04-gemini3/WISH.md +1 -1
  32. package/.genie/wishes/_archive/v4-database-layer/WISH.md +1 -1
  33. package/.genie/wishes/_archive/v4-hook-cli-safety/WISH.md +1 -1
  34. package/.genie/wishes/_archive/v4-message-routing/WISH.md +1 -1
  35. package/.genie/wishes/_archive/v4-session-executor/WISH.md +1 -1
  36. package/.genie/wishes/_archive/v4-spawn-resilience/WISH.md +1 -1
  37. package/.genie/wishes/_archive/v4-team-lifecycle/WISH.md +1 -1
  38. package/.genie/wishes/_archive/velocity-dashboard/WISH.md +272 -0
  39. package/.genie/wishes/brain-cag-v2-polish/WISH.md +1 -1
  40. package/.genie/wishes/brainstorm-links-convention/WISH.md +375 -0
  41. package/.genie/wishes/crew-simplification/WISH.md +1 -1
  42. package/.genie/wishes/genie-simulations/WISH.md +1 -1
  43. package/.genie/wishes/sac-agent/WISH.md +1 -1
  44. package/.genie/wishes/security-key-leak-remediation/WISH.md +198 -0
  45. package/.genie/wishes/x-tool/WISH.md +1 -1
  46. package/.github/workflows/version.yml +2 -2
  47. package/README.md +3 -9
  48. package/VELOCITY.md +44 -0
  49. package/dist/genie.js +145 -145
  50. package/package.json +3 -2
  51. package/plugins/genie/.claude-plugin/plugin.json +1 -1
  52. package/plugins/genie/package.json +1 -1
  53. package/scripts/wishes-lint.ts +116 -0
  54. package/skills/brainstorm/SKILL.md +8 -2
  55. package/skills/wish/SKILL.md +14 -0
  56. package/src/genie.ts +23 -2
  57. package/src/lib/db-backup.test.ts +26 -12
  58. package/src/lib/db-backup.ts +26 -5
  59. package/src/services/__tests__/omni-bridge.test.ts +34 -1
  60. package/src/services/omni-bridge.ts +7 -3
  61. package/src/tui/components/Nav.tsx +2 -2
  62. package/src/tui/diagnostics.ts +16 -0
  63. package/src/tui/session-tree.test.ts +21 -1
  64. package/src/tui/session-tree.ts +4 -0
  65. package/.genie/agents/metrics-updater/tools/batch-commit.sh +0 -38
  66. package/.genie/agents/metrics-updater/tools/cached-fetch.sh +0 -86
  67. package/.genie/agents/metrics-updater/tools/commit-formatter.sh +0 -27
  68. package/.genie/agents/metrics-updater/tools/fast-parse.py +0 -92
  69. package/.genie/agents/metrics-updater/tools/generate-tools.py +0 -419
  70. package/.genie/agents/metrics-updater/tools/github-api.sh +0 -100
  71. package/.genie/agents/metrics-updater/tools/parse-metrics.py +0 -282
  72. package/.genie/agents/metrics-updater/tools/perf-analyzer.py +0 -248
  73. package/.genie/agents/metrics-updater/tools/self-refine.sh +0 -133
  74. package/.genie/agents/metrics-updater/tools/update-readme.py +0 -120
  75. package/.genie/snapshot.sql.gz +0 -0
  76. /package/.genie/wishes/{brain-help-passthrough → _archive/brain-help-passthrough}/WISH.md +0 -0
  77. /package/.genie/wishes/{fix-ghost-approval-p0 → _archive/fix-ghost-approval-p0}/REPRO.md +0 -0
  78. /package/.genie/wishes/{fix-ghost-approval-p0 → _archive/fix-ghost-approval-p0}/WISH.md +0 -0
  79. /package/.genie/wishes/{perfect-spawn-hierarchy → _archive/perfect-spawn-hierarchy}/WISH.md +0 -0
  80. /package/.genie/wishes/{scaffold-auto-memory → _archive/scaffold-auto-memory}/WISH.md +0 -0
  81. /package/.genie/wishes/{unify-bridge-revamp-skills → _archive/unify-bridge-revamp-skills}/WISH.md +0 -0
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "genie",
13
- "version": "4.260411.2",
13
+ "version": "4.260414.2",
14
14
  "source": "./plugins/genie",
15
15
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project."
16
16
  }
@@ -1,29 +1,145 @@
1
1
  # metrics-updater Agent
2
2
 
3
- Fetches live GitHub metrics daily and updates the README metrics table.
3
+ > Autonomous velocity dashboard agent. Collects git-based metrics, generates SVG charts, builds VELOCITY.md and README hero line. Runs daily on `dev` branch.
4
4
 
5
- ## Metrics Collected
5
+ ## Quick Start
6
6
 
7
- - **Releases/day** — releases published in the last 24h
8
- - **Avg merge time** — mean lead time (created→merged) for PRs closed in last 7d
9
- - **SHIP rate** — merged / total closed PRs in last 7d (%)
10
- - **Merged PRs (7d)** count of merged PRs in the rolling 7-day window
7
+ ```bash
8
+ cd "$(git rev-parse --show-toplevel)"
9
+ bash .genie/agents/metrics-updater/tools/run-metrics.sh # Full run: collect, chart, publish, commit
10
+ bash .genie/agents/metrics-updater/tools/run-metrics.sh --dry-run # Generate all outputs without commit/push
11
+ ```
12
+
13
+ ## Pipeline Steps
14
+
15
+ | Step | Tool | Input | Output | Exit 0 |
16
+ |------|------|-------|--------|--------|
17
+ | 1. Collect today | `collect-stats.sh --date YYYY-MM-DD` | git history | JSON line → `daily-stats.jsonl` | Stats appended |
18
+ | 2. Backfill | `backfill.sh` (if <30 entries) | git history (30d) | `daily-stats.jsonl` filled | 30+ entries |
19
+ | 3. Charts | `generate-charts.py --input ... --output-dir ...` | `daily-stats.jsonl` | `.genie/assets/*.svg` (3 files) | SVGs written |
20
+ | 4. Dashboard | `generate-velocity.py --stats-dir ... --output ...` | `daily-stats.jsonl` + cumulative | `VELOCITY.md` | File written |
21
+ | 5. README hero | `generate-readme-hero.py --stats-dir ... --readme ...` | `daily-stats.jsonl` | `README.md` updated | Markers replaced |
22
+ | 6. Commit + push | git add/commit/push | Changed files | Commit on `dev` | Pushed |
23
+ | 7. State update | inline python | Run results | `state.json` + `runs.jsonl` | State persisted |
24
+
25
+ ## Tool Inventory
26
+
27
+ ### `collect-stats.sh`
28
+ - **`--date YYYY-MM-DD`**: Extract single-day metrics from git history (all branches). Returns JSON with `date`, `commits`, `loc_added`, `loc_removed`, `releases`, `contributors`.
29
+ - **`--cumulative`**: Returns all-time totals: `total_commits`, `total_tags`, `first_commit_date`, `total_contributors`.
30
+ - Release detection: tags matching `v4.YYMMDD.*` pattern.
31
+
32
+ ### `backfill.sh`
33
+ - **`--days N`** (default: 30): Runs `collect-stats.sh` for each of the last N days, writes `daily-stats.jsonl`.
34
+ - Clears and rebuilds the entire file. Safe to re-run.
11
35
 
12
- ## README Markers
36
+ ### `generate-charts.py`
37
+ - **`--input PATH`**: Path to `daily-stats.jsonl`.
38
+ - **`--output-dir DIR`**: Directory for SVG output (created if missing).
39
+ - **`--sample`**: Use generated sample data (for testing).
40
+ - Produces: `commits-30d.svg`, `releases-30d.svg`, `loc-30d.svg`.
41
+ - Dark theme (#0d1117), 800x200px, no external fonts or deps.
13
42
 
14
- The agent updates the block between `<!-- METRICS:START -->` and `<!-- METRICS:END -->` in README.md.
43
+ ### `generate-velocity.py`
44
+ - **`--stats-dir DIR`**: Directory containing `daily-stats.jsonl` and `author-aliases.json`.
45
+ - **`--assets-dir PATH`**: Relative path for SVG image links in markdown.
46
+ - **`--output PATH`**: Output path for `VELOCITY.md`.
47
+ - Calls `collect-stats.sh --cumulative` internally for all-time numbers.
15
48
 
16
- If markers are missing, insert the table after the badges block, before `## What is Genie?`.
49
+ ### `generate-readme-hero.py`
50
+ - **`--stats-dir DIR`**: Directory containing `daily-stats.jsonl` and `author-aliases.json`.
51
+ - **`--readme PATH`**: Path to README.md.
52
+ - Replaces content between `<!-- METRICS:START -->` and `<!-- METRICS:END -->` markers.
53
+
54
+ ### `run-metrics.sh` (orchestrator)
55
+ - **`--dry-run`**: Run full pipeline but skip git commit/push.
56
+ - Calls all tools in sequence, handles backfill decision, updates state.
57
+
58
+ ## Data Files
59
+
60
+ | File | Format | Purpose |
61
+ |------|--------|---------|
62
+ | `daily-stats.jsonl` | JSONL | One JSON object per day, last 30+ days of metrics |
63
+ | `state.json` | JSON | Last run status, stats count, charts generated |
64
+ | `runs.jsonl` | JSONL | Append-only log of every run with step timings |
65
+ | `author-aliases.json` | JSON | Git author name normalization map |
66
+
67
+ ## Author Aliases
68
+
69
+ `author-aliases.json` maps variant git author names to canonical names:
70
+ ```json
71
+ {"felipe": "Felipe Rosa", "filipexyz": "Felipe Rosa", "genie": "Genie"}
72
+ ```
73
+ Edit this file to add new aliases. Used by `generate-velocity.py` and `generate-readme-hero.py`.
17
74
 
18
75
  ## Commit Convention
19
76
 
20
77
  ```
21
- chore: update live metrics (X/day, Yh avg, Z% SHIP)
78
+ chore: update live metrics (N commits, N releases, +N/-N LoC)
22
79
  ```
23
80
 
24
81
  Push to `dev` branch only. Never push to `main`.
25
82
 
26
- ## State Files
83
+ ## Error Handling
27
84
 
28
- - `state.json` last successful metrics run
29
- - `runs.jsonl` — append-only log of every run
85
+ | Condition | Behavior |
86
+ |-----------|----------|
87
+ | `git` unavailable | `collect-stats.sh` exits 1, pipeline aborts |
88
+ | `daily-stats.jsonl` missing | Backfill runs automatically |
89
+ | `daily-stats.jsonl` < 30 entries | Backfill runs automatically |
90
+ | Chart generation fails | Pipeline aborts with error in `runs.jsonl` |
91
+ | README missing METRICS markers | `generate-readme-hero.py` exits 1 |
92
+ | `author-aliases.json` missing | Aliases ignored, raw git names used |
93
+ | No changes to commit | Commit step skipped, run logged as success |
94
+ | Push fails | Logged as error, run continues |
95
+
96
+ ## Backfill Decision Tree
97
+
98
+ ```
99
+ daily-stats.jsonl exists?
100
+ NO → backfill.sh (creates file with 30 days)
101
+ YES → count entries
102
+ < 30 → backfill.sh (rebuilds with 30 days)
103
+ >= 30 → skip backfill, append today only
104
+ ```
105
+
106
+ ## Self-Diagnosis Checklist
107
+
108
+ If output looks wrong, check in order:
109
+
110
+ 1. **No commits showing**: Run `git log --all --oneline | head -5` — is git history accessible?
111
+ 2. **0 releases**: Run `git tag -l "v4.*" | head -5` — are v4.YYMMDD.* tags present?
112
+ 3. **Stale data**: Check `daily-stats.jsonl` dates — is today's entry present?
113
+ 4. **Wrong contributor names**: Check `author-aliases.json` — missing alias?
114
+ 5. **Charts empty**: Check `daily-stats.jsonl` — are values all zero?
115
+ 6. **README not updated**: Check for `<!-- METRICS:START -->` and `<!-- METRICS:END -->` markers.
116
+ 7. **State not persisted**: Check `state.json` — does it have `daily_stats_count`?
117
+
118
+ ## State Schema
119
+
120
+ ### state.json
121
+ ```json
122
+ {
123
+ "last_run": "2026-04-12T00:00:00Z",
124
+ "last_run_status": "success",
125
+ "daily_stats_count": 31,
126
+ "charts_generated": 3,
127
+ "velocity_md_updated": true,
128
+ "duration_ms": 12345
129
+ }
130
+ ```
131
+
132
+ ### runs.jsonl (each line)
133
+ ```json
134
+ {
135
+ "timestamp": "2026-04-12T00:00:00Z",
136
+ "duration_ms": 12345,
137
+ "status": "success",
138
+ "dry_run": false,
139
+ "daily_stats_count": 31,
140
+ "charts_generated": 3,
141
+ "velocity_md_updated": true,
142
+ "errors": [],
143
+ "steps": [{"name": "collect_stats", "duration_ms": 500}, ...]
144
+ }
145
+ ```
@@ -0,0 +1,9 @@
1
+ {
2
+ "felipe": "Felipe Rosa",
3
+ "filipexyz": "Felipe Rosa",
4
+ "genie": "Genie",
5
+ "Automagik Genie 🧞": "Genie",
6
+ "Luís": "Luís Sousa",
7
+ "Test": "Test User",
8
+ "clawtom": "clawtom"
9
+ }
@@ -0,0 +1,33 @@
1
+ {"date":"2026-03-13","commits":13,"loc_added":6920,"loc_removed":2948,"releases":0,"contributors":["Genie","Test","Test User"]}
2
+ {"date":"2026-03-14","commits":45,"loc_added":2761,"loc_removed":5243,"releases":0,"contributors":["Felipe Rosa","Test User","github-actions[bot]"]}
3
+ {"date":"2026-03-15","commits":14,"loc_added":3552,"loc_removed":3096,"releases":0,"contributors":["Automagik Genie 🧞","Felipe Rosa","Test User","github-actions[bot]"]}
4
+ {"date":"2026-03-16","commits":82,"loc_added":11464,"loc_removed":10990,"releases":0,"contributors":["Felipe Rosa","Test User","clawtom","github-actions[bot]"]}
5
+ {"date":"2026-03-17","commits":86,"loc_added":6482,"loc_removed":1928,"releases":0,"contributors":["Felipe Rosa","Genie","Test","Test User","github-actions[bot]"]}
6
+ {"date":"2026-03-18","commits":41,"loc_added":789,"loc_removed":4077,"releases":0,"contributors":["Felipe Rosa","Genie","github-actions[bot]"]}
7
+ {"date":"2026-03-19","commits":5,"loc_added":1183,"loc_removed":202,"releases":0,"contributors":["Felipe Rosa","Genie","Luís","github-actions[bot]"]}
8
+ {"date":"2026-03-20","commits":15,"loc_added":10671,"loc_removed":234,"releases":0,"contributors":["Felipe Rosa","Genie","Luís Sousa"]}
9
+ {"date":"2026-03-21","commits":8,"loc_added":609,"loc_removed":436,"releases":0,"contributors":["Felipe Rosa","Genie","Luís Sousa"]}
10
+ {"date":"2026-03-22","commits":46,"loc_added":4065,"loc_removed":1256,"releases":0,"contributors":["Felipe Rosa","Genie","Luís Sousa"]}
11
+ {"date":"2026-03-23","commits":33,"loc_added":7093,"loc_removed":2792,"releases":5,"contributors":["Felipe Rosa","Genie","github-actions[bot]"]}
12
+ {"date":"2026-03-24","commits":66,"loc_added":6248,"loc_removed":888,"releases":20,"contributors":["Automagik Genie 🧞","Felipe Rosa","Genie","github-actions[bot]"]}
13
+ {"date":"2026-03-25","commits":133,"loc_added":16626,"loc_removed":3927,"releases":34,"contributors":["Automagik Genie 🧞","Claude","Felipe Rosa","Genie","github-actions[bot]"]}
14
+ {"date":"2026-03-26","commits":17,"loc_added":627,"loc_removed":177,"releases":3,"contributors":["Felipe Rosa","Genie","github-actions[bot]"]}
15
+ {"date":"2026-03-27","commits":32,"loc_added":6836,"loc_removed":5629,"releases":7,"contributors":["Felipe Rosa","Genie","Luís","github-actions[bot]"]}
16
+ {"date":"2026-03-28","commits":42,"loc_added":23738,"loc_removed":2763,"releases":15,"contributors":["Claude","Felipe Rosa","Genie","Luís","github-actions[bot]"]}
17
+ {"date":"2026-03-29","commits":84,"loc_added":12534,"loc_removed":2379,"releases":33,"contributors":["Claude","Felipe Rosa","Genie","github-actions[bot]"]}
18
+ {"date":"2026-03-30","commits":100,"loc_added":5574,"loc_removed":2602,"releases":26,"contributors":["Claude","Felipe Rosa","Genie","github-actions[bot]"]}
19
+ {"date":"2026-03-31","commits":76,"loc_added":4503,"loc_removed":5663,"releases":20,"contributors":["Cezar Vasconcelos","Claude","Felipe Rosa","Genie","Luís","Luís Sousa","github-actions[bot]"]}
20
+ {"date":"2026-04-01","commits":58,"loc_added":3754,"loc_removed":842,"releases":6,"contributors":["Claude","Felipe Rosa","Genie","Test","github-actions[bot]"]}
21
+ {"date":"2026-04-02","commits":84,"loc_added":3484,"loc_removed":1136,"releases":20,"contributors":["Claude","Felipe Rosa","Genie","Luís Sousa","Test","github-actions[bot]"]}
22
+ {"date":"2026-04-03","commits":42,"loc_added":2187,"loc_removed":338,"releases":4,"contributors":["Automagik Genie 🧞","Claude","Felipe Rosa","Genie","Test","Zakir Jiwani","github-actions[bot]"]}
23
+ {"date":"2026-04-04","commits":58,"loc_added":10390,"loc_removed":649,"releases":2,"contributors":["Claude","Felipe Rosa","Genie","Test","filipexyz","github-actions[bot]"]}
24
+ {"date":"2026-04-05","commits":109,"loc_added":22624,"loc_removed":2611,"releases":16,"contributors":["Claude","Felipe Rosa","Test","github-actions[bot]"]}
25
+ {"date":"2026-04-06","commits":28,"loc_added":1745,"loc_removed":225,"releases":5,"contributors":["Claude","Felipe Rosa","Test","github-actions[bot]"]}
26
+ {"date":"2026-04-07","commits":35,"loc_added":3865,"loc_removed":208,"releases":7,"contributors":["Cezar Vasconcelos","Claude","Felipe Rosa","Genie","Test","github-actions[bot]"]}
27
+ {"date":"2026-04-08","commits":35,"loc_added":3780,"loc_removed":2109,"releases":5,"contributors":["Claude","Felipe Rosa","Genie","filipexyz","genie","github-actions[bot]"]}
28
+ {"date":"2026-04-09","commits":73,"loc_added":9761,"loc_removed":1253,"releases":14,"contributors":["Felipe Rosa","Genie","Luís","Test","filipexyz","genie","github-actions[bot]"]}
29
+ {"date":"2026-04-10","commits":37,"loc_added":34969,"loc_removed":205,"releases":5,"contributors":["Felipe Rosa","Genie","Test","felipe","github-actions[bot]"]}
30
+ {"date":"2026-04-11","commits":12,"loc_added":81,"loc_removed":38,"releases":3,"contributors":["Claude","Felipe Rosa","genie","github-actions[bot]"]}
31
+ {"date":"2026-04-12","commits":14,"loc_added":2754,"loc_removed":3653,"releases":1,"contributors":["Claude","Felipe Rosa","Test","genie","github-actions[bot]"]}
32
+ {"date":"2026-04-13","commits":14,"loc_added":668,"loc_removed":70,"releases":0,"contributors":["Claude","Felipe Rosa","genie","github-actions[bot]"]}
33
+ {"date":"2026-04-14","commits":0,"loc_added":0,"loc_removed":0,"releases":0,"contributors":[]}
@@ -22,3 +22,9 @@
22
22
  {"timestamp":"2026-04-07T12:04:30Z","duration_ms":27000,"api_calls":0,"tools_generated":0,"errors":["gh CLI not available","no GitHub token or MCP GitHub tools present in session"],"status":"failed","fallback":false,"metrics":null}
23
23
  {"timestamp":"2026-04-08T00:00:00.000Z","duration_ms":18000,"api_calls":0,"tools_generated":0,"errors":["gh CLI not available","GitHub MCP tools not present in session","fallback data older than current README — README update skipped"],"status":"failed","fallback":false,"metrics":null}
24
24
  {"timestamp":"2026-04-11T12:35:40Z","duration_ms":72153,"api_calls":0,"tools_generated":0,"errors":["gh CLI not available","GitHub MCP tools not present in session"],"status":"success","fallback":true,"metrics":{"releases_24h":2,"merged_prs_7d":35,"avg_merge_time_h":0.3,"ship_rate_pct":85}}
25
+ {"timestamp":"2026-04-12T00:00:00.000Z","duration_ms":30000,"api_calls":2,"tools_generated":0,"errors":[],"status":"success","fallback":false,"metrics":{"releases_24h":0,"merged_prs_7d":5,"avg_merge_time_h":2.5,"ship_rate_pct":100}}
26
+ {"timestamp": "2026-04-12T18:38:51Z", "duration_ms": 732, "status": "success", "dry_run": true, "daily_stats_count": 31, "charts_generated": 3, "velocity_md_updated": true, "errors": [], "steps": [{"name": "collect_stats", "duration_ms": 93}, {"name": "backfill_check", "duration_ms": 22}, {"name": "generate_charts", "duration_ms": 62}, {"name": "generate_velocity", "duration_ms": 336}, {"name": "generate_readme", "duration_ms": 66}, {"name": "commit_push", "duration_ms": 19}]}
27
+ {"timestamp": "2026-04-12T19:41:00Z", "duration_ms": 817, "status": "success", "dry_run": true, "daily_stats_count": 31, "charts_generated": 3, "velocity_md_updated": true, "errors": [], "steps": [{"name": "collect_stats", "duration_ms": 110}, {"name": "backfill_check", "duration_ms": 22}, {"name": "generate_charts", "duration_ms": 68}, {"name": "generate_velocity", "duration_ms": 372}, {"name": "generate_readme", "duration_ms": 73}, {"name": "commit_push", "duration_ms": 20}]}
28
+ {"timestamp":"2026-04-13T00:00:00.000Z","duration_ms":45000,"api_calls":2,"tools_generated":0,"errors":[],"status":"success","fallback":false,"metrics":{"releases_24h":0,"merged_prs_7d":9,"avg_merge_time_h":1.4,"ship_rate_pct":100}}
29
+ {"timestamp": "2026-04-14T12:09:00Z", "duration_ms": 1264, "status": "success", "dry_run": true, "daily_stats_count": 33, "charts_generated": 3, "velocity_md_updated": true, "errors": [], "steps": [{"name": "collect_stats", "duration_ms": 216}, {"name": "backfill_check", "duration_ms": 47}, {"name": "generate_charts", "duration_ms": 158}, {"name": "generate_velocity", "duration_ms": 359}, {"name": "generate_readme", "duration_ms": 159}, {"name": "commit_push", "duration_ms": 33}]}
30
+ {"timestamp": "2026-04-14T12:09:00Z", "duration_ms": 1576, "api_calls": 2, "tools_generated": 3, "errors": [], "metrics": {"releases_24h": 1, "merged_prs_7d": 12, "avg_merge_time_h": 10.4, "ship_rate_pct": 100, "git_commits_7d": 185, "git_releases_7d": 28, "git_loc_net_7d": 44700, "git_contributors_7d": 6, "daily_stats_count": 33, "charts_generated": 3}}
@@ -1,10 +1,14 @@
1
1
  {
2
- "last_run": "2026-04-11T12:35:40.000Z",
2
+ "last_run": "2026-04-14T12:09:00Z",
3
3
  "last_run_status": "success",
4
4
  "last_metrics": {
5
- "releases_24h": 2,
6
- "merged_prs_7d": 35,
7
- "avg_merge_time_h": 0.3,
8
- "ship_rate_pct": 85
9
- }
5
+ "releases_24h": 1,
6
+ "merged_prs_7d": 12,
7
+ "avg_merge_time_h": 10.4,
8
+ "ship_rate_pct": 100
9
+ },
10
+ "daily_stats_count": 33,
11
+ "charts_generated": 3,
12
+ "velocity_md_updated": true,
13
+ "duration_ms": 1576
10
14
  }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # backfill.sh — Run collect-stats.sh for the last 30 days, write daily-stats.jsonl.
3
+ #
4
+ # Usage:
5
+ # backfill.sh [--days N] Backfill N days (default: 30)
6
+ #
7
+ # Output: daily-stats.jsonl in the metrics-updater agent directory.
8
+ #
9
+ # daily-stats.jsonl schema (one JSON object per line):
10
+ # {
11
+ # "date": "YYYY-MM-DD", // Calendar date
12
+ # "commits": <int>, // Commit count across all branches
13
+ # "loc_added": <int>, // Lines of code added
14
+ # "loc_removed": <int>, // Lines of code removed
15
+ # "releases": <int>, // Release tags (v4.YYMMDD.*) for this date
16
+ # "contributors": [<string>] // Unique author names for this date
17
+ # }
18
+
19
+ set -euo pipefail
20
+
21
+ cd "$(git rev-parse --show-toplevel)"
22
+
23
+ DAYS=30
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --days)
27
+ DAYS="$2"
28
+ shift 2
29
+ ;;
30
+ *)
31
+ echo "Usage: $0 [--days N]" >&2
32
+ exit 1
33
+ ;;
34
+ esac
35
+ done
36
+
37
+ AGENT_DIR=".genie/agents/metrics-updater"
38
+ TOOLS_DIR="$AGENT_DIR/tools"
39
+ OUTPUT="$AGENT_DIR/daily-stats.jsonl"
40
+
41
+ # Clear existing file
42
+ > "$OUTPUT"
43
+
44
+ echo "Backfilling $DAYS days of stats..." >&2
45
+
46
+ for i in $(seq "$DAYS" -1 0); do
47
+ target_date=$(date -d "today - ${i} days" +%Y-%m-%d 2>/dev/null || date -v-${i}d +%Y-%m-%d 2>/dev/null)
48
+ result=$(bash "$TOOLS_DIR/collect-stats.sh" --date "$target_date")
49
+ echo "$result" >> "$OUTPUT"
50
+ echo " $target_date: done" >&2
51
+ done
52
+
53
+ lines=$(wc -l < "$OUTPUT")
54
+ echo "Backfill complete: $lines entries in $OUTPUT" >&2
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env bash
2
+ # collect-stats.sh — Extract daily metrics from git history across all branches.
3
+ #
4
+ # Usage:
5
+ # collect-stats.sh --date YYYY-MM-DD Output JSON for a single day
6
+ # collect-stats.sh --cumulative Output all-time cumulative JSON
7
+ #
8
+ # Output schema (daily):
9
+ # {
10
+ # "date": "YYYY-MM-DD",
11
+ # "commits": <int>,
12
+ # "loc_added": <int>,
13
+ # "loc_removed": <int>,
14
+ # "releases": <int>,
15
+ # "contributors": ["name1", "name2", ...]
16
+ # }
17
+ #
18
+ # Output schema (cumulative):
19
+ # {
20
+ # "total_commits": <int>,
21
+ # "total_tags": <int>,
22
+ # "first_commit_date": "YYYY-MM-DD",
23
+ # "total_contributors": <int>
24
+ # }
25
+
26
+ set -euo pipefail
27
+
28
+ # Navigate to repo root
29
+ cd "$(git rev-parse --show-toplevel)"
30
+
31
+ MODE=""
32
+ TARGET_DATE=""
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --date)
37
+ MODE="daily"
38
+ TARGET_DATE="$2"
39
+ shift 2
40
+ ;;
41
+ --cumulative)
42
+ MODE="cumulative"
43
+ shift
44
+ ;;
45
+ *)
46
+ echo "Usage: $0 --date YYYY-MM-DD | --cumulative" >&2
47
+ exit 1
48
+ ;;
49
+ esac
50
+ done
51
+
52
+ if [[ -z "$MODE" ]]; then
53
+ echo "Usage: $0 --date YYYY-MM-DD | --cumulative" >&2
54
+ exit 1
55
+ fi
56
+
57
+ if [[ "$MODE" == "cumulative" ]]; then
58
+ total_commits=$(git log --all --oneline | wc -l)
59
+ total_tags=$(git tag -l "v4.*" | wc -l)
60
+ first_commit_date=$(git log --all --reverse --format='%aI' -- | head -1 | cut -d'T' -f1 || true)
61
+ total_contributors=$(git log --all --format='%aN' | sort -u | wc -l)
62
+
63
+ printf '{"total_commits":%d,"total_tags":%d,"first_commit_date":"%s","total_contributors":%d}\n' \
64
+ "$total_commits" "$total_tags" "$first_commit_date" "$total_contributors"
65
+ exit 0
66
+ fi
67
+
68
+ # Daily mode
69
+ AFTER="${TARGET_DATE} 00:00:00"
70
+ BEFORE="${TARGET_DATE} 23:59:59"
71
+
72
+ # Pathspec exclusions — keep LoC focused on real source code.
73
+ # Drops vendored deps, build outputs, lockfiles, minified bundles,
74
+ # orphaned worktree artifacts, and the agent's own generated assets.
75
+ # Without these, a single cleanup commit (e.g. 9bf66347 on 2026-03-14
76
+ # which deleted 524k lines of claude worktree artifacts) can dominate
77
+ # an entire 30d window and destroy the signal.
78
+ EXCLUDE_PATHS=(
79
+ ':(exclude,glob)node_modules/**'
80
+ ':(exclude,glob)**/node_modules/**'
81
+ ':(exclude,glob).claude/worktrees/**'
82
+ ':(exclude,glob).genie/worktrees/**'
83
+ ':(exclude,glob).worktrees/**'
84
+ ':(exclude,glob)dist/**'
85
+ ':(exclude,glob)**/dist/**'
86
+ ':(exclude,glob)build/**'
87
+ ':(exclude,glob)**/build/**'
88
+ ':(exclude,glob).cache/**'
89
+ ':(exclude,glob)**/*.lock'
90
+ ':(exclude,glob)**/*.lockb'
91
+ ':(exclude,glob)**/package-lock.json'
92
+ ':(exclude,glob)**/*.min.js'
93
+ ':(exclude,glob)**/*.min.css'
94
+ ':(exclude,glob).genie/assets/**'
95
+ )
96
+
97
+ # Commits count (all branches, deduplicated)
98
+ commits=$(git log --all --after="$AFTER" --before="$BEFORE" --oneline | wc -l)
99
+
100
+ # LoC added/removed via shortstat (source files only — see EXCLUDE_PATHS)
101
+ loc_stats=$(git log --all --after="$AFTER" --before="$BEFORE" --shortstat --format="" -- . "${EXCLUDE_PATHS[@]}" | \
102
+ awk '
103
+ /insertion/ {
104
+ for (i=1; i<=NF; i++) {
105
+ if ($(i+1) ~ /insertion/) added += $i
106
+ if ($(i+1) ~ /deletion/) removed += $i
107
+ }
108
+ }
109
+ END { printf "%d %d", added+0, removed+0 }
110
+ ')
111
+ loc_added=$(echo "$loc_stats" | awk '{print $1}')
112
+ loc_removed=$(echo "$loc_stats" | awk '{print $2}')
113
+
114
+ # Release count: tags matching v4.YYMMDD.* for this date
115
+ # Convert YYYY-MM-DD to YYMMDD for tag pattern
116
+ tag_date=$(date -d "$TARGET_DATE" +%y%m%d 2>/dev/null || date -j -f "%Y-%m-%d" "$TARGET_DATE" +%y%m%d 2>/dev/null)
117
+ releases=$(git tag -l "v4.${tag_date}.*" | wc -l)
118
+
119
+ # Contributors (unique author names)
120
+ contributors_json=$(git log --all --after="$AFTER" --before="$BEFORE" --format='%aN' | sort -u | \
121
+ awk 'BEGIN { printf "[" }
122
+ NR>1 { printf "," }
123
+ { gsub(/"/, "\\\""); printf "\"%s\"", $0 }
124
+ END { printf "]" }')
125
+
126
+ # Handle empty contributors
127
+ if [[ "$contributors_json" == "[]" ]] || [[ -z "$contributors_json" ]]; then
128
+ contributors_json="[]"
129
+ fi
130
+
131
+ printf '{"date":"%s","commits":%d,"loc_added":%d,"loc_removed":%d,"releases":%d,"contributors":%s}\n' \
132
+ "$TARGET_DATE" "$commits" "$loc_added" "$loc_removed" "$releases" "$contributors_json"