git-impact 0.3.0 → 0.6.3

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 (71) hide show
  1. package/README.md +125 -78
  2. package/dist/cli/index.js +32 -211
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/init/installer.d.ts +6 -0
  5. package/dist/init/installer.d.ts.map +1 -1
  6. package/dist/init/installer.js +58 -5
  7. package/dist/init/installer.js.map +1 -1
  8. package/dist/init/installer.test.d.ts +2 -0
  9. package/dist/init/installer.test.d.ts.map +1 -0
  10. package/dist/init/installer.test.js +127 -0
  11. package/dist/init/installer.test.js.map +1 -0
  12. package/dist/init/templates.d.ts +8 -7
  13. package/dist/init/templates.d.ts.map +1 -1
  14. package/dist/init/templates.js +73 -371
  15. package/dist/init/templates.js.map +1 -1
  16. package/dist/mcp/repo.d.ts +8 -3
  17. package/dist/mcp/repo.d.ts.map +1 -1
  18. package/dist/mcp/repo.js +27 -9
  19. package/dist/mcp/repo.js.map +1 -1
  20. package/dist/mcp/repo.test.d.ts +2 -0
  21. package/dist/mcp/repo.test.d.ts.map +1 -0
  22. package/dist/mcp/repo.test.js +133 -0
  23. package/dist/mcp/repo.test.js.map +1 -0
  24. package/dist/mcp/resources.js +0 -1
  25. package/dist/mcp/resources.js.map +1 -1
  26. package/dist/mcp/tools.d.ts.map +1 -1
  27. package/dist/mcp/tools.js +100 -12
  28. package/dist/mcp/tools.js.map +1 -1
  29. package/dist/mcp/tools.test.d.ts +2 -0
  30. package/dist/mcp/tools.test.d.ts.map +1 -0
  31. package/dist/mcp/tools.test.js +123 -0
  32. package/dist/mcp/tools.test.js.map +1 -0
  33. package/dist/readers/git.d.ts +2 -1
  34. package/dist/readers/git.d.ts.map +1 -1
  35. package/dist/readers/git.js +12 -6
  36. package/dist/readers/git.js.map +1 -1
  37. package/dist/readers/redact.d.ts +29 -0
  38. package/dist/readers/redact.d.ts.map +1 -0
  39. package/dist/readers/redact.js +82 -0
  40. package/dist/readers/redact.js.map +1 -0
  41. package/dist/readers/redact.test.d.ts +2 -0
  42. package/dist/readers/redact.test.d.ts.map +1 -0
  43. package/dist/readers/redact.test.js +72 -0
  44. package/dist/readers/redact.test.js.map +1 -0
  45. package/dist/report/html.d.ts +2 -0
  46. package/dist/report/html.d.ts.map +1 -1
  47. package/dist/report/html.js +35 -1
  48. package/dist/report/html.js.map +1 -1
  49. package/dist/report/render.d.ts.map +1 -1
  50. package/dist/report/render.js +2 -0
  51. package/dist/report/render.js.map +1 -1
  52. package/dist/storage/db.d.ts +23 -2
  53. package/dist/storage/db.d.ts.map +1 -1
  54. package/dist/storage/db.js +12 -0
  55. package/dist/storage/db.js.map +1 -1
  56. package/dist/storage/db.test.d.ts +2 -0
  57. package/dist/storage/db.test.d.ts.map +1 -0
  58. package/dist/storage/db.test.js +152 -0
  59. package/dist/storage/db.test.js.map +1 -0
  60. package/package.json +7 -6
  61. package/skill/SKILL.md +142 -205
  62. package/skill/references/html-template.md +131 -0
  63. package/skill/references/translation-rules.md +87 -0
  64. package/dist/translator/prompt.d.ts +0 -21
  65. package/dist/translator/prompt.d.ts.map +0 -1
  66. package/dist/translator/prompt.js +0 -117
  67. package/dist/translator/prompt.js.map +0 -1
  68. package/dist/translator/translate.d.ts +0 -36
  69. package/dist/translator/translate.d.ts.map +0 -1
  70. package/dist/translator/translate.js +0 -73
  71. package/dist/translator/translate.js.map +0 -1
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const db_1 = require("./db");
41
+ let tmp;
42
+ (0, vitest_1.beforeEach)(() => {
43
+ tmp = fs.mkdtempSync(path.join(os.tmpdir(), "git-impact-test-"));
44
+ });
45
+ (0, vitest_1.afterEach)(() => {
46
+ fs.rmSync(tmp, { recursive: true, force: true });
47
+ });
48
+ (0, vitest_1.describe)("context.json round-trip", () => {
49
+ (0, vitest_1.it)("saves and loads context", () => {
50
+ (0, db_1.saveContext)({
51
+ companyDescription: "Test Co",
52
+ managerPriorities: "Ship fast",
53
+ glossary: { RLS: "data security" },
54
+ }, tmp);
55
+ const loaded = (0, db_1.loadContext)(tmp);
56
+ (0, vitest_1.expect)(loaded?.companyDescription).toBe("Test Co");
57
+ (0, vitest_1.expect)(loaded?.glossary.RLS).toBe("data security");
58
+ });
59
+ (0, vitest_1.it)("returns null when no context file exists", () => {
60
+ (0, vitest_1.expect)((0, db_1.loadContext)(tmp)).toBeNull();
61
+ });
62
+ (0, vitest_1.it)("preserves privacy config", () => {
63
+ (0, db_1.saveContext)({
64
+ companyDescription: "X",
65
+ managerPriorities: "Y",
66
+ glossary: {},
67
+ privacy: { redact: false, filePatterns: ["*.internal"] },
68
+ }, tmp);
69
+ const loaded = (0, db_1.loadContext)(tmp);
70
+ (0, vitest_1.expect)(loaded?.privacy?.redact).toBe(false);
71
+ (0, vitest_1.expect)(loaded?.privacy?.filePatterns).toEqual(["*.internal"]);
72
+ });
73
+ });
74
+ (0, vitest_1.describe)("impact entries", () => {
75
+ const item = (over = {}) => ({
76
+ status: "done",
77
+ summary: "Shipped X",
78
+ impact: "Unblocked Y",
79
+ provenance: "pr",
80
+ ...over,
81
+ });
82
+ (0, vitest_1.it)("getLastEntryDate returns null when empty", () => {
83
+ (0, vitest_1.expect)((0, db_1.getLastEntryDate)(tmp)).toBeNull();
84
+ });
85
+ (0, vitest_1.it)("saves an entry and getLastEntryDate finds it", () => {
86
+ (0, db_1.saveEntry)({
87
+ date: "2026-05-09",
88
+ repoPath: tmp,
89
+ repoName: "test",
90
+ totalCommits: 1,
91
+ totalFiles: 1,
92
+ filesSummary: "src",
93
+ items: [item()],
94
+ rawJson: "[]",
95
+ createdAt: new Date().toISOString(),
96
+ }, tmp);
97
+ (0, vitest_1.expect)((0, db_1.getLastEntryDate)(tmp)).toBe("2026-05-09");
98
+ });
99
+ (0, vitest_1.it)("getLastEntryDate returns the most recent across many entries", () => {
100
+ const dates = ["2026-05-01", "2026-05-09", "2026-05-05"];
101
+ for (const date of dates) {
102
+ (0, db_1.saveEntry)({
103
+ date,
104
+ repoPath: tmp,
105
+ repoName: "r",
106
+ totalCommits: 1,
107
+ totalFiles: 1,
108
+ filesSummary: "",
109
+ items: [item()],
110
+ rawJson: "[]",
111
+ createdAt: new Date().toISOString(),
112
+ }, tmp);
113
+ }
114
+ (0, vitest_1.expect)((0, db_1.getLastEntryDate)(tmp)).toBe("2026-05-09");
115
+ });
116
+ (0, vitest_1.it)("preserves provenance and refs through save → get", () => {
117
+ (0, db_1.saveEntry)({
118
+ date: "2026-05-09",
119
+ repoPath: tmp,
120
+ repoName: "r",
121
+ totalCommits: 1,
122
+ totalFiles: 1,
123
+ filesSummary: "",
124
+ items: [
125
+ item({ provenance: "inferred", refs: ["PR #42", "ENG-1234"] }),
126
+ ],
127
+ rawJson: "[]",
128
+ createdAt: new Date().toISOString(),
129
+ }, tmp);
130
+ const entries = (0, db_1.getEntriesForRange)("2026-05-09", "2026-05-09", tmp);
131
+ (0, vitest_1.expect)(entries[0].items[0].provenance).toBe("inferred");
132
+ (0, vitest_1.expect)(entries[0].items[0].refs).toEqual(["PR #42", "ENG-1234"]);
133
+ });
134
+ (0, vitest_1.it)("getEntriesForDaysAgo returns recent entries", () => {
135
+ const today = new Date().toISOString().slice(0, 10);
136
+ (0, db_1.saveEntry)({
137
+ date: today,
138
+ repoPath: tmp,
139
+ repoName: "r",
140
+ totalCommits: 1,
141
+ totalFiles: 1,
142
+ filesSummary: "",
143
+ items: [item()],
144
+ rawJson: "[]",
145
+ createdAt: new Date().toISOString(),
146
+ }, tmp);
147
+ const entries = (0, db_1.getEntriesForDaysAgo)(7, tmp);
148
+ (0, vitest_1.expect)(entries).toHaveLength(1);
149
+ (0, vitest_1.expect)(entries[0].date).toBe(today);
150
+ });
151
+ });
152
+ //# sourceMappingURL=db.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.test.js","sourceRoot":"","sources":["../../src/storage/db.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "git-impact",
3
- "version": "0.3.0",
4
- "description": "Translate your git commits into plain-English business impact",
3
+ "version": "0.6.3",
4
+ "description": "Translate your git commits into plain-English business impact — runs inside your AI editor, no API key.",
5
5
  "author": "Efraim Nafady",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -25,7 +25,9 @@
25
25
  "prepublishOnly": "npm run build",
26
26
  "dev": "ts-node src/cli/index.ts",
27
27
  "start": "node dist/cli/index.js",
28
- "mcp": "node dist/mcp/server.js"
28
+ "mcp": "node dist/mcp/server.js",
29
+ "test": "vitest run",
30
+ "test:watch": "vitest"
29
31
  },
30
32
  "keywords": [
31
33
  "git",
@@ -38,18 +40,17 @@
38
40
  ],
39
41
  "type": "commonjs",
40
42
  "dependencies": {
41
- "@anthropic-ai/sdk": "^0.95.0",
42
43
  "@modelcontextprotocol/sdk": "^1.29.0",
43
44
  "@octokit/rest": "^22.0.1",
44
45
  "better-sqlite3": "^12.9.0",
45
46
  "commander": "^14.0.3",
46
- "conf": "^15.1.0",
47
47
  "simple-git": "^3.36.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/better-sqlite3": "^7.6.13",
51
51
  "@types/node": "^25.6.0",
52
52
  "ts-node": "^10.9.2",
53
- "typescript": "^6.0.3"
53
+ "typescript": "^6.0.3",
54
+ "vitest": "^2.1.9"
54
55
  }
55
56
  }
package/skill/SKILL.md CHANGED
@@ -1,278 +1,210 @@
1
1
  ---
2
2
  name: git-impact
3
3
  description: >
4
- Translates git commits into plain-English business impact bullets ideal for
5
- daily standups, manager updates, and end-of-quarter performance reviews.
6
- Use this skill whenever the user says anything like: "do my standup",
7
- "what did I work on today", "translate my commits", "summarize my git activity",
8
- "write my standup update", "what should I say in standup", "git-impact",
9
- "/git-impact", "show my impact", "translate today's work", "what did I ship",
10
- "generate a performance review", "what have I done this week/month/quarter",
11
- "review my commits". Also trigger for "since yesterday", "last 3 days commits",
12
- or any request to turn technical git output into something a manager can read.
4
+ Translates git commits into plain-English business impact bullets. Use this
5
+ skill when the user says any of: "do my standup", "/git-impact", "what did I
6
+ ship today", "what did I work on", "translate my commits", "summarize my
7
+ git activity", "write my standup update", "what should I say in standup",
8
+ "show my impact", "since yesterday", "since 3d", "since last standup",
9
+ "what have I done this week / month / quarter", "generate a performance
10
+ review", "performance review prep", "review my commits", "what's blocked",
11
+ "weekly summary". Trigger on any request to turn raw git output into
12
+ something a non-technical manager can read. The skill orchestrates MCP
13
+ tools (get_git_activity, save_impact_entry, get_history, etc.) — it does
14
+ not run sqlite3 or other DB commands directly.
13
15
  ---
14
16
 
15
17
  # git-impact
16
18
 
17
- Translate git commits into plain-English business impact for standups, manager
18
- updates, and performance reviews — without an API key. You do the translation
19
- inline using bash to read git data and a per-repo context file for personalization.
19
+ You translate git commits into plain-English business impact for standups,
20
+ manager updates, and performance review prep. Your job is **orchestration**:
21
+ call the MCP tools provided by the `git-impact` server, apply the translation
22
+ rules, and produce the output. The MCP server owns all data access (git, DB).
23
+ You own the language.
20
24
 
21
- ---
22
-
23
- ## Sub-commands
24
-
25
- Parse the user's message to determine which mode to run:
26
-
27
- | What the user says | Mode |
28
- |---|---|
29
- | `do my standup`, `today`, `/git-impact`, no args | **today** |
30
- | `since yesterday`, `since 3d`, `since 2026-05-01` | **since \<when\>** |
31
- | `review`, `performance review`, `last 30 days`, `Q2 review` | **review** |
32
- | `init`, `set up context`, `configure for this repo` | **init** |
25
+ If the `git-impact` MCP server is not available in this conversation, fall
26
+ back to the bash steps in `references/fallback-bash.md` — but the MCP path
27
+ is preferred everywhere it works.
33
28
 
34
29
  ---
35
30
 
36
- ## Step 1 Find the repo root
31
+ ## Pick a mode from the user's message
37
32
 
38
- Run this to find the git root from wherever you are:
39
-
40
- ```bash
41
- git rev-parse --show-toplevel 2>/dev/null
42
- ```
33
+ | User says | Mode |
34
+ |---|---|
35
+ | "do my standup", "today", "/git-impact", no args | **standup** (default) |
36
+ | "since yesterday", "since 3d", "since 2026-05-01" | **standup** with explicit `since_iso` |
37
+ | "review", "performance review", "last 90 days", "Q2 review" | **review** |
38
+ | "init", "set up context", "configure for this repo" | **init** |
39
+ | "make a presentation", "make a slide", "make me a shareable" | **standup** + **bespoke HTML** (extra step) |
43
40
 
44
- If it fails, tell the user: *"No git repository found in the current directory. Open
45
- a project folder first, or `cd` into a repo."* Stop there.
41
+ The default standup writes to the rolling HTML dashboard at
42
+ `.git-impact/result.html` (regenerated by the report renderer the user
43
+ opens with `git-impact view`). The per-day bespoke HTML is only created
44
+ when the user explicitly asks for a presentation — don't write one by
45
+ default; it's expensive and most days don't need it.
46
46
 
47
- Store the result as `REPO_ROOT`.
47
+ If the message is ambiguous, default to **standup**.
48
48
 
49
49
  ---
50
50
 
51
- ## Step 2 — Load context (if it exists)
51
+ ## Mode: standup
52
52
 
53
- ```bash
54
- cat "$REPO_ROOT/.git-impact/context.json" 2>/dev/null || echo "NONE"
55
- ```
53
+ ### 1. Resolve the lookback window
56
54
 
57
- If the file exists, parse it. It looks like:
55
+ - If the user said an explicit "since X", convert to an ISO timestamp and use it.
56
+ - Otherwise call **`get_last_standup_date`**. If it returns a date, use the
57
+ start of the day after that date as `since_iso`. If it returns null,
58
+ default to start-of-today.
58
59
 
59
- ```json
60
- {
61
- "companyDescription": "B2B SaaS for workforce analytics",
62
- "managerPriorities": "Shipping on time, not breaking prod",
63
- "glossary": {
64
- "RLS": "data security layer",
65
- "TabPFN": "AI predictions",
66
- "MFA": "login security"
67
- }
68
- }
69
- ```
60
+ This is the "since last standup" default — it survives weekends and days off.
70
61
 
71
- Use this to personalise your translation — apply the glossary and frame impact
72
- around what the manager cares about. If the file doesn't exist, use general
73
- technical language and suggest running `init` at the end.
74
-
75
- ---
76
-
77
- ## Mode: today / since \<when\>
78
-
79
- ### Fetch commits
80
-
81
- For **today**:
82
- ```bash
83
- git -C "$REPO_ROOT" log \
84
- --since="$(date '+%Y-%m-%d') 00:00:00" \
85
- --format="%h|%s|%b|%an|%ad" \
86
- --date=short \
87
- HEAD
88
- ```
62
+ ### 2. Fetch git activity
89
63
 
90
- For **since \<when\>** convert the user's input to a git `--since` value:
91
- - `yesterday` `--since="yesterday 00:00:00"`
92
- - `3d` `--since="3 days ago 00:00:00"`
93
- - `2026-05-01` → `--since="2026-05-01 00:00:00"`
64
+ Call **`get_git_activity`** with `since_iso` and (optionally) `until_iso`.
65
+ You'll get back commits, file stats, branch, and `_repo_root` the
66
+ absolute path the MCP server resolved.
94
67
 
95
- ### Fetch files changed
68
+ > **CRITICAL: capture `_repo_root` from this response and pass it as
69
+ > `repo_path` to every subsequent tool call** (`save_impact_entry`,
70
+ > `render_dashboard`, `get_history`). The MCP server caches it after
71
+ > the first successful call, but threading it explicitly is the safe
72
+ > belt-and-suspenders move and removes any chance the second tool call
73
+ > resolves to a different directory.
96
74
 
97
- ```bash
98
- FIRST=$(git -C "$REPO_ROOT" log --since="..." --format="%h" HEAD | tail -1)
99
- git -C "$REPO_ROOT" diff --stat "$FIRST"^ HEAD 2>/dev/null || \
100
- git -C "$REPO_ROOT" show --stat "$FIRST" 2>/dev/null
101
- ```
75
+ If there are no commits in the window: tell the user clearly and stop. Do
76
+ NOT save an empty entry.
102
77
 
103
- ### Translate
78
+ ### 3. Optional: enrich with GitHub PR data
104
79
 
105
- If there are no commits, say so clearly and stop.
80
+ If the user has a `github_token` saved in their context (the
81
+ `get_git_activity` response or a prior `get_github_activity` call will tell
82
+ you), call **`get_github_activity`** to pull PR titles and descriptions.
83
+ PR descriptions are the single best source for accurate "why it matters"
84
+ text — strongly prefer them over inference.
106
85
 
107
- Otherwise translate into **2–5 bullet points**. Follow these rules:
86
+ ### 4. Translate
108
87
 
109
- 1. **What + why, not what + how.** Each bullet must say what was accomplished
110
- AND why it matters to the business. "Fixed a bug in the auth middleware" is
111
- useless to a manager. "Fixed login failures for admin users — unblocks the
112
- Q2 portal launch" is useful.
88
+ Read **`references/translation-rules.md`** for the prompt-engineering details.
89
+ Key rules at a glance:
113
90
 
114
- 2. **Apply the glossary.** Replace every technical term listed in context.json
115
- with its plain-English equivalent. If no glossary, infer from context.
91
+ - **What + why**, not what + how
92
+ - **Apply the glossary** from `context.json` (returned by the context resource
93
+ or visible in `get_git_activity` output)
94
+ - **Provenance is mandatory** — every saved bullet gets `pr` / `commit_body` /
95
+ `commit_message` / `ticket` / `inferred`
96
+ - **Group related commits** into one bullet, not many
97
+ - **2-5 bullets total**, never more
116
98
 
117
- 3. **Never hallucinate impact.** If you can't infer the business reason, say
118
- "technical foundation work for [area]" rather than inventing an outcome.
99
+ ### 5. Print the user-facing output
119
100
 
120
- 4. **Group related commits.** Three commits all touching auth tell one story —
121
- write one bullet, not three.
101
+ ```
102
+ 📅 [Day, Date or date range]
122
103
 
123
- 5. **WIP / draft commits** get `⏳ In progress:` and state what the expected
124
- outcome is, not what was done so far.
104
+ [Summary]
105
+ [Why it matters]
125
106
 
126
- 6. **Be specific.** "Improved performance" is vague. "Reduced login latency
127
- by ~40%" is specific. Use whatever numbers exist in the commit messages.
107
+ In progress: [What]
108
+ [Expected outcome]
128
109
 
129
- ### Output format
110
+ 🚫 Blocked: [What] (only if applicable)
111
+ → [What's needed]
130
112
 
113
+ 📁 [N] files across [areas]
114
+ [N] commit(s) on [branch]
131
115
  ```
132
- 📅 [Day, Date]
133
116
 
134
- [Plain-English summary of what was accomplished]
135
- → [Why it matters — who it unblocks, what risk it reduces, what it enables]
117
+ ### 6. Save to history
136
118
 
137
- In progress: [What is being worked on]
138
- [Expected outcome when complete]
119
+ Call **`save_impact_entry`** with the structured items including
120
+ `provenance` for each. The MCP tool handles the SQLite write — never run
121
+ `sqlite3` directly. Required fields per item: `status`, `summary`,
122
+ `provenance`. Recommended: `impact`, `refs`.
139
123
 
140
- 📁 [N] files changed across [brief description of areas touched]
141
- [N] commit(s) on [branch name]
124
+ Example item:
125
+ ```json
126
+ {
127
+ "status": "done",
128
+ "summary": "Shipped multi-tenant data isolation",
129
+ "impact": "Unblocks SOC2 sign-off, prevents cross-company data leaks",
130
+ "provenance": "pr",
131
+ "refs": ["PR #142", "ENG-1234"]
132
+ }
142
133
  ```
143
134
 
144
- ### Save to history
145
-
146
- After printing the output, silently save to `.git-impact/history.db`:
147
-
148
- ```bash
149
- mkdir -p "$REPO_ROOT/.git-impact"
150
- grep -qxF '.git-impact/history.db' "$REPO_ROOT/.gitignore" 2>/dev/null || \
151
- printf '\n# git-impact local history (private, per-machine)\n.git-impact/history.db\n' \
152
- >> "$REPO_ROOT/.gitignore"
153
-
154
- sqlite3 "$REPO_ROOT/.git-impact/history.db" "
155
- CREATE TABLE IF NOT EXISTS impact_entries (
156
- id INTEGER PRIMARY KEY AUTOINCREMENT,
157
- date TEXT NOT NULL,
158
- repo_name TEXT NOT NULL,
159
- total_commits INTEGER NOT NULL DEFAULT 0,
160
- total_files INTEGER NOT NULL DEFAULT 0,
161
- items_json TEXT NOT NULL DEFAULT '[]',
162
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
163
- );
164
- INSERT INTO impact_entries (date, repo_name, total_commits, total_files, items_json)
165
- VALUES ('$(date +%Y-%m-%d)', '$(basename $REPO_ROOT)', $COMMIT_COUNT, $FILE_COUNT, '$ITEMS_JSON');
166
- "
167
- ```
135
+ ### 7. Regenerate the rolling dashboard, then print its URL
136
+
137
+ Call **`render_dashboard`** with the same `repo_path`. It rewrites
138
+ `.git-impact/result.html` so today's entry is included, and returns
139
+ the `file://` URL.
168
140
 
169
- If `sqlite3` is not available, skip silently.
170
-
171
- ### Build a polished HTML presentation
172
-
173
- After saving to history, **use your Write tool to create a bespoke HTML
174
- presentation** for today's standup. This is not a generic template — each day's
175
- file should be tailored to the actual content, with custom layout, charts, or
176
- diagrams when they help.
177
-
178
- **Where to write:**
179
- - `$REPO_ROOT/.git-impact/standups/YYYY-MM-DD.html` — today's file
180
- - `$REPO_ROOT/.git-impact/standups/index.html` — list of all standups (regenerate it)
181
-
182
- **Stack (all CDN, no install):**
183
- - Tailwind CSS via `<script src="https://cdn.tailwindcss.com">`
184
- - Inter font via Google Fonts
185
- - Chart.js (`https://cdn.jsdelivr.net/npm/chart.js`) only if there are real numbers worth charting
186
- - Mermaid (`https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs`) for flow/architecture diagrams when relevant
187
-
188
- **Required structure:**
189
- 1. **Hero** — date label + a bold, plain-English headline that captures the day
190
- ("Shipped safety analytics, hardened tenant isolation" — not "9 commits")
191
- 2. **Stats grid** — 3-4 metric cards (commits, files, PRs merged, areas touched)
192
- 3. **Achievement cards** — one per ✅ item with status pill, title, summary,
193
- "→ Why it matters" line, and tags (PR #, area, file count)
194
- 4. **Optional visual** — only when the content warrants one (a Mermaid flow
195
- diagram for architecture changes, a Chart.js chart for ratios/comparisons,
196
- a code-style block for a key formula). Skip if forced.
197
- 5. **Footer** — file count, branch, link back to `index.html`
198
-
199
- **Design language:**
200
- - Dark theme by default (`bg-slate-950`), generous spacing, max width 4xl
201
- - Cards: `bg-slate-900/50 border border-slate-800 rounded-2xl p-6`
202
- - Status pills: emerald for done, amber for in-progress, rose for blocked
203
- - Print-friendly via `@media print`
204
-
205
- **Status pill colors:**
206
- - ✅ Shipped → `bg-emerald-500/20 text-emerald-400`
207
- - ⏳ In Progress → `bg-amber-500/20 text-amber-400`
208
- - 🚫 Blocked → `bg-rose-500/20 text-rose-400`
209
-
210
- After writing both files, print the file URL on the very last line of your reply:
141
+ End your reply with that URL on its own last line so the user can
142
+ ⌘-click to open:
211
143
 
212
144
  ```
213
- 🎯 file://$REPO_ROOT/.git-impact/standups/YYYY-MM-DD.html
145
+ 🎯 file:///<absolute-repo-path>/.git-impact/result.html
214
146
  ```
215
147
 
216
- Replace `$REPO_ROOT` with the real absolute path so the user can ⌘-click.
148
+ Don't try to open a browser yourself printing the URL is enough.
149
+
150
+ **Only if the user explicitly asked for a "presentation", "slide",
151
+ "shareable", or "screenshot-friendly" output**, ALSO read
152
+ `references/html-template.md` and use your `Write` tool to create
153
+ `<repo>/.git-impact/standups/YYYY-MM-DD.html` (plus update
154
+ `standups/index.html`). Most days don't need this — skip by default.
217
155
 
218
156
  ---
219
157
 
220
158
  ## Mode: review
221
159
 
222
- Fetch saved history and synthesise a performance review.
223
-
224
- Parse the period from the user's message:
225
- - `last 30 days` / `30d` → last 30 days
226
- - `last 90 days` / `90d` → last 90 days (default)
227
- - `Q2-2026` April 1 June 30, 2026
228
-
229
- ```bash
230
- sqlite3 "$REPO_ROOT/.git-impact/history.db" \
231
- "SELECT date, repo_name, total_commits, items_json
232
- FROM impact_entries
233
- WHERE date >= '$FROM' AND date <= '$TO'
234
- ORDER BY date ASC;" 2>/dev/null
160
+ 1. Parse the period from the user's message:
161
+ - "last 30 days" / "30d" → `last_days: 30`
162
+ - "last 90 days" / "90d" / no arg → `last_days: 90`
163
+ - "Q2-2026" `from_date: 2026-04-01`, `to_date: 2026-06-30`
164
+ 2. Call **`get_history`** with the parsed window.
165
+ 3. If no entries returned: tell the user
166
+ *"No saved history yet for this period. Use the standup mode daily to
167
+ build up history, then come back."* and stop.
168
+ 4. Synthesise themes (Features, Reliability, Security, Code review,
169
+ Infrastructure). Only include themes that apply.
170
+ 5. Frame as **performance review prep** (evidence pack), not a finished
171
+ review bullets should include dates and refs the user can paste into
172
+ their own writing.
173
+
174
+ Output:
235
175
  ```
176
+ Performance Review Prep — [Period]
236
177
 
237
- If the DB doesn't exist or returns nothing: *"No saved history found for this
238
- period. Use the standup mode daily to build up history, then come back."*
239
-
240
- Otherwise synthesise:
241
-
242
- ```
243
- Performance Review — [Period]
244
-
245
- [One headline sentence — biggest contribution this period]
178
+ [One headline sentence biggest contribution]
246
179
 
247
180
  🚀 [High-impact theme]
248
- • Specific achievement...
181
+ • Specific achievement [date, refs]
249
182
 
250
183
  ✅ [Medium-impact theme]
251
- Specific achievement...
184
+ • ...
252
185
 
253
186
  🔧 [Lower-impact theme]
254
187
  • ...
255
188
 
256
- ---
257
189
  📊 [N] commits across [N] working days
258
190
  ```
259
191
 
260
- Group by theme (Features shipped, Reliability, Security, Code review,
261
- Infrastructure). Only include themes that apply.
262
-
263
192
  ---
264
193
 
265
194
  ## Mode: init
266
195
 
267
- Ask the user three questions one at a time:
196
+ Ask one at a time:
268
197
 
269
198
  1. *"What does your company/product do? (1–2 sentences)"*
270
199
  2. *"What does your manager care most about?"*
271
- 3. *"Any technical terms to translate? Format: RLS=data security, MFA=login security (leave blank to skip)"*
200
+ 3. *"Technical terms to translate? Format: RLS=data security, MFA=login security (blank to skip)"*
272
201
 
273
- Then write `.git-impact/context.json` and tell the user:
274
- *"Saved to `.git-impact/context.json`. Commit this to share the glossary with
275
- your team. history.db is gitignored automatically."*
202
+ Then call **`update_context`** with the answers. Tell the user:
203
+ *"Saved to `.git-impact/context.json`. Commit this to share the glossary with your team."*
204
+
205
+ If the install also wrote slash command files for other editors (Cursor,
206
+ Copilot, Gemini), point that out so they know the same workflow works
207
+ elsewhere.
276
208
 
277
209
  ---
278
210
 
@@ -280,6 +212,11 @@ your team. history.db is gitignored automatically."*
280
212
 
281
213
  - Write for a non-technical manager. No jargon that isn't in the glossary.
282
214
  - Short sentences. No filler. Skip "this change" / "this commit" constructions.
283
- - Confident — if you know the impact, state it. If not, use
284
- "technical foundation work for X", never hedge with "might potentially".
215
+ - Confident — if you know the impact, state it. If you guessed, label
216
+ `provenance: inferred` and use phrases like "technical foundation work
217
+ for X". Never hedge with "might potentially".
285
218
  - 2 accurate bullets beat 5 vague ones.
219
+
220
+ For prompt details, anti-patterns, and the full output format, see
221
+ `references/translation-rules.md`. For the bespoke daily HTML, see
222
+ `references/html-template.md`.