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.
- package/README.md +125 -78
- package/dist/cli/index.js +32 -211
- package/dist/cli/index.js.map +1 -1
- package/dist/init/installer.d.ts +6 -0
- package/dist/init/installer.d.ts.map +1 -1
- package/dist/init/installer.js +58 -5
- package/dist/init/installer.js.map +1 -1
- package/dist/init/installer.test.d.ts +2 -0
- package/dist/init/installer.test.d.ts.map +1 -0
- package/dist/init/installer.test.js +127 -0
- package/dist/init/installer.test.js.map +1 -0
- package/dist/init/templates.d.ts +8 -7
- package/dist/init/templates.d.ts.map +1 -1
- package/dist/init/templates.js +73 -371
- package/dist/init/templates.js.map +1 -1
- package/dist/mcp/repo.d.ts +8 -3
- package/dist/mcp/repo.d.ts.map +1 -1
- package/dist/mcp/repo.js +27 -9
- package/dist/mcp/repo.js.map +1 -1
- package/dist/mcp/repo.test.d.ts +2 -0
- package/dist/mcp/repo.test.d.ts.map +1 -0
- package/dist/mcp/repo.test.js +133 -0
- package/dist/mcp/repo.test.js.map +1 -0
- package/dist/mcp/resources.js +0 -1
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +100 -12
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/tools.test.d.ts +2 -0
- package/dist/mcp/tools.test.d.ts.map +1 -0
- package/dist/mcp/tools.test.js +123 -0
- package/dist/mcp/tools.test.js.map +1 -0
- package/dist/readers/git.d.ts +2 -1
- package/dist/readers/git.d.ts.map +1 -1
- package/dist/readers/git.js +12 -6
- package/dist/readers/git.js.map +1 -1
- package/dist/readers/redact.d.ts +29 -0
- package/dist/readers/redact.d.ts.map +1 -0
- package/dist/readers/redact.js +82 -0
- package/dist/readers/redact.js.map +1 -0
- package/dist/readers/redact.test.d.ts +2 -0
- package/dist/readers/redact.test.d.ts.map +1 -0
- package/dist/readers/redact.test.js +72 -0
- package/dist/readers/redact.test.js.map +1 -0
- package/dist/report/html.d.ts +2 -0
- package/dist/report/html.d.ts.map +1 -1
- package/dist/report/html.js +35 -1
- package/dist/report/html.js.map +1 -1
- package/dist/report/render.d.ts.map +1 -1
- package/dist/report/render.js +2 -0
- package/dist/report/render.js.map +1 -1
- package/dist/storage/db.d.ts +23 -2
- package/dist/storage/db.d.ts.map +1 -1
- package/dist/storage/db.js +12 -0
- package/dist/storage/db.js.map +1 -1
- package/dist/storage/db.test.d.ts +2 -0
- package/dist/storage/db.test.d.ts.map +1 -0
- package/dist/storage/db.test.js +152 -0
- package/dist/storage/db.test.js.map +1 -0
- package/package.json +7 -6
- package/skill/SKILL.md +142 -205
- package/skill/references/html-template.md +131 -0
- package/skill/references/translation-rules.md +87 -0
- package/dist/translator/prompt.d.ts +0 -21
- package/dist/translator/prompt.d.ts.map +0 -1
- package/dist/translator/prompt.js +0 -117
- package/dist/translator/prompt.js.map +0 -1
- package/dist/translator/translate.d.ts +0 -36
- package/dist/translator/translate.d.ts.map +0 -1
- package/dist/translator/translate.js +0 -73
- 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
|
|
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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
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
|
-
|
|
18
|
-
updates, and performance
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
31
|
+
## Pick a mode from the user's message
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
47
|
+
If the message is ambiguous, default to **standup**.
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
-
##
|
|
51
|
+
## Mode: standup
|
|
52
52
|
|
|
53
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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
|
-
###
|
|
78
|
+
### 3. Optional: enrich with GitHub PR data
|
|
104
79
|
|
|
105
|
-
If
|
|
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
|
-
|
|
86
|
+
### 4. Translate
|
|
108
87
|
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
"technical foundation work for [area]" rather than inventing an outcome.
|
|
99
|
+
### 5. Print the user-facing output
|
|
119
100
|
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
```
|
|
102
|
+
📅 [Day, Date or date range]
|
|
122
103
|
|
|
123
|
-
|
|
124
|
-
|
|
104
|
+
✅ [Summary]
|
|
105
|
+
→ [Why it matters]
|
|
125
106
|
|
|
126
|
-
|
|
127
|
-
|
|
107
|
+
⏳ In progress: [What]
|
|
108
|
+
→ [Expected outcome]
|
|
128
109
|
|
|
129
|
-
|
|
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
|
-
|
|
135
|
-
→ [Why it matters — who it unblocks, what risk it reduces, what it enables]
|
|
117
|
+
### 6. Save to history
|
|
136
118
|
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
###
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
|
145
|
+
🎯 file:///<absolute-repo-path>/.git-impact/result.html
|
|
214
146
|
```
|
|
215
147
|
|
|
216
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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
|
-
•
|
|
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
|
|
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. *"
|
|
200
|
+
3. *"Technical terms to translate? Format: RLS=data security, MFA=login security (blank to skip)"*
|
|
272
201
|
|
|
273
|
-
Then
|
|
274
|
-
*"Saved to `.git-impact/context.json`. Commit this to share the glossary with
|
|
275
|
-
|
|
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
|
|
284
|
-
|
|
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`.
|