跪拜 Guibai
← Back to the summary

A Node Script That Writes Your Git Commits and Daily Standup Reports

Foreword

Last week, the group chat suddenly announced a performance review — everyone had to submit a daily report.

I have a pretty bad memory. By the end of the workday, I genuinely can't recall what I did all day. I could only open git log, flip through the commit history, copy-paste, and then polish it into "human language" that the boss could understand. Half an hour gone, just like that. I could have left on time, but ended up sitting there for another half hour.

And my commit messages weren't great either. After changing a bunch of files, my mind would go blank at the moment of committing, and I'd often just type update: code update and call it done. Even I'd be confused looking back at it later.

One day it suddenly hit me: All the information is right there in git log, isn't it? I just lack someone to summarize it for me.

The DeepSeek API is pretty cheap — one yuan per million tokens. So let it do the work. I spent an afternoon hacking together a small tool that does two things:

From then on, I never had to write them myself. Here's how I built it, along with the pitfalls I ran into.


1. Overall Structure

Just one git-hooks folder with two scripts:

Project Root/
├── git-hooks/
│   ├── commit.js             # For committing: AI generates message + my confirmation
│   ├── generate-report.js    # For daily report: AI reads commits and generates report
│   ├── package.json          # Manages node-fetch dependency separately
│   └── node_modules/
├── .git/hooks/
│   └── post-commit           # Automatically runs report script after commit
├── package.json              # Root, added an npm run commit script
└── daily-report.md           # Report output here (added to .gitignore)

Nothing fancy — two scripts, one hook, good enough.


2. The Commit Part: commit.js

Pitfalls I hit at the start

Initially, I wanted to use the prepare-commit-msg hook — AI generates the message, writes it directly into the commit message file, then Git opens the editor for me to modify. Sounded elegant, but it flopped immediately:

After messing around for a while, I switched approaches: Forget the hook, run the command manually, and confirm in the terminal.

git add .  →  AI generates message  →  Press Y/N in terminal  →  Commit

A bit crude, but reliable.

Implementation

The core code is just this; the rest is error handling:

const { execSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const readline = require('readline');
const fetch = require('node-fetch');

const AI_API_URL = 'https://api.deepseek.com/v1/chat/completions';
const AI_API_KEY = 'your-api-key';

function ask(rl, question) {
  return new Promise(resolve => {
    rl.question(question, answer => resolve(answer));
  });
}

async function main() {
  // 1. First, git add .
  execSync('git add .', { stdio: 'pipe' });

  // 2. Get diff — both overview and details for AI
  const diffStat = execSync('git diff --cached --stat', { encoding: 'utf-8' });
  const diffDetail = execSync('git diff --cached --unified=3', {
    encoding: 'utf-8',
    maxBuffer: 1024 * 1024 * 5
  });

  // 3. Feed to AI
  const prompt = `You are a professional developer. Based on the following Git code changes, generate a concise Chinese commit message.
Requirements:
1. Format: type: short description (e.g., "fix: xxx" / "feat: xxx" / "refactor: xxx")
2. Output only one line, no explanations
3. No more than 50 characters

Changed files overview:
${diffStat}

Code change details:
${diffDetail}
`;

  const response = await fetch(AI_API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${AI_API_KEY}`
    },
    body: JSON.stringify({
      model: 'deepseek-chat',
      messages: [{ role: 'user', content: prompt }],
      max_tokens: 100,
      temperature: 0.3
    })
  });
  const data = await response.json();
  const aiMsg = data.choices[0].message.content.trim().replace(/^["']|["']$/g, '');

  // 4. Terminal confirmation
  console.log('AI Generated: ' + aiMsg);
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
  const choice = await ask(rl, '[Y]Use / [N]Modify / [Other]Cancel: ');

  let finalMsg = aiMsg;
  if (choice.toLowerCase() === 'n') {
    finalMsg = (await ask(rl, 'Edit: ')).trim();
  } else if (choice.toLowerCase() !== 'y' && choice !== '') {
    rl.close(); return;
  }
  rl.close();

  // 5. Commit
  const tmpFile = path.join(os.tmpdir(), `git-msg-${Date.now()}.txt`);
  fs.writeFileSync(tmpFile, finalMsg, 'utf-8');
  execSync(`git commit -F "${tmpFile}"`, { stdio: 'inherit' });
  fs.unlinkSync(tmpFile);
}

main();

A few details

Why use git commit -F instead of -m?

If the message contains a quote or a $ sign, -m "${msg}" blows up. Writing to a temp file and reading with -F is the safest bet:

// Don't do this — if the message contains quotes, it's game over
execSync(`git commit -m "${finalMsg}"`);

// Do this — no special character can hurt you
fs.writeFileSync(tmpFile, finalMsg, 'utf-8');
execSync(`git commit -F "${tmpFile}"`);

What does temperature: 0.3 mean?

A lower temperature makes the AI output more stable and prevents it from going rogue — like adding clickbait to a commit message or appending "The above is an AI-generated explanation."

What if the AI fails?

The API can hiccup or rate-limit; it shouldn't deadlock the commit flow. So in the catch block, it gracefully degrades to manual input:

catch (err) {
  console.error('AI died:', err.message);
  const rl = createRl();
  aiMsg = (await ask(rl, 'Write it yourself: ')).trim();
  rl.close();
}

A tool is meant to help, not become a burden.


3. The Daily Report Part: generate-report.js

The Idea

Every time git commit finishes, the post-commit hook runs this script in the background. It does three things:

  1. git log --since="midnight" fetches all of today's commits
  2. Asks AI to summarize them into a daily report
  3. Writes it into daily-report.md

Step 3 has a pitfall, which I'll single out.

Don't Append Duplicates for the Same Day (Key Point)

In the first version, I directly used fs.appendFileSync. Every commit appended one entry. The result: after 5 commits in a day, the report file had 5 entries for the same day, all with roughly the same content — handing that to the boss would get me yelled at.

Fix: Delete all old blocks for today first, then append a new one.

const reportPath = path.resolve(__dirname, '../daily-report.md');
const dateStr = new Date().toLocaleDateString('zh-CN'); // 2026/8/25
const newBlock = `\n\n## 📅 ${dateStr} Work Daily Report\n\n${aiReport}\n`;

// Read existing content, empty string if none
let oldContent = '';
try { oldContent = fs.readFileSync(reportPath, 'utf-8'); } catch (e) {}

// Match and delete all old blocks for today
// The / in the date needs escaping, add the g flag to match all (multiple old entries possible)
const escapedDate = dateStr.replace(/\//g, '\\/');
const todayBlockRegex = new RegExp(
  '\\n*## 📅 ' + escapedDate + '[\\s\\S]*?(?=\\n## 📅|$)', 'g'
);

const cleanedContent = oldContent.replace(todayBlockRegex, '');

if (cleanedContent !== oldContent) {
  // Old entries for today exist, clear them then append the new one
  fs.writeFileSync(reportPath, cleanedContent.trimEnd() + newBlock, 'utf-8');
} else {
  // No entry for today yet, just append
  fs.appendFileSync(reportPath, newBlock, 'utf-8');
}

Breaking down that regex: /\\n*## 📅 2026\\/8\\/25[\\s\\S]*?(?=\\n## 📅|$)/g

With this, no matter how many commits you make in a day, the report file always has just one entry, containing the full summary after the last commit.

Prompt

const prompt = `
You are a professional project manager. Based on the following Git commit records, please generate a work daily report for me.
Requirements:
1. Concise language, clear structure, divided into [Today's Completed] and [In Progress / Issues Encountered].
2. Merge similar commits; do not translate commit messages line by line.
3. Output only the Markdown formatted report content; do not output any explanatory fluff.

Git commit records are as follows:
${logs}
`;

The key is point 2 — don't translate line by line. Otherwise, the AI just rewrites 5 commit messages into 5 lines, which is no different from me copy-pasting. Letting it categorize on its own produces output that actually looks like a "report."


4. Hook Configuration

post-commit (Run in background, don't block)

#!/bin/sh
node "git-hooks/generate-report.js" > git-hooks/report.log 2>&1 &

That trailing & is a must. I didn't add it at first, and every git commit had to wait for the AI API to return before completing. The lag made me want to smash my keyboard. Adding & runs it in the background — the commit finishes instantly, and the report generates at its own pace.

Add a line to package.json

{
  "scripts": {
    "commit": "node git-hooks/commit.js"
  }
}

From now on, committing is just npm run commit.

Don't forget .gitignore

# Daily report is private, don't let it into the codebase
daily-report.md
git-hooks/report.log

5. What It Looks Like in Practice

Committing

$ npm run commit

📦 Staging changes (git add .)...
🤖 AI is generating commit message...

──────────────────────────────────────────
📝 AI generated commit message:
   feat: Add AI-assisted commit tool and daily report update
──────────────────────────────────────────

Use it? [Y]Use / [N]Modify / [Other key]Cancel: y

[main 6f663da] feat: Add AI-assisted commit tool and daily report update
 4 files changed, 165 insertions(+), 4 deletions(-)
✅ Commit successful

Daily Report

## 📅 2026/8/25 Work Daily Report

## Today's Completed
- Developed and introduced an AI-assisted commit tool, supporting automatic change staging and linked daily report updates, improving commit efficiency
- Refactored daily report generation logic, perfected province mapping relationships, optimized generation accuracy

## In Progress / Issues Encountered
- There were multiple scattered code updates during the period, with non-standard commit messages, gradually improving commit quality through tooling

No matter how many commits, the daily report always has just one entry, containing the full summary of the day.


6. Pitfalls Encountered

Pitfall 1: Truncated API URL

// ❌ Calling this returns webpage HTML, not JSON
const AI_API_URL = 'https://api.deepseek.com';

// ✅ Must use the full endpoint URL
const AI_API_URL = 'https://api.deepseek.com/v1/chat/completions';

The error invalid json response body occurs because the request hits the root path, and the server returns a webpage.

Pitfall 2: post-commit Blocking the Commit

# ❌ Runs in foreground, commit waits for AI to return
node "git-hooks/generate-report.js"

# ✅ Runs in background, commit finishes instantly
node "git-hooks/generate-report.js" > git-hooks/report.log 2>&1 &

Pitfall 3: No chmod on Windows

I wanted to add execute permissions to the hook file, but PowerShell simply doesn't have that command. I later learned that on Windows, as long as the Git hook file exists and is accessible, it executes automatically — no extra permissions needed.

Pitfall 4: prepare-commit-msg + Editor

As mentioned earlier, after the hook writes the message, it tells Git to open the editor for me to modify — the editor path was wrong, the IDE panel didn't trigger the editor, nothing worked. In the end, I ditched the hook and switched to manual execution + terminal readline interaction, which was the least hassle.

Pitfall 5: Duplicate Daily Report Appends

The first version appended one entry per commit. Five commits a day meant five duplicate reports. Later, I used a regex with the g flag to first clear all old blocks for today, then append the new one. Problem solved.


7. How Much Did It Cost

DeepSeek pricing: Input 0.001 yuan per thousand tokens, Output 0.002 yuan per thousand tokens.

Crunching the numbers, generating one commit message uses about 2000 tokens, costing 0.004 yuan. One daily report uses about 500 tokens, costing 0.001 yuan.

Committing 10 times a day + generating 10 reports, totals 5 cents. One and a half yuan a month. Cheaper than a bottle of water.


8. Final Words

The core of this tool boils down to one sentence:

Everything is already in git diff and git log; AI is just a translator turning it into human language.

So the focus isn't on how powerful the AI is, but on:

The code is all pasted above. Change the API_KEY and you're good to go. Register for a key at platform.deepseek.com, top up one yuan, and it'll run for a long time.

The boss wants daily reports? Let the AI write them. I'm heading out first.