Skip to main content

Commit writer · auto-split commits from morning changes

Feed uncommitted git diff to Qwen3.8-flash; it auto-splits into multiple commits, writes Conventional Commits titles and bodies, and flags breaking changes.

Hand uncommitted multi-file diff to the model; let it decide how many commits to split, which files go where, and whether any change breaks callers.
Disclaimer: All content on this page is AI-generated, for reference only, and does not constitute any performance guarantee. Actual results and Credits consumption are subject to the console "Usage Details".
This walkthrough uses a multi-file diff mixing three unrelated changes (bug fix + new feature + cleanup): hand the diff to qwen3.8-flash, and it decides split granularity, writes Conventional Commits subject + body for each, flags breaking changes, and gives a PR title and pre-commit reminders. The diff includes 5 traps to test whether it reads the diff or guesses from function names. Call chain: qwen3.8-flash (uncommitted diff in one input → commit list + PR title + pre-commit reminders). Thinking mode off, short input, shorter output.

Steps: hand the diff to the model for splitting

Send the git diff content along with the prompt below to qwen3.8-flash. Writing commits is a several-times-a-day action; thinking off is recommended. The diff below is excerpted for readability:
You are a Git commit assistant. Below are uncommitted changes accumulated over a morning in my working tree.
Decide how many commits these changes should be split into, and write commit messages for each.

Return only a JSON object, no other text, with this fixed structure:

{
  "commits": [
    {
      "subject":  "Conventional Commits title, format type(scope): description, max 50 chars",
      "body":     "Explain why and impact, two or three sentences; if nothing to say, empty string",
      "files":    ["file paths belonging to this commit"],
      "breaking": true or false
    }
  ],
  "pr_title": "If these changes go in one PR, what's the PR title",
  "notes": ["things worth reminding me before committing; empty array if none"]
}

Requirements:
- Unrelated changes must be split; don't lump into one. Related changes should be grouped; don't over-split.
- subject should state what behavior changed; don't write "update code" or "modify files"—zero information.
- body should explain why and who's affected; don't restate the diff line by line.
- If a change breaks callers (function signature changed, return value changed, incompatible behavior),
  set breaking to true and state in body how callers should adapt.
- Judge based on actual diff content, not function or file names.

Changes to commit:
diff --git a/src/service/user_service.py b/src/service/user_service.py
@@ -45,7 +45,10 @@ class UserService:
     def get_profile(self, uid):
         raw = self.cache.get(uid)
-        return json.loads(raw)
+        if raw is None:
+            return None
+        return json.loads(raw)
 
@@ -78,9 +81,9 @@ class UserService:
-    def validateCache(self, uid):
-        ts = self.cache.ttl(uid)
-        return ts > 0
+    def validateCache(self, uid):
+        ts = self.cache.ttl(uid)
+        # originally only checked if ttl is positive; cross-day keys would be judged valid
+        return ts > 0 and (time.time() - self.cache.mtime(uid)) < 86400

diff --git a/src/api/export.py b/src/api/export.py
@@ -0,0 +1,24 @@
+import csv
+from io import StringIO
+
+@app.route("/api/users/export")
+def export_users():
+    """Export filtered user list as CSV"""
+    rows = UserService().query(request.args)
+    buf = StringIO()
+    w = csv.writer(buf)
+    w.writerow(["uid", "name", "created_at", "status"])
+    for r in rows:
+        w.writerow([r.uid, r.name, r.created_at, r.status])
+    # TODO: add tests later
+    return Response(buf.getvalue(), mimetype="text/csv")

diff --git a/src/service/exporter.py b/src/service/exporter.py
@@ -12,8 +12,8 @@ class UserExporter:
-    def export(self, rows, encoding, with_header):
-        out = self._render(rows, encoding, with_header)
+    def export(self, rows, with_header):
+        out = self._render(rows, "utf-8", with_header)
         return out

diff --git a/src/api/handler.py b/src/api/handler.py
@@ -33,7 +33,6 @@ def handle_request(req):
     ctx = build_context(req)
-    print("DEBUG ctx:", ctx)
     return dispatch(ctx)

diff --git a/README.md b/README.md
@@ -8,7 +8,7 @@
-This project requires Python 3.9+. Please creat a virtual environment before instaling.
+This project requires Python 3.9+. Please create a virtual environment before installing.

diff --git a/requirements.txt b/requirements.txt
@@ -3,4 +3,4 @@ flask==3.0.0
-redis==4.5.1
+redis==5.0.1
 requests==2.31.0
Key parameters:
{
  "model": "qwen3.8-flash",
  "messages": [{"role": "user", "content": "…git diff body…"}],
  "enable_thinking": false
}
Under OpenAI-compatible mode, enable_thinking goes at the top level of the request body.

Notes

  • Consumption: This playbook uses text generation (qwen3.8-flash, thinking off). Both input and output are short—the lowest cost and fastest of the 7 playbooks in this batch.
  • Measured result: 6 files of changes split into 5 commits; file attribution all correct. The 5 preset traps all handled correctly—three typical ones: UserExporter.export() parameter removal flagged as breaking with caller adaptation instructions; function named validateCache but actually changes expiry-time calculation—it read the diff, not the name; # TODO: add tests later in the diff wasn't copied into a commit message, but promoted to a pre-commit reminder.
  • Note: The prompt asks for subject ≤ 50 chars; 2 of 5 exceeded. Hard constraints like char count should be re-checked in a commit hook.
  • Note: The diff shown here is excerpted; when running it yourself, input length will differ from the tested round, and split results may vary.
Model Playground
Fine-tuning
Model Compression
Statistics and Monitoring
Support